linux 读取键盘上下左右键小程序
月城雪兔 2016-06-03 17:28:31 因为想读取键盘的上下左右方向键,却发现在网上不容易找到恰当的例子,最后是以一个ros程序简化而来的。第一,为什么在运行程序时,在终端按下箭头up down left right 箭头会出现 ^[A^[B^[C^[D
这个跟换键码有关,“escape sequence”实际上是用来生成换码符的关键字的顺序。换码符会告诉打印机将不再打印后面的字符,但是还要将这些字符解释为一类打印机控制码或其他。需要 Escape Sequences 顺序的应用程序通常是早期的 DOS 应用程序、UNIX (R) 应用程序或非常特殊的应用程序。
up - "\033[A"
down - "\033[B"
left - "\033[D"
right - "\033[C"
\033 = 0x1b = 27 = ^[= Esc 键 , 也就是说up down left right 这些键,每个都由三个字符组成。
读到键值为 读到27时,再读一个是'[' = 0x5B = 91, 然后再读一个键,若是'A' = 0x41 = 65则表示向上的箭头。其他的类似。
第二个知识点是关于signal()的。简单地说是我们在终端运行一个程序时,要想让这个程序终止可以同时按下Ctrl 和 c 键, 这是默认的.但是我们想要在结束一个程序之前,做一些我们想要事情,或是按下键盘,引起发射信号,执行我们想要执行的函数时,我们可以用signal()。如本例中的按下ctrl+c 执行quit()函数,在quit()函数中我们执行了其他函数。
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。
是在软件层次上对中断机制的一种模拟,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
信号机制除了基本通知功能外,还可以传递附加信息。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
linux主要有两个函数实现信号的安装:signal()、sigaction()。
其中signal()只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;
而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),
有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,
sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。
signal()
------------
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
DESCRIPTION
The behavior of signal() varies across UNIX versions, and has also varied historically across
different versions of Linux. Avoid its use: use sigaction instead. See Portability below.
signal() sets the disposition of the signal signum to handler, which is either SIG_IGN,
SIG_DFL, or the address of a programmer-defined function (a "signal handler").
If the signal signum is delivered to the process, then one of the following happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default action associated with the signal occurs.
* If the disposition is set to a function, then first either the disposition is reset to SIG_DFL,
or the signal is blocked, and then handler is called with argument signum. If invocation of the
handler caused the signal to be blocked, then the signal is unblocked upon return from the
handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR on error.
In the event of an error, errno is set to indicate the cause.
signum指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
signal里的一个SIGQUIT和SIGINT。根据apue的解释,ctl+c产生SIGINT,而ctl+\产生SIGQUIT。
=======================================================================
#include <signal.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define KEYCODE_R 0x43
#define KEYCODE_L 0x44
#define KEYCODE_U 0x41
#define KEYCODE_D 0x42
#define KEYCODE_Q 0x71
int kfd = 0;
struct termios cooked, raw;
void quit(int sig)
{
(void)sig;
tcsetattr(kfd, TCSANOW, &cooked);//在程序结束时在恢复原来的配置
exit(0);
}
void keyLoop()
{
char c;
// get the console in raw mode
tcgetattr(kfd, &cooked); // 得到 termios 结构体保存,然后重新配置终端
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &=~ (ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the robot.");
puts("otherwise the key values will be printed");
for(;;)
{
// get the next event from the keyboard
if(read(kfd, &c, 1) < 0)
{
perror("read():");
exit(-1);
}
switch(c)
{
case KEYCODE_L:
printf("LEFT\n");
break;
case KEYCODE_R:
printf("RIGHT\n");
break;
case KEYCODE_U:
printf("UP\n");
break;
case KEYCODE_D:
printf("DOWN\n");
break;
default:
printf("value: %c = 0x%02X = %d\n", c, c, c);
}
}
return;
}
int main(int argc, char** argv)
{
signal(SIGINT,quit);
keyLoop();
return(0);
}