📄 key.c
字号:
/* Keyboard support routines.
Copyright (C) 1994,1995 the Free Software Foundation.
Written by: 1994, 1995 Miguel de Icaza.
1994, 1995 Janne Kukonlehto.
1995 Jakub Jelinek.
1997 Norbert Warmuth
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/types.h> /* FD_ZERO et al */
#ifndef SCO_FLAVOR
/* alex: sys/select.h defines struct timeval */
# include <sys/time.h> /* struct timeval */
#endif /* SCO_FLAVOR */
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#include "tty.h"
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include "util.h" /* For xmalloc prototype */
#include "mad.h" /* The memory debugger */
#include "global.h"
#include "mouse.h"
#include "key.h"
#include "main.h"
#include "file.h"
#include "win.h"
#include "cons.saver.h"
#include "../vfs/vfs.h"
#ifdef __linux__
# if defined(__GLIBC__) && (__GLIBC__ < 2)
# include <linux/termios.h> /* This is needed for TIOCLINUX */
# else
# include <termios.h>
# endif
# include <sys/ioctl.h>
#endif
#include "x.h"
/* "$Id: key.c 15091 2005-05-07 21:24:31Z sedwards $" */
/* This macros were stolen from gpm 0.15 */
#define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
#define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
(t2.tv_usec-t1.tv_usec)/1000)
/* timeout for old_esc_mode in usec */
#define ESCMODE_TIMEOUT 1000000
int mou_auto_repeat = 100;
int double_click_speed = 250;
int old_esc_mode = 0;
int use_8th_bit_as_meta = 1;
#ifndef HAVE_X
typedef struct key_def {
char ch; /* Holds the matching char code */
int code; /* The code returned, valid if child == NULL */
struct key_def *next;
struct key_def *child; /* sequence continuation */
int action; /* optional action to be done. Now used only
to mark that we are just after the first
Escape */
} key_def;
/* This holds all the key definitions */
static key_def *keys = 0;
#endif
static int input_fd;
static fd_set select_set;
static int disabled_channels = 0; /* Disable channels checking */
int xgetch_second (void);
#ifndef PORT_HAS_FILE_HANDLERS
/* File descriptor monitoring add/remove routines */
typedef struct SelectList {
int fd;
select_fn callback;
void *info;
struct SelectList *next;
} SelectList;
SelectList *select_list = 0;
void add_select_channel (int fd, select_fn callback, void *info)
{
SelectList *new;
new = xmalloc (sizeof (SelectList), "add_select_channel");
new->fd = fd;
new->callback = callback;
new->info = info;
new->next = select_list;
select_list = new;
}
void delete_select_channel (int fd)
{
SelectList *p = select_list;
SelectList *prev = 0;
while (p){
if (p->fd == fd){
if (prev)
prev->next = p->next;
else
select_list = p->next;
free (p);
}
prev = p;
p = p->next;
}
}
inline static int add_selects (fd_set *select_set)
{
SelectList *p;
int top_fd = 0;
if (disabled_channels)
return 0;
for (p = select_list; p; p = p->next){
FD_SET (p->fd, select_set);
if (p->fd > top_fd)
top_fd = p->fd;
}
return top_fd;
}
static void check_selects (fd_set *select_set)
{
SelectList *p;
if (disabled_channels)
return;
for (p = select_list; p; p = p->next)
if (FD_ISSET (p->fd, select_set))
(*p->callback)(p->fd, p->info);
}
#endif
void channels_down (void)
{
disabled_channels ++;
}
void channels_up (void)
{
if (!disabled_channels)
fprintf (stderr,
"Error: channels_up called with disabled_channels = 0\n");
disabled_channels--;
}
typedef struct {
int code;
char *seq;
int action;
} key_define_t;
#ifndef HAVE_X
key_define_t mc_bindings [] = {
{ KEY_END, ESC_STR ">", MCKEY_NOACTION },
{ KEY_HOME, ESC_STR "<", MCKEY_NOACTION },
#ifdef linux
/* Incredible, but many Linuxes still have old databases */
{ KEY_IC, ESC_STR "[2~", MCKEY_NOACTION },
#endif
{ 0, 0, MCKEY_NOACTION },
};
/* Broken terminfo and termcap databases on xterminals */
key_define_t xterm_key_defines [] = {
{ KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
{ KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
{ KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
{ KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
{ KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
{ KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
{ KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
{ KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
{ KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
{ KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
{ KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
{ KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
{ KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
{ KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
{ 0, 0, MCKEY_NOACTION },
};
key_define_t mc_default_keys [] = {
{ ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
{ ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
{ 0, 0, MCKEY_NOACTION },
};
#endif
void define_sequences (key_define_t *kd)
{
#ifndef HAVE_X
int i;
for (i = 0; kd [i].code; i++)
define_sequence(kd [i].code, kd [i].seq, kd [i].action);
#endif
}
/* This has to be called before slang_init or whatever routine
calls any define_sequence */
void init_key (void)
{
#ifndef HAVE_X
char *term = (char *) getenv ("TERM");
/* This has to be the first define_sequence */
/* So, we can assume that the first keys member has ESC */
define_sequences (mc_default_keys);
/* Terminfo on irix does not have some keys */
if ((!strncmp (term, "iris-ansi", 9)) || (!strncmp (term, "xterm", 5)))
define_sequences (xterm_key_defines);
define_sequences (mc_bindings);
/* load some additional keys (e.g. direct Alt-? support) */
load_xtra_key_defines();
#ifdef __QNX__
if (strncmp(term, "qnx", 3) == 0){
/* Modify the default value of use_8th_bit_as_meta: we would
* like to provide a working mc for a newbie who knows nothing
* about [Options|Display bits|Full 8 bits input]...
*
* Don't use 'meta'-bit, when we are dealing with a
* 'qnx*'-type terminal: clear the default value!
* These terminal types use 0xFF as an escape character,
* so use_8th_bit_as_meta==1 must not be enabled!
*
* [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
* is not used now (doesn't even depend on use_8th_bit_as_meta
* as in mc-3.1.2)...GREAT!...no additional code is required!]
*/
use_8th_bit_as_meta = 0;
}
#endif /* __QNX__ */
#endif /* !HAVE_X */
}
/* This has to be called after SLang_init_tty/slint_init */
void init_key_input_fd (void)
{
#ifndef HAVE_X
#ifdef HAVE_SLANG
input_fd = SLang_TT_Read_FD;
#endif
#endif /* !HAVE_X */
}
#ifndef HAVE_X
void xmouse_get_event (Gpm_Event *ev)
{
int btn;
static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
static struct timeval tv2;
static int clicks;
/* Decode Xterm mouse information to a GPM style event */
/* Variable btn has following meaning: */
/* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
btn = xgetch () - 32;
/* There seems to be no way of knowing which button was released */
/* So we assume all the buttons were released */
if (btn == 3){
ev->type = GPM_UP | (GPM_SINGLE << clicks);
ev->buttons = 0;
GET_TIME (tv1);
clicks = 0;
} else {
ev->type = GPM_DOWN;
GET_TIME (tv2);
if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
clicks++;
clicks %= 3;
} else
clicks = 0;
switch (btn) {
case 0:
ev->buttons = GPM_B_LEFT;
break;
case 1:
ev->buttons = GPM_B_MIDDLE;
break;
case 2:
ev->buttons = GPM_B_RIGHT;
break;
default:
/* Nothing */
break;
}
}
/* Coordinates are 33-based */
/* Transform them to 1-based */
ev->x = xgetch () - 32;
ev->y = xgetch () - 32;
}
static key_def *create_sequence (char *seq, int code, int action)
{
key_def *base, *p, *attach;
for (base = attach = NULL; *seq; seq++){
p = xmalloc (sizeof (key_def), "create_sequence");
if (!base) base = p;
if (attach) attach->child = p;
p->ch = *seq;
p->code = code;
p->child = p->next = NULL;
if (!seq[1])
p->action = action;
else
p->action = MCKEY_NOACTION;
attach = p;
}
return base;
}
/* The maximum sequence length (32 + null terminator) */
static int seq_buffer [33];
static int *seq_append = 0;
static int push_char (int c)
{
if (!seq_append)
seq_append = seq_buffer;
if (seq_append == &(seq_buffer [sizeof (seq_buffer)-2]))
return 0;
*(seq_append++) = c;
*seq_append = 0;
return 1;
}
#endif /* !HAVE_X */
void define_sequence (int code, char *seq, int action)
{
#ifndef HAVE_X
key_def *base;
if (strlen (seq) > sizeof (seq_buffer)-1)
return;
for (base = keys; (base != 0) && *seq; ){
if (*seq == base->ch){
if (base->child == 0){
if (*(seq+1)){
base->child = create_sequence (seq+1, code, action);
return;
} else {
/* The sequence clashes */
return;
}
} else {
base = base->child;
seq++;
}
} else {
if (base->next)
base = base->next;
else {
base->next = create_sequence (seq, code, action);
return;
}
}
}
keys = create_sequence (seq, code, action);
#endif
}
#ifndef HAVE_X
static int *pending_keys;
#endif
int correct_key_code (int c)
{
/* This is needed on some OS that do not support ncurses and */
/* do some magic after read()ing the data */
if (c == '\r')
return '\n';
#ifdef IS_AIX
if (c == KEY_SCANCEL)
return '\t';
#endif
if (c == KEY_F(0))
return KEY_F(10);
if (!alternate_plus_minus)
switch (c) {
case KEY_KP_ADD: c = '+'; break;
case KEY_KP_SUBTRACT: c = '-'; break;
case KEY_KP_MULTIPLY: c = '*'; break;
}
return c;
}
int get_key_code (int no_delay)
{
#ifndef HAVE_X
int c;
static key_def *this = NULL, *parent;
static struct timeval esctime = { -1, -1 };
static int lastnodelay = -1;
if (no_delay != lastnodelay) {
this = NULL;
lastnodelay = no_delay;
}
pend_send:
if (pending_keys){
int d = *pending_keys++;
check_pend:
if (!*pending_keys){
pending_keys = 0;
seq_append = 0;
}
if (d == ESC_CHAR && pending_keys){
d = ALT(*pending_keys++);
goto check_pend;
}
if ((d & 0x80) && use_8th_bit_as_meta)
d = ALT(d & 0x7f);
this = NULL;
return correct_key_code (d);
}
nodelay_try_again:
if (no_delay) {
#ifdef BUGGY_CURSES
wtimeout(stdscr, 500);
#else
nodelay (stdscr, TRUE);
#endif
}
c = xgetch ();
if (no_delay) {
#ifdef BUGGY_CURSES
notimeout (stdscr, TRUE);
#else
nodelay (stdscr, FALSE);
#endif
if (c == ERR) {
if (this != NULL && parent != NULL &&
parent->action == MCKEY_ESCAPE && old_esc_mode) {
struct timeval current, timeout;
if (esctime.tv_sec == -1)
return ERR;
GET_TIME (current);
timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
if (timeout.tv_usec > 1000000) {
timeout.tv_usec -= 1000000;
timeout.tv_sec++;
}
if (current.tv_sec < timeout.tv_sec)
return ERR;
if (current.tv_sec == timeout.tv_sec &&
current.tv_usec < timeout.tv_usec)
return ERR;
this = NULL;
pending_keys = seq_append = NULL;
return ESC_CHAR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -