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.
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. */
26 #include <sys/types.h>
40 #include "cons.saver.h"
41 #include "../vfs/vfs.h"
43 #ifdef HAVE_TEXTMODE_X11_SUPPORT
46 #endif /* HAVE_GMODULE */
51 # if defined(__GLIBC__) && (__GLIBC__ < 2)
52 # include <linux/termios.h> /* TIOCLINUX */
56 # include <sys/ioctl.h>
57 #endif /* __linux__ */
62 # include <sys/dcmd_chr.h>
65 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
66 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
67 (t2.tv_usec-t1.tv_usec)/1000)
69 /* timeout for old_esc_mode in usec */
70 #define ESCMODE_TIMEOUT 1000000
72 /* Linux console keyboard modifiers */
73 #define SHIFT_PRESSED 1
74 #define ALTR_PRESSED 2
75 #define CONTROL_PRESSED 4
76 #define ALTL_PRESSED 8
79 int mou_auto_repeat
= 100;
80 int double_click_speed
= 250;
83 int use_8th_bit_as_meta
= 1;
85 typedef struct key_def
{
86 char ch
; /* Holds the matching char code */
87 int code
; /* The code returned, valid if child == NULL */
89 struct key_def
*child
; /* sequence continuation */
90 int action
; /* optional action to be done. Now used only
91 to mark that we are just after the first
95 /* This holds all the key definitions */
96 static key_def
*keys
= 0;
99 static int disabled_channels
= 0; /* Disable channels checking */
100 static int xgetch_second (void);
101 static int get_modifier (void);
103 /* File descriptor monitoring add/remove routines */
104 typedef struct SelectList
{
108 struct SelectList
*next
;
112 typedef int (*ph_dv_f
) (void *, void *);
113 typedef int (*ph_ov_f
) (void *);
114 typedef int (*ph_pqc_f
) (unsigned short, PhCursorInfo_t
*);
116 ph_ov_f ph_input_group
;
117 ph_pqc_f ph_query_cursor
;
120 static SelectList
*select_list
= 0;
122 void add_select_channel (int fd
, select_fn callback
, void *info
)
126 new = g_new (SelectList
, 1);
128 new->callback
= callback
;
130 new->next
= select_list
;
134 void delete_select_channel (int fd
)
136 SelectList
*p
= select_list
;
137 SelectList
*p_prev
= 0;
145 p_prev
->next
= p_next
;
147 select_list
= p_next
;
159 inline static int add_selects (fd_set
*select_set
)
164 if (disabled_channels
)
167 for (p
= select_list
; p
; p
= p
->next
){
168 FD_SET (p
->fd
, select_set
);
175 static void check_selects (fd_set
*select_set
)
179 if (disabled_channels
)
182 for (p
= select_list
; p
; p
= p
->next
)
183 if (FD_ISSET (p
->fd
, select_set
))
184 (*p
->callback
)(p
->fd
, p
->info
);
187 void channels_down (void)
189 disabled_channels
++;
192 void channels_up (void)
194 if (!disabled_channels
)
195 fputs ("Error: channels_up called with disabled_channels = 0\n",
200 typedef const struct {
206 static key_define_t mc_bindings
[] = {
207 { KEY_END
, ESC_STR
">", MCKEY_NOACTION
},
208 { KEY_HOME
, ESC_STR
"<", MCKEY_NOACTION
},
209 { 0, 0, MCKEY_NOACTION
},
212 /* Broken terminfo and termcap databases on xterminals */
213 static key_define_t xterm_key_defines
[] = {
214 { KEY_F(1), ESC_STR
"OP", MCKEY_NOACTION
},
215 { KEY_F(2), ESC_STR
"OQ", MCKEY_NOACTION
},
216 { KEY_F(3), ESC_STR
"OR", MCKEY_NOACTION
},
217 { KEY_F(4), ESC_STR
"OS", MCKEY_NOACTION
},
218 { KEY_F(1), ESC_STR
"[11~", MCKEY_NOACTION
},
219 { KEY_F(2), ESC_STR
"[12~", MCKEY_NOACTION
},
220 { KEY_F(3), ESC_STR
"[13~", MCKEY_NOACTION
},
221 { KEY_F(4), ESC_STR
"[14~", MCKEY_NOACTION
},
222 { KEY_F(5), ESC_STR
"[15~", MCKEY_NOACTION
},
223 { KEY_F(6), ESC_STR
"[17~", MCKEY_NOACTION
},
224 { KEY_F(7), ESC_STR
"[18~", MCKEY_NOACTION
},
225 { KEY_F(8), ESC_STR
"[19~", MCKEY_NOACTION
},
226 { KEY_F(9), ESC_STR
"[20~", MCKEY_NOACTION
},
227 { KEY_F(10), ESC_STR
"[21~", MCKEY_NOACTION
},
229 /* xterm keys with modifiers */
230 { KEY_M_SHIFT
| KEY_UP
, ESC_STR
"O2A", MCKEY_NOACTION
},
231 { KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"O2B", MCKEY_NOACTION
},
232 { KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"O2C", MCKEY_NOACTION
},
233 { KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"O2D", MCKEY_NOACTION
},
234 { KEY_M_CTRL
| KEY_PPAGE
, ESC_STR
"[5;5~", MCKEY_NOACTION
},
235 { KEY_M_CTRL
| KEY_NPAGE
, ESC_STR
"[6;5~", MCKEY_NOACTION
},
236 { KEY_M_CTRL
| KEY_IC
, ESC_STR
"[2;5~", MCKEY_NOACTION
},
237 { KEY_M_CTRL
| KEY_DC
, ESC_STR
"[3;5~", MCKEY_NOACTION
},
239 /* rxvt keys with modifiers */
240 { KEY_M_SHIFT
| KEY_UP
, ESC_STR
"[a", MCKEY_NOACTION
},
241 { KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"[b", MCKEY_NOACTION
},
242 { KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"[c", MCKEY_NOACTION
},
243 { KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"[d", MCKEY_NOACTION
},
244 { KEY_M_CTRL
| KEY_PPAGE
, ESC_STR
"[5^", MCKEY_NOACTION
},
245 { KEY_M_CTRL
| KEY_NPAGE
, ESC_STR
"[6^", MCKEY_NOACTION
},
246 { KEY_M_CTRL
| KEY_HOME
, ESC_STR
"[7^", MCKEY_NOACTION
},
247 { KEY_M_CTRL
| KEY_END
, ESC_STR
"[8^", MCKEY_NOACTION
},
248 { KEY_M_SHIFT
| KEY_HOME
, ESC_STR
"[7$", MCKEY_NOACTION
},
249 { KEY_M_SHIFT
| KEY_END
, ESC_STR
"[8$", MCKEY_NOACTION
},
250 { KEY_M_CTRL
| KEY_IC
, ESC_STR
"[2^", MCKEY_NOACTION
},
251 { KEY_M_CTRL
| KEY_DC
, ESC_STR
"[3^", MCKEY_NOACTION
},
252 { KEY_M_SHIFT
| KEY_DC
, ESC_STR
"[3$", MCKEY_NOACTION
},
254 /* konsole keys with modifiers */
255 { KEY_M_SHIFT
| KEY_HOME
, ESC_STR
"O2H", MCKEY_NOACTION
},
256 { KEY_M_SHIFT
| KEY_END
, ESC_STR
"O2F", MCKEY_NOACTION
},
257 { KEY_M_SHIFT
| KEY_DC
, ESC_STR
"[3;2~", MCKEY_NOACTION
},
259 { 0, 0, MCKEY_NOACTION
},
262 static key_define_t mc_default_keys
[] = {
263 { ESC_CHAR
, ESC_STR
, MCKEY_ESCAPE
},
264 { ESC_CHAR
, ESC_STR ESC_STR
, MCKEY_NOACTION
},
265 { 0, 0, MCKEY_NOACTION
},
269 define_sequences (key_define_t
*kd
)
273 for (i
= 0; kd
[i
].code
; i
++)
274 define_sequence(kd
[i
].code
, kd
[i
].seq
, kd
[i
].action
);
277 #ifdef HAVE_TEXTMODE_X11_SUPPORT
280 static int (*func_XCloseDisplay
) (Display
*);
281 static Bool (*func_XQueryPointer
) (Display
*, Window
, Window
*, Window
*,
282 int *, int *, int *, int *,
285 static GModule
*x11_module
;
286 #endif /* HAVE_GMODULE */
288 static Display
*x11_display
;
289 static Window x11_window
;
295 static Display
*(*func_XOpenDisplay
) (_Xconst
char *);
296 gchar
*x11_module_fname
;
297 #endif /* HAVE_GMODULE */
299 if (!getenv ("DISPLAY"))
303 x11_module_fname
= g_module_build_path (NULL
, "X11");
305 if (!x11_module_fname
)
308 x11_module
= g_module_open (x11_module_fname
, G_MODULE_BIND_LAZY
);
309 g_free (x11_module_fname
);
315 (x11_module
, "XOpenDisplay", (gpointer
*) & func_XOpenDisplay
)
316 && g_module_symbol (x11_module
, "XCloseDisplay",
317 (gpointer
*) & func_XCloseDisplay
)
318 && g_module_symbol (x11_module
, "XQueryPointer",
319 (gpointer
*) & func_XQueryPointer
)) {
320 x11_display
= (*func_XOpenDisplay
) (0);
323 x11_display
= XOpenDisplay (0);
324 #endif /* HAVE_GMODULE */
327 x11_window
= DefaultRootWindow (x11_display
);
329 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
332 /* This has to be called before slang_init or whatever routine
333 calls any define_sequence */
336 char *term
= (char *) getenv ("TERM");
338 /* This has to be the first define_sequence */
339 /* So, we can assume that the first keys member has ESC */
340 define_sequences (mc_default_keys
);
342 /* Terminfo on irix does not have some keys */
343 if ((!strncmp (term
, "iris-ansi", 9)) || (!strncmp (term
, "xterm", 5)))
344 define_sequences (xterm_key_defines
);
346 define_sequences (mc_bindings
);
348 /* load some additional keys (e.g. direct Alt-? support) */
349 load_xtra_key_defines();
352 if (strncmp(term
, "qnx", 3) == 0){
353 /* Modify the default value of use_8th_bit_as_meta: we would
354 * like to provide a working mc for a newbie who knows nothing
355 * about [Options|Display bits|Full 8 bits input]...
357 * Don't use 'meta'-bit, when we are dealing with a
358 * 'qnx*'-type terminal: clear the default value!
359 * These terminal types use 0xFF as an escape character,
360 * so use_8th_bit_as_meta==1 must not be enabled!
362 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
363 * is not used now (doesn't even depend on use_8th_bit_as_meta
364 * as in mc-3.1.2)...GREAT!...no additional code is required!]
366 use_8th_bit_as_meta
= 0;
370 #ifdef HAVE_TEXTMODE_X11_SUPPORT
372 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
375 /* This has to be called after SLang_init_tty/slint_init */
376 void init_key_input_fd (void)
379 input_fd
= SLang_TT_Read_FD
;
385 xmouse_get_event (Gpm_Event
*ev
)
388 static struct timeval tv1
= { 0, 0 }; /* Force first click as single */
389 static struct timeval tv2
;
391 static int last_btn
= 0;
393 /* Decode Xterm mouse information to a GPM style event */
395 /* Variable btn has following meaning: */
396 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
399 /* There seems to be no way of knowing which button was released */
400 /* So we assume all the buttons were released */
404 ev
->type
= GPM_UP
| (GPM_SINGLE
<< clicks
);
410 /* Bogus event, maybe mouse wheel */
416 if (tv1
.tv_sec
&& (DIF_TIME (tv1
,tv2
) < double_click_speed
)){
424 ev
->buttons
= GPM_B_LEFT
;
427 ev
->buttons
= GPM_B_MIDDLE
;
430 ev
->buttons
= GPM_B_RIGHT
;
433 ev
->buttons
= GPM_B_UP
;
436 ev
->buttons
= GPM_B_DOWN
;
444 last_btn
= ev
->buttons
;
446 /* Coordinates are 33-based */
447 /* Transform them to 1-based */
448 ev
->x
= getch () - 32;
449 ev
->y
= getch () - 32;
452 static key_def
*create_sequence (char *seq
, int code
, int action
)
454 key_def
*base
, *p
, *attach
;
456 for (base
= attach
= NULL
; *seq
; seq
++){
457 p
= g_new (key_def
, 1);
459 if (attach
) attach
->child
= p
;
463 p
->child
= p
->next
= NULL
;
467 p
->action
= MCKEY_NOACTION
;
473 /* The maximum sequence length (32 + null terminator) */
474 #define SEQ_BUFFER_LEN 33
475 static int seq_buffer
[SEQ_BUFFER_LEN
];
476 static int *seq_append
= 0;
478 static int push_char (int c
)
481 seq_append
= seq_buffer
;
483 if (seq_append
== &(seq_buffer
[SEQ_BUFFER_LEN
-2]))
491 * Return 1 on success, 0 on error.
492 * An error happens if SEQ is a beginning of an existing longer sequence.
494 int define_sequence (int code
, char *seq
, int action
)
498 if (strlen (seq
) > SEQ_BUFFER_LEN
-1)
501 for (base
= keys
; (base
!= 0) && *seq
; ){
502 if (*seq
== base
->ch
){
503 if (base
->child
== 0){
505 base
->child
= create_sequence (seq
+1, code
, action
);
508 /* The sequence matches an existing one. */
510 base
->action
= action
;
521 base
->next
= create_sequence (seq
, code
, action
);
528 /* Attempt to redefine a sequence with a shorter sequence. */
532 keys
= create_sequence (seq
, code
, action
);
536 static int *pending_keys
;
538 /* Apply corrections for the keycode generated in get_key_code() */
540 correct_key_code (int code
)
542 unsigned int c
= code
& ~KEY_M_MASK
; /* code without modifier */
543 unsigned int mod
= code
& KEY_M_MASK
; /* modifier */
546 * Add key modifiers directly from X11 or OS.
547 * Ordinary characters only get modifiers from sequences.
549 if (c
< 32 || c
>= 256) {
550 mod
|= get_modifier ();
553 /* This is needed if the newline is reported as carriage return */
557 /* This is reported to be useful on AIX */
558 if (c
== KEY_SCANCEL
)
561 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
562 if ((c
== '\t') && (mod
& (KEY_M_SHIFT
| KEY_M_CTRL
))) {
567 /* F0 is the same as F10 for out purposes */
572 * We are not interested if Ctrl was pressed when entering control
573 * characters, so assume that it was. When checking for such keys,
574 * XCTRL macro should be used. In some cases, we are interested,
575 * e.g. to distinguish Ctrl-Enter from Enter.
577 if (c
< 32 && c
!= ESC_CHAR
&& c
!= '\t' && c
!= '\n') {
581 /* Unrecognized 0177 is delete (preserve Ctrl) */
586 /* Unrecognized Ctrl-d is delete */
587 if (c
== (31 & 'd')) {
592 /* Unrecognized Ctrl-h is backspace */
593 if (c
== (31 & 'h')) {
598 /* Convert Shift+Fn to F(n+10) */
599 if (c
>= KEY_F (1) && c
<= KEY_F (10) && (mod
& KEY_M_SHIFT
)) {
603 /* Remove Shift information from function keys */
604 if (c
>= KEY_F (1) && c
<= KEY_F (20)) {
608 if (!alternate_plus_minus
)
613 case KEY_KP_SUBTRACT
:
616 case KEY_KP_MULTIPLY
:
624 int get_key_code (int no_delay
)
627 static key_def
*this = NULL
, *parent
;
628 static struct timeval esctime
= { -1, -1 };
629 static int lastnodelay
= -1;
631 if (no_delay
!= lastnodelay
) {
633 lastnodelay
= no_delay
;
638 int d
= *pending_keys
++;
644 if (d
== ESC_CHAR
&& pending_keys
){
645 d
= ALT(*pending_keys
++);
648 if ((d
& 0x80) && use_8th_bit_as_meta
)
651 return correct_key_code (d
);
656 nodelay (stdscr
, TRUE
);
659 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
661 goto nodelay_try_again
;
664 nodelay (stdscr
, FALSE
);
666 if (this != NULL
&& parent
!= NULL
&&
667 parent
->action
== MCKEY_ESCAPE
&& old_esc_mode
) {
668 struct timeval current
, timeout
;
670 if (esctime
.tv_sec
== -1)
673 timeout
.tv_sec
= ESCMODE_TIMEOUT
/ 1000000 + esctime
.tv_sec
;
674 timeout
.tv_usec
= ESCMODE_TIMEOUT
% 1000000 + esctime
.tv_usec
;
675 if (timeout
.tv_usec
> 1000000) {
676 timeout
.tv_usec
-= 1000000;
679 if (current
.tv_sec
< timeout
.tv_sec
)
681 if (current
.tv_sec
== timeout
.tv_sec
&&
682 current
.tv_usec
< timeout
.tv_usec
)
685 pending_keys
= seq_append
= NULL
;
691 /* Maybe we got an incomplete match.
692 This we do only in delay mode, since otherwise
693 getch can return -1 at any time. */
695 pending_keys
= seq_buffer
;
702 /* Search the key on the root */
703 if (!no_delay
|| this == NULL
) {
707 if ((c
& 0x80) && use_8th_bit_as_meta
) {
710 /* The first sequence defined starts with esc */
719 pending_keys
= seq_buffer
;
724 if (parent
->action
== MCKEY_ESCAPE
&& old_esc_mode
) {
728 /* Shouldn't happen */
729 fputs ("Internal error\n", stderr
);
732 goto nodelay_try_again
;
735 c
= xgetch_second ();
737 pending_keys
= seq_append
= NULL
;
743 goto nodelay_try_again
;
747 /* We got a complete match, return and reset search */
750 pending_keys
= seq_append
= NULL
;
753 return correct_key_code (code
);
759 if (parent
!= NULL
&& parent
->action
== MCKEY_ESCAPE
) {
760 /* This is just to save a lot of define_sequences */
762 || (c
== '\n') || (c
== '\t') || (c
== (31 & 'h'))
763 || (c
== KEY_BACKSPACE
) || (c
== '!') || (c
== '\r')
764 || c
== 0177 || c
== '+' || c
== '-' || c
== '\\'
771 pending_keys
= seq_append
= NULL
;
773 return correct_key_code (c
);
775 /* Did not find a match or {c} was changed in the if above,
776 so we have to return everything we had skipped
779 pending_keys
= seq_buffer
;
785 return correct_key_code (c
);
788 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
790 try_channels (int set_timeout
)
792 struct timeval timeout
;
793 static fd_set select_set
;
794 struct timeval
*timeptr
;
799 FD_ZERO (&select_set
);
800 FD_SET (input_fd
, &select_set
); /* Add stdin */
801 maxfdp
= max (add_selects (&select_set
), input_fd
);
805 timeout
.tv_usec
= 100000;
810 v
= select (maxfdp
+ 1, &select_set
, NULL
, NULL
, timeptr
);
812 check_selects (&select_set
);
813 if (FD_ISSET (input_fd
, &select_set
))
819 /* Workaround for System V Curses vt100 bug */
820 static int getch_with_delay (void)
824 /* This routine could be used on systems without mouse support,
825 so we need to do the select check :-( */
830 /* Try to get a character */
831 c
= get_key_code (0);
834 /* Failed -> wait 0.1 secs and try again */
837 /* Success -> return the character */
841 /* Returns a character read from stdin with appropriate interpretation */
842 /* Also takes care of generated mouse events */
843 /* Returns EV_MOUSE if it is a mouse event */
844 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
846 get_event (Gpm_Event
* event
, int redo_event
, int block
)
849 static int flag
; /* Return value from select */
851 static Gpm_Event ev
; /* Mouse event */
853 struct timeval timeout
;
854 struct timeval
*time_addr
= NULL
;
855 static int dirty
= 3;
857 if ((dirty
== 3) || is_idle ()) {
864 vfs_timeout_handler ();
866 /* Ok, we use (event->x < 0) to signal that the event does not contain
867 a suitable position for the mouse, so we can't use show_mouse_pointer
871 show_mouse_pointer (event
->x
, event
->y
);
876 /* Repeat if using mouse */
877 while (mouse_enabled
&& !pending_keys
) {
881 FD_ZERO (&select_set
);
882 FD_SET (input_fd
, &select_set
);
883 maxfdp
= max (add_selects (&select_set
), input_fd
);
886 if (use_mouse_p
== MOUSE_GPM
) {
888 /* Connection to gpm broken, possibly gpm has died */
890 use_mouse_p
= MOUSE_NONE
;
893 FD_SET (gpm_fd
, &select_set
);
894 maxfdp
= max (maxfdp
, gpm_fd
);
899 timeout
.tv_usec
= mou_auto_repeat
* 1000;
902 time_addr
= &timeout
;
906 if ((seconds
= vfs_timeouts ())) {
907 /* the timeout could be improved and actually be
908 * the number of seconds until the next vfs entry
909 * timeouts in the stamp list.
912 timeout
.tv_sec
= seconds
;
914 time_addr
= &timeout
;
920 time_addr
= &timeout
;
924 enable_interrupt_key ();
925 flag
= select (maxfdp
+ 1, &select_set
, NULL
, NULL
, time_addr
);
926 disable_interrupt_key ();
928 /* select timed out: it could be for any of the following reasons:
929 * redo_event -> it was because of the MOU_REPEAT handler
930 * !block -> we did not block in the select call
931 * else -> 10 second timeout to check the vfs status.
938 vfs_timeout_handler ();
940 if (flag
== -1 && errno
== EINTR
)
943 check_selects (&select_set
);
945 if (FD_ISSET (input_fd
, &select_set
))
948 if (use_mouse_p
== MOUSE_GPM
&& FD_ISSET (gpm_fd
, &select_set
)) {
954 #endif /* !HAVE_LIBGPM */
957 flag
= is_wintouched (stdscr
);
959 #endif /* !HAVE_SLANG */
960 c
= block
? getch_with_delay () : get_key_code (1);
965 #endif /* !HAVE_SLANG */
970 #endif /* KEY_MOUSE */
973 xmouse_get_event (event
);
983 /* Returns a key press, mouse events are discarded */
990 while ((key
= get_event (&ev
, 0, 1)) == EV_NONE
)
995 static int xgetch_second (void)
999 struct timeval timeout
;
1001 timeout
.tv_sec
= ESCMODE_TIMEOUT
/ 1000000;
1002 timeout
.tv_usec
= ESCMODE_TIMEOUT
% 1000000;
1003 nodelay (stdscr
, TRUE
);
1004 FD_ZERO (&Read_FD_Set
);
1005 FD_SET (input_fd
, &Read_FD_Set
);
1006 select (input_fd
+ 1, &Read_FD_Set
, NULL
, NULL
, &timeout
);
1008 nodelay (stdscr
, FALSE
);
1013 learn_store_key (char *buffer
, char **p
, int c
)
1015 if (*p
- buffer
> 253)
1017 if (c
== ESC_CHAR
) {
1020 } else if (c
< ' ') {
1022 *(*p
)++ = c
+ 'a' - 1;
1023 } else if (c
== '^') {
1030 char *learn_key (void)
1032 /* LEARN_TIMEOUT in usec */
1033 #define LEARN_TIMEOUT 200000
1036 struct timeval endtime
;
1037 struct timeval timeout
;
1042 keypad(stdscr
, FALSE
); /* disable intepreting keys by ncurses */
1045 c
= getch (); /* Sanity check, should be unnecessary */
1046 learn_store_key (buffer
, &p
, c
);
1048 endtime
.tv_usec
+= LEARN_TIMEOUT
;
1049 if (endtime
.tv_usec
> 1000000) {
1050 endtime
.tv_usec
-= 1000000;
1053 nodelay (stdscr
, TRUE
);
1055 while ((c
= getch ()) == -1) {
1057 timeout
.tv_usec
= endtime
.tv_usec
- timeout
.tv_usec
;
1058 if (timeout
.tv_usec
< 0)
1060 timeout
.tv_sec
= endtime
.tv_sec
- timeout
.tv_sec
;
1061 if (timeout
.tv_sec
>= 0 && timeout
.tv_usec
> 0) {
1062 FD_ZERO (&Read_FD_Set
);
1063 FD_SET (input_fd
, &Read_FD_Set
);
1064 select (input_fd
+ 1, &Read_FD_Set
, NULL
, NULL
, &timeout
);
1070 learn_store_key (buffer
, &p
, c
);
1072 keypad(stdscr
, TRUE
);
1073 nodelay (stdscr
, FALSE
);
1075 return g_strdup (buffer
);
1078 /* xterm and linux console only: set keypad to numeric or application
1079 mode. Only in application keypad mode it's possible to distinguish
1080 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1082 numeric_keypad_mode (void)
1084 if (console_flag
|| xterm_flag
) {
1085 fputs ("\033>", stdout
);
1091 application_keypad_mode (void)
1093 if (console_flag
|| xterm_flag
) {
1094 fputs ("\033=", stdout
);
1101 * Check if we are idle, i.e. there are no pending keyboard or mouse
1102 * events. Return 1 is idle, 0 is there are pending events.
1109 struct timeval timeout
;
1111 FD_ZERO (&select_set
);
1112 FD_SET (input_fd
, &select_set
);
1115 if (use_mouse_p
== MOUSE_GPM
&& mouse_enabled
&& gpm_fd
!= -1) {
1116 FD_SET (gpm_fd
, &select_set
);
1117 maxfdp
= max (maxfdp
, gpm_fd
);
1121 timeout
.tv_usec
= 0;
1122 return (select (maxfdp
+ 1, &select_set
, 0, 0, &timeout
) <= 0);
1127 * Get modifier state (shift, alt, ctrl) for the last key pressed.
1128 * We are assuming that the state didn't change since the key press.
1129 * This is only correct if get_modifier() is called very fast after
1130 * the input was received, so that the user didn't release the
1131 * modifier keys yet.
1138 int mod_status
, shift_ext_status
;
1139 static int in_photon
= 0;
1140 static int ph_ig
= 0;
1141 char phlib_path
[PATH_MAX
];
1142 PhCursorInfo_t cursor_info
;
1143 static void *ph_handle
;
1145 #endif /* __QNXNTO__ */
1147 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1155 (*func_XQueryPointer
) (x11_display
, x11_window
, &root
, &child
,
1156 &root_x
, &root_y
, &win_x
, &win_y
, &mask
);
1158 XQueryPointer (x11_display
, x11_window
, &root
, &child
, &root_x
,
1159 &root_y
, &win_x
, &win_y
, &mask
);
1160 #endif /* HAVE_GMODULE */
1162 if (mask
& ShiftMask
)
1163 result
|= KEY_M_SHIFT
;
1164 if (mask
& ControlMask
)
1165 result
|= KEY_M_CTRL
;
1168 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1171 if (in_photon
== 0) {
1172 /* First time here, let's load Photon library and attach
1175 ph_env
= getenv ("PHOTON2_PATH");
1176 if (ph_env
!= NULL
) {
1177 g_snprintf (phlib_path
, sizeof (phlib_path
),
1178 "%s/lib/libph.so.1", ph_env
);
1179 /* QNX 6.x has no support for RTLD_LAZY */
1180 ph_handle
= dlopen (phlib_path
, RTLD_NOW
);
1181 if (ph_handle
!= NULL
) {
1182 ph_attach
= (ph_dv_f
) dlsym (ph_handle
, "PhAttach");
1184 (ph_ov_f
) dlsym (ph_handle
, "PhInputGroup");
1186 (ph_pqc_f
) dlsym (ph_handle
, "PhQueryCursor");
1187 if ((ph_attach
!= NULL
) && (ph_input_group
!= NULL
)
1188 && (ph_query_cursor
!= NULL
)) {
1189 if ((*ph_attach
) (0, 0)) { /* Attached */
1190 ph_ig
= (*ph_input_group
) (0);
1197 /* We do not have Photon running. Assume we are in text
1199 if (in_photon
== -1) {
1201 (fileno (stdin
), DCMD_CHR_LINESTATUS
, &mod_status
,
1202 sizeof (int), NULL
) == -1)
1204 shift_ext_status
= mod_status
& 0xffffff00UL
;
1206 if (mod_status
& _LINESTATUS_CON_ALT
)
1207 result
|= KEY_M_ALT
;
1208 if (mod_status
& _LINESTATUS_CON_CTRL
)
1209 result
|= KEY_M_CTRL
;
1210 if ((mod_status
& _LINESTATUS_CON_SHIFT
)
1211 || (shift_ext_status
& 0x00000800UL
))
1212 result
|= KEY_M_SHIFT
;
1214 (*ph_query_cursor
) (ph_ig
, &cursor_info
);
1215 if (cursor_info
.key_mods
& 0x04)
1216 result
|= KEY_M_ALT
;
1217 if (cursor_info
.key_mods
& 0x02)
1218 result
|= KEY_M_CTRL
;
1219 if (cursor_info
.key_mods
& 0x01)
1220 result
|= KEY_M_SHIFT
;
1222 #endif /* __QNXNTO__ */
1225 unsigned char modifiers
= 6;
1227 if (ioctl (0, TIOCLINUX
, &modifiers
) < 0)
1230 /* Translate Linux modifiers into mc modifiers */
1231 if (modifiers
& SHIFT_PRESSED
)
1232 result
|= KEY_M_SHIFT
;
1233 if (modifiers
& (ALTL_PRESSED
| ALTR_PRESSED
))
1234 result
|= KEY_M_ALT
;
1235 if (modifiers
& CONTROL_PRESSED
)
1236 result
|= KEY_M_CTRL
;
1242 #endif /* !__linux__ */
1245 static void k_dispose (key_def
*k
)
1249 k_dispose (k
->child
);
1250 k_dispose (k
->next
);
1254 static void s_dispose (SelectList
*sel
)
1259 s_dispose (sel
->next
);
1266 s_dispose (select_list
);
1268 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1271 (*func_XCloseDisplay
) (x11_display
);
1273 g_module_close (x11_module
);
1276 XCloseDisplay (x11_display
);
1277 #endif /* HAVE_GMODULE */
1278 #endif /* HAVE_TEXTMODE_X11_SUPPORT */