Android系统对鼠标的支持并不好,因为Android系统原本是为手机量身定做的,手机系统基本上是不需要鼠标的。但是随着Android系统移植到其他领域,对鼠标的支持也越来越有意义。
在android中鼠标的绘制代码在:
\frameworks\base\services\java\com\android\server\WindowManagerService.java ,
在performLayoutAndPlaceSurfacesLockedInner方法中:
if (mMouseSurface == null)
{
int mMx, mMy, mMw, mMh;
Canvas mCanvas;
Path mPath = new Path();
mMw = 13;
mMh = 22;
mMx = (mDisplay.getWidth() - mMw) / 2;
mMy = (mDisplay.getHeight() - mMh) / 2;
try
{
/*
* First Mouse event, create Surface
*/
mMouseSurface =
new Surface(mFxSession,
0, -1, mMw, mMh,
PixelFormat.TRANSPARENT,
Surface.FX_SURFACE_NORMAL);
mCanvas = mMouseSurface.lockCanvas(null);
Paint tPaint = new Paint();
tPaint.setStyle(Paint.Style.STROKE);
tPaint.setStrokeWidth(2);
tPaint.setColor(0xffffffff);
mPath.moveTo(0.0f, 0.0f);
mPath.lineTo(12.0f, 12.0f);
mPath.lineTo(7.0f, 12.0f);
mPath.lineTo(11.0f, 20.0f);
mPath.lineTo(8.0f, 21.0f);
mPath.lineTo(4.0f, 13.0f);
mPath.lineTo(0.0f, 17.0f);
mPath.close();
mCanvas.clipPath(mPath);
mCanvas.drawColor(0xff000000);
mCanvas.drawPath(mPath, tPaint);


mMouseSurface.unlockCanvasAndPost(mCanvas);
mMouseSurface.openTransaction();
mMouseSurface.setSize(mMw, mMh);
mMouseSurface.closeTransaction();
}
catch (Exception e)
{
Slog.e(TAG, "Exception creating mouse surface",e);
}
mMlx = mMx;
mMly = mMy;
mMlw = mMw;
mMlh = mMh;
}
直接利用Surface绘制鼠标光标的形状,一般情况下都是利用bitmap位图资源进行绘制,这里简化了。
对于android2.3版本来说,发布的arm/sh4版本没有提供鼠标功能,直到前几天才发现mips社区已经将这块代码加入进去了,之间我在arm上搞这个功能时,重新进行实现,不过增加了一点功能,比如可以通过java更换鼠标光标,并编写了一个服务用于光标隐藏的方法等。
接口函数如下:
int cursor_util_initialize();
int cursor_util_finalize();
int cursor_util_start();
int cursor_util_stop();
int cursor_util_locate(int x,int y);
int cursor_util_move(int dx,int dy);
int cursor_util_get_location(int *x,int *y);
int cursor_util_set_timeout(int timeout);
int cursor_util_get_timeout(int *timeout);
int cursor_util_set_bitmap(const char*buf,int w,int h);
由于代码涉及到版权问题,这里就将mips做的改动介绍一下吧:


1、首先在eventhub.cpp中增加鼠标的支持:
代码路径: \frameworks\base\libs\ui\EventHub.cpp
/* The input device has mouse. */
INPUT_DEVICE_CLASS_MOUSE = 0x00000100,
if (test_bit(BTN_MOUSE, key_bitmask)) {
uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
/* 增加鼠标类的事件处理流程 */
if (test_bit(BTN_LEFT, key_bitmask) && test_bit(BTN_RIGHT, key_bitmask))
device->classes |= INPUT_DEVICE_CLASS_MOUSE;
else
device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
}
}
}

2、inputreader中增加mousemapper的处理代码:
代码路径: \frameworks\base\libs\ui\InputReader.cpp
重点就是下面两个函数:(注意鼠标传上来的值是相对坐标,如果需要处理绝对坐标,在process函数进行转换即可,比较简单)
void MouseInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type)
{
case EV_KEY:
switch (rawEvent->scanCode)
{
case BTN_MOUSE:
mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = rawEvent->value != 0;
sync(rawEvent->when);
break;
case BTN_RIGHT:
mAccumulator.fields |= Accumulator::FIELD_BTN_RIGHT;
mAccumulator.btnRight = rawEvent->value != 0;
sync(rawEvent->when);
break;
case BTN_MIDDLE:
mAccumulator.fields |= Accumulator::FIELD_BTN_MIDDLE;
mAccumulator.btnMiddle = rawEvent->value != 0;
sync(rawEvent->when);
break;
}
break;




case EV_REL:
switch (rawEvent->scanCode)
{
case REL_X:
mAccumulator.fields |= Accumulator::FIELD_REL_X;
mAccumulator.relX = rawEvent->value;
break;
case REL_Y:
mAccumulator.fields |= Accumulator::FIELD_REL_Y;
mAccumulator.relY = rawEvent->value;
break;
}
break;




case EV_SYN:
switch (rawEvent->scanCode)
{
case SYN_REPORT:
sync(rawEvent->when);
break;
}
break;
}
}


void MouseInputMapper::sync(nsecs_t when)
{
uint32_t fields = mAccumulator.fields;
if (fields == 0)
{
return; // no new state changes, so nothing to do
}




int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
{ // acquire lock
AutoMutex _l(mLock);




if (fields & Accumulator::FIELD_BTN_RIGHT)
{
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_DPAD, 0,
mAccumulator.btnRight ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, 0x4 /*Keycode for back key*/,
0x18 /*Scancode*/, mContext->getGlobalMetaState(), when);
}




if (fields & Accumulator::FIELD_BTN_MIDDLE)
{
getDispatcher()->notifyKey(when, getDeviceId(),AINPUT_SOURCE_DPAD, 0,
mAccumulator.btnMiddle ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, 0x52 /*Keycode for menu key*/,
0x19 /*Scancode*/, mContext->getGlobalMetaState(), when);
}




bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
if (downChanged)
{
if (mAccumulator.btnMouse)
{
mLocked.down = true;
mLocked.downTime = when;
}
else
{
mLocked.down = false;
}
motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
}
else
{
motionEventAction = AMOTION_EVENT_ACTION_MOVE;
}




downTime = mLocked.downTime;




float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX : 0.0f;
float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY : 0.0f;




int32_t screenWidth;
int32_t screenHeight;
int32_t orientation;




if (mAssociatedDisplayId < 0 || ! getPolicy()->getDisplayInfo(mAssociatedDisplayId, &screenWidth, &screenHeight, &orientation))
{
return;
}




float temp;
switch (orientation)
{
case InputReaderPolicyInterface::ROTATION_90:
{
temp = x;
x = y;
y = - temp;
temp = screenHeight;
screenHeight = screenWidth;
screenWidth = temp;
break;
}
case InputReaderPolicyInterface::ROTATION_180:
{
x = - x;
y = - y;
break;
}
case InputReaderPolicyInterface::ROTATION_270:
{
temp = x;
x = - y;
y = temp;
temp = screenHeight;
screenHeight = screenWidth;
screenWidth = temp;
break;
}
}




mAccumulator.absX =
(mAccumulator.absX + x) > screenWidth ? screenWidth -1 :
((mAccumulator.absX + x) < 0 ? 0 : mAccumulator.absX + x);
mAccumulator.absY = (mAccumulator.absY + y) > screenHeight ? screenHeight -1 :
((mAccumulator.absY + y) < 0 ? 0 : mAccumulator.absY + y);
pointerCoords.x = mAccumulator.absX;
pointerCoords.y = mAccumulator.absY;
pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
pointerCoords.size = 0;
pointerCoords.touchMajor = 0;
pointerCoords.touchMinor = 0;
pointerCoords.toolMajor = 0;
pointerCoords.toolMinor = 0;
pointerCoords.orientation = 0;




} // release lock




int32_t metaState = mContext->getGlobalMetaState();
int32_t pointerId = 0;
getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_MOUSE, 0,
motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
1, &pointerId, &pointerCoords, 1, 1, downTime);
mAccumulator.clear();
}
3、事件分发InputDispatcher.cpp 函数修改
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
bool isMouseEvent = entry->source & (AINPUT_SOURCE_MOUSE & ~AINPUT_SOURCE_CLASS_POINTER);
bool isTouchEvent = entry->source & (AINPUT_SOURCE_TOUCHSCREEN & ~AINPUT_SOURCE_CLASS_POINTER);
bool isDownEvent = (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN;


// Identify targets.
if (! mCurrentInputTargetsValid) {
int32_t injectionResult;
if ( isPointerEvent && (isTouchEvent || (isMouseEvent && (isDownEvent || mTouchState.down))))
{
// Touch-like event. (eg. touchscreen or mouse drag-n-drop )
injectionResult = findTouchedWindowTargetsLocked(currentTime, entry, nextWakeupTime);
}
else
{
// Non touch event. (eg. trackball or mouse simple move)
injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, nextWakeupTime);
}

4、java 处理鼠标事件
private void dispatchMotion(MotionEvent event, boolean sendDone) {
int source = event.getSource();
if ((source & (InputDevice.SOURCE_MOUSE & ~InputDevice.SOURCE_CLASS_POINTER)) != 0) {
try{
wm.moveMouseSurface((int)event.getX() - (int)event.getXOffset(),
(int)event.getY() - (int)event.getYOffset());
}catch (RemoteException e){
Log.e(TAG,"RemoteException thrown in moveMouseSurface");
}
dispatchPointer(event, sendDone);
} else if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
dispatchPointer(event, sendDone);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
dispatchTrackball(event, sendDone);
} else {
// TODO
Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
if (sendDone) {
finishInputEvent();
}
}
}

5、利用binder传递到WindowManagerService.java 中
代码路径: frameworks\base\services\java\android\server\WindowManagerService.java
如果没有硬件光标层,可能就需要在此进行修改.
/* 移动光标 ,如果有硬件光标层,直接利用jni调用即可 */
public boolean moveMouseSurface(int x, int y)
{
if (mMouseSurface != null && (x != 0 || y != 0))
{
synchronized(mWindowMap)
{
Surface.openTransaction();
WindowState top =
(WindowState)mWindows.get(mWindows.size() - 1);
try
{
int mDisplayWidth = mDisplay.getWidth();
mMlx = x;
mMly = y;
mMouseSurface.setPosition(mMlx, mMly);
mMouseSurface.setLayer(top.mAnimLayer + 1);
if (!mMouseDisplayed)
{
mMouseSurface.show();
mMouseDisplayed = !mMouseDisplayed;
}
}
catch ( RuntimeException e)
{
Slog.e(TAG, "Failure showing mouse surface",e);
}

Surface.closeTransaction();
}
}

return true;
}

/* 绘制光标图形 */
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
...
if (mMouseSurface == null)
{
int mMx, mMy, mMw, mMh;
Canvas mCanvas;
Path mPath = new Path();
mMw = 13;
mMh = 22;
mMx = (mDisplay.getWidth() - mMw) / 2;
mMy = (mDisplay.getHeight() - mMh) / 2;
try
{
/*
* First Mouse event, create Surface
*/
mMouseSurface =
new Surface(mFxSession,
0, -1, mMw, mMh,
PixelFormat.TRANSPARENT,
Surface.FX_SURFACE_NORMAL);
mCanvas = mMouseSurface.lockCanvas(null);
Paint tPaint = new Paint();
tPaint.setStyle(Paint.Style.STROKE);
tPaint.setStrokeWidth(2);
tPaint.setColor(0xffffffff);
mPath.moveTo(0.0f, 0.0f);
mPath.lineTo(12.0f, 12.0f);
mPath.lineTo(7.0f, 12.0f);
mPath.lineTo(11.0f, 20.0f);
mPath.lineTo(8.0f, 21.0f);
mPath.lineTo(4.0f, 13.0f);
mPath.lineTo(0.0f, 17.0f);
mPath.close();
mCanvas.clipPath(mPath);
mCanvas.drawColor(0xff000000);
mCanvas.drawPath(mPath, tPaint);

mMouseSurface.unlockCanvasAndPost(mCanvas);
mMouseSurface.openTransaction();
mMouseSurface.setSize(mMw, mMh);
mMouseSurface.closeTransaction();

}
catch (Exception e)
{
Slog.e(TAG, "Exception creating mouse surface",e);
}
mMlx = mMx;
mMly = mMy;
mMlw = mMw;
mMlh = mMh;
}

...



6、直接利用jni实现硬件光标层
frameworks\base\services\jni\com_android_server_WindowManagerService.cpp
这个文件是一个新建的文件,在onload.cpp中增加调用
int register_android_server_WindowManagerService(JNIEnv* env);
并在android.mk将这个文件com_android_server_WindowManagerService.cpp加入编译

#define LOG_TAG "Input"#include "jni.h"#include "JNIHelp.h"#include <utils/misc.h>#include <utils/Log.h>#include <linux/fb.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>namespace android {static int fd = -1;static struct fb_cursor hw_cur;static void mouseInit(){    if (-1 == fd)        fd = open("/dev/graphics/cursor", O_RDWR);    hw_cur.set = FB_CUR_SETALL;    hw_cur.enable = 0;    hw_cur.hot.x = 0;    hw_cur.hot.y = 0;    ioctl(fd, 0x4608, &hw_cur);}static void android_server_WindwManagerService_setHWCursorPosition(JNIEnv *env, jobject obj, jint x, jint y){    if (-1 == fd)        mouseInit();    hw_cur.enable = 1;    hw_cur.set = FB_CUR_SETHOT;    hw_cur.hot.x = x;    hw_cur.hot.y = y;    ioctl(fd, 0x4608, &hw_cur);}static JNINativeMethod gWindowManagerMethods[] = {    /* name, signature, funcPtr */    { "setHWCursorPosition",              "(II)V",        (void *) android_server_WindwManagerService_setHWCursorPosition },};int register_android_server_WindowManagerService(JNIEnv* env){    jclass input = env->FindClass("com/android/server/WindowManagerService");    LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/WindowManagerService");    int res = jniRegisterNativeMethods(env, "com/android/server/WindowManagerService",                                        gWindowManagerMethods, NELEM(gWindowManagerMethods));    return res;}}; // namespace android

ok,基本上的工作已经完成,此时鼠标将可以正常工作。可以看出Android系统鼠标光标的定制不是非常容易,需要修改不少的代码。不过mips社区已经将功能完成了90%了,仅需要简单与光标层对接一下接口即可。



附注: 我将两者diff出来的代码一起上传,可以有文件遗漏,大家也可从mips网站下载gingerbread代码分析

更多相关文章

  1. android webview的代替品
  2. Android:你要的WebView与 JS 交互方式
  3. 最简单的基于FFmpeg的移动端例子:Android(安卓)视频转码器
  4. Android(安卓)中H.264/AVC codec的开发
  5. android语音识别方法
  6. Android(安卓)动态加载APK--代码安装、获取资源及Intent调用已安
  7. Android与JS之间跨平台异步调用实例详解
  8. 去掉android的屏幕上的title bar
  9. Android应用程序启动过程源代码分析

随机推荐

  1. Android 多线程之Handler
  2. Android热修复框架AndFix核心代码分析并
  3. Android用AlarmManager实现后台任务-andr
  4. 深入浅出Android的多线程
  5. dos窗口下输入adb无效的问题及解决办法
  6. Android安全框架:Verfied boot -- Secure
  7. Android Studio安装ButterKnife插件
  8. android 联系人快速搜索
  9. android 扩展屏幕 多屏显示
  10. Android实现滑动菜单—SlidingMenu