概述
Android系统对鼠标的支持并不好,因为Android系统原本是为手机量身定做的,手机系统基本上是不需要鼠标的。但是随着Android系统移植到其他领域,对鼠标的支持也越来越有意义。
在android中鼠标的绘制代码在:
frameworksbaseservicesjavacomandroidserverWindowManagerService.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中增加鼠标的支持:
代码路径: frameworksbaselibsuiEventHub.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的处理代码:
代码路径: frameworksbaselibsuiInputReader.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 中
代码路径: frameworksbaseservicesjavaandroidserverWindowManagerService.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实现硬件光标层
frameworksbaseservicesjnicom_android_server_WindowManagerService.cpp
ok,基本上的工作已经完成,此时鼠标将可以正常工作。可以看出Android系统鼠标光标的定制不是非常容易,需要修改不少的代码。不过mips社区已经将功能完成了90%了,仅需要简单与光标层对接一下接口即可。
最后
以上就是如意玉米为你收集整理的Android 增加对鼠标的支持的全部内容,希望文章能够帮你解决Android 增加对鼠标的支持所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复