键盘体系

来源:互联网 发布:源码怎么做成软件 编辑:程序博客网 时间:2024/05/12 07:04

从什么开始说比较合理呀?就从硬件开始把:严格来说称不上什么键盘体系,但由于键盘的driver code比较的涩晦,所以就称之为键盘体系了。

后注:什么叫后注?也就是写完后想说点什么的意思呀!这篇文挡太长了(本来想写的更长,真的,还有一些文件都没有写上去呀),大家还是用“文挡结构图”来看把,厉害把,这么多,全部手写呀。

硬件相关

硬件,其实有一些内容,但我实在不想一段段的翻译,大家想要的话,我把english文挡发给大家好了。

Keyboard Key 

键盘代码

键盘模式

键盘模式有4种, 在Linux 下你可以用kbd_mode -参数 来设置和显示你的模式:

1)  Scancode mode raw raw模式:将键盘端口上读出的扫描码放入缓冲区

2)   Keycode  mode (mediumraw)   mediumraw模式:将扫描码过滤为键盘码放入缓冲区

3)  ASCII mode (XLATE )    XLATE模式:识别各种键盘码的组合,转换为TTY终端代码放入缓冲区

4)  UTF-8 MODE (UNICODE)  Unicode 模式:UNICODE模式基本上与XLATE相同,只不过可以通过数字小键盘间接输入UNICODE代码。

键盘模块的上层漫游:

键盘模块的最上层严格的说应该是TTY设备,这在以后描述。对于使用者而言,keyboard.c是我们的最上层。

keyboard.c中,不涉及底层操作,也不涉及到任何体系结构,他主要负责:键盘初始化、键盘tasklet的挂入、按键盘后的处理、keymap的装入、scancode的转化、与TTY设备的通信。

Keyboard.c是一个大杂烩,包含了大多数keyboardkey的处理,因此代码才变的庞大和复杂,我们可以修改它,让他变的很小(但这样做并不会使空间节省,因为keyboard.c的许多代码是动态加载的)。

Keyboard.c中的int __init kbd_init(void) 函数是键盘代码执行的开始点,但keyboard.c并非是一定执行的,你必须先定义CONFIG_VTCharacter devices -> Virtual terminal)才有效,为什么?因为kbd_init()是在tty_io.c 的 tty_init()中被调用的。

  ★Kbd_init()在进行一些基本初始化后,执行kbd_init_hw(),此函数可以看做是一个统一的上层界面,对于不同的体系或相同体系下不同的board,他们的kbd_init_hw()的实现代码是不同的(同过CONFIG_XXX_XXX来实现),他的实现代码就是操作硬件。 kbd_init_hw()会:检查硬件及有效性、分配资源和中断、操作寄存器的实现函数调用。

  初始化一完成,就把keyboard_tasklet放到CPU tasklet中(注意到keyboard_tasklet 是开放出来的,等下可以看到怎么使用它)。

  现在就等你按键了,当有按键时,会调用键盘中断处理函数,一般都是体系下的keyboard_interrupt(),此函数会调用到keyboard.c中的handle_scancode() 

  ★handle_scancode()就是我们的重点,这个函数最终决定了我们按下的key如何处理,在下面会分析他的流程,他主要做:与TTY的通信、keymap的装入、按键的处理。

  handle_scancode()处理的结果就是把你按下的key发到不同的处理函数中,一般的,这些函数离最终的结果已经很近了。这其中,大多数的函数都会调用put_queue() 函数。

  put_queue()的工作原理就是利用TTY, 它将经过转换的键盘功能码用tty_insert_flip_char()放入到当前TTYflip buffer中,然后将缓冲区输出任务函数(flush_to_ldisc)添加到控制台任务队列(con_task_queue)并激活控制台软中断执行该任务函数. flush_to_ldisc()翻转读写缓冲区,将缓冲区接收数据传递给tty终端规程的n_tty_receive_buf()接收函数,n_tty_receive_buf()处理输入字符,将输出字符缓冲在终端的循环缓冲区(read_buf)之中.用户通过tty规程的read_chan()读取read_buf中的字符.当多个进程同时读取同一终端时使用tty->atomic_read信号灯来竞争读取权.

Kbd_init()开始点:

int __init kbd_init(void)

{

int i;

struct kbd_struct kbd0; /* 定义一个kbd_struct, 他用于保存当前键盘LED灯状态、缺省keymap表、键盘复合锁定状态、一些功能灯的定义、键盘模式定义、及modeflags模式*/

    

extern struct tty_driver console_driver; /* console_driver这个全局变量是很重要的,他维护着庞大的TTY/Console对象,承当TTY对外的输入和输出 */

kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; /* 缺省不亮灯 */

kbd0.ledmode = LED_SHOW_FLAGS; /* 缺省,用于显示flags */

kbd0.lockstate = KBD_DEFLOCK; /* 缺省,表示用key_map的第一个表,即没有lock*/

kbd0.slockstate = 0; /* 没有粘键 */

kbd0.modeflags = KBD_DEFMODE; /* modeflags=0 */

kbd0.kbdmode = VC_XLATE; /* ASCII模式 */

for (i = 0 ; i < MAX_NR_CONSOLES ; i++)/* 为每个控制台分配一个KBD的结构 */

kbd_table[i] = kbd0;

ttytab = console_driver.table; /* ttytab是一个很重要的指针,他维护着当前各个控制台的tty_struct表(即相当于一个2维表),tty_struct可看成/dev/tty*的输入设备,只有在/dev/tty*打开时它才接收输入数据*/

/* 以下就是涉及到硬件的操作,由于我们没有KBD硬件,因此我们可能希望屏蔽所产生的error信息*/

#ifdef CONFIG_SA1100_ROSETTA

button_setup();/* initialize button hardware */

#else

kbd_init_hw();  /*下面会分析他*/

#endif

 /* 我们希望把keyboard_tasklet 挂到CPU的运行队列中去,下面就是 */

tasklet_enable(&keyboard_tasklet);

tasklet_schedule(&keyboard_tasklet);

/* 下面register一个电源管理的KBD设备?操作函数为NULL? */

pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, NULL);

return 0;

}

kbd_init_hw()函数:

我们已经说了,kbd_init_hw() 相当与一个统一的界面,不同硬件操作代码是不同的,因为是涉及硬件的操作。

  对arm体系而言,目前的kbd_init_hw() 提供了4种硬件代码,象如果是ADS板,就要提供ADS的相应代码。

  SA1100体系的此函数放在文件include/asm-arm/arch-sa1100/keyboard.h 中。大家可以看到在这个文件中除了kbd_init_hw() 外,还有kbd_setkeycode() /  kbd_getkeycode()kbd_translate()……等函数都是函数名相同,处理代码不同。而这些函数都会在keyboard.c中被调用到。

  SA1100定义了“CONFIG_SA1100_BRUTUS / CONFIG_SA1100_GRAPHICSCLIENTCONFIG_SA1111 / other board4种已知硬件的操作代码,对于Assabet就会调用other board的代码,也就是无论什么函数都为NULL(因为没有这个硬件呀!),CONFIG_SA1100_GRAPHICSCLIENT也就是ADS板的处理代码(SmartIO芯片决定的),CONFIG_SA1111也有自己的一块KBD芯片,而且功能居然和PC的差不多。

  那现在我们怎么办呀(我们的板不知道有没有KBD模块芯片)?我们来挑一个最复杂的来分析把。那就是SA1111了。

  我们从kbd_init_hw() 进入把:

void __init sa1111_init_hw(void)

{

       kbd_request_region(); /* 分配kbd端口,其实根本就不用分配了,对PC来说,0x60~0x68就是KBDIO口,对sa1100更不用分配了,所以此函数为NULL*/

SKPCR |= (1<<6); /* 0x4000,0000+0x0200处的register操作 */

/* 以下初始化KBD4register,以便开始下面的test工作 */

       KBDCLKDIV = 0;

       KBDPRECNT = 127;

       KBDCR = 0x08;

       mdelay(50);

       KBDDATA = 0xff;

       mdelay(50);

       /* Flush any pending input. */

       kbd_clear_input();

       if (kbd_startup_reset) { /* int kbd_startup_reset =1,所以肯定执行 */

               char *msg = initialize_kbd(); /* initialize_kbd() 真正开始init 硬件,出错会return错误message,显示在系统启动的时候 */

               if (msg)

                       printk(KERN_WARNING "initialize_kbd: %s\n", msg);

       }

#if defined CONFIG_PSMOUSE

       psaux_init();

#endif

       /* allocate the IRQ, keyboard_interrupt 是处理函数 */

       kbd_request_irq(keyboard_interrupt);

#if 0 /*@@@*/

       for (;;) {

               if (KBDSTAT & KBD_STAT_RXF)

                       printk("K_%.2x ", KBDDATA & 0xff);

               if (MSESTAT & MSE_STAT_RXF)

                       printk("M_%.2x ", MSEDATA & 0xff);

       }

#endif

#if 0 /*@@@*/

       timer_table[COMTROL_TIMER].fn = pckbd_timer;

       timer_table[COMTROL_TIMER].expires = jiffies + 2*HZ/100;

       timer_active |= 1 << COMTROL_TIMER;

#endif

}

initialize_kbd()函数:

这个函数如果逐层展开,会比较庞大,我们就分析最上层的把。

  首先,要reset键盘,即对键盘的 KBDDATA寄存器写KBD_CMD_RESET数据,然后读状态位,假如超时,那么可能是没有AT键盘挂上。这里有一个很重要的问题就是你在对register读的时候,先收到KBD_REPLY_ACK数据标志,然后再受到数据,因此你需要读2次。

  另,这些硬件代码实在是太多了,我想,如果大家有什么不懂来问我把,我吃不削写了。这里涉及到很多的 键盘命令/键盘标志,你可以参考pc_keyb.h文件,它包括了特殊的键盘命令操作,你可能需要对键盘的原理有所了解才看的懂。

static char * __init initialize_kbd(void)

{

       int status;

#if 0 /*@@@*/

       /*

        * Test the keyboard interface.

        * This seems to be the only way to get it going.

        * If the test is successful a x55 is placed in the input buffer.

        */

       kbd_write_command_w(KBD_CCMD_SELF_TEST);

       if (kbd_wait_for_input() != 0x55)

               return "Keyboard failed self test";

       /*

        * Perform a keyboard interface test.  This causes the controller

        * to test the keyboard clock and data lines.  The results of the

        * test are placed in the input buffer.

        */

       kbd_write_command_w(KBD_CCMD_KBD_TEST);

       if (kbd_wait_for_input() != 0x00)

               return "Keyboard interface failed self test";

       /*

        * Enable the keyboard by allowing the keyboard clock to run.

        */

       kbd_write_command_w(KBD_CCMD_KBD_ENABLE);

#endif

       /*

        * Reset keyboard. If the read times out

        * then the assumption is that no keyboard is

        * plugged into the machine.

        * This defaults the keyboard to scan-code set 2.

        *

        * Set up to try again if the keyboard asks for RESEND.

        */

       do {

               kbd_write_output_w(KBD_CMD_RESET);

               status = kbd_wait_for_input();

               if (status == KBD_REPLY_ACK)

                       break;

               if (status != KBD_REPLY_RESEND)

                       return "Keyboard reset failed, no ACK";

       } while (1);

       if (kbd_wait_for_input() != KBD_REPLY_POR)

               return "Keyboard reset failed, no POR";

       /*

        * Set keyboard controller mode. During this, the keyboard should be

        * in the disabled state.

        *

        * Set up to try again if the keyboard asks for RESEND.

        */

       do {

               kbd_write_output_w(KBD_CMD_DISABLE);

               status = kbd_wait_for_input();

               if (status == KBD_REPLY_ACK)

                       break;

               if (status != KBD_REPLY_RESEND)

                       return "Disable keyboard: no ACK";

       } while (1);

#if 0 /*@@@*/

       kbd_write_command_w(KBD_CCMD_WRITE_MODE);

       kbd_write_output_w(KBD_MODE_KBD_INT

                             | KBD_MODE_SYS

                             | KBD_MODE_DISABLE_MOUSE

                             | KBD_MODE_KCC);

       /* ibm powerpc portables need this to use scan-code set 1 -- Cort */

       kbd_write_command_w(KBD_CCMD_READ_MODE);

       if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {

               /*

                * If the controller does not support conversion,

                * Set the keyboard to scan-code set 1.

                */

               kbd_write_output_w(0xF0);

               kbd_wait_for_input();

               kbd_write_output_w(0x01);

               kbd_wait_for_input();

       }

#else

       kbd_write_output_w(0xf0);

       kbd_wait_for_input();

       kbd_write_output_w(0x01);

       kbd_wait_for_input();

#endif

       kbd_write_output_w(KBD_CMD_ENABLE);

       if (kbd_wait_for_input() != KBD_REPLY_ACK)

               return "Enable keyboard: no ACK";

       /*

        * Finally, set the typematic rate to maximum.

        */

       kbd_write_output_w(KBD_CMD_SET_RATE);

       if (kbd_wait_for_input() != KBD_REPLY_ACK)

               return "Set rate: no ACK";

       kbd_write_output_w(0x00);

       if (kbd_wait_for_input() != KBD_REPLY_ACK)

               return "Set rate: no ACK";

       return NULL;

}

键盘中断:

在做完init后,接下来就等待key press事件了,如果产生了key press,好,调到keyboard_interrupt()来处理,在sa1111中就是调用handle_kbd_event()函数。

handle_kbd_event()函数:首先去读status port,看是不是有按键事件,然后判断是键盘还是鼠标的,如果是键盘的,读出scancode,然后判断status register的第8位是否为1,如果是1(表示正确,即key press时状态),那么调用handle_keyboard_event(scancode)

static inline void handle_keyboard_event(unsigned char scancode)

{

#ifdef CONFIG_VT

       kbd_exists = 1;

       if (do_acknowledge(scancode)) /* 我们希望读出的不是键盘的CMD数据,正确,返回1,否则0 */

               handle_scancode(scancode, !(scancode & 0x80)); /* 这就是我们要注意的最重要的函数,我们看到scancode & 0x80为什么要这样,因为KBDDATA register最高位是判断键是press还是release,如果是1就是release。后面的7位才是我们要的数据。 */

#endif

       tasklet_schedule(&keyboard_tasklet); /* 一不做,二不修,我们再一次把keyboard_tasklet放到tasklet中,确保bottom half的执行。*/

}

handle_scancode()函数:

进入handle_scancode(), 你准备好了吗?,首先我们要看到此函数是kernel EXPORT出去的函数,也就是说我们可以在user-level程序中调用他,不过,如果把这段代码改写一下,在user-level中调用才有意义的多。

Handle_scancode()主要是:判断key是按下还是释放,然后把当前的tty_driver赋给变量ttytty_driver可看成/dev/tty*的输出设备,不要和tty_struct混乱起来。

void handle_scancode(unsigned char scancode, int down)

{

unsigned char keycode;

char up_flag = down ? 0 : 0200; /* 判断,如果是press那么up_flag=0,否则up_flag=0200 */

char raw_mode;  /* 键盘模式 */

pm_access(pm_kbd);

add_keyboard_randomness(scancode | up_flag);

tty = ttytab? ttytab[fg_console]: NULL; /* 把当前的TTY参数传给变量 tty, tty来控制操作 */

if (tty && (!tty->driver_data)) { /* 我们如果直接操作tty是危险的,因为我们不知道tty是否被打开了,所以我们通过tty->driver_data来判断,tty打开的时候,tty->driver_data是被设置的,tty关闭的时候,他被clear */

/*

* We touch the tty structure via the ttytab array

* without knowing whether or not tty is open, which

* is inherently dangerous.  We currently rely on that

* fact that console_open sets tty->driver_data when

* it opens it, and clears it when it closes it.

*/

tty = NULL;

}

kbd = kbd_table + fg_console; /*kbd_tablekbd[]的指针,所以kbd就是当前ttykbd_struct的指针 */

if ((raw_mode = (kbd->kbdmode == VC_RAW))) { /* 我们判断当前的kbd模式是否为VC_RAW,我们知道如果是的话,我们就可以把scancode 直接放到tty_flip_buffer中。 */

put_queue(scancode | up_flag);

/* we do not return yet, because we want to maintain

the key_down array, so that we have the correct

values when finishing RAW mode or when changing VT's */

}

/*  

scancode 转化为 keycode。 kbd_translate()函数,

我们分析sa1111的。分析在下面

*/

if (!kbd_translate(scancode, &keycode, raw_mode))

return;

if (0) printk(__FUNCTION__ ": scancode=%d keycode=%d raw_mode=%d\n", scancode, keycode, raw_mode);

/*

变量keycode包含的是键控代码(u-char

* 注意到keycode 不允许为0  (on m68k 0 是允许的).

我们明了了键的 up/down 状态,我们要把状态传进去

并且如果是MEDIUMRAW 模式,我们要返回keycode的值

*/

kbd_processkeycode(keycode, up_flag, 0); 

}

sa1111_translate()函数:

因为scancode 转化成keycode 是这样的重要,所以我才不的不把他拿出来单独讲解了。我们只有知道scancodekeycode 的区别,我们才可以做下面的事情。

我们知道我们从键盘的 IO口读出的是scancode,那我们怎么知道每个键的scancode 呀?通过:Linux下的showkey s我们就可以知道每个键的scancode, 如果想知道相应的keycode,你重要用showkey就可以了。(后面的代码我写了一个模仿showkey的程序)

通过测试,我们知道1~88键按下的scancode是“0x**”,而89~128是以 “0xe0(0xe1) 0x**”表示的。如果是释放,只要加上个128就可以了。

然后我们来读以下的代码:

int sa1111_translate(unsigned char scancode, unsigned char *keycode,

                   char raw_mode)

{

       static int prev_scancode = 0;

       /* special prefix scancodes.. */

       if (scancode == 0xe0 || scancode == 0xe1) {

               prev_scancode = scancode;

               return 0;

       }

       /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */

       if (scancode == 0x00 || scancode == 0xff) {

               prev_scancode = 0;

               return 0;

       }

       scancode &= 0x7f;

       if (prev_scancode) {

         /*

          * usually it will be 0xe0, but a Pause key generates

          * e1 1d 45 e1 9d c5 when pressed, and nothing when released

          */

         if (prev_scancode != 0xe0) {

             if (prev_scancode == 0xe1 && scancode == 0x1d) {

                 prev_scancode = 0x100;

                 return 0;

             } else if (prev_scancode == 0x100 && scancode == 0x45) {

                 *keycode = E1_PAUSE;

                 prev_scancode = 0;

             } else {

#ifdef KBD_REPORT_UNKN

                 if (!raw_mode)

                   printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");

#endif

                 prev_scancode = 0;

                 return 0;

             }

         } else {

             prev_scancode = 0;

             /*

              *  The keyboard maintains its own internal caps lock and

              *  num lock statuses. In caps lock mode E0 AA precedes make

              *  code and E0 2A follows break code. In num lock mode,

              *  E0 2A precedes make code and E0 AA follows break code.

              *  We do our own book-keeping, so we will just ignore these.

              */

             /*

              *  For my keyboard there is no caps lock mode, but there are

              *  both Shift-L and Shift-R modes. The former mode generates

              *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.

              *  So, we should also ignore the latter. - aeb@cwi.nl

              */

             if (scancode == 0x2a || scancode == 0x36)

               return 0;

             if (e0_keys[scancode])

               *keycode = e0_keys[scancode];

             else {

#ifdef KBD_REPORT_UNKN

                 if (!raw_mode)

                   printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",

                          scancode);

#endif

                 return 0;

             }

         }

       } else if (scancode >= SC_LIM) {

           /* This happens with the FOCUS 9000 keyboard

              Its keys PF1..PF12 are reported to generate

              55 73 77 78 79 7a 7b 7c 74 7e 6d 6f

              Moreover, unless repeated, they do not generate

              key-down events, so we have to zero up_flag below */

           /* Also, Japanese 86/106 keyboards are reported to

              generate 0x73 and 0x7d for \ - and \ | respectively. */

           /* Also, some Brazilian keyboard is reported to produce

              0x73 and 0x7e for \ ? and KP-dot, respectively. */

         *keycode = high_keys[scancode - SC_LIM];

         if (!*keycode) {

             if (!raw_mode) {

#ifdef KBD_REPORT_UNKN

                 printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"

                        " - ignored\n", scancode);

#endif

             }

             return 0;

         }

       } else

         *keycode = scancode;

       return 1;

}

因为涉及到high_keys[] 和 e0_keys[],因此我们把代码拿进来读一读:

static unsigned char high_keys[128 - SC_LIM] = {

  RGN1, RGN2, RGN3, RGN4, 0, 0, 0,                   /* 0x59-0x5f */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */

  0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,          /* 0x68-0x6f */

  0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3,    /* 0x70-0x77 */

  FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,        /* 0x78-0x7b */

  FOCUS_PF8, JAP_86, FOCUS_PF10, 0                   /* 0x7c-0x7f */

};

static unsigned char e0_keys[128] = {

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x00-0x07 */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x08-0x0f */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x10-0x17 */

  0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,            /* 0x18-0x1f */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x20-0x27 */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x28-0x2f */

  0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,            /* 0x30-0x37 */

  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,              /* 0x38-0x3f */

  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,              /* 0x40-0x47 */

  E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */

  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,              /* 0x50-0x57 */

  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,          /* 0x58-0x5f */

  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */

  0, 0, 0, 0, 0, 0, 0, E0_MACRO,                     /* 0x68-0x6f */

  //0, 0, 0, 0, 0, 0, 0, 0,                          /* 0x70-0x77 */

  0, 0, 0, 0, 0, E0_BACKSLASH, 0, 0,                         /* 0x70-0x77 */

  0, 0, 0, E0_YEN, 0, 0, 0, 0                        /* 0x78-0x7f */

};

我们可以知道scancode 到 keycode的转化了。

对于1~88scancode,就等于keycode, 比如 a 的scancode 0x1e0x9e,对应的keycode 就是30158

对于89-128scancode*keycode = e0_keys[scancode]来转化,我们可以举列,比如小键盘的回车:scancode = 0xe0 0x1c,(大家别奇怪怎么发送,它会先发送0xe0 然后发送0x1c,根据上面代码,流程应为: prev_scancode = 0xe0 --> scancode = 0x1c --> prev_scancode = 0 --> 执行 *keycode = e0_keys[scancode]; --> 找到e0_keys[]0x1c个元素,即第28个元素,即E0_KPENTER即为96#define E0_KPENTER 96)。

另对于键是按下还是放开,keycode是相同的。

kbd_processkeycode()函数:

  这才是我们真正要仔细考虑和分析的函数,keyboard.c文件的庞大就是由于它的存在,kbd_processkeycode()最主要的工作就是根据 keycodekeymap 来决定最终的处理函数。

在这个函数里有几个很重要的变量:

1. 一个就是tty,它的值就是当前tty设备的环境;

2. u_short keysym 是我们真正要用的表示一个键的唯一的值,它是一个32位的数,高16位表示typetype的基数是0xf0;低16位表示键的值;现在你就知道了为什么我们要不费其烦的把scancode转化成keycode,就是因为我们要用keycode 来找到其对应的key_map[]中的元素。

3. u_char type 就是keysym的高16位值,这个值一定大于0xf0。我们引入他是因为我们需要对不同的见类型做不同的处理代码,比如方向键,小键盘的键,功能键等的处理代码都是不同的。

4. int  shift_final  key_maps[]是一个2维表,那我们怎么知道我们要用哪张表呀,答案就是这个变量,他对kbd_struct中相应的变量进行读取,得到这个值,然后就用此值来决定使用哪张表,见下面代码。

5. ushort *key_maps[MAX_NR_KEYMAPS] =

      {

        plain_map, shift_map, altgr_map, 0,

ctrl_map, shift_ctrl_map, 0, 0,

alt_map, 0, 0, 0,

ctrl_alt_map,0

};这个变量维护着所有的可能出现的表,每个表都有自己的128个元素。

6.  ushort *key_map    key_map就指向key_maps[]中具体的一张表了。见下面的代码 

static void

kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)

{

char raw_mode = (kbd->kbdmode == VC_RAW); /* 键盘模式 */

if (up_flag) { /* 放开键 */

rep = 0;

if(!test_and_clear_bit(keycode, key_down))

up_flag = kbd_unexpected_up(keycode);

} else { /* 按下键 */

rep = test_and_set_bit(keycode, key_down);

/* If the keyboard autorepeated for us, ignore it.

* We do our own autorepeat processing.

*/

if (rep && !autorepeat)

return;

}

/* 以下是关与重发的处理,我们暂时忽略他 */

    

if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {

kbd_repeatkeycode = -1;

del_timer(&key_autorepeat_timer);

}

do_poke_blanked_console = 1;

tasklet_schedule(&console_tasklet);

#ifdef CONFIG_MAGIC_SYSRQ/* Handle the SysRq Hack */

if (keycode == SYSRQ_KEY) {

sysrq_pressed = !up_flag;

return;

} else if (sysrq_pressed) {

if (!up_flag) {

handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);

return;

}

}

#endif

/*

* Calculate the next time when we have to do some autorepeat

* processing.  Note that we do not do autorepeat processing

* while in raw mode but we do do autorepeat processing in

* medium raw mode.

*/

if (!up_flag && !raw_mode) {

kbd_repeatkeycode = keycode;

if (vc_kbd_mode(kbd, VC_REPEAT)) {

if (rep)

key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;

else

key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;

add_timer(&key_autorepeat_timer);

}

}

if (kbd->kbdmode == VC_MEDIUMRAW) {

/* soon keycodes will require more than one byte */

put_queue(keycode + up_flag);

raw_mode = 1;/* Most key classes will be ignored */

}

    

    /*  以下就是我们的重要的部分了,下面的代码必须被执行,由于代码展开的话比较琐碎,我把重要的地方用蓝色来表示 */

/*

* Small change in philosophy: earlier we defined repetition by

* rep = keycode == prev_keycode;

* prev_keycode = keycode;

* but now by the fact that the depressed key was down already.

* Does this ever make a difference? Yes.

*/

/*

*  Repeat a key only if the input buffers are empty or the

*  characters get echoed locally. This makes key repeat usable

*  with slow applications and under heavy loads.

*/

if (!rep ||

(vc_kbd_mode(kbd,VC_REPEAT) && tty &&

(L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) 

{

u_short keysym;

u_char type;

/* the XOR below used to be an OR */

        /* 我们要计算出当前的键盘状态,键盘状态你别告诉我你不知道呀,就是有没有按住“shift / ctrl / alt / shift+alt /……”呀,当然还有正常状态,也就是什么都 没有按住(=0) */

int shift_final = (shift_state | kbd->slockstate) ^

kbd->lockstate;

ushort *key_map = key_maps[shift_final];  /* key_maps[shift_final] 的定义在defkeymap.c中,他就是大名鼎鼎的key表,下面会详细的介绍,那么这个时候,key_map就指向具体的一张表了。 */ 

if (key_map != NULL) {

keysym = key_map[keycode];  /* 我们已经把scancode转化成了keycode,很多人问为什么,就是要在这里用呀,根据keycode找到一个相对应的keysym的值,keysym才是我们真正要用的值呀! */

type = KTYP(keysym); /* 分析一下那些key_maps表,你就知道了,type要取出这些32位数的高16位,为什么?因为,高16位是表示键类型用的,那什么叫“键类型”,键类型就在include/linux/keyboard.h 中定义,如KT_LATIN表示一般键,KT_CUR表示方向键(就4个,你别说不知道哪4个呀!),…………. */

if (0) printk(__FUNCTION__ ":  keysym=%#x type=%d shift_final=%d\n", keysym, type, shift_final);

if (type >= 0xf0)  /* 一定会执行了 */

{

type -= 0xf0; /* 我们要减掉一个基数0xf0,当然了,你也可以不减,那就直接判断呀。 */

if (raw_mode && ! (TYPES_ALLOWED_IN_RAW_MODE & (1 << type)))

return;

if (type == KT_LETTER) /* 大写键比较特殊,再次转化以后在执行 */

{ 

type = KT_LATIN; /* 一般的键的type */

if (vc_kbd_led(kbd, VC_CAPSLOCK))

      {

key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];

if (key_map)

keysym = key_map[keycode];

}

}

if (0) printk(__FUNCTION__ ": key_handler=%p keysym=%#x up_flag=%d\n", key_handler[type], keysym, up_flag);

(*key_handler[type])(keysym & 0xff, up_flag); /* 这就是重点中的重点了,针对不同的键,有不同的操作。比如a和方向键的操作就是不同的,在keyboard.c中我们定义了很多的针对不同的键的不同处理函数,并且放在static k_hand key_handler[16]中,根据type我们选择相应的处理函数,这些处理函数都带2个参数,第一个就是32位数keysym的低16位,另一个就是up_flag标志。这句代码会去调用诸如:do_cur()即方向键的处理代码,do_shift()即按住shift 键时的处理代码。 */

if (type != KT_SLOCK)

kbd->slockstate = 0;

} else {

/* maybe only if (kbd->kbdmode == VC_UNICODE) ? */

if (!up_flag && !raw_mode)

to_utf8(keysym); /* */

}

} else {

/* maybe beep? */

/* we have at least to update shift_state */

#if 1/* how? two almost equivalent choices follow */

compute_shiftstate();

kbd->slockstate = 0; /* play it safe */

#else

keysym = U(plain_map[keycode]);

type = KTYP(keysym);

if (type == KT_SHIFT)

(*key_handler[type])(keysym & 0xff, up_flag);

#endif

}

}

rep = 0;

}

各种不同的键类型:

#define KT_LATIN0/* we depend on this being zero */

#define KT_LETTER11/* symbol that can be acted upon by CapsLock */

#define KT_FN1   /* 功能键 */

#define KT_SPEC2   /* 专用键 */

#define KT_PAD3   /* 小键盘上的键 */

#define KT_DEAD4   /* 没有使用过 */

#define KT_CONS5   /* 没有使用过*/

#define KT_CUR6   /* 方向键 */

#define KT_SHIFT7       /* shift按下时?*/

#define KT_META8   

#define KT_ASCII9       

#define KT_LOCK10  /*  shift, alt, ctrl 是否被锁住 */

#define KT_SLOCK12  

ushort *key_maps[MAX_NR_KEYMAPS] = {

plain_map, shift_map, altgr_map, 0,

ctrl_map, shift_ctrl_map, 0, 0,

alt_map, 0, 0, 0,

ctrl_alt_map,0

};

处理函数:

由于处理函数比较多,而且都涉及到tty的操作,因此我把处理函数的代码分析另外写一份文挡。

处理函数

Keysym

Keycode

函数值

对应事件

Do_self

0xf01b

1

0x1b27

单击Esc

0xf031

2

0x31(49)

单击1

处理函数

Keysym

Keycode

Fuc_buf的值

对应事件

do_fn

0xf100

59

'\033', '[', '[', 'A', 0,

单击F1

0xf101

60

'\033', '[', '[', 'B', 0,

单击F2

处理函数

Do_spec

Keysym

Keycode

下一处理函数

对应事件

0xf200

0,89-95,120-127

NULL

没有定义

0xf201

28

Enter()

单击CR(回车)

0xf203

70 shift_map

show_mem()

Ctl+scrolllock

处理函数

Keysym

Keycode

下一处理函数

对应事件

do_pad

0xf300

82

do_fn(KVAL(K_INSERT), 0)

单击kp_0

numlock 开、关分别处理

pad_chars[0]=0

0xf305

76

applkey('G', vc_kbd_mode(kbd, VC_APPLIC));

单击kp_5

numlock 开、关分别处理

pad_chars[5]=5

一段自己读取键盘按键处理的代码(模块式):

  以下这段代码能够在X86上很好的运行,他主要是重新分配键盘的IRQ,读取键盘的数据寄存器,安排队列的运行,最终打印出scancode的值和按键状态。

  

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/irq.h>

#include <linux/keyboard.h>

/* Bottom Half - 一旦内核模块认为它做任何事都是安全的时候这将被内核调用。 */  

static void got_char(void *scancode)  

{  

printk("Scan Code %x %s.n",  

(int) *((char *) scancode) & 0x7F,  

*((char *) scancode) & 0x80 ? "Released" : "Pressed");  

}  

/* 这个函数为键盘中断服务。它读取来自键盘的相关信息然后安排当内核认为bottom half安全的时候让它运行 */  

void irq_handler(int irq,  

 void *dev_id,  

 struct pt_regs *regs)  

{  

/* 这些变量是静态的,因为它们需要对 bottom half 可见(通过指针)。 */  

static unsigned char scancode;  

static struct tq_struct task =    {NULL, 0, got_char, &scancode};  

unsigned char status;  

/* Read keyboard status */  

status = inb(0x64);  

scancode = inb(0x60);  

/* 安排 bottom half 运行 */  

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)  

queue_task(&task, &tq_immediate);  

#else  

queue_task_irq(&task, &tq_immediate);  

#endif  

mark_bh(IMMEDIATE_BH);  

}  

/* 初始化模块--登记 IRQ 句柄 */  

int init_module()  

{  

/* 既然键盘的句柄不能和我们的共存,在我们做事情前我们不得不关闭它(释放它的 IRQ)。  

因为我们不知道它在哪儿,所以以后没有办法恢复它--因此当我们做完时计算机将被重新启动。  

*/  

free_irq(1, NULL);  

/* 请求 IRQ 1,键盘的 IRQ,指向我们的 irq_handler。 */  

return request_irq(  

1, /* PC上的键盘的 IRQ 号 */  

irq_handler, /* 我们的句柄 */  

SA_SHIRQ,  

/* SA_SHIRQ 意味着我们将另一个句柄用于这个 IRQ。  

*  

* SA_INTERRUPT 能使句柄为一个快速中断。  

*/  

"test_keyboard_irq_handler", NULL);  

}  

/* 清除 */  

void cleanup_module()  

{  

/* 它在这儿只是为了完全。它是完全不相关的,因为我们没有办法恢复通常的键盘中断因此计算机完全没用 了,需要被重新启动。 */  

free_irq(1, NULL);  

}  

一段功能和showkey同样强大的代码:

这段代码是参考了一个样本的,很有意义的代码,他演示了任何写一个应用程序,如何考虑到X的情况,如何使用信号来处理代码,如何使你的应用程序支持参数,以及如何使用tty参数,当然更重要的是如何实现showkey的功能(甚至实现了键盘模式设置和显示的代码)。

#include <stdio.h>

#include <unistd.h>

#include <getopt.h>

#include <signal.h>

#include <fcntl.h>

#include <termios.h>

#include <linux/kd.h>

#include <linux/keyboard.h>

#include <sys/ioctl.h>

#define VERSION "via showkey 0.01"

int tmp;        /* for debugging */

#define OPTION "medium raw"

extern int getfd();

int fd;

int oldkbmode;

struct termios old;

/*

* restore kbmode unconditially to XLATE,

* thus making the console unusable when it was called under X.

*/

void get_mode(void) {

char *m;

if (ioctl(fd, KDGKBMODE, &oldkbmode)) {

perror("KDGKBMODE");

exit(1);

}

switch(oldkbmode) 

{

case K_RAW:

m = "RAW"; break;

case K_XLATE:

m = "XLATE"; break;

case K_MEDIUMRAW:

m = "MEDIUMRAW"; break;

default:

m = "?UNKNOWN?"; break;

}

printf("kb mode was %s\n", m);

if (oldkbmode != K_XLATE) {

printf("[ if you are trying this under X, it might not work\n");

printf("since the X server is also reading /dev/console ]\n");

}

printf("\n");

}

void clean_up(void) 

{

if (ioctl(fd, KDSKBMODE, oldkbmode)) {

perror("KDSKBMODE");

exit(1);

}

tmp = tcsetattr(fd, 0, &old);

if (tmp)

printf("tcsetattr = %d\n", tmp);

close(fd);

}

void die(int x) {

printf("caught signal %d, cleaning up...\n", x);

clean_up();

exit(1);

}

void watch_dog(int x) {

clean_up();

exit(0);

}

void usage(void) {

fprintf(stderr, "\

showkey version " VERSION " (" OPTION ")

usage: showkey [options...]

   

   valid options are:

-h --help       display this help text

        -s --scancodes  display only the raw scan-codes

        -k --keycodes   display only the interpreted keycodes (default)

");

        exit(1);

}

int

main (int argc, char *argv[]) {

const char *short_opts = "hsk";

const struct option long_opts[] = {

{ "help",       no_argument, NULL, 'h' },

{ "scancodes",  no_argument, NULL, 's' },

{ "keycodes",   no_argument, NULL, 'k' },

{ NULL, 0, NULL, 0 }

};

int c;

int show_keycodes = 1;

struct termios new;

unsigned char buf[16];

int i, n;

while ((c = getopt_long(argc, argv,

short_opts, long_opts, NULL)) != -1) {

switch (c) {

case 's':

show_keycodes = 0;

break;

case 'k':

show_keycodes = 1;

break;

case 'h':

case '?':

usage();

}

}

if (optind < argc)

usage();

fd = getfd();

/* the program terminates when there is no input for 10 secs */

signal(SIGALRM, watch_dog);

/*

if we receive a signal, we want to exit nicely, in

order not to leave the keyboard in an unusable mode

*/

signal(SIGHUP, die);

signal(SIGINT, die);

signal(SIGQUIT, die);

signal(SIGILL, die);

signal(SIGTRAP, die);

signal(SIGABRT, die);

signal(SIGIOT, die);

signal(SIGFPE, die);

signal(SIGKILL, die);

signal(SIGUSR1, die);

signal(SIGSEGV, die);

signal(SIGUSR2, die);

signal(SIGPIPE, die);

signal(SIGTERM, die);

#ifdef SIGSTKFLT

signal(SIGSTKFLT, die);

#endif

signal(SIGCHLD, die);

signal(SIGCONT, die);

signal(SIGSTOP, die);

signal(SIGTSTP, die);

signal(SIGTTIN, die);

signal(SIGTTOU, die);

get_mode();

tmp = tcgetattr(fd, &old);

if (tmp)

printf("tcgetattr = %d\n", tmp);

tmp = tcgetattr(fd, &new);

if (tmp)

printf("tcgetattr = %d\n", tmp);

new.c_lflag &= ~ (ICANON | ECHO | ISIG);

new.c_iflag = 0;

new.c_cc[VMIN] = sizeof(buf);

new.c_cc[VTIME] = 1;    /* 0.1 sec intercharacter timeout */

tmp = tcsetattr(fd, TCSAFLUSH, &new);

if (tmp)

printf("tcsetattr = %d\n", tmp);

if (ioctl(fd, KDSKBMODE,

show_keycodes ? K_MEDIUMRAW : K_RAW)) {

perror("KDSKBMODE");

exit(1);

}

printf("press any key (program terminates after 10s of last keypress)...\n");

while (1) {

alarm(10);

n = read(fd, buf, sizeof(buf));

for (i = 0; i < n; i++) {

if (!show_keycodes)

printf("0x%02x ", buf[i]);

else

printf("keycode %3d %s\n",

buf[i] & 0x7f,

buf[i] & 0x80 ? "release" : "press");

}

if (!show_keycodes)

printf("\n");

}

clean_up();

exit(0);

}

scancodekeycode一一对应:

以下其实就是defkeymap.map文件你可以找出相应关系:

# Default kernel keymap. This uses 7 modifier combinations.

keymaps 0-2,4-5,8,12

# Change the above line into

#keymaps 0-2,4-6,8,12

# in case you want the entries

#altgr   control keycode  83 = Boot            

#altgr   control keycode 111 = Boot            

# below.

#

# In fact AltGr is used very little, and one more keymap can

# be saved by mapping AltGr to Alt (and adapting a few entries):

# keycode 100 = Alt

#

keycode   1 = Escape           Escape          

alt     keycode   1 = Meta_Escape     

keycode   2 = one              exclam          

alt     keycode   2 = Meta_one        

keycode   3 = two              at               at              

controlkeycode   3 = nul             

shiftcontrolkeycode   3 = nul             

altkeycode   3 = Meta_two        

keycode   4 = three            numbersign      

control keycode   4 = Escape          

alt     keycode   4 = Meta_three      

keycode   5 = four             dollar           dollar          

control keycode   5 = Control_backslash

alt     keycode   5 = Meta_four       

keycode   6 = five             percent         

control keycode   6 = Control_bracketright

alt     keycode   6 = Meta_five       

keycode   7 = six              asciicircum     

control keycode   7 = Control_asciicircum

alt     keycode   7 = Meta_six        

keycode   8 = seven            ampersand        braceleft       

control keycode   8 = Control_underscore

alt     keycode   8 = Meta_seven      

keycode   9 = eight            asterisk         bracketleft     

control keycode   9 = Delete          

alt     keycode   9 = Meta_eight      

keycode  10 = nine             parenleft        bracketright    

alt     keycode  10 = Meta_nine       

keycode  11 = zero             parenright       braceright      

alt     keycode  11 = Meta_zero       

keycode  12 = minus            underscore       backslash       

controlkeycode  12 = Control_underscore

shiftcontrolkeycode  12 = Control_underscore

altkeycode  12 = Meta_minus      

keycode  13 = equal            plus            

alt     keycode  13 = Meta_equal      

keycode  14 = Delete           Delete          

control keycode  14 = BackSpace

alt     keycode  14 = Meta_Delete     

keycode  15 = Tab              Tab             

alt     keycode  15 = Meta_Tab        

keycode  16 = q               

keycode  17 = w               

keycode  18 = e

altgr   keycode  18 = Hex_E   

keycode  19 = r               

keycode  20 = t               

keycode  21 = y               

keycode  22 = u               

keycode  23 = i               

keycode  24 = o               

keycode  25 = p               

keycode  26 = bracketleft      braceleft       

control keycode  26 = Escape          

alt     keycode  26 = Meta_bracketleft

keycode  27 = bracketright     braceright       asciitilde      

control keycode  27 = Control_bracketright

alt     keycode  27 = Meta_bracketright

keycode  28 = Return          

alt     keycode  28 = Meta_Control_m  

keycode  29 = Control         

keycode  30 = a

altgr   keycode  30 = Hex_A

keycode  31 = s               

keycode  32 = d

altgr   keycode  32 = Hex_D   

keycode  33 = f

altgr   keycode  33 = Hex_F               

keycode  34 = g               

keycode  35 = h               

keycode  36 = j               

keycode  37 = k               

keycode  38 = l               

keycode  39 = semicolon        colon           

alt     keycode  39 = Meta_semicolon  

keycode  40 = apostrophe       quotedbl        

control keycode  40 = Control_g       

alt     keycode  40 = Meta_apostrophe 

keycode  41 = grave            asciitilde      

control keycode  41 = nul             

alt     keycode  41 = Meta_grave      

keycode  42 = Shift           

keycode  43 = backslash        bar             

control keycode  43 = Control_backslash

alt     keycode  43 = Meta_backslash  

keycode  44 = z               

keycode  45 = x               

keycode  46 = c

altgr   keycode  46 = Hex_C   

keycode  47 = v               

keycode  48 = b

altgr   keycode  48 = Hex_B

keycode  49 = n               

keycode  50 = m               

keycode  51 = comma            less            

alt     keycode  51 = Meta_comma      

keycode  52 = period           greater         

control keycode  52 = Compose         

alt     keycode  52 = Meta_period     

keycode  53 = slash            question        

control keycode  53 = Delete          

alt     keycode  53 = Meta_slash      

keycode  54 = Shift           

keycode  55 = KP_Multiply     

keycode  56 = Alt             

keycode  57 = space            space           

control keycode  57 = nul             

alt     keycode  57 = Meta_space      

keycode  58 = Caps_Lock       

keycode  59 = F1               F11              Console_13      

control keycode  59 = F1              

alt     keycode  59 = Console_1       

control alt     keycode  59 = Console_1       

keycode  60 = F2               F12              Console_14      

control keycode  60 = F2              

alt     keycode  60 = Console_2       

control alt     keycode  60 = Console_2       

keycode  61 = F3               F13              Console_15      

control keycode  61 = F3              

alt     keycode  61 = Console_3       

control alt     keycode  61 = Console_3       

keycode  62 = F4               F14              Console_16      

control keycode  62 = F4              

alt     keycode  62 = Console_4       

control alt     keycode  62 = Console_4       

keycode  63 = F5               F15              Console_17      

control keycode  63 = F5              

alt     keycode  63 = Console_5       

control alt     keycode  63 = Console_5       

keycode  64 = F6               F16              Console_18      

control keycode  64 = F6              

alt     keycode  64 = Console_6       

control alt     keycode  64 = Console_6       

keycode  65 = F7               F17              Console_19      

control keycode  65 = F7              

alt     keycode  65 = Console_7       

control alt     keycode  65 = Console_7       

keycode  66 = F8               F18              Console_20      

control keycode  66 = F8              

alt     keycode  66 = Console_8       

control alt     keycode  66 = Console_8       

keycode  67 = F9               F19              Console_21      

control keycode  67 = F9              

alt     keycode  67 = Console_9       

control alt     keycode  67 = Console_9       

keycode  68 = F10              F20              Console_22      

control keycode  68 = F10             

alt     keycode  68 = Console_10      

control alt     keycode  68 = Console_10      

keycode  69 = Num_Lock

shift   keycode  69 = Bare_Num_Lock

keycode  70 = Scroll_Lock      Show_Memory      Show_Registers  

control keycode  70 = Show_State      

alt     keycode  70 = Scroll_Lock     

keycode  71 = KP_7            

alt     keycode  71 = Ascii_7         

altgr   keycode  71 = Hex_7         

keycode  72 = KP_8            

alt     keycode  72 = Ascii_8         

altgr   keycode  72 = Hex_8         

keycode  73 = KP_9            

alt     keycode  73 = Ascii_9         

altgr   keycode  73 = Hex_9         

keycode  74 = KP_Subtract     

keycode  75 = KP_4            

alt     keycode  75 = Ascii_4         

altgr   keycode  75 = Hex_4         

keycode  76 = KP_5            

alt     keycode  76 = Ascii_5         

altgr   keycode  76 = Hex_5         

keycode  77 = KP_6            

alt     keycode  77 = Ascii_6         

altgr   keycode  77 = Hex_6         

keycode  78 = KP_Add          

keycode  79 = KP_1            

alt     keycode  79 = Ascii_1         

altgr   keycode  79 = Hex_1         

keycode  80 = KP_2            

alt     keycode  80 = Ascii_2         

altgr   keycode  80 = Hex_2         

keycode  81 = KP_3            

alt     keycode  81 = Ascii_3         

altgr   keycode  81 = Hex_3         

keycode  82 = KP_0            

alt     keycode  82 = Ascii_0         

altgr   keycode  82 = Hex_0         

keycode  83 = KP_Period       

#altgr   control keycode  83 = Boot            

control alt     keycode  83 = Boot            

keycode  84 = Last_Console    

keycode  85 =

keycode  86 = less             greater          bar             

alt     keycode  86 = Meta_less       

keycode  87 = F11              F11              Console_23      

control keycode  87 = F11             

alt     keycode  87 = Console_11      

control alt     keycode  87 = Console_11      

keycode  88 = F12              F12              Console_24      

control keycode  88 = F12             

alt     keycode  88 = Console_12      

control alt     keycode  88 = Console_12      

keycode  89 =

keycode  90 =

keycode  91 =

keycode  92 =

keycode  93 =

keycode  94 =

keycode  95 =

keycode  96 = KP_Enter        

keycode  97 = Control         

keycode  98 = KP_Divide       

keycode  99 = Control_backslash

control keycode  99 = Control_backslash

alt     keycode  99 = Control_backslash

keycode 100 = AltGr           

keycode 101 = Break           

keycode 102 = Find            

keycode 103 = Up              

keycode 104 = Prior           

shift   keycode 104 = Scroll_Backward 

keycode 105 = Left            

alt     keycode 105 = Decr_Console

keycode 106 = Right           

alt     keycode 106 = Incr_Console

keycode 107 = Select          

keycode 108 = Down            

keycode 109 = Next            

shift   keycode 109 = Scroll_Forward  

keycode 110 = Insert          

keycode 111 = Remove          

#altgr   control keycode 111 = Boot            

control alt     keycode 111 = Boot            

keycode 112 = Macro           

keycode 113 = F13             

keycode 114 = F14             

keycode 115 = Help            

keycode 116 = Do              

keycode 117 = F17             

keycode 118 = KP_MinPlus      

keycode 119 = Pause           

keycode 120 =

keycode 121 =

keycode 122 =

keycode 123 =

keycode 124 =

keycode 125 =

keycode 126 =

keycode 127 =

string F1 = "\033[[A"

string F2 = "\033[[B"

string F3 = "\033[[C"

string F4 = "\033[[D"

string F5 = "\033[[E"

string F6 = "\033[17~"

string F7 = "\033[18~"

string F8 = "\033[19~"

string F9 = "\033[20~"

string F10 = "\033[21~"

string F11 = "\033[23~"

string F12 = "\033[24~"

string F13 = "\033[25~"

string F14 = "\033[26~"

string F15 = "\033[28~"

string F16 = "\033[29~"

string F17 = "\033[31~"

string F18 = "\033[32~"

string F19 = "\033[33~"

string F20 = "\033[34~"

string Find = "\033[1~"

string Insert = "\033[2~"

string Remove = "\033[3~"

string Select = "\033[4~"

string Prior = "\033[5~"

string Next = "\033[6~"

string Macro = "\033[M"

string Pause = "\033[P"

compose '`' 'A' to '?

compose '`' 'a' to '?

compose '\'' 'A' to '?

compose '\'' 'a' to '?

compose '^' 'A' to '?

compose '^' 'a' to '?

compose '~' 'A' to '?

compose '~' 'a' to '?

compose '"' 'A' to '?

compose '"' 'a' to '?

compose 'O' 'A' to '?

compose 'o' 'a' to '?

compose '0' 'A' to '?

compose '0' 'a' to '?

compose 'A' 'A' to '?

compose 'a' 'a' to '?

compose 'A' 'E' to '?

compose 'a' 'e' to '?

compose ',' 'C' to '?

compose ',' 'c' to '?

compose '`' 'E' to '?

compose '`' 'e' to '?

compose '\'' 'E' to '?

compose '\'' 'e' to '?

compose '^' 'E' to '?

compose '^' 'e' to '?

compose '"' 'E' to '?

compose '"' 'e' to '?

compose '`' 'I' to '?

compose '`' 'i' to '?

compose '\'' 'I' to '?

compose '\'' 'i' to '?

compose '^' 'I' to '?

compose '^' 'i' to '?

compose '"' 'I' to '?

compose '"' 'i' to '?

compose '-' 'D' to '?

compose '-' 'd' to '?

compose '~' 'N' to '?

compose '~' 'n' to '?

compose '`' 'O' to '?

compose '`' 'o' to '?

compose '\'' 'O' to '?

compose '\'' 'o' to '?

compose '^' 'O' to '?

compose '^' 'o' to '?

compose '~' 'O' to '?

compose '~' 'o' to '?

compose '"' 'O' to '?

compose '"' 'o' to '?

compose '/' 'O' to '?

compose '/' 'o' to '?

compose '`' 'U' to '?

compose '`' 'u' to '?

compose '\'' 'U' to '?

compose '\'' 'u' to '?

compose '^' 'U' to '?

compose '^' 'u' to '?

compose '"' 'U' to '?

compose '"' 'u' to '?

compose '\'' 'Y' to '?

compose '\'' 'y' to '?

compose 'T' 'H' to '?

compose 't' 'h' to '?

compose 's' 's' to '?

compose '"' 'y' to ''

compose 's' 'z' to '?

compose 'i' 'j' to ''

原创粉丝点击