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>