Android2.3 鼠标输入集成
jopen
13年前
<span style="font-size:18px;">Android系统对鼠标的支持并不好,因为Android系统原本是为手机量身定做的,手机系统基本上是不需要鼠标的。但是随着Android系统移植到其他领域,对鼠标的支持也越来越有意义。<br /> 在android中鼠标的绘制代码在:<br /> \frameworks\base\services\java\com\android\server\WindowManagerService.java ,<br /> 在performLayoutAndPlaceSurfacesLockedInner方法中:<br /> if (mMouseSurface == null)<br /> {<br /> int mMx, mMy, mMw, mMh;<br /> Canvas mCanvas;<br /> Path mPath = new Path();<br /> mMw = 13;<br /> mMh = 22;<br /> mMx = (mDisplay.getWidth() - mMw) / 2;<br /> mMy = (mDisplay.getHeight() - mMh) / 2;<br /> try<br /> {<br /> /*<br /> * First Mouse event, create Surface<br /> */<br /> mMouseSurface =<br /> new Surface(mFxSession,<br /> 0, -1, mMw, mMh,<br /> PixelFormat.TRANSPARENT,<br /> Surface.FX_SURFACE_NORMAL);<br /> mCanvas = mMouseSurface.lockCanvas(null);<br /> Paint tPaint = new Paint();<br /> tPaint.setStyle(Paint.Style.STROKE);<br /> tPaint.setStrokeWidth(2);<br /> tPaint.setColor(0xffffffff);<br /> mPath.moveTo(0.0f, 0.0f);<br /> mPath.lineTo(12.0f, 12.0f);<br /> mPath.lineTo(7.0f, 12.0f);<br /> mPath.lineTo(11.0f, 20.0f);<br /> mPath.lineTo(8.0f, 21.0f);<br /> mPath.lineTo(4.0f, 13.0f);<br /> mPath.lineTo(0.0f, 17.0f);<br /> mPath.close();<br /> mCanvas.clipPath(mPath);<br /> mCanvas.drawColor(0xff000000);<br /> mCanvas.drawPath(mPath, tPaint);<br /> <br /> <br /> mMouseSurface.unlockCanvasAndPost(mCanvas);<br /> mMouseSurface.openTransaction();<br /> mMouseSurface.setSize(mMw, mMh);<br /> mMouseSurface.closeTransaction();<br /> }<br /> catch (Exception e)<br /> {<br /> Slog.e(TAG, "Exception creating mouse surface",e);<br /> }<br /> mMlx = mMx;<br /> mMly = mMy;<br /> mMlw = mMw;<br /> mMlh = mMh;<br /> }<br /> 直接利用Surface绘制鼠标光标的形状,一般情况下都是利用bitmap位图资源进行绘制,这里简化了。<br /> 对于android2.3版本来说,发布的arm/sh4版本没有提供鼠标功能,直到前几天才发现mips社区已经将这块代码加入进去了,之间我在arm上搞这个功能时,重新进行实现,不过增加了一点功能,比如可以通过java 更换鼠标光标,并编写了一个服务用于光标隐藏的方法等。<br /> 接口函数如下:<br /> int cursor_util_initialize();<br /> int cursor_util_finalize();<br /> int cursor_util_start();<br /> <span style="white-space:pre;"> </span>int cursor_util_stop();<br /> <span style="white-space:pre;"> </span>int cursor_util_locate(int x,int y);<br /> <span style="white-space:pre;"> </span>int cursor_util_move(int dx,int dy);<br /> <span style="white-space:pre;"> </span>int cursor_util_get_location(int *x,int *y);<br /> <span style="white-space:pre;"> </span>int cursor_util_set_timeout(int timeout);<br /> <span style="white-space:pre;"> </span>int cursor_util_get_timeout(int *timeout);<br /> <span style="white-space:pre;"> </span>int cursor_util_set_bitmap(const char*buf,int w,int h);<br /> 由于代码涉及到版权问题,这里就将mips做的改动介绍一下吧:<br /> <br /> <br /> 1、首先在eventhub.cpp中增加鼠标的支持:<br /> 代码路径: \frameworks\base\libs\ui\EventHub.cpp<br /> /* The input device has mouse. */<br /> INPUT_DEVICE_CLASS_MOUSE = 0x00000100,<br /> if (test_bit(BTN_MOUSE, key_bitmask)) {<br /> uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];<br /> memset(rel_bitmask, 0, sizeof(rel_bitmask));<br /> LOGV("Getting relative controllers...");<br /> if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {<br /> if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {<br /> <span style="white-space:pre;"> </span>/* 增加鼠标类的事件处理流程 */<br /> <span style="white-space:pre;"> </span>if (test_bit(BTN_LEFT, key_bitmask) && test_bit(BTN_RIGHT, key_bitmask))<br /> device->classes |= INPUT_DEVICE_CLASS_MOUSE;<br /> else<br /> <span style="white-space:pre;"> </span>device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;<br /> }<br /> }<br /> }<br /> <br /> 2、inputreader中增加mousemapper的处理代码:<br /> 代码路径: \frameworks\base\libs\ui\InputReader.cpp<br /> 重点就是下面两个函数:(注意鼠标传上来的值是相对坐标,如果需要处理绝对坐标,在process函数进行转换即可,比较简单)<br /> void MouseInputMapper::process(const RawEvent* rawEvent) {<br /> switch (rawEvent->type)<br /> {<br /> case EV_KEY:<br /> switch (rawEvent->scanCode)<br /> {<br /> case BTN_MOUSE:<br /> mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;<br /> mAccumulator.btnMouse = rawEvent->value != 0;<br /> sync(rawEvent->when);<br /> break;<br /> case BTN_RIGHT:<br /> mAccumulator.fields |= Accumulator::FIELD_BTN_RIGHT;<br /> mAccumulator.btnRight = rawEvent->value != 0;<br /> sync(rawEvent->when);<br /> break;<br /> case BTN_MIDDLE:<br /> mAccumulator.fields |= Accumulator::FIELD_BTN_MIDDLE;<br /> mAccumulator.btnMiddle = rawEvent->value != 0;<br /> sync(rawEvent->when);<br /> break;<br /> }<br /> break;<br /> <br /> <br /> <br /> <br /> case EV_REL:<br /> switch (rawEvent->scanCode)<br /> {<br /> case REL_X:<br /> mAccumulator.fields |= Accumulator::FIELD_REL_X;<br /> mAccumulator.relX = rawEvent->value;<br /> break;<br /> case REL_Y:<br /> mAccumulator.fields |= Accumulator::FIELD_REL_Y;<br /> mAccumulator.relY = rawEvent->value;<br /> break;<br /> }<br /> break;<br /> <br /> <br /> <br /> <br /> case EV_SYN:<br /> switch (rawEvent->scanCode)<br /> {<br /> case SYN_REPORT:<br /> sync(rawEvent->when);<br /> break;<br /> }<br /> break;<br /> }<br /> }<br /> <br /> <br /> void MouseInputMapper::sync(nsecs_t when)<br /> {<br /> uint32_t fields = mAccumulator.fields;<br /> if (fields == 0)<br /> {<br /> return; // no new state changes, so nothing to do<br /> }<br /> <br /> <br /> <br /> <br /> int motionEventAction;<br /> PointerCoords pointerCoords;<br /> nsecs_t downTime;<br /> { // acquire lock<br /> AutoMutex _l(mLock);<br /> <br /> <br /> <br /> <br /> if (fields & Accumulator::FIELD_BTN_RIGHT)<br /> {<br /> getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_DPAD, 0,<br /> mAccumulator.btnRight ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,<br /> AKEY_EVENT_FLAG_FROM_SYSTEM, 0x4 /*Keycode for back key*/,<br /> 0x18 /*Scancode*/, mContext->getGlobalMetaState(), when);<br /> }<br /> <br /> <br /> <br /> <br /> if (fields & Accumulator::FIELD_BTN_MIDDLE)<br /> {<br /> getDispatcher()->notifyKey(when, getDeviceId(),AINPUT_SOURCE_DPAD, 0,<br /> mAccumulator.btnMiddle ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,<br /> AKEY_EVENT_FLAG_FROM_SYSTEM, 0x52 /*Keycode for menu key*/,<br /> 0x19 /*Scancode*/, mContext->getGlobalMetaState(), when);<br /> }<br /> <br /> <br /> <br /> <br /> bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;<br /> if (downChanged)<br /> {<br /> if (mAccumulator.btnMouse)<br /> {<br /> mLocked.down = true;<br /> mLocked.downTime = when;<br /> }<br /> else<br /> {<br /> mLocked.down = false;<br /> }<br /> motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;<br /> }<br /> else<br /> {<br /> motionEventAction = AMOTION_EVENT_ACTION_MOVE;<br /> }<br /> <br /> <br /> <br /> <br /> downTime = mLocked.downTime;<br /> <br /> <br /> <br /> <br /> float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX : 0.0f;<br /> float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY : 0.0f;<br /> <br /> <br /> <br /> <br /> int32_t screenWidth;<br /> int32_t screenHeight;<br /> int32_t orientation;<br /> <br /> <br /> <br /> <br /> if (mAssociatedDisplayId < 0 || ! getPolicy()->getDisplayInfo(mAssociatedDisplayId, &screenWidth, &screenHeight, &orientation))<br /> {<br /> return;<br /> }<br /> <br /> <br /> <br /> <br /> float temp;<br /> switch (orientation)<br /> {<br /> case InputReaderPolicyInterface::ROTATION_90:<br /> {<br /> temp = x;<br /> x = y;<br /> y = - temp;<br /> temp = screenHeight;<br /> screenHeight = screenWidth;<br /> screenWidth = temp;<br /> break;<br /> }<br /> case InputReaderPolicyInterface::ROTATION_180:<br /> {<br /> x = - x;<br /> y = - y;<br /> break;<br /> }<br /> case InputReaderPolicyInterface::ROTATION_270:<br /> {<br /> temp = x;<br /> x = - y;<br /> y = temp;<br /> temp = screenHeight;<br /> screenHeight = screenWidth;<br /> screenWidth = temp;<br /> break;<br /> }<br /> }<br /> <br /> <br /> <br /> <br /> mAccumulator.absX =<br /> (mAccumulator.absX + x) > screenWidth ? screenWidth -1 :<br /> ((mAccumulator.absX + x) < 0 ? 0 : mAccumulator.absX + x);<br /> mAccumulator.absY = (mAccumulator.absY + y) > screenHeight ? screenHeight -1 :<br /> ((mAccumulator.absY + y) < 0 ? 0 : mAccumulator.absY + y);<br /> pointerCoords.x = mAccumulator.absX;<br /> pointerCoords.y = mAccumulator.absY;<br /> pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;<br /> pointerCoords.size = 0;<br /> pointerCoords.touchMajor = 0;<br /> pointerCoords.touchMinor = 0;<br /> pointerCoords.toolMajor = 0;<br /> pointerCoords.toolMinor = 0;<br /> pointerCoords.orientation = 0;<br /> <br /> <br /> <br /> <br /> } // release lock<br /> <br /> <br /> <br /> <br /> int32_t metaState = mContext->getGlobalMetaState();<br /> int32_t pointerId = 0;<br /> getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_MOUSE, 0,<br /> motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,<br /> 1, &pointerId, &pointerCoords, 1, 1, downTime);<br /> mAccumulator.clear();<br /> }<br /> 3、事件分发InputDispatcher.cpp 函数修改<br /> bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;<br /> bool isMouseEvent = entry->source & (AINPUT_SOURCE_MOUSE & ~AINPUT_SOURCE_CLASS_POINTER);<br /> bool isTouchEvent = entry->source & (AINPUT_SOURCE_TOUCHSCREEN & ~AINPUT_SOURCE_CLASS_POINTER);<br /> bool isDownEvent = (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;<br /> <br /> <br /> // Identify targets.<br /> if (! mCurrentInputTargetsValid) {<br /> int32_t injectionResult;<br /> if ( isPointerEvent && (isTouchEvent || (isMouseEvent && (isDownEvent || mTouchState.down))))<br /> {<br /> // Touch-like event. (eg. touchscreen or mouse drag-n-drop )<br /> injectionResult = findTouchedWindowTargetsLocked(currentTime, entry, nextWakeupTime);<br /> }<br /> else<br /> {<br /> // Non touch event. (eg. trackball or mouse simple move)<br /> injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, nextWakeupTime);<br /> } <br /> <br /> 4、java 处理鼠标事件<br /> private void dispatchMotion(MotionEvent event, boolean sendDone) {<br /> int source = event.getSource();<br /> if ((source & (InputDevice.SOURCE_MOUSE & ~InputDevice.SOURCE_CLASS_POINTER)) != 0) {<br /> try{<br /> wm.moveMouseSurface((int)event.getX() - (int)event.getXOffset(),<br /> (int)event.getY() - (int)event.getYOffset());<br /> }catch (RemoteException e){<br /> Log.e(TAG,"RemoteException thrown in moveMouseSurface");<br /> }<br /> dispatchPointer(event, sendDone);<br /> } else if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {<br /> dispatchPointer(event, sendDone);<br /> } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {<br /> dispatchTrackball(event, sendDone);<br /> } else {<br /> // TODO<br /> Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);<br /> if (sendDone) {<br /> finishInputEvent();<br /> }<br /> }<br /> }<br /> <br /> 5、利用binder传递到WindowManagerService.java 中<br /> 代码路径: frameworks\base\services\java\android\server\WindowManagerService.java<br /> 如果没有硬件光标层,可能就需要在此进行修改.<br /> /* 移动光标 ,如果有硬件光标层,直接利用jni调用即可 */<br /> public boolean moveMouseSurface(int x, int y)<br /> {<br /> if (mMouseSurface != null && (x != 0 || y != 0))<br /> {<br /> synchronized(mWindowMap)<br /> {<br /> Surface.openTransaction();<br /> WindowState top =<br /> (WindowState)mWindows.get(mWindows.size() - 1);<br /> try<br /> {<br /> int mDisplayWidth = mDisplay.getWidth();<br /> mMlx = x;<br /> mMly = y;<br /> mMouseSurface.setPosition(mMlx, mMly);<br /> mMouseSurface.setLayer(top.mAnimLayer + 1);<br /> if (!mMouseDisplayed)<br /> {<br /> mMouseSurface.show();<br /> mMouseDisplayed = !mMouseDisplayed;<br /> }<br /> }<br /> catch ( RuntimeException e)<br /> {<br /> Slog.e(TAG, "Failure showing mouse surface",e);<br /> }<br /> <br /> Surface.closeTransaction();<br /> }<br /> }<br /> <br /> return true;<br /> }<br /> <br /> /* 绘制光标图形 */<br /> private final void performLayoutAndPlaceSurfacesLockedInner(<br /> boolean recoveringMemory) {<br /> ...<br /> if (mMouseSurface == null)<br /> {<br /> int mMx, mMy, mMw, mMh;<br /> Canvas mCanvas;<br /> Path mPath = new Path();<br /> mMw = 13;<br /> mMh = 22;<br /> mMx = (mDisplay.getWidth() - mMw) / 2;<br /> mMy = (mDisplay.getHeight() - mMh) / 2;<br /> try<br /> {<br /> /*<br /> * First Mouse event, create Surface<br /> */<br /> mMouseSurface =<br /> new Surface(mFxSession,<br /> 0, -1, mMw, mMh,<br /> PixelFormat.TRANSPARENT,<br /> Surface.FX_SURFACE_NORMAL);<br /> mCanvas = mMouseSurface.lockCanvas(null);<br /> Paint tPaint = new Paint();<br /> tPaint.setStyle(Paint.Style.STROKE);<br /> tPaint.setStrokeWidth(2);<br /> tPaint.setColor(0xffffffff);<br /> mPath.moveTo(0.0f, 0.0f);<br /> mPath.lineTo(12.0f, 12.0f);<br /> mPath.lineTo(7.0f, 12.0f);<br /> mPath.lineTo(11.0f, 20.0f);<br /> mPath.lineTo(8.0f, 21.0f);<br /> mPath.lineTo(4.0f, 13.0f);<br /> mPath.lineTo(0.0f, 17.0f);<br /> mPath.close();<br /> mCanvas.clipPath(mPath);<br /> mCanvas.drawColor(0xff000000);<br /> mCanvas.drawPath(mPath, tPaint);<br /> <br /> mMouseSurface.unlockCanvasAndPost(mCanvas);<br /> mMouseSurface.openTransaction();<br /> mMouseSurface.setSize(mMw, mMh);<br /> mMouseSurface.closeTransaction();<br /> <br /> }<br /> catch (Exception e)<br /> {<br /> Slog.e(TAG, "Exception creating mouse surface",e);<br /> }<br /> mMlx = mMx;<br /> mMly = mMy;<br /> mMlw = mMw;<br /> mMlh = mMh;<br /> }<br /> <br /> ...<br /> }<br /> <br /> <br /> 6、直接利用jni实现硬件光标层<br /> frameworks\base\services\jni\com_android_server_WindowManagerService.cpp<br /> <br /> ok,基本上的工作已经完成,此时鼠标将可以正常工作。可以看出Android系统鼠标光标的定制不是非常容易,需要修改不少的代码。不过mips社区已经将功能完成了90%了,仅需要简单与光标层对接一下接口即可。</span>