Android 通过uinput模拟touch事件发出onActionDown onActionUp onActionMove

     手机中的屏幕触摸事件是通过驱动将事件上报到/dev/input设备上,然后被input模块读取发送到APP

     如果我没有物理的屏幕但我想发出触摸事件怎么办?通过Linux的uinput模块就可以不需要写驱动代码就能模拟一块触摸屏,当然我们也可以模拟出虚拟鼠标和键盘

     本文讨论的是模拟触摸屏,鼠标和键盘比较easy

      

      前提准备:getevent命令使用

      1通过adb shell getevent -p 查看设备的输入设备

     

     

       /dev/input/event0...n就是输入设备 KEY一般对应的是键值Keycode 一般指设备上的物理按键和虚拟按键ABS代表绝对值得意思


    2:adb shell getevent -l /dev/input/event1 获得该设备底层发送的事件,我们看一下手机屏幕被触摸后getevent获得的事件

       

  我们可以看到 有一系列的按键码被发出组合成onActionDown-->onActionMove-->onActionUp事件流


 到此我们要做的就是创建一个/dev/input/eventX代表虚拟屏幕, 然后发出按键码形成触摸事件送到input模块发给APP

 

 一:通过uinput创建虚拟屏幕

//创建触摸屏 通过systemproperty保存fd
static void createTouchScreen()
{
        static int uinp_fd;
		struct uinput_user_dev uinp;
		struct input_event event;
		uinp_fd = open("/dev/uinput", O_WRONLY|O_NONBLOCK);
		if(uinp_fd == 0) {
			ALOGD("Unable to open /dev/uinput\n");
			return;
		}

		// configure touch device event properties
		memset(&uinp, 0, sizeof(uinp));
                //设备的别名
		strncpy(uinp.name, "ShaoTouchScreen", UINPUT_MAX_NAME_SIZE);
		uinp.id.version = 4;
		uinp.id.bustype = BUS_USB;
		uinp.absmin[ABS_MT_SLOT] = 0;
		uinp.absmax[ABS_MT_SLOT] = 9; // MT代表multi touch 多指触摸 最大手指的数量我们设置9
		uinp.absmin[ABS_MT_TOUCH_MAJOR] = 0;
		uinp.absmax[ABS_MT_TOUCH_MAJOR] = 15;
		uinp.absmin[ABS_MT_POSITION_X] = 0; // 屏幕最小的X尺寸
		uinp.absmax[ABS_MT_POSITION_X] = 1020; // 屏幕最大的X尺寸
		uinp.absmin[ABS_MT_POSITION_Y] = 0; // 屏幕最小的Y尺寸
		uinp.absmax[ABS_MT_POSITION_Y] = 1020; //屏幕最大的Y尺寸
		uinp.absmin[ABS_MT_TRACKING_ID] = 0;
		uinp.absmax[ABS_MT_TRACKING_ID] = 65535;//按键码ID累计叠加最大值
		uinp.absmin[ABS_MT_PRESSURE] = 0;   
		uinp.absmax[ABS_MT_PRESSURE] = 255;     //屏幕按下的压力值

		// Setup the uinput device
		ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);   //该设备支持按键
		ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);   //支持鼠标
    
		// Touch
		ioctl (uinp_fd, UI_SET_EVBIT,  EV_ABS); //支持触摸
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_SLOT);
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
		ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);    
		ioctl (uinp_fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);

		char str[20];
		memset(str,0,sizeof(str));
		sprintf(str,"%d",uinp_fd);

		property_set("nvr_touch_screen_device",str);
		ALOGD("nvr touch screen device strfd = %s , id = %d\n",str ,uinp_fd);
    
		/* Create input device into input sub-system */
		write(uinp_fd, &uinp, sizeof(uinp));
		ioctl(uinp_fd, UI_DEV_CREATE);

}
 我们来看一下设备有没有我们创建的触摸屏ShaoTouchScreen
 


到这设别已经创建完成,接下来我们需要发出touch事件,MULTI_TOUCH 支持多点触摸,我们这边模拟的是MultiTouch下单点触摸

#include <jni.h>
#include <stdint.h>
#include <cutils/log.h>
#include <linux/uinput.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> 
#include <cutils/properties.h>


#define ACTION_DOWN 0
#define ACTION_UP 1
#define ACTION_MOVE 2

#define API_EXPORT __attribute__((visibility("default")))

static int global_tracking_id = 1;

namespace shao{

extern long jptr(void *native_dtr) {
    return reinterpret_cast<intptr_t>(native_dtr);
};


extern void *native(long ptr) {
    return reinterpret_cast<void *>(ptr);
};

}

using namespace nvr;

extern "C"{

	static bool device_writeEvent(int fd, uint16_t type, uint16_t keycode, int32_t value) {
    struct input_event ev;  
  
    memset(&ev, 0, sizeof(struct input_event));  
  
    ev.type = type;  
    ev.code = keycode;  
    ev.value = value;
    if (write(fd, &ev, sizeof(struct input_event)) < 0) {
		char * mesg = strerror(errno);
        ALOGD("nibiru uinput errormag info :%s\n",mesg); 
        return false;
    }  

    return true;
   }

	static void execute_sleep(int duration_msec)
	{
		usleep(duration_msec*1000); 
	}

        
        //startX startY 代表触摸down的坐标 endX 和 endY代表Up的坐标
        //如果startX = startY 同时  endX = endY ,没有actionMove事件产生只有actionDown和actionUp事件
	API_EXPORT void nvr_execute_touch(int fd,int startX,int startY,int endX,int endY)
	{	
                //actionDown事件 
		device_writeEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, global_tracking_id++);
		device_writeEvent(fd, EV_ABS, ABS_MT_POSITION_X, startX);
		device_writeEvent(fd, EV_ABS, ABS_MT_POSITION_Y, startY);
		device_writeEvent(fd, EV_ABS, ABS_MT_PRESSURE, 60);
		device_writeEvent(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
		device_writeEvent(fd, EV_SYN, SYN_REPORT, 0);
 
                //action_move事件
		device_writeEvent(fd, EV_ABS, ABS_MT_POSITION_X, endX);
		device_writeEvent(fd, EV_ABS, ABS_MT_POSITION_Y, endY);
		device_writeEvent(fd, EV_SYN, SYN_REPORT, 0);
                 
                //action_up事件
		device_writeEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, -1);
                //事件发送完毕需要sync 
		device_writeEvent(fd, EV_SYN, SYN_REPORT, 0);
		ALOGD(" one touch operation send end");
	}


	API_EXPORT void sendScreenTouch(int startX,int startY,int endX,int endY)
	{
	
		char package_status[PROPERTY_VALUE_MAX]={0};
		property_get("touch_screen_device",package_status,NULL);
		int fd =  atoi(package_status);
        ALOGD("touch screen fd = %d\n",fd);
		nvr_execute_touch(fd,startX,startY,endX,endY);
		execute_sleep(20);
	}

}

到这我们就可以模拟SingleTouch了,如果需要模拟多点触摸的话同样的思路,按键码可能不一致需要组合

到这一旦发送成功事件会被input模块识别,会对我们的X和Y坐标进行运算,这边将代码路径贴出来作为提示

 涉及代码路径:\frameworks\native\services\inputflinger\inputreader.cpp

void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
    size_t outCount = 0;
    BitSet32 newPointerIdBits;

    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
        const MultiTouchMotionAccumulator::Slot* inSlot =
                mMultiTouchMotionAccumulator.getSlot(inIndex);
        if (!inSlot->isInUse()) {
            continue;
        }

        if (outCount >= MAX_POINTERS) {
#if DEBUG_POINTERS
            ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
                    "ignoring the rest.",
                    getDeviceName().string(), MAX_POINTERS);
#endif
            break; // too many fingers!
        }

        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
        outPointer.x = inSlot->getX();
        outPointer.y = inSlot->getY();
        outPointer.pressure = inSlot->getPressure();
        outPointer.touchMajor = inSlot->getTouchMajor();
        outPointer.touchMinor = inSlot->getTouchMinor();
        outPointer.toolMajor = inSlot->getToolMajor();
        outPointer.toolMinor = inSlot->getToolMinor();
        outPointer.orientation = inSlot->getOrientation();
        outPointer.distance = inSlot->getDistance();
        outPointer.tiltX = 0;
        outPointer.tiltY = 0;

		ALOGD("shao fwk receive multi x = %d , y = %d\n", outPointer.x , outPointer.y);

        outPointer.toolType = inSlot->getToolType();
        if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
            outPointer.toolType = mTouchButtonAccumulator.getToolType();
            if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
                outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
            }
        }

 MultiTouchInputMapper::syncTouch()函数里会拿到event数据

 在 TouchInputMapper::cookPointerData() 函数里会根据屏幕方向来计算x,y坐标传给app

        switch (mSurfaceOrientation) {
        case DISPLAY_ORIENTATION_90:
            //x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            //y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
            x = xTransformed;
		    y = yTransformed;
		    ALOGD("shao fwk hadlere 1 x = %f , y = %f \n",x ,y);
            left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
            top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
            orientation -= M_PI_2;
            if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
                orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
            }
            break;
        case DISPLAY_ORIENTATION_180:
            x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
            y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
		    ALOGD("shao fwk hadlere 2 x = %f , y = %f\n",x ,y);
            left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
            right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
            bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
            top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
            orientation -= M_PI;
            if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
                orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
            }
            break;
        case DISPLAY_ORIENTATION_270:
            x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
            y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
		    ALOGD("shao fwk hadlere 3 x = %f , y = %f\n",x ,y);
            left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
            right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
            bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            orientation += M_PI_2;
            if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) {
                orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
            }
            break;
        default:
            x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
			ALOGD("shao fwk hadlere 4 x = %f , y = %f\n",x ,y);

所以大家注意一下x y坐标的转换



  

               

     

 

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值