Linux键盘输入读取

1. 找到键盘设备

linux的键盘设备在 /dev/input/eventX 中,通过 ls /dev/input 通常会看到很多个eventX
ls /dev/input
那么哪个才是键盘?继续使用指令 cat /proc/bus/input/devices 查看各个设备的描述,通过关键字 keyboard 可以定位到 event1
Linux键盘输入读取_第1张图片

2. 读取按键

按照linux “一切皆文件” 的特点,读取按键只需三步,open、read和解析,特别注意的是读取的数据为 struct input_event 结构

#include 
... ...
struct input_event key_info;
... ...
if (read(fd, &key_info, sizeof(struct input_event)) > 0)
{
	//这是按键事件
	if (key_info.type == EV_KEY)
		//是哪个按键 及 按键状态
		printf("按键: %d 状态: %d \r\n", key_info.code, key_info.value);
}
  • key_info.code 是按键对应的标号,比如数字键1对应标号为2,具体对应可以在 linux/input.h 中查看(下面给出一段截图),或者把要用的按键都按一遍打印出来就知道了;
  • key_info.value 是按键状态,只有三种数值,0/松开 1/按下 2/按住(按住不放会连续触发这个事件);特别注意的是,当已经有按键(假设按键a)处于 2/按住 状态时,再去按别的按键(假设按键b),会先读到按键b的 1/按下 事件,而按键a的 2/按住 事件将不再触发,如果继续按住b不放会连续触发按键b的 2/按住 事件,而按键a只会在松开时触发 0/松开 事件;如果有更多的按键同时操作,逻辑同上。
    Linux键盘输入读取_第2张图片

3. 代码示例

鉴于前面提到的多按键同时按下会掩盖掉前面按键的 2/按住 事件问题,以下代码对 2/按住 事件进行了自行管理,可自行定义最大同时按下按键数和连续触发间隔。

  • key.h
#ifndef _KEY_H_
#define _KEY_H_

/*
 *  如何确认键盘在"/dev/input/event"几号?
 *  通过"cat /proc/bus/input/devices"可以看到"keyboard"所在的event号
 */
#define INPUT_DEV_PATH "/dev/input/event1"

/*
 *  按键回调注册
 *  参数:
 *      obj: 用户私有指针,会在互调的时候传回给用户
 *      callback: 回调函数原型 void callback(void *obj, int key, int type)
 *  回调函数参数:
 *      obj: 前面传入的用户私有指针
 *      key: 键位,可以看头文件中的定义,或者先测试打印一遍就知道哪个按键对哪个值了
 *      type: 按键状态,0/松开时,1/按下时,2/一直按住(会反复触发回调,间隔多久我也忘了)
 *  返回: 0/成功 -1/失败,找不到设备或者没有sudo运行
 */
int key_register(void *obj, void (*callback)(void *, int, int));

#endif
  • key.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "key.h"

//支持最大同按下的按键数量
#define KEY_COMBINATION 10

//长按按键时会反复触发事件,这里设置时间间隔ms,为0时不触发长按事件
#define KEY_HOLD_EVENT_INTERVALMS 50

//本地主结构体
typedef struct
{
    int fd;
    void *obj;
    void (*callback)(void *, int, int);
} Key_Struct;

//回调线程传入参数结构体(就是把一堆参数打包成一个好传递)
typedef struct
{
    void *obj;
    int key;
    int type;
    void (*callback)(void *, int, int);
    //数组指针
    int *combin;
} Key_Param;

//延时工具
#include 
void key_delayms(unsigned int ms)
{
    struct timeval tv;
    tv.tv_sec = ms / 1000;
    tv.tv_usec = ms % 1000 * 1000;
    select(0, NULL, NULL, NULL, &tv);
}

//抛线程工具
static void throwOut_thread(void *obj, void (*callback)(void *))
{
    pthread_t th;
    pthread_attr_t attr;
    //attr init
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //禁用线程同步, 线程运行结束后自动释放
    //抛出线程
    pthread_create(&th, &attr, (void *)callback, (void *)obj);
    //attr destroy
    pthread_attr_destroy(&attr);
}

//回调线程,在这里回调用户传入的callback函数
static void key_callback(void *argv)
{
    Key_Param *kp = (Key_Param *)argv;
    do {
        //按键事件回调
        if (kp->callback)
            kp->callback(kp->obj, kp->key, kp->type);
        //该值为0时,不触发长按事件
        if (KEY_HOLD_EVENT_INTERVALMS > 0) {
            //按下事件,经过2倍延时后切换为长按事件
            if (kp->type == 1) {
                key_delayms(KEY_HOLD_EVENT_INTERVALMS * 2);
                kp->type = 2;
            }
            //长按事件
            else if (kp->type == 2)
                key_delayms(KEY_HOLD_EVENT_INTERVALMS);
        }
        //周期触发长按事件
    } while (kp->type == 2 && kp->combin[0] == kp->key);
    free(kp);
}

//数组元素的设置和清除,返回位置
static int _arrayAdd(int *array, int len, int value)
{
    int i;
    for (i = 0; i < len; i++) {
        if (array[i] == 0) {
            array[i] = value;
            return i;
        }
    }
    return 0;
}
static int _arrayClear(int *array, int len, int value)
{
    int i;
    for (i = 0; i < len; i++) {
        if (array[i] == value) {
            array[i] = 0;
            return i;
        }
    }
    return 0;
}

static void key_thread(void *argv)
{
    Key_Struct *ks = (Key_Struct *)argv;
    Key_Param *kp;
    struct input_event key_info;
    int order;
    int combin[KEY_COMBINATION] = {0};
    while (1)
    {
        //阻塞读
        if (read(ks->fd, &key_info, sizeof(struct input_event)) > 0)
        {
            //这是按键类事件(触屏类事件也是这样读的)
            if (key_info.type == EV_KEY)
            {
                if (!ks->callback || key_info.value > 1)
                    continue;
                //按键按下时注册到数组,释放时清除
                order = 0;
                if (key_info.value == 1)
                    order = _arrayAdd(combin, KEY_COMBINATION, key_info.code);
                else
                    _arrayClear(combin, KEY_COMBINATION, key_info.code);
                //参数准备
                kp = (Key_Param *)calloc(1, sizeof(Key_Param));
                kp->obj = ks->obj;
                kp->key = key_info.code;   //键位
                kp->type = key_info.value; //键值
                kp->callback = ks->callback;
                kp->combin = &combin[order];
                //抛线程,在异步线程中触发用户回调函数
                throwOut_thread(kp, &key_callback);
            }
        }
    }
}

/*
 *  按键回调注册
 *  参数:
 *      obj: 用户私有指针,会在互调的时候传回给用户
 *      callback: 回调函数原型 void callback(void *obj, int key, int type)
 *  回调函数参数:
 *      obj: 前面传入的用户私有指针
 *      key: 键位值,可以看中的定义,或者先测试打印一遍就知道哪个按键对哪个值了
 *      type: 按键状态,0/松开时,1/按下时,2/一直按住(会反复触发回调)
 *  返回: 0/成功 -1/失败,找不到设备或者没有sudo运行
 */
int key_register(void *obj, void (*callback)(void *, int, int))
{
    Key_Struct *ks;
    //关键参数检查
    if (!callback)
        return -1;
    //只读打开键盘所在input设备
    int fd = open(INPUT_DEV_PATH, O_RDONLY);
    if (fd < 1)
    {
        printf("key_register: open %s failed\r\n", INPUT_DEV_PATH);
        return -1;
    }
    //参数备份,抛线程检测按键
    ks = (Key_Struct *)calloc(1, sizeof(Key_Struct));
    ks->fd = fd;
    ks->obj = obj;
    ks->callback = callback;
    throwOut_thread(ks, &key_thread);
    return 0;
}

  • main.c
#include 
#include 
#include "key.h"

void key_callback(void *obj, int key, int type)
{
    printf("key/%d type/%d\r\n", key, type);
}

int main(void)
{
    key_register(NULL, &key_callback);
    while(1)
        sleep(1);
    return 0;
}
  • 编译: gcc -o out main.c key.c -lpthread
  • 运行: sudo ./out (由于打开 /dev/input/event1 需要管理员权限)

推荐文章

https://blog.csdn.net/lanmanck/article/details/8423669

你可能感兴趣的:(linux,驱动,linux,运维,服务器)