diff options
Diffstat (limited to 'services/input/PointerController.cpp')
-rw-r--r-- | services/input/PointerController.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp new file mode 100644 index 0000000..92af51e --- /dev/null +++ b/services/input/PointerController.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PointerController" + +//#define LOG_NDEBUG 0 + +// Log debug messages about pointer updates +#define DEBUG_POINTER_UPDATES 0 + +#include "PointerController.h" + +#include <cutils/log.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> +#include <SkXfermode.h> + +namespace android { + +// --- PointerController --- + +PointerController::PointerController(int32_t pointerLayer) : + mPointerLayer(pointerLayer) { + AutoMutex _l(mLock); + + mLocked.displayWidth = -1; + mLocked.displayHeight = -1; + mLocked.displayOrientation = DISPLAY_ORIENTATION_0; + + mLocked.pointerX = 0; + mLocked.pointerY = 0; + mLocked.buttonState = 0; + + mLocked.iconBitmap = NULL; + mLocked.iconHotSpotX = 0; + mLocked.iconHotSpotY = 0; + + mLocked.wantVisible = false; + mLocked.visible = false; + mLocked.drawn = false; +} + +PointerController::~PointerController() { + if (mSurfaceControl != NULL) { + mSurfaceControl->clear(); + mSurfaceControl.clear(); + } + + if (mSurfaceComposerClient != NULL) { + mSurfaceComposerClient->dispose(); + mSurfaceComposerClient.clear(); + } + + delete mLocked.iconBitmap; +} + +bool PointerController::getBounds(float* outMinX, float* outMinY, + float* outMaxX, float* outMaxY) const { + AutoMutex _l(mLock); + + return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); +} + +bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, + float* outMaxX, float* outMaxY) const { + if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) { + return false; + } + + *outMinX = 0; + *outMinY = 0; + switch (mLocked.displayOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + *outMaxX = mLocked.displayHeight; + *outMaxY = mLocked.displayWidth; + break; + default: + *outMaxX = mLocked.displayWidth; + *outMaxY = mLocked.displayHeight; + break; + } + return true; +} + +void PointerController::move(float deltaX, float deltaY) { +#if DEBUG_POINTER_UPDATES + LOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); +#endif + if (deltaX == 0.0f && deltaY == 0.0f) { + return; + } + + AutoMutex _l(mLock); + + setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); +} + +void PointerController::setButtonState(uint32_t buttonState) { +#if DEBUG_POINTER_UPDATES + LOGD("Set button state 0x%08x", buttonState); +#endif + AutoMutex _l(mLock); + + if (mLocked.buttonState != buttonState) { + mLocked.buttonState = buttonState; + mLocked.wantVisible = true; + updateLocked(); + } +} + +uint32_t PointerController::getButtonState() const { + AutoMutex _l(mLock); + + return mLocked.buttonState; +} + +void PointerController::setPosition(float x, float y) { +#if DEBUG_POINTER_UPDATES + LOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); +#endif + AutoMutex _l(mLock); + + setPositionLocked(x, y); +} + +void PointerController::setPositionLocked(float x, float y) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + if (x <= minX) { + mLocked.pointerX = minX; + } else if (x >= maxX) { + mLocked.pointerX = maxX; + } else { + mLocked.pointerX = x; + } + if (y <= minY) { + mLocked.pointerY = minY; + } else if (y >= maxY) { + mLocked.pointerY = maxY; + } else { + mLocked.pointerY = y; + } + mLocked.wantVisible = true; + updateLocked(); + } +} + +void PointerController::getPosition(float* outX, float* outY) const { + AutoMutex _l(mLock); + + *outX = mLocked.pointerX; + *outY = mLocked.pointerY; +} + +void PointerController::updateLocked() { + bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap; + + if (wantVisibleAndHavePointerIcon) { + // Want the pointer to be visible. + // Ensure the surface is created and drawn. + if (!createSurfaceIfNeededLocked() || !drawPointerIfNeededLocked()) { + return; + } + } else { + // Don't want the pointer to be visible. + // If it is not visible then we are done. + if (mSurfaceControl == NULL || !mLocked.visible) { + return; + } + } + + status_t status = mSurfaceComposerClient->openTransaction(); + if (status) { + LOGE("Error opening surface transaction to update pointer surface."); + return; + } + + if (wantVisibleAndHavePointerIcon) { + status = mSurfaceControl->setPosition( + mLocked.pointerX - mLocked.iconHotSpotX, + mLocked.pointerY - mLocked.iconHotSpotY); + if (status) { + LOGE("Error %d moving pointer surface.", status); + goto CloseTransaction; + } + + if (!mLocked.visible) { + status = mSurfaceControl->setLayer(mPointerLayer); + if (status) { + LOGE("Error %d setting pointer surface layer.", status); + goto CloseTransaction; + } + + status = mSurfaceControl->show(mPointerLayer); + if (status) { + LOGE("Error %d showing pointer surface.", status); + goto CloseTransaction; + } + + mLocked.visible = true; + } + } else { + if (mLocked.visible) { + status = mSurfaceControl->hide(); + if (status) { + LOGE("Error %d hiding pointer surface.", status); + goto CloseTransaction; + } + + mLocked.visible = false; + } + } + +CloseTransaction: + status = mSurfaceComposerClient->closeTransaction(); + if (status) { + LOGE("Error closing surface transaction to update pointer surface."); + } +} + +void PointerController::setDisplaySize(int32_t width, int32_t height) { + AutoMutex _l(mLock); + + if (mLocked.displayWidth != width || mLocked.displayHeight != height) { + mLocked.displayWidth = width; + mLocked.displayHeight = height; + + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + mLocked.pointerX = (minX + maxX) * 0.5f; + mLocked.pointerY = (minY + maxY) * 0.5f; + } else { + mLocked.pointerX = 0; + mLocked.pointerY = 0; + } + + updateLocked(); + } +} + +void PointerController::setDisplayOrientation(int32_t orientation) { + AutoMutex _l(mLock); + + if (mLocked.displayOrientation != orientation) { + float absoluteX, absoluteY; + + // Map from oriented display coordinates to absolute display coordinates. + switch (mLocked.displayOrientation) { + case DISPLAY_ORIENTATION_90: + absoluteX = mLocked.displayWidth - mLocked.pointerY; + absoluteY = mLocked.pointerX; + break; + case DISPLAY_ORIENTATION_180: + absoluteX = mLocked.displayWidth - mLocked.pointerX; + absoluteY = mLocked.displayHeight - mLocked.pointerY; + break; + case DISPLAY_ORIENTATION_270: + absoluteX = mLocked.pointerY; + absoluteY = mLocked.displayHeight - mLocked.pointerX; + break; + default: + absoluteX = mLocked.pointerX; + absoluteY = mLocked.pointerY; + break; + } + + // Map from absolute display coordinates to oriented display coordinates. + switch (orientation) { + case DISPLAY_ORIENTATION_90: + mLocked.pointerX = absoluteY; + mLocked.pointerY = mLocked.displayWidth - absoluteX; + break; + case DISPLAY_ORIENTATION_180: + mLocked.pointerX = mLocked.displayWidth - absoluteX; + mLocked.pointerY = mLocked.displayHeight - absoluteY; + break; + case DISPLAY_ORIENTATION_270: + mLocked.pointerX = mLocked.displayHeight - absoluteY; + mLocked.pointerY = absoluteX; + break; + default: + mLocked.pointerX = absoluteX; + mLocked.pointerY = absoluteY; + break; + } + + mLocked.displayOrientation = orientation; + + updateLocked(); + } +} + +void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) { + AutoMutex _l(mLock); + + if (mLocked.iconBitmap) { + delete mLocked.iconBitmap; + mLocked.iconBitmap = NULL; + } + + if (bitmap) { + mLocked.iconBitmap = new SkBitmap(); + bitmap->copyTo(mLocked.iconBitmap, SkBitmap::kARGB_8888_Config); + } + + mLocked.iconHotSpotX = hotSpotX; + mLocked.iconHotSpotY = hotSpotY; + mLocked.drawn = false; +} + +bool PointerController::createSurfaceIfNeededLocked() { + if (!mLocked.iconBitmap) { + // If we don't have a pointer icon, then no point allocating a surface now. + return false; + } + + if (mSurfaceComposerClient == NULL) { + mSurfaceComposerClient = new SurfaceComposerClient(); + } + + if (mSurfaceControl == NULL) { + mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(), + String8("Pointer Icon"), 0, + mLocked.iconBitmap->width(), mLocked.iconBitmap->height(), + PIXEL_FORMAT_RGBA_8888); + if (mSurfaceControl == NULL) { + LOGE("Error creating pointer surface."); + return false; + } + } + return true; +} + +bool PointerController::drawPointerIfNeededLocked() { + if (!mLocked.drawn) { + if (!mLocked.iconBitmap) { + return false; + } + + if (!resizeSurfaceLocked(mLocked.iconBitmap->width(), mLocked.iconBitmap->height())) { + return false; + } + + sp<Surface> surface = mSurfaceControl->getSurface(); + + Surface::SurfaceInfo surfaceInfo; + status_t status = surface->lock(&surfaceInfo); + if (status) { + LOGE("Error %d locking pointer surface before drawing.", status); + return false; + } + + SkBitmap surfaceBitmap; + ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format); + surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, surfaceInfo.w, surfaceInfo.h, bpr); + surfaceBitmap.setPixels(surfaceInfo.bits); + + SkCanvas surfaceCanvas; + surfaceCanvas.setBitmapDevice(surfaceBitmap); + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + surfaceCanvas.drawBitmap(*mLocked.iconBitmap, 0, 0, &paint); + + status = surface->unlockAndPost(); + if (status) { + LOGE("Error %d unlocking pointer surface after drawing.", status); + return false; + } + } + + mLocked.drawn = true; + return true; +} + +bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) { + status_t status = mSurfaceComposerClient->openTransaction(); + if (status) { + LOGE("Error opening surface transaction to resize pointer surface."); + return false; + } + + status = mSurfaceControl->setSize(width, height); + if (status) { + LOGE("Error %d setting pointer surface size.", status); + return false; + } + + status = mSurfaceComposerClient->closeTransaction(); + if (status) { + LOGE("Error closing surface transaction to resize pointer surface."); + return false; + } + + return true; +} + +} // namespace android |