手机遥控器要求有模拟鼠标功能,之前别人做的模拟鼠标是用service加上自己画的图标来实现的,不能像真正鼠标一样方便,而且实现比较难。网上查找资料发现可以通过UInput来实现这个功能。

  Uinput是一个虚拟的设备,使得可以在用户控件处理input设备,一般来讲uinput设备文件存在于/dev/input或者/dev/input/uinput目录中。在Linux中一切都是文件,所以使用uinput也很简单只需要open这个设备就可以了

打开设备:

extern "C"
JNIEXPORT int JNICALL
Java_com_remote_uinput_UInput_open(JNIEnv *env, jclass clazz) {
    struct uinput_user_dev uidev;
    //以只读的方式打开字符设备
    fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
//    if (fd < 0) die("error: open ");
    LOGD( "fd == %d", fd);
    //打印出打开设备失败的原因,可以看看是否因为权限导致
    LOGD("open %s fail, %s\n", "/dev/uinput", strerror(errno));
    if (fd<0) {
        LOGD( "fd == %d", fd);
        return -1;
    }

    //config uinput working mode,  mouse or touchscreen?  relative coordinates or absolute coordinate?
    if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)         //support key button
        die("error: ioctl UI_SET_EVBIT");
    if (ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0)  //support mouse left key
        die("error: ioctl  UI_SET_KEYBIT");

    if (ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT) < 0)  //support mouse right key
        die("error: ioctl UI_SET_KEYBIT");

    if (ioctl(fd, UI_SET_EVBIT, EV_REL) < 0)       //uinput use relative coordinates
        die("error: ioctl UI_SET_EVBIT");
    if (ioctl(fd, UI_SET_RELBIT, REL_X) < 0)         //uinput use x coordinates
        die("error: ioctl UI_SET_RELBIT REL_X");
    if (ioctl(fd, UI_SET_RELBIT, REL_Y) < 0)         //uinput use y coordinates
        die("error: ioctl REL_Y");

    memset(&uidev, 0,
           sizeof(uidev));                  //creat an virtul input device node in /dev/input/***
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample");//配置设备名称
    uidev.id.bustype = BUS_USB;//配置设备类型
    uidev.id.vendor = 0x1;//配置设备vendor
    uidev.id.product = 0x1;//配置设备product
    uidev.id.version = 1;//配置设备version
    //将配置写到uinput中
    if (write(fd, &uidev, sizeof(uidev)) < 0){
        die("error: write");
        return -1;
    }

    //通过ioctl创建这个虚拟设备
    if (ioctl(fd, UI_DEV_CREATE) < 0){
        die("error: ioctl");
        return -1;
    }
    return 1;
}

使用设备:

extern "C"
JNIEXPORT void JNICALL
Java_com_remote_uinput_UInput_simulateMouseMove(JNIEnv *env, jclass clazz, jint dx,
                                                          jint dy) {
    struct input_event ev;//input_event 结构体
    //send input event to kernel input system
    memset(&ev, 0, sizeof(struct input_event));
    ev.type = EV_REL;         //send x coordinates 事件类型  EV_REL相对事件, EV_KEY key事件, EV_ABS绝对事件
    ev.code = REL_X;//键值
    ev.value = dx;//key 1 表示按下,0表示释放  鼠标代表移动距离
    if (write(fd, &ev, sizeof(struct input_event)) < 0)
        die("error: write");

    memset(&ev, 0, sizeof(struct input_event));
    ev.type = EV_REL;  //send y coordinates
    ev.code = REL_Y;
    ev.value = dy;
    if (write(fd, &ev, sizeof(struct input_event)) < 0)
        die("error: write");

//    memset(&ev, 0, sizeof(struct input_event));
//    ev.type = EV_KEY;  //mouse left key 左键
//    ev.code = BTN_LEFT;
//    ev.value = 1;
//    if(write(fd, &ev, sizeof(struct input_event)) < 0)
//        die("error: write");

    //EV_SYN 同步 即执行这个事件
    memset(&ev, 0, sizeof(struct input_event));
    ev.type = EV_SYN; // inform input system to process this input event
    ev.code = 0;
    ev.value = 0;
    if (write(fd, &ev, sizeof(struct input_event)) < 0)
        die("error: write");
}

销毁:

extern "C"
JNIEXPORT void JNICALL
Java_com_ktc_remote_uinput_UInput_close(JNIEnv *env, jclass clazz) {
    //销毁这个设备
    if (ioctl(fd, UI_DEV_DESTROY) < 0)
        die("error: ioctl");
    //关闭这个文件
    close(fd);
}

这个方法不只是支持鼠标模拟,包括按键模拟,触摸屏模拟都可以实现,功能比较强大。

使用时需要注意,uinput这个文件不是可写的,低版本的系统可以在应用类修改文件权限,但是在高版本的Android中动态修改权限会失败,所以只能在系统初始化 init.rc中直接修改该文件为可读写,其次需要修改selinux权限,否则会因为权限不足无法创建设备。

Logo

更多推荐