summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.mk1
-rw-r--r--libs/androidfw/AssetManager.cpp231
-rw-r--r--libs/androidfw/BackupData.cpp6
-rw-r--r--libs/androidfw/ResourceTypes.cpp2479
-rw-r--r--libs/androidfw/TypeWrappers.cpp70
-rw-r--r--libs/androidfw/tests/Android.mk78
-rw-r--r--libs/androidfw/tests/BackupData_test.cpp438
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp41
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp115
-rw-r--r--libs/androidfw/tests/ObbFile_test.cpp2
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp214
-rw-r--r--libs/androidfw/tests/ResourceTypes_test.cpp4
-rw-r--r--libs/androidfw/tests/Split_test.cpp203
-rw-r--r--libs/androidfw/tests/TestHelpers.h17
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp109
-rw-r--r--libs/androidfw/tests/data/.gitignore2
-rw-r--r--libs/androidfw/tests/data/basic/AndroidManifest.xml21
-rw-r--r--libs/androidfw/tests/data/basic/R.h55
-rw-r--r--libs/androidfw/tests/data/basic/basic_arsc.h161
-rwxr-xr-xlibs/androidfw/tests/data/basic/build9
-rw-r--r--libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml3
-rw-r--r--libs/androidfw/tests/data/basic/res/layout/main.xml3
-rw-r--r--libs/androidfw/tests/data/basic/res/values-de/values.xml5
-rw-r--r--libs/androidfw/tests/data/basic/res/values-fr/values.xml5
-rw-r--r--libs/androidfw/tests/data/basic/res/values-sv/values.xml4
-rw-r--r--libs/androidfw/tests/data/basic/res/values/values.xml26
-rw-r--r--libs/androidfw/tests/data/basic/split_de_fr_arsc.h85
-rw-r--r--libs/androidfw/tests/data/feature/AndroidManifest.xml19
-rwxr-xr-xlibs/androidfw/tests/data/feature/build6
-rw-r--r--libs/androidfw/tests/data/feature/feature_arsc.h73
-rw-r--r--libs/androidfw/tests/data/feature/res/values/values.xml7
-rw-r--r--libs/androidfw/tests/data/lib/AndroidManifest.xml21
-rw-r--r--libs/androidfw/tests/data/lib/R.h22
-rwxr-xr-xlibs/androidfw/tests/data/lib/build6
-rw-r--r--libs/androidfw/tests/data/lib/lib_arsc.h84
-rw-r--r--libs/androidfw/tests/data/lib/res/values/values.xml8
-rw-r--r--libs/androidfw/tests/data/overlay/AndroidManifest.xml21
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build6
-rw-r--r--libs/androidfw/tests/data/overlay/overlay_arsc.h69
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml8
-rw-r--r--libs/hwui/AmbientShadow.cpp333
-rw-r--r--libs/hwui/AmbientShadow.h57
-rw-r--r--libs/hwui/Android.mk52
-rw-r--r--libs/hwui/Animator.cpp319
-rw-r--r--libs/hwui/Animator.h196
-rw-r--r--libs/hwui/AnimatorManager.cpp109
-rw-r--r--libs/hwui/AnimatorManager.h58
-rw-r--r--libs/hwui/AssetAtlas.cpp4
-rw-r--r--libs/hwui/AssetAtlas.h6
-rw-r--r--libs/hwui/Caches.cpp100
-rw-r--r--libs/hwui/Caches.h41
-rw-r--r--libs/hwui/CanvasProperty.h47
-rw-r--r--libs/hwui/DamageAccumulator.cpp230
-rw-r--r--libs/hwui/DamageAccumulator.h71
-rw-r--r--libs/hwui/Debug.h6
-rw-r--r--libs/hwui/DeferredDisplayList.cpp27
-rw-r--r--libs/hwui/DeferredDisplayList.h6
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp131
-rw-r--r--libs/hwui/DeferredLayerUpdater.h111
-rw-r--r--libs/hwui/DisplayList.cpp531
-rw-r--r--libs/hwui/DisplayList.h535
-rw-r--r--libs/hwui/DisplayListOp.h556
-rw-r--r--libs/hwui/DisplayListRenderer.cpp362
-rw-r--r--libs/hwui/DisplayListRenderer.h317
-rw-r--r--libs/hwui/DrawProfiler.cpp261
-rw-r--r--libs/hwui/DrawProfiler.h96
-rw-r--r--libs/hwui/FontRenderer.cpp56
-rw-r--r--libs/hwui/FontRenderer.h28
-rw-r--r--libs/hwui/GradientCache.cpp7
-rw-r--r--libs/hwui/GradientCache.h2
-rw-r--r--libs/hwui/Interpolator.cpp127
-rw-r--r--libs/hwui/Interpolator.h117
-rw-r--r--libs/hwui/Layer.cpp101
-rw-r--r--libs/hwui/Layer.h78
-rw-r--r--libs/hwui/LayerCache.cpp4
-rw-r--r--libs/hwui/LayerCache.h4
-rw-r--r--libs/hwui/LayerRenderer.cpp83
-rw-r--r--libs/hwui/LayerRenderer.h24
-rw-r--r--libs/hwui/Matrix.cpp28
-rw-r--r--libs/hwui/Matrix.h26
-rwxr-xr-x[-rw-r--r--]libs/hwui/OpenGLRenderer.cpp2050
-rwxr-xr-x[-rw-r--r--]libs/hwui/OpenGLRenderer.h636
-rw-r--r--libs/hwui/Outline.h122
-rw-r--r--libs/hwui/Patch.h3
-rw-r--r--libs/hwui/PathCache.cpp47
-rw-r--r--libs/hwui/PathCache.h60
-rw-r--r--libs/hwui/PathTessellator.cpp401
-rw-r--r--libs/hwui/PathTessellator.h147
-rw-r--r--libs/hwui/Program.cpp15
-rw-r--r--libs/hwui/Program.h63
-rw-r--r--libs/hwui/ProgramCache.cpp99
-rw-r--r--libs/hwui/Properties.h32
-rw-r--r--libs/hwui/Rect.h57
-rw-r--r--libs/hwui/RenderNode.cpp909
-rw-r--r--libs/hwui/RenderNode.h293
-rw-r--r--libs/hwui/RenderProperties.cpp215
-rw-r--r--libs/hwui/RenderProperties.h623
-rw-r--r--libs/hwui/RenderState.cpp112
-rw-r--r--libs/hwui/RenderState.h70
-rw-r--r--libs/hwui/Renderer.h211
-rw-r--r--libs/hwui/ResourceCache.cpp120
-rw-r--r--libs/hwui/ResourceCache.h48
-rw-r--r--libs/hwui/RevealClip.h73
-rw-r--r--libs/hwui/ShadowTessellator.cpp249
-rw-r--r--libs/hwui/ShadowTessellator.h108
-rw-r--r--libs/hwui/SkiaColorFilter.cpp117
-rw-r--r--libs/hwui/SkiaColorFilter.h128
-rw-r--r--libs/hwui/SkiaShader.cpp562
-rw-r--r--libs/hwui/SkiaShader.h258
-rw-r--r--libs/hwui/Snapshot.cpp79
-rw-r--r--libs/hwui/Snapshot.h107
-rw-r--r--libs/hwui/SpotShadow.cpp939
-rw-r--r--libs/hwui/SpotShadow.h82
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp287
-rw-r--r--libs/hwui/StatefulBaseRenderer.h171
-rw-r--r--libs/hwui/Stencil.cpp2
-rw-r--r--libs/hwui/Stencil.h2
-rw-r--r--libs/hwui/TessellationCache.cpp476
-rw-r--r--libs/hwui/TessellationCache.h192
-rw-r--r--libs/hwui/TextDropShadowCache.cpp4
-rw-r--r--libs/hwui/TextDropShadowCache.h4
-rw-r--r--libs/hwui/Texture.cpp4
-rw-r--r--libs/hwui/Texture.h6
-rw-r--r--libs/hwui/TextureCache.cpp118
-rw-r--r--libs/hwui/TextureCache.h37
-rw-r--r--libs/hwui/TreeInfo.h129
-rw-r--r--libs/hwui/Vector.h33
-rw-r--r--libs/hwui/Vertex.h41
-rw-r--r--libs/hwui/VertexBuffer.h143
-rw-r--r--libs/hwui/font/CacheTexture.cpp4
-rw-r--r--libs/hwui/font/CacheTexture.h2
-rw-r--r--libs/hwui/font/Font.cpp49
-rw-r--r--libs/hwui/font/Font.h27
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp301
-rw-r--r--libs/hwui/renderthread/CanvasContext.h116
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp151
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h94
-rw-r--r--libs/hwui/renderthread/EglManager.cpp288
-rw-r--r--libs/hwui/renderthread/EglManager.h88
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp423
-rw-r--r--libs/hwui/renderthread/RenderProxy.h121
-rw-r--r--libs/hwui/renderthread/RenderTask.cpp (renamed from libs/input/InputApplication.cpp)38
-rw-r--r--libs/hwui/renderthread/RenderTask.h99
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp350
-rw-r--r--libs/hwui/renderthread/RenderThread.h139
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp46
-rw-r--r--libs/hwui/renderthread/TimeLord.h49
-rw-r--r--libs/hwui/thread/Task.h4
-rw-r--r--libs/hwui/thread/TaskManager.cpp16
-rw-r--r--libs/hwui/thread/TaskManager.h1
-rw-r--r--libs/hwui/utils/Blur.cpp45
-rw-r--r--libs/hwui/utils/Blur.h10
-rw-r--r--libs/hwui/utils/GLUtils.cpp52
-rw-r--r--libs/hwui/utils/GLUtils.h35
-rw-r--r--libs/hwui/utils/Macros.h32
-rw-r--r--libs/hwui/utils/MathUtils.h85
-rw-r--r--libs/input/Android.mk20
-rw-r--r--libs/input/EventHub.cpp1676
-rw-r--r--libs/input/EventHub.h455
-rw-r--r--libs/input/InputApplication.h83
-rw-r--r--libs/input/InputDispatcher.cpp4496
-rw-r--r--libs/input/InputDispatcher.h1121
-rw-r--r--libs/input/InputListener.cpp182
-rw-r--r--libs/input/InputListener.h196
-rw-r--r--libs/input/InputManager.cpp93
-rw-r--r--libs/input/InputManager.h109
-rw-r--r--libs/input/InputReader.cpp6543
-rw-r--r--libs/input/InputReader.h1820
-rw-r--r--libs/input/InputWindow.cpp65
-rw-r--r--libs/input/InputWindow.h207
-rw-r--r--libs/input/PointerController.h79
-rw-r--r--libs/input/SpriteController.cpp14
-rw-r--r--libs/input/SpriteController.h2
-rw-r--r--libs/input/tests/Android.mk48
-rw-r--r--libs/input/tests/InputDispatcher_test.cpp258
-rw-r--r--libs/input/tests/InputReader_test.cpp5099
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/Android.mk1
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/linux/usb/f_accessory.h53
-rw-r--r--libs/usb/tests/accessorytest/Android.mk25
-rw-r--r--libs/usb/tests/accessorytest/accessory.c79
-rw-r--r--libs/usb/tests/accessorytest/accessory.h26
-rw-r--r--libs/usb/tests/accessorytest/audio.c221
-rw-r--r--libs/usb/tests/accessorytest/f_accessory.h148
-rw-r--r--libs/usb/tests/accessorytest/hid.c202
-rw-r--r--libs/usb/tests/accessorytest/usb.c227
185 files changed, 18347 insertions, 28534 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 258a296..dbee7ed 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -25,6 +25,7 @@ commonSources := \
ObbFile.cpp \
ResourceTypes.cpp \
StreamingZipInflater.cpp \
+ TypeWrappers.cpp \
ZipFileRO.cpp \
ZipUtils.cpp
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 56c95bd..de6a33c 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -74,6 +74,7 @@ static const char* kAssetsRoot = "assets";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kSystemAssets = "framework/framework-res.apk";
static const char* kResourceCache = "resource-cache";
+static const char* kAndroidManifest = "AndroidManifest.xml";
static const char* kExcludeExtension = ".EXCLUDE";
@@ -205,6 +206,16 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
ALOGV("In %p Asset %s path: %s", this,
ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
+ // Check that the path has an AndroidManifest.xml
+ Asset* manifestAsset = const_cast<AssetManager*>(this)->openNonAssetInPathLocked(
+ kAndroidManifest, Asset::ACCESS_BUFFER, ap);
+ if (manifestAsset == NULL) {
+ // This asset path does not contain any resources.
+ delete manifestAsset;
+ return false;
+ }
+ delete manifestAsset;
+
mAssetPaths.add(ap);
// new paths are always added at the end
@@ -220,6 +231,10 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
}
#endif
+ if (mResources != NULL) {
+ appendPathToResTable(ap);
+ }
+
return true;
}
@@ -245,7 +260,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
String8 targetPath;
String8 overlayPath;
if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
- NULL, NULL, &targetPath, &overlayPath)) {
+ NULL, NULL, NULL, &targetPath, &overlayPath)) {
ALOGW("failed to read idmap file %s\n", idmapPath.string());
delete idmap;
return false;
@@ -300,7 +315,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk
ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
return false;
}
- tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
+ tables[i].add(ass);
}
return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
@@ -508,7 +523,7 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode)
* The "fileName" is the partial path starting from the application
* name.
*/
-Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode)
+Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
{
AutoMutex _l(mLock);
@@ -529,6 +544,7 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode)
Asset* pAsset = openNonAssetInPathLocked(
fileName, mode, mAssetPaths.itemAt(i));
if (pAsset != NULL) {
+ if (outCookie != NULL) *outCookie = static_cast<int32_t>(i + 1);
return pAsset != kExcludedAsset ? pAsset : NULL;
}
}
@@ -584,6 +600,96 @@ FileType AssetManager::getFileType(const char* fileName)
return kFileTypeRegular;
}
+bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+ Asset* ass = NULL;
+ ResTable* sharedRes = NULL;
+ bool shared = true;
+ bool onlyEmptyResources = true;
+ MY_TRACE_BEGIN(ap.path.string());
+ Asset* idmap = openIdmapLocked(ap);
+ size_t nextEntryIdx = mResources->getTableCount();
+ ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
+ if (ap.type != kFileTypeDirectory) {
+ if (nextEntryIdx == 0) {
+ // The first item is typically the framework resources,
+ // which we want to avoid parsing every time.
+ sharedRes = const_cast<AssetManager*>(this)->
+ mZipSet.getZipResourceTable(ap.path);
+ if (sharedRes != NULL) {
+ // skip ahead the number of system overlay packages preloaded
+ nextEntryIdx = sharedRes->getTableCount();
+ }
+ }
+ if (sharedRes == NULL) {
+ ass = const_cast<AssetManager*>(this)->
+ mZipSet.getZipResourceTableAsset(ap.path);
+ if (ass == NULL) {
+ ALOGV("loading resource table %s\n", ap.path.string());
+ ass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ ap);
+ if (ass != NULL && ass != kExcludedAsset) {
+ ass = const_cast<AssetManager*>(this)->
+ mZipSet.setZipResourceTableAsset(ap.path, ass);
+ }
+ }
+
+ if (nextEntryIdx == 0 && ass != NULL) {
+ // If this is the first resource table in the asset
+ // manager, then we are going to cache it so that we
+ // can quickly copy it out for others.
+ ALOGV("Creating shared resources for %s", ap.path.string());
+ sharedRes = new ResTable();
+ sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
+#ifdef HAVE_ANDROID_OS
+ const char* data = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+ String8 overlaysListPath(data);
+ overlaysListPath.appendPath(kResourceCache);
+ overlaysListPath.appendPath("overlays.list");
+ addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+#endif
+ sharedRes = const_cast<AssetManager*>(this)->
+ mZipSet.setZipResourceTable(ap.path, sharedRes);
+ }
+ }
+ } else {
+ ALOGV("loading resource table %s\n", ap.path.string());
+ ass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ ap);
+ shared = false;
+ }
+
+ if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
+ ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
+ if (sharedRes != NULL) {
+ ALOGV("Copying existing resources for %s", ap.path.string());
+ mResources->add(sharedRes);
+ } else {
+ ALOGV("Parsing resources for %s", ap.path.string());
+ mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+ }
+ onlyEmptyResources = false;
+
+ if (!shared) {
+ delete ass;
+ }
+ } else {
+ ALOGV("Installing empty resources in to table %p\n", mResources);
+ mResources->addEmpty(nextEntryIdx + 1);
+ }
+
+ if (idmap != NULL) {
+ delete idmap;
+ }
+ MY_TRACE_END();
+
+ return onlyEmptyResources;
+}
+
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt = mResources;
@@ -603,100 +709,27 @@ const ResTable* AssetManager::getResTable(bool required) const
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
}
- if (mCacheMode != CACHE_OFF && !mCacheValid)
+ if (mCacheMode != CACHE_OFF && !mCacheValid) {
const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
+ }
+ mResources = new ResTable();
+ updateResourceParamsLocked();
+
+ bool onlyEmptyResources = true;
const size_t N = mAssetPaths.size();
for (size_t i=0; i<N; i++) {
- Asset* ass = NULL;
- ResTable* sharedRes = NULL;
- bool shared = true;
- const asset_path& ap = mAssetPaths.itemAt(i);
- MY_TRACE_BEGIN(ap.path.string());
- Asset* idmap = openIdmapLocked(ap);
- ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
- if (ap.type != kFileTypeDirectory) {
- if (i == 0) {
- // The first item is typically the framework resources,
- // which we want to avoid parsing every time.
- sharedRes = const_cast<AssetManager*>(this)->
- mZipSet.getZipResourceTable(ap.path);
- if (sharedRes != NULL) {
- // skip ahead the number of system overlay packages preloaded
- i += sharedRes->getTableCount() - 1;
- }
- }
- if (sharedRes == NULL) {
- ass = const_cast<AssetManager*>(this)->
- mZipSet.getZipResourceTableAsset(ap.path);
- if (ass == NULL) {
- ALOGV("loading resource table %s\n", ap.path.string());
- ass = const_cast<AssetManager*>(this)->
- openNonAssetInPathLocked("resources.arsc",
- Asset::ACCESS_BUFFER,
- ap);
- if (ass != NULL && ass != kExcludedAsset) {
- ass = const_cast<AssetManager*>(this)->
- mZipSet.setZipResourceTableAsset(ap.path, ass);
- }
- }
-
- if (i == 0 && ass != NULL) {
- // If this is the first resource table in the asset
- // manager, then we are going to cache it so that we
- // can quickly copy it out for others.
- ALOGV("Creating shared resources for %s", ap.path.string());
- sharedRes = new ResTable();
- sharedRes->add(ass, i + 1, false, idmap);
-#ifdef HAVE_ANDROID_OS
- const char* data = getenv("ANDROID_DATA");
- LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
- String8 overlaysListPath(data);
- overlaysListPath.appendPath(kResourceCache);
- overlaysListPath.appendPath("overlays.list");
- addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, i);
-#endif
- sharedRes = const_cast<AssetManager*>(this)->
- mZipSet.setZipResourceTable(ap.path, sharedRes);
- }
- }
- } else {
- ALOGV("loading resource table %s\n", ap.path.string());
- ass = const_cast<AssetManager*>(this)->
- openNonAssetInPathLocked("resources.arsc",
- Asset::ACCESS_BUFFER,
- ap);
- shared = false;
- }
- if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
- if (rt == NULL) {
- mResources = rt = new ResTable();
- updateResourceParamsLocked();
- }
- ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
- if (sharedRes != NULL) {
- ALOGV("Copying existing resources for %s", ap.path.string());
- rt->add(sharedRes);
- } else {
- ALOGV("Parsing resources for %s", ap.path.string());
- rt->add(ass, i + 1, !shared, idmap);
- }
-
- if (!shared) {
- delete ass;
- }
- }
- if (idmap != NULL) {
- delete idmap;
- }
- MY_TRACE_END();
+ bool empty = appendPathToResTable(mAssetPaths.itemAt(i));
+ onlyEmptyResources = onlyEmptyResources && empty;
}
- if (required && !rt) ALOGW("Unable to find resources file resources.arsc");
- if (!rt) {
- mResources = rt = new ResTable();
+ if (required && onlyEmptyResources) {
+ ALOGW("Unable to find resources file resources.arsc");
+ delete mResources;
+ mResources = NULL;
}
- return rt;
+
+ return mResources;
}
void AssetManager::updateResourceParamsLocked() const
@@ -762,7 +795,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList,
if (oass != NULL) {
Asset* oidmap = openIdmapLocked(oap);
offset++;
- sharedRes->add(oass, offset + 1, false, oidmap);
+ sharedRes->add(oass, oidmap, offset + 1, false);
const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
}
@@ -903,7 +936,7 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m
/* look at the filesystem on disk */
String8 path(createPathNameLocked(ap, locale, vendor));
path.appendPath(fileName);
-
+
String8 excludeName(path);
excludeName.append(kExcludeExtension);
if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
@@ -911,28 +944,28 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m
//printf("+++ excluding '%s'\n", (const char*) excludeName);
return kExcludedAsset;
}
-
+
pAsset = openAssetFromFileLocked(path, mode);
-
+
if (pAsset == NULL) {
/* try again, this time with ".gz" */
path.append(".gz");
pAsset = openAssetFromFileLocked(path, mode);
}
-
+
if (pAsset != NULL)
pAsset->setAssetSource(path);
} else {
/* find in cache */
String8 path(createPathNameLocked(ap, locale, vendor));
path.appendPath(fileName);
-
+
AssetDir::FileInfo tmpInfo;
bool found = false;
-
+
String8 excludeName(path);
excludeName.append(kExcludeExtension);
-
+
if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
/* go no farther */
//printf("+++ Excluding '%s'\n", (const char*) excludeName);
@@ -1756,7 +1789,7 @@ bool AssetManager::fncScanAndMergeDirLocked(
// XXX This is broken -- the filename cache needs to hold the base
// asset path separately from its filename.
-
+
partialPath = createPathNameLocked(ap, locale, vendor);
if (dirName[0] != '\0') {
partialPath.appendPath(dirName);
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index bd9dc76..d16d5498 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -59,9 +59,10 @@ padding_extra(size_t n)
BackupDataWriter::BackupDataWriter(int fd)
:m_fd(fd),
m_status(NO_ERROR),
- m_pos(0),
m_entityCount(0)
{
+ m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR);
+ if (DEBUG) ALOGI("BackupDataWriter(%d) @ %ld", fd, (long)m_pos);
}
BackupDataWriter::~BackupDataWriter()
@@ -184,10 +185,11 @@ BackupDataReader::BackupDataReader(int fd)
:m_fd(fd),
m_done(false),
m_status(NO_ERROR),
- m_pos(0),
m_entityCount(0)
{
memset(&m_header, 0, sizeof(m_header));
+ m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR);
+ if (DEBUG) ALOGI("BackupDataReader(%d) @ %ld", fd, (long)m_pos);
}
BackupDataReader::~BackupDataReader()
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1dec977..2a4dec0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,7 +17,9 @@
#define LOG_TAG "ResourceType"
//#define LOG_NDEBUG 0
+#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
#include <utils/Atomic.h>
#include <utils/ByteOrder.h>
#include <utils/Debug.h>
@@ -30,6 +32,7 @@
#include <memory.h>
#include <ctype.h>
#include <stdint.h>
+#include <stddef.h>
#ifndef INT32_MAX
#define INT32_MAX ((int32_t)(2147483647))
@@ -42,6 +45,7 @@
#define TABLE_SUPER_NOISY(x) //x
#define LOAD_TABLE_NOISY(x) //x
#define TABLE_THEME(x) //x
+#define LIB_NOISY(x) //x
namespace android {
@@ -62,9 +66,11 @@ namespace android {
#endif
#endif
-#define IDMAP_MAGIC 0x706d6469
-// size measured in sizeof(uint32_t)
-#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
+#define IDMAP_MAGIC 0x504D4449
+#define IDMAP_CURRENT_VERSION 0x00000001
+
+#define APP_PACKAGE_ID 0x7f
+#define SYS_PACKAGE_ID 0x01
// Standard C isspace() is only required to look at the low byte of its input, so
// produces incorrect results for UTF-16 characters. For safety's sake, assume that
@@ -73,6 +79,11 @@ inline int isspace16(char16_t c) {
return (c < 0x0080 && isspace(c));
}
+template<typename T>
+inline static T max(T a, T b) {
+ return a > b ? a : b;
+}
+
// range checked; guaranteed to NUL-terminate within the stated number of available slots
// NOTE: if this truncates the dst string due to running out of space, no attempt is
// made to avoid splitting surrogate pairs.
@@ -98,7 +109,7 @@ static status_t validate_chunk(const ResChunk_header* chunk,
if (headerSize >= minSize) {
if (headerSize <= size) {
if (((headerSize|size)&0x3) == 0) {
- if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) {
+ if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
return NO_ERROR;
}
ALOGW("%s data size 0x%x extends beyond resource end %p.",
@@ -113,7 +124,7 @@ static status_t validate_chunk(const ResChunk_header* chunk,
name, size, headerSize);
return BAD_TYPE;
}
- ALOGW("%s header size 0x%x is too small.",
+ ALOGW("%s header size 0x%04x is too small.",
name, headerSize);
return BAD_TYPE;
}
@@ -211,104 +222,179 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
}
-static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
-{
- if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
- ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes);
+static bool assertIdmapHeader(const void* idmap, size_t size) {
+ if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
+ ALOGE("idmap: header is not word aligned");
+ return false;
+ }
+
+ if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+ ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
+ return false;
+ }
+
+ const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
+ if (magic != IDMAP_MAGIC) {
+ ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
+ magic, IDMAP_MAGIC);
return false;
}
- if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
- ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
- *map, htodl(IDMAP_MAGIC));
+
+ const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
+ if (version != IDMAP_CURRENT_VERSION) {
+ // We are strict about versions because files with this format are
+ // auto-generated and don't need backwards compatibility.
+ ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
+ version, IDMAP_CURRENT_VERSION);
return false;
}
return true;
}
-static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
-{
- // see README for details on the format of map
- if (!assertIdmapHeader(map, sizeBytes)) {
- return UNKNOWN_ERROR;
- }
- map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
- // size of data block, in uint32_t
- const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
- const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
- const uint32_t entry = Res_GETENTRY(key);
- const uint32_t typeCount = *map;
+class IdmapEntries {
+public:
+ IdmapEntries() : mData(NULL) {}
- if (type > typeCount) {
- ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
- return UNKNOWN_ERROR;
+ bool hasEntries() const {
+ if (mData == NULL) {
+ return false;
+ }
+
+ return (dtohs(*mData) > 0);
}
- if (typeCount > size) {
- ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size);
- return UNKNOWN_ERROR;
+
+ size_t byteSize() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ uint16_t entryCount = dtohs(mData[2]);
+ return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
}
- const uint32_t typeOffset = map[type];
- if (typeOffset == 0) {
- *outValue = 0;
- return NO_ERROR;
+
+ uint8_t targetTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[0]);
}
- if (typeOffset + 1 > size) {
- ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
- typeOffset, (int)size);
- return UNKNOWN_ERROR;
+
+ uint8_t overlayTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[1]);
}
- const uint32_t entryCount = map[typeOffset];
- const uint32_t entryOffset = map[typeOffset + 1];
- if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
- *outValue = 0;
+
+ status_t setTo(const void* entryHeader, size_t size) {
+ if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
+ ALOGE("idmap: entry header is not word aligned");
+ return UNKNOWN_ERROR;
+ }
+
+ if (size < sizeof(uint16_t) * 4) {
+ ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
+ return UNKNOWN_ERROR;
+ }
+
+ const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
+ const uint16_t targetTypeId = dtohs(header[0]);
+ const uint16_t overlayTypeId = dtohs(header[1]);
+ if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
+ ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
+ return UNKNOWN_ERROR;
+ }
+
+ uint16_t entryCount = dtohs(header[2]);
+ if (size < sizeof(uint32_t) * (entryCount + 2)) {
+ ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
+ (uint32_t) size, (uint32_t) entryCount);
+ return UNKNOWN_ERROR;
+ }
+ mData = header;
return NO_ERROR;
}
- const uint32_t index = typeOffset + 2 + entry - entryOffset;
- if (index > size) {
- ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size);
- *outValue = 0;
+
+ status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
+ uint16_t entryCount = dtohs(mData[2]);
+ uint16_t offset = dtohs(mData[3]);
+
+ if (entryId < offset) {
+ // The entry is not present in this idmap
+ return BAD_INDEX;
+ }
+
+ entryId -= offset;
+
+ if (entryId >= entryCount) {
+ // The entry is not present in this idmap
+ return BAD_INDEX;
+ }
+
+ // It is safe to access the type here without checking the size because
+ // we have checked this when it was first loaded.
+ const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
+ uint32_t mappedEntry = dtohl(entries[entryId]);
+ if (mappedEntry == 0xffffffff) {
+ // This entry is not present in this idmap
+ return BAD_INDEX;
+ }
+ *outEntryId = static_cast<uint16_t>(mappedEntry);
return NO_ERROR;
}
- *outValue = map[index];
- return NO_ERROR;
-}
+private:
+ const uint16_t* mData;
+};
-static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
-{
- if (!assertIdmapHeader(map, mapSize)) {
+status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
+ if (!assertIdmapHeader(idmap, size)) {
return UNKNOWN_ERROR;
}
- if (mapSize <= IDMAP_HEADER_SIZE + 1) {
- ALOGW("corrupt idmap: map size %d too short\n", mapSize);
+
+ size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+ if (size < sizeof(uint16_t) * 2) {
+ ALOGE("idmap: too small to contain any mapping");
return UNKNOWN_ERROR;
}
- uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
- if (typeCount == 0) {
- ALOGW("corrupt idmap: no types\n");
+
+ const uint16_t* data = reinterpret_cast<const uint16_t*>(
+ reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
+
+ uint16_t targetPackageId = dtohs(*(data++));
+ if (targetPackageId == 0 || targetPackageId > 255) {
+ ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
return UNKNOWN_ERROR;
}
- if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
- ALOGW("corrupt idmap: number of types %d extends past idmap size %d\n", typeCount, mapSize);
+
+ uint16_t mapCount = dtohs(*(data++));
+ if (mapCount == 0) {
+ ALOGE("idmap: no mappings");
return UNKNOWN_ERROR;
}
- const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
- // find first defined type
- while (*p == 0) {
- ++p;
- if (--typeCount == 0) {
- ALOGW("corrupt idmap: types declared, none found\n");
- return UNKNOWN_ERROR;
- }
+
+ if (mapCount > 255) {
+ ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
}
- // determine package id from first entry of first type
- const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
- if (offset > mapSize) {
- ALOGW("corrupt idmap: entry offset %d points outside map size %d\n", offset, mapSize);
- return UNKNOWN_ERROR;
+ while (size > sizeof(uint16_t) * 4) {
+ IdmapEntries entries;
+ status_t err = entries.setTo(data, size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ ssize_t index = outMap->add(entries.overlayTypeId(), entries);
+ if (index < 0) {
+ return NO_MEMORY;
+ }
+
+ data += entries.byteSize() / sizeof(uint16_t);
+ size -= entries.byteSize();
}
- *outId = (map[offset] >> 24) & 0x000000ff;
+ if (outPackageId != NULL) {
+ *outPackageId = static_cast<uint8_t>(targetPackageId);
+ }
return NO_ERROR;
}
@@ -342,6 +428,22 @@ ResStringPool::~ResStringPool()
uninit();
}
+void ResStringPool::setToEmpty()
+{
+ uninit();
+
+ mOwnedData = calloc(1, sizeof(ResStringPool_header));
+ ResStringPool_header* header = (ResStringPool_header*) mOwnedData;
+ mSize = 0;
+ mEntries = NULL;
+ mStrings = NULL;
+ mStringPoolSize = 0;
+ mEntryStyles = NULL;
+ mStyles = NULL;
+ mStylePoolSize = 0;
+ mHeader = (const ResStringPool_header*) header;
+}
+
status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
{
if (!data || !size) {
@@ -402,19 +504,22 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
charSize = sizeof(char16_t);
}
- mStrings = (const void*)
- (((const uint8_t*)data)+mHeader->stringsStart);
- if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) {
+ // There should be at least space for the smallest string
+ // (2 bytes length, null terminator).
+ if (mHeader->stringsStart >= (mSize - sizeof(uint16_t))) {
ALOGW("Bad string block: string pool starts at %d, after total size %d\n",
(int)mHeader->stringsStart, (int)mHeader->header.size);
return (mError=BAD_TYPE);
}
+
+ mStrings = (const void*)
+ (((const uint8_t*)data) + mHeader->stringsStart);
+
if (mHeader->styleCount == 0) {
- mStringPoolSize =
- (mHeader->header.size-mHeader->stringsStart)/charSize;
+ mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
} else {
// check invariant: styles starts before end of data
- if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) {
+ if (mHeader->stylesStart >= (mSize - sizeof(uint16_t))) {
ALOGW("Bad style block: style block starts at %d past data size of %d\n",
(int)mHeader->stylesStart, (int)mHeader->header.size);
return (mError=BAD_TYPE);
@@ -697,11 +802,16 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
const String8 ResStringPool::string8ObjectAt(size_t idx) const
{
size_t len;
- const char *str = (const char*)string8At(idx, &len);
+ const char *str = string8At(idx, &len);
if (str != NULL) {
- return String8(str);
+ return String8(str, len);
+ }
+
+ const char16_t *str16 = stringAt(idx, &len);
+ if (str16 != NULL) {
+ return String8(str16, len);
}
- return String8(stringAt(idx, &len));
+ return String8();
}
const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
@@ -1111,7 +1221,14 @@ int32_t ResXMLParser::getAttributeDataType(size_t idx) const
(((const uint8_t*)tag)
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
- return attr->typedValue.dataType;
+ uint8_t type = attr->typedValue.dataType;
+ if (type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ return type;
+ }
+
+ // This is a dynamic reference. We adjust those references
+ // to regular references at this level, so lie to the caller.
+ return Res_value::TYPE_REFERENCE;
}
}
return Res_value::TYPE_NULL;
@@ -1126,7 +1243,15 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const
(((const uint8_t*)tag)
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
- return dtohl(attr->typedValue.data);
+ if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
+ mTree.mDynamicRefTable == NULL) {
+ return dtohl(attr->typedValue.data);
+ }
+
+ uint32_t data = dtohl(attr->typedValue.data);
+ if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
+ return data;
+ }
}
}
return 0;
@@ -1142,6 +1267,10 @@ ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
outValue->copyFrom_dtoh(attr->typedValue);
+ if (mTree.mDynamicRefTable != NULL &&
+ mTree.mDynamicRefTable->lookupResourceValue(outValue) != NO_ERROR) {
+ return BAD_TYPE;
+ }
return sizeof(Res_value);
}
}
@@ -1333,25 +1462,26 @@ void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos)
mCurExt = pos.curExt;
}
-
// --------------------------------------------------------------------
static volatile int32_t gCount = 0;
-ResXMLTree::ResXMLTree()
+ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
: ResXMLParser(*this)
+ , mDynamicRefTable(dynamicRefTable)
, mError(NO_INIT), mOwnedData(NULL)
{
//ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
restart();
}
-ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData)
+ResXMLTree::ResXMLTree()
: ResXMLParser(*this)
+ , mDynamicRefTable(NULL)
, mError(NO_INIT), mOwnedData(NULL)
{
//ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
- setTo(data, size, copyData);
+ restart();
}
ResXMLTree::~ResXMLTree()
@@ -2412,15 +2542,19 @@ String8 ResTable_config::toString() const {
if (mcc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmcc", dtohs(mcc));
+ res.appendFormat("mcc%d", dtohs(mcc));
}
if (mnc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmnc", dtohs(mnc));
+ res.appendFormat("mnc%d", dtohs(mnc));
}
+
char localeStr[RESTABLE_MAX_LOCALE_LEN];
getBcp47Locale(localeStr);
- res.append(localeStr);
+ if (strlen(localeStr) > 0) {
+ if (res.size() > 0) res.append("-");
+ res.append(localeStr);
+ }
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
@@ -2562,6 +2696,9 @@ String8 ResTable_config::toString() const {
case ResTable_config::DENSITY_XXHIGH:
res.append("xxhdpi");
break;
+ case ResTable_config::DENSITY_XXXHIGH:
+ res.append("xxxhdpi");
+ break;
case ResTable_config::DENSITY_NONE:
res.append("nodpi");
break;
@@ -2587,6 +2724,20 @@ String8 ResTable_config::toString() const {
break;
}
}
+ if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (inputFlags&MASK_KEYSHIDDEN) {
+ case ResTable_config::KEYSHIDDEN_NO:
+ res.append("keysexposed");
+ break;
+ case ResTable_config::KEYSHIDDEN_YES:
+ res.append("keyshidden");
+ break;
+ case ResTable_config::KEYSHIDDEN_SOFT:
+ res.append("keyssoft");
+ break;
+ }
+ }
if (keyboard != KEYBOARD_ANY) {
if (res.size() > 0) res.append("-");
switch (keyboard) {
@@ -2604,17 +2755,18 @@ String8 ResTable_config::toString() const {
break;
}
}
- if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if ((inputFlags&MASK_NAVHIDDEN) != 0) {
if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_KEYSHIDDEN) {
- case ResTable_config::KEYSHIDDEN_NO:
- res.append("keysexposed");
+ switch (inputFlags&MASK_NAVHIDDEN) {
+ case ResTable_config::NAVHIDDEN_NO:
+ res.append("navexposed");
break;
- case ResTable_config::KEYSHIDDEN_YES:
- res.append("keyshidden");
+ case ResTable_config::NAVHIDDEN_YES:
+ res.append("navhidden");
break;
- case ResTable_config::KEYSHIDDEN_SOFT:
- res.append("keyssoft");
+ default:
+ res.appendFormat("inputFlagsNavHidden=%d",
+ dtohs(inputFlags&MASK_NAVHIDDEN));
break;
}
}
@@ -2638,21 +2790,6 @@ String8 ResTable_config::toString() const {
break;
}
}
- if ((inputFlags&MASK_NAVHIDDEN) != 0) {
- if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_NAVHIDDEN) {
- case ResTable_config::NAVHIDDEN_NO:
- res.append("navsexposed");
- break;
- case ResTable_config::NAVHIDDEN_YES:
- res.append("navhidden");
- break;
- default:
- res.appendFormat("inputFlagsNavHidden=%d",
- dtohs(inputFlags&MASK_NAVHIDDEN));
- break;
- }
- }
if (screenSize != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
@@ -2682,7 +2819,7 @@ struct ResTable::Header
free(resourceIDMap);
}
- ResTable* const owner;
+ const ResTable* const owner;
void* ownedData;
const ResTable_header* header;
size_t size;
@@ -2695,6 +2832,17 @@ struct ResTable::Header
size_t resourceIDMapSize;
};
+struct ResTable::Entry {
+ ResTable_config config;
+ const ResTable_entry* entry;
+ const ResTable_type* type;
+ uint32_t specFlags;
+ const Package* package;
+
+ StringPoolRef typeStr;
+ StringPoolRef keyStr;
+};
+
struct ResTable::Type
{
Type(const Header* _header, const Package* _package, size_t count)
@@ -2705,33 +2853,29 @@ struct ResTable::Type
const size_t entryCount;
const ResTable_typeSpec* typeSpec;
const uint32_t* typeSpecFlags;
+ IdmapEntries idmapEntries;
Vector<const ResTable_type*> configs;
};
struct ResTable::Package
{
Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
- : owner(_owner), header(_header), package(_package) { }
- ~Package()
- {
- size_t i = types.size();
- while (i > 0) {
- i--;
- delete types[i];
+ : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
+ if (dtohs(package->header.headerSize) == sizeof(package)) {
+ // The package structure is the same size as the definition.
+ // This means it contains the typeIdOffset field.
+ typeIdOffset = package->typeIdOffset;
}
}
- ResTable* const owner;
+ const ResTable* const owner;
const Header* const header;
const ResTable_package* const package;
- Vector<Type*> types;
ResStringPool typeStrings;
ResStringPool keyStrings;
- const Type* getType(size_t idx) const {
- return idx < types.size() ? types[idx] : NULL;
- }
+ size_t typeIdOffset;
};
// A group of objects describing a particular resource package.
@@ -2740,9 +2884,27 @@ struct ResTable::Package
struct ResTable::PackageGroup
{
PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
- : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { }
+ : owner(_owner)
+ , name(_name)
+ , id(_id)
+ , largestTypeId(0)
+ , bags(NULL)
+ , dynamicRefTable(static_cast<uint8_t>(_id))
+ { }
+
~PackageGroup() {
clearBagCache();
+ const size_t numTypes = types.size();
+ for (size_t i = 0; i < numTypes; i++) {
+ const TypeList& typeList = types[i];
+ const size_t numInnerTypes = typeList.size();
+ for (size_t j = 0; j < numInnerTypes; j++) {
+ if (typeList[j]->package->owner == owner) {
+ delete typeList[j];
+ }
+ }
+ }
+
const size_t N = packages.size();
for (size_t i=0; i<N; i++) {
Package* pkg = packages[i];
@@ -2755,17 +2917,15 @@ struct ResTable::PackageGroup
void clearBagCache() {
if (bags) {
TABLE_NOISY(printf("bags=%p\n", bags));
- Package* pkg = packages[0];
- TABLE_NOISY(printf("typeCount=%x\n", typeCount));
- for (size_t i=0; i<typeCount; i++) {
+ for (size_t i = 0; i < bags->size(); i++) {
TABLE_NOISY(printf("type=%d\n", i));
- const Type* type = pkg->getType(i);
- if (type != NULL) {
- bag_set** typeBags = bags[i];
+ const TypeList& typeList = types[i];
+ if (typeList.isEmpty()) {
+ bag_set** typeBags = bags->get(i);
TABLE_NOISY(printf("typeBags=%p\n", typeBags));
if (typeBags) {
- TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount));
- const size_t N = type->entryCount;
+ const size_t N = typeList[0]->entryCount;
+ TABLE_NOISY(printf("type->entryCount=%x\n", N));
for (size_t j=0; j<N; j++) {
if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
free(typeBags[j]);
@@ -2774,25 +2934,45 @@ struct ResTable::PackageGroup
}
}
}
- free(bags);
+ delete bags;
bags = NULL;
}
}
- ResTable* const owner;
+ ssize_t findType16(const char16_t* type, size_t len) const {
+ const size_t N = packages.size();
+ for (size_t i = 0; i < N; i++) {
+ ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
+ if (index >= 0) {
+ return index + packages[i]->typeIdOffset;
+ }
+ }
+ return -1;
+ }
+
+ const ResTable* const owner;
String16 const name;
uint32_t const id;
+
+ // This is mainly used to keep track of the loaded packages
+ // and to clean them up properly. Accessing resources happens from
+ // the 'types' array.
Vector<Package*> packages;
- // This is for finding typeStrings and other common package stuff.
- Package* basePackage;
+ ByteBucketArray<TypeList> types;
- // For quick access.
- size_t typeCount;
+ uint8_t largestTypeId;
// Computed attribute bags, first indexed by the type and second
// by the entry in that type.
- bag_set*** bags;
+ ByteBucketArray<bag_set**>* bags;
+
+ // The table mapping dynamic references to resolved references for
+ // this package group.
+ // TODO: We may be able to support dynamic references in overlays
+ // by having these tables in a per-package scope rather than
+ // per-package-group.
+ DynamicRefTable dynamicRefTable;
};
struct ResTable::bag_set
@@ -2821,7 +3001,7 @@ ResTable::Theme::~Theme()
void ResTable::Theme::free_package(package_info* pi)
{
- for (size_t j=0; j<pi->numTypes; j++) {
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
theme_entry* te = pi->types[j].entries;
if (te != NULL) {
free(te);
@@ -2832,10 +3012,8 @@ void ResTable::Theme::free_package(package_info* pi)
ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
{
- package_info* newpi = (package_info*)malloc(
- sizeof(package_info) + (pi->numTypes*sizeof(type_info)));
- newpi->numTypes = pi->numTypes;
- for (size_t j=0; j<newpi->numTypes; j++) {
+ package_info* newpi = (package_info*)malloc(sizeof(package_info));
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
size_t cnt = pi->types[j].numEntries;
newpi->types[j].numEntries = cnt;
theme_entry* te = pi->types[j].entries;
@@ -2888,17 +3066,14 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
curPI = mPackages[pidx];
if (curPI == NULL) {
PackageGroup* const grp = mTable.mPackageGroups[pidx];
- int cnt = grp->typeCount;
- curPI = (package_info*)malloc(
- sizeof(package_info) + (cnt*sizeof(type_info)));
- curPI->numTypes = cnt;
- memset(curPI->types, 0, cnt*sizeof(type_info));
+ curPI = (package_info*)malloc(sizeof(package_info));
+ memset(curPI, 0, sizeof(*curPI));
mPackages[pidx] = curPI;
}
curType = 0xffffffff;
}
if (curType != t) {
- if (t >= curPI->numTypes) {
+ if (t > Res_MAXTYPE) {
ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
bag++;
continue;
@@ -2907,8 +3082,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
curEntries = curPI->types[t].entries;
if (curEntries == NULL) {
PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
- const Type* type = grp->packages[0]->getType(t);
- int cnt = type != NULL ? type->entryCount : 0;
+ const TypeList& typeList = grp->types[t];
+ int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
curPI->types[t].numEntries = cnt;
@@ -2923,8 +3098,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
}
theme_entry* curEntry = curEntries + e;
TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
- attrRes, bag->map.value.dataType, bag->map.value.data,
- curEntry->value.dataType));
+ attrRes, bag->map.value.dataType, bag->map.value.data,
+ curEntry->value.dataType));
if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
curEntry->stringBlock = bag->stringBlock;
curEntry->typeSpecFlags |= bagTypeSpecFlags;
@@ -2999,8 +3174,8 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
const package_info* const pi = mPackages[p];
TABLE_THEME(ALOGI("Found package: %p", pi));
if (pi != NULL) {
- TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes));
- if (t < pi->numTypes) {
+ TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1));
+ if (t <= Res_MAXTYPE) {
const type_info& ti = pi->types[t];
TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
if (e < ti.numEntries) {
@@ -3062,14 +3237,13 @@ void ResTable::Theme::dumpToLog() const
package_info* pi = mPackages[i];
if (pi == NULL) continue;
- ALOGI(" Package #0x%02x:\n", (int)(i+1));
- for (size_t j=0; j<pi->numTypes; j++) {
+ ALOGI(" Package #0x%02x:\n", (int)(i + 1));
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
type_info& ti = pi->types[j];
if (ti.numEntries == 0) continue;
-
- ALOGI(" Type #0x%02x:\n", (int)(j+1));
- for (size_t k=0; k<ti.numEntries; k++) {
- theme_entry& te = ti.entries[k];
+ ALOGI(" Type #0x%02x:\n", (int)(j + 1));
+ for (size_t k = 0; k < ti.numEntries; k++) {
+ const theme_entry& te = ti.entries[k];
if (te.value.dataType == Res_value::TYPE_NULL) continue;
ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
(int)Res_MAKEID(i, j, k),
@@ -3080,7 +3254,7 @@ void ResTable::Theme::dumpToLog() const
}
ResTable::ResTable()
- : mError(NO_INIT)
+ : mError(NO_INIT), mNextPackageId(2)
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
@@ -3088,11 +3262,11 @@ ResTable::ResTable()
}
ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
- : mError(NO_INIT)
+ : mError(NO_INIT), mNextPackageId(2)
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, cookie, NULL /* asset */, copyData, NULL /* idMap */);
+ addInternal(data, size, NULL, 0, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
//ALOGI("Creating ResTable %p\n", this);
}
@@ -3108,21 +3282,45 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
}
-status_t ResTable::add(const void* data, size_t size) {
- return addInternal(data, size, 0 /* cookie */, NULL /* asset */,
- false /* copyData */, NULL /* idMap */);
+status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
+ return addInternal(data, size, NULL, 0, cookie, copyData);
}
-status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap)
-{
+status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+ const int32_t cookie, bool copyData) {
+ return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
return UNKNOWN_ERROR;
}
- size_t size = (size_t)asset->getLength();
- return addInternal(data, size, cookie, asset, copyData,
- reinterpret_cast<const Asset*>(idmap));
+
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+ const void* data = asset->getBuffer(true);
+ if (data == NULL) {
+ ALOGW("Unable to get buffer of resource asset file");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t idmapSize = 0;
+ const void* idmapData = NULL;
+ if (idmapAsset != NULL) {
+ idmapData = idmapAsset->getBuffer(true);
+ if (idmapData == NULL) {
+ ALOGW("Unable to get buffer of idmap asset file");
+ return UNKNOWN_ERROR;
+ }
+ idmapSize = static_cast<size_t>(idmapAsset->getLength());
+ }
+
+ return addInternal(data, static_cast<size_t>(asset->getLength()),
+ idmapData, idmapSize, cookie, copyData);
}
status_t ResTable::add(ResTable* src)
@@ -3139,8 +3337,16 @@ status_t ResTable::add(ResTable* src)
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
- pg->basePackage = srcPg->basePackage;
- pg->typeCount = srcPg->typeCount;
+
+ for (size_t j = 0; j < srcPg->types.size(); j++) {
+ if (srcPg->types[j].isEmpty()) {
+ continue;
+ }
+
+ TypeList& typeList = pg->types.editItemAt(j);
+ typeList.appendVector(srcPg->types[j]);
+ }
+ pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
mPackageGroups.add(pg);
}
@@ -3149,38 +3355,62 @@ status_t ResTable::add(ResTable* src)
return mError;
}
-status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
- Asset* /*asset*/, bool copyData, const Asset* idmap)
+status_t ResTable::addEmpty(const int32_t cookie) {
+ Header* header = new Header(this);
+ header->index = mHeaders.size();
+ header->cookie = cookie;
+ header->values.setToEmpty();
+ header->ownedData = calloc(1, sizeof(ResTable_header));
+
+ ResTable_header* resHeader = (ResTable_header*) header->ownedData;
+ resHeader->header.type = RES_TABLE_TYPE;
+ resHeader->header.headerSize = sizeof(ResTable_header);
+ resHeader->header.size = sizeof(ResTable_header);
+
+ header->header = (const ResTable_header*) resHeader;
+ mHeaders.add(header);
+ return (mError=NO_ERROR);
+}
+
+status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
+ const int32_t cookie, bool copyData)
{
- if (!data) return NO_ERROR;
+ if (!data) {
+ return NO_ERROR;
+ }
+
+ if (dataSize < sizeof(ResTable_header)) {
+ ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).",
+ (int) dataSize, (int) sizeof(ResTable_header));
+ return UNKNOWN_ERROR;
+ }
+
Header* header = new Header(this);
header->index = mHeaders.size();
header->cookie = cookie;
- if (idmap != NULL) {
- const size_t idmap_size = idmap->getLength();
- const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
- header->resourceIDMap = (uint32_t*)malloc(idmap_size);
+ if (idmapData != NULL) {
+ header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
if (header->resourceIDMap == NULL) {
delete header;
return (mError = NO_MEMORY);
}
- memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
- header->resourceIDMapSize = idmap_size;
+ memcpy(header->resourceIDMap, idmapData, idmapDataSize);
+ header->resourceIDMapSize = idmapDataSize;
}
mHeaders.add(header);
const bool notDeviceEndian = htods(0xf0) != 0xf0;
LOAD_TABLE_NOISY(
- ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
- "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
+ ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d "
+ "idmap=%p\n", data, dataSize, cookie, copyData, idmap));
if (copyData || notDeviceEndian) {
- header->ownedData = malloc(size);
+ header->ownedData = malloc(dataSize);
if (header->ownedData == NULL) {
return (mError=NO_MEMORY);
}
- memcpy(header->ownedData, data, size);
+ memcpy(header->ownedData, data, dataSize);
data = header->ownedData;
}
@@ -3189,13 +3419,11 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook
//ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size,
// dtohl(header->header->header.size), header->header->header.size);
LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header));
- LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256,
- 16, 16, 0, false, printToLogFunc));
if (dtohs(header->header->header.headerSize) > header->size
- || header->size > size) {
+ || header->size > dataSize) {
ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
(int)dtohs(header->header->header.headerSize),
- (int)header->size, (int)size);
+ (int)header->size, (int)dataSize);
return (mError=BAD_TYPE);
}
if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
@@ -3240,16 +3468,8 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook
dtohl(header->header->packageCount));
return (mError=BAD_TYPE);
}
- uint32_t idmap_id = 0;
- if (idmap != NULL) {
- uint32_t tmp;
- if (getIdmapPackageId(header->resourceIDMap,
- header->resourceIDMapSize,
- &tmp) == NO_ERROR) {
- idmap_id = tmp;
- }
- }
- if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
+
+ if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
return mError;
}
curPackage++;
@@ -3332,46 +3552,38 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou
ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
return false;
}
- if (grp->packages.size() > 0) {
- const Package* const package = grp->packages[0];
- const ResTable_type* type;
- const ResTable_entry* entry;
- ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL);
- if (offset <= 0) {
- return false;
- }
+ Entry entry;
+ status_t err = getEntry(grp, t, e, NULL, &entry);
+ if (err != NO_ERROR) {
+ return false;
+ }
- outName->package = grp->name.string();
- outName->packageLen = grp->name.size();
- if (allowUtf8) {
- outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen);
- outName->name8 = grp->basePackage->keyStrings.string8At(
- dtohl(entry->key.index), &outName->nameLen);
- } else {
- outName->type8 = NULL;
- outName->name8 = NULL;
- }
- if (outName->type8 == NULL) {
- outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
- // If we have a bad index for some reason, we should abort.
- if (outName->type == NULL) {
- return false;
- }
+ outName->package = grp->name.string();
+ outName->packageLen = grp->name.size();
+ if (allowUtf8) {
+ outName->type8 = entry.typeStr.string8(&outName->typeLen);
+ outName->name8 = entry.keyStr.string8(&outName->nameLen);
+ } else {
+ outName->type8 = NULL;
+ outName->name8 = NULL;
+ }
+ if (outName->type8 == NULL) {
+ outName->type = entry.typeStr.string16(&outName->typeLen);
+ // If we have a bad index for some reason, we should abort.
+ if (outName->type == NULL) {
+ return false;
}
- if (outName->name8 == NULL) {
- outName->name = grp->basePackage->keyStrings.stringAt(
- dtohl(entry->key.index), &outName->nameLen);
- // If we have a bad index for some reason, we should abort.
- if (outName->name == NULL) {
- return false;
- }
+ }
+ if (outName->name8 == NULL) {
+ outName->name = entry.keyStr.string16(&outName->nameLen);
+ // If we have a bad index for some reason, we should abort.
+ if (outName->name == NULL) {
+ return false;
}
-
- return true;
}
- return false;
+ return true;
}
ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
@@ -3398,15 +3610,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
return BAD_INDEX;
}
- const Res_value* bestValue = NULL;
- const Package* bestPackage = NULL;
- ResTable_config bestItem;
- memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
-
- if (outSpecFlags != NULL) *outSpecFlags = 0;
-
- // Look through all resource packages, starting with the most
- // recently added.
const PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
@@ -3414,133 +3617,62 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
}
// Allow overriding density
- const ResTable_config* desiredConfig = &mParams;
- ResTable_config* overrideConfig = NULL;
+ ResTable_config desiredConfig = mParams;
if (density > 0) {
- overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config));
- if (overrideConfig == NULL) {
- ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno));
- return BAD_INDEX;
- }
- memcpy(overrideConfig, &mParams, sizeof(ResTable_config));
- overrideConfig->density = density;
- desiredConfig = overrideConfig;
- }
-
- ssize_t rc = BAD_VALUE;
- size_t ip = grp->packages.size();
- while (ip > 0) {
- ip--;
- int T = t;
- int E = e;
-
- const Package* const package = grp->packages[ip];
- if (package->header->resourceIDMap) {
- uint32_t overlayResID = 0x0;
- status_t retval = idmapLookup(package->header->resourceIDMap,
- package->header->resourceIDMapSize,
- resID, &overlayResID);
- if (retval == NO_ERROR && overlayResID != 0x0) {
- // for this loop iteration, this is the type and entry we really want
- ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
- T = Res_GETTYPE(overlayResID);
- E = Res_GETENTRY(overlayResID);
- } else {
- // resource not present in overlay package, continue with the next package
- continue;
- }
- }
-
- const ResTable_type* type;
- const ResTable_entry* entry;
- const Type* typeClass;
- ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass);
- if (offset <= 0) {
- // No {entry, appropriate config} pair found in package. If this
- // package is an overlay package (ip != 0), this simply means the
- // overlay package did not specify a default.
- // Non-overlay packages are still required to provide a default.
- if (offset < 0 && ip == 0) {
- ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
- resID, T, E, ip, (int)offset);
- rc = offset;
- goto out;
- }
- continue;
- }
-
- if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
- if (!mayBeBag) {
- ALOGW("Requesting resource 0x%x failed because it is complex\n",
- resID);
- }
- continue;
- }
+ desiredConfig.density = density;
+ }
- TABLE_NOISY(aout << "Resource type data: "
- << HexDump(type, dtohl(type->header.size)) << endl);
+ Entry entry;
+ status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
+ if (err != NO_ERROR) {
+ ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
+ resID, t, e, err);
+ return err;
+ }
- if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
- ALOGW("ResTable_item at %d is beyond type chunk data %d",
- (int)offset, dtohl(type->header.size));
- rc = BAD_TYPE;
- goto out;
+ if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+ if (!mayBeBag) {
+ ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
+ return BAD_VALUE;
+ }
- const Res_value* item =
- (const Res_value*)(((const uint8_t*)type) + offset);
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
-
- if (outSpecFlags != NULL) {
- if (typeClass->typeSpecFlags != NULL) {
- *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- *outSpecFlags = -1;
- }
- }
+ const Res_value* value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
- if (bestPackage != NULL &&
- (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
- // Discard thisConfig not only if bestItem is more specific, but also if the two configs
- // are identical (diff == 0), or overlay packages will not take effect.
- continue;
- }
+ outValue->size = dtohs(value->size);
+ outValue->res0 = value->res0;
+ outValue->dataType = value->dataType;
+ outValue->data = dtohl(value->data);
- bestItem = thisConfig;
- bestValue = item;
- bestPackage = package;
+ // The reference may be pointing to a resource in a shared library. These
+ // references have build-time generated package IDs. These ids may not match
+ // the actual package IDs of the corresponding packages in this ResTable.
+ // We need to fix the package ID based on a mapping.
+ if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
+ ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
+ return BAD_VALUE;
}
- TABLE_NOISY(printf("Found result: package %p\n", bestPackage));
+ TABLE_NOISY(size_t len;
+ printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
+ entry.package->header->index,
+ outValue->dataType,
+ outValue->dataType == Res_value::TYPE_STRING
+ ? String8(entry.package->header->values.stringAt(
+ outValue->data, &len)).string()
+ : "",
+ outValue->data));
- if (bestValue) {
- outValue->size = dtohs(bestValue->size);
- outValue->res0 = bestValue->res0;
- outValue->dataType = bestValue->dataType;
- outValue->data = dtohl(bestValue->data);
- if (outConfig != NULL) {
- *outConfig = bestItem;
- }
- TABLE_NOISY(size_t len;
- printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
- bestPackage->header->index,
- outValue->dataType,
- outValue->dataType == bestValue->TYPE_STRING
- ? String8(bestPackage->header->values.stringAt(
- outValue->data, &len)).string()
- : "",
- outValue->data));
- rc = bestPackage->header->index;
- goto out;
+ if (outSpecFlags != NULL) {
+ *outSpecFlags = entry.specFlags;
}
-out:
- if (overrideConfig != NULL) {
- free(overrideConfig);
+ if (outConfig != NULL) {
+ *outConfig = entry.config;
}
- return rc;
+ return entry.package->header->index;
}
ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -3548,8 +3680,8 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
ResTable_config* outConfig) const
{
int count=0;
- while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE
- && value->data != 0 && count < 20) {
+ while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE
+ && value->data != 0 && count < 20) {
if (outLastRef) *outLastRef = value->data;
uint32_t lastRef = value->data;
uint32_t newFlags = 0;
@@ -3639,29 +3771,25 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
- return false;
+ return BAD_INDEX;
}
- if (t >= (int)grp->typeCount) {
- ALOGW("Type identifier 0x%x is larger than type count 0x%x",
- t+1, (int)grp->typeCount);
+ const TypeList& typeConfigs = grp->types[t];
+ if (typeConfigs.isEmpty()) {
+ ALOGW("Type identifier 0x%x does not exist.", t+1);
return BAD_INDEX;
}
- const Package* const basePackage = grp->packages[0];
-
- const Type* const typeConfigs = basePackage->getType(t);
-
- const size_t NENTRY = typeConfigs->entryCount;
+ const size_t NENTRY = typeConfigs[0]->entryCount;
if (e >= (int)NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
- e, (int)typeConfigs->entryCount);
+ e, (int)typeConfigs[0]->entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
if (grp->bags) {
- bag_set** typeSet = grp->bags[t];
+ bag_set** typeSet = grp->bags->get(t);
if (typeSet) {
bag_set* set = typeSet[e];
if (set) {
@@ -3682,211 +3810,184 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
// Bag not found, we need to compute it!
if (!grp->bags) {
- grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*));
+ grp->bags = new ByteBucketArray<bag_set**>();
if (!grp->bags) return NO_MEMORY;
}
- bag_set** typeSet = grp->bags[t];
+ bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
- grp->bags[t] = typeSet;
+ grp->bags->set(t, typeSet);
}
// Mark that we are currently working on this one.
typeSet[e] = (bag_set*)0xFFFFFFFF;
+ TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+
+ // Now collect all bag attributes
+ Entry entry;
+ status_t err = getEntry(grp, t, e, &mParams, &entry);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ const uint16_t entrySize = dtohs(entry.entry->size);
+ const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
+ ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
+ const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
+ ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
+
+ size_t N = count;
+
+ TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
+ entrySize, parent, count));
+
+ // If this map inherits from another, we need to start
+ // with its parent's values. Otherwise start out empty.
+ TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
+ entrySize, parent));
+
// This is what we are building.
bag_set* set = NULL;
- TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+ if (parent) {
+ uint32_t resolvedParent = parent;
- ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig));
+ // Bags encode a parent reference without using the standard
+ // Res_value structure. That means we must always try to
+ // resolve a parent reference in case it is actually a
+ // TYPE_DYNAMIC_REFERENCE.
+ status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
+ if (err != NO_ERROR) {
+ ALOGE("Failed resolving bag parent id 0x%08x", parent);
+ return UNKNOWN_ERROR;
+ }
- // Now collect all bag attributes from all packages.
- size_t ip = grp->packages.size();
- while (ip > 0) {
- ip--;
- int T = t;
- int E = e;
-
- const Package* const package = grp->packages[ip];
- if (package->header->resourceIDMap) {
- uint32_t overlayResID = 0x0;
- status_t retval = idmapLookup(package->header->resourceIDMap,
- package->header->resourceIDMapSize,
- resID, &overlayResID);
- if (retval == NO_ERROR && overlayResID != 0x0) {
- // for this loop iteration, this is the type and entry we really want
- ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
- T = Res_GETTYPE(overlayResID);
- E = Res_GETENTRY(overlayResID);
- } else {
- // resource not present in overlay package, continue with the next package
- continue;
- }
+ const bag_entry* parentBag;
+ uint32_t parentTypeSpecFlags = 0;
+ const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
+ const size_t NT = ((NP >= 0) ? NP : 0) + N;
+ set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
+ if (set == NULL) {
+ return NO_MEMORY;
+ }
+ if (NP > 0) {
+ memcpy(set+1, parentBag, NP*sizeof(bag_entry));
+ set->numAttrs = NP;
+ TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
+ } else {
+ TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
+ set->numAttrs = 0;
+ }
+ set->availAttrs = NT;
+ set->typeSpecFlags = parentTypeSpecFlags;
+ } else {
+ set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
+ if (set == NULL) {
+ return NO_MEMORY;
}
+ set->numAttrs = 0;
+ set->availAttrs = N;
+ set->typeSpecFlags = 0;
+ }
- const ResTable_type* type;
- const ResTable_entry* entry;
- const Type* typeClass;
- ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
- ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
- ALOGV("Resulting offset=%d\n", offset);
- if (offset <= 0) {
- // No {entry, appropriate config} pair found in package. If this
- // package is an overlay package (ip != 0), this simply means the
- // overlay package did not specify a default.
- // Non-overlay packages are still required to provide a default.
- if (offset < 0 && ip == 0) {
- if (set) free(set);
- return offset;
- }
- continue;
+ set->typeSpecFlags |= entry.specFlags;
+
+ // Now merge in the new attributes...
+ size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
+ + dtohs(entry.entry->size);
+ const ResTable_map* map;
+ bag_entry* entries = (bag_entry*)(set+1);
+ size_t curEntry = 0;
+ uint32_t pos = 0;
+ TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
+ set, entries, set->availAttrs));
+ while (pos < count) {
+ TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
+
+ if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
+ ALOGW("ResTable_map at %d is beyond type chunk data %d",
+ (int)curOff, dtohl(entry.type->header.size));
+ return BAD_TYPE;
}
+ map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
+ N++;
- if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
- ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n",
- resID, ip);
- continue;
+ uint32_t newName = htodl(map->name.ident);
+ if (!Res_INTERNALID(newName)) {
+ // Attributes don't have a resource id as the name. They specify
+ // other data, which would be wrong to change via a lookup.
+ if (grp->dynamicRefTable.lookupResourceId(&newName) != NO_ERROR) {
+ ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
+ (int) curOff, (int) newName);
+ return UNKNOWN_ERROR;
+ }
}
- if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
- continue;
+ bool isInside;
+ uint32_t oldName = 0;
+ while ((isInside=(curEntry < set->numAttrs))
+ && (oldName=entries[curEntry].map.name.ident) < newName) {
+ TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
+ curEntry, entries[curEntry].map.name.ident));
+ curEntry++;
}
- bestConfig = type->config;
- if (set) {
- free(set);
- set = NULL;
- }
-
- const uint16_t entrySize = dtohs(entry->size);
- const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
- const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
- size_t N = count;
-
- TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
- entrySize, parent, count));
-
- // If this map inherits from another, we need to start
- // with its parent's values. Otherwise start out empty.
- TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
- entrySize, parent));
- if (parent) {
- const bag_entry* parentBag;
- uint32_t parentTypeSpecFlags = 0;
- const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags);
- const size_t NT = ((NP >= 0) ? NP : 0) + N;
- set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
- if (set == NULL) {
- return NO_MEMORY;
- }
- if (NP > 0) {
- memcpy(set+1, parentBag, NP*sizeof(bag_entry));
- set->numAttrs = NP;
- TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
- } else {
- TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
- set->numAttrs = 0;
- }
- set->availAttrs = NT;
- set->typeSpecFlags = parentTypeSpecFlags;
+
+ if ((!isInside) || oldName != newName) {
+ // This is a new attribute... figure out what to do with it.
+ if (set->numAttrs >= set->availAttrs) {
+ // Need to alloc more memory...
+ const size_t newAvail = set->availAttrs+N;
+ set = (bag_set*)realloc(set,
+ sizeof(bag_set)
+ + sizeof(bag_entry)*newAvail);
+ if (set == NULL) {
+ return NO_MEMORY;
+ }
+ set->availAttrs = newAvail;
+ entries = (bag_entry*)(set+1);
+ TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
+ set, entries, set->availAttrs));
+ }
+ if (isInside) {
+ // Going in the middle, need to make space.
+ memmove(entries+curEntry+1, entries+curEntry,
+ sizeof(bag_entry)*(set->numAttrs-curEntry));
+ set->numAttrs++;
+ }
+ TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
+ curEntry, newName));
} else {
- set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
- if (set == NULL) {
- return NO_MEMORY;
- }
- set->numAttrs = 0;
- set->availAttrs = N;
- set->typeSpecFlags = 0;
+ TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
+ curEntry, oldName));
}
- if (typeClass->typeSpecFlags != NULL) {
- set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- set->typeSpecFlags = -1;
- }
-
- // Now merge in the new attributes...
- ssize_t curOff = offset;
- const ResTable_map* map;
- bag_entry* entries = (bag_entry*)(set+1);
- size_t curEntry = 0;
- uint32_t pos = 0;
- TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
- while (pos < count) {
- TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
-
- if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) {
- ALOGW("ResTable_map at %d is beyond type chunk data %d",
- (int)curOff, dtohl(type->header.size));
- return BAD_TYPE;
- }
- map = (const ResTable_map*)(((const uint8_t*)type) + curOff);
- N++;
-
- const uint32_t newName = htodl(map->name.ident);
- bool isInside;
- uint32_t oldName = 0;
- while ((isInside=(curEntry < set->numAttrs))
- && (oldName=entries[curEntry].map.name.ident) < newName) {
- TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
- curEntry, entries[curEntry].map.name.ident));
- curEntry++;
- }
-
- if ((!isInside) || oldName != newName) {
- // This is a new attribute... figure out what to do with it.
- if (set->numAttrs >= set->availAttrs) {
- // Need to alloc more memory...
- const size_t newAvail = set->availAttrs+N;
- set = (bag_set*)realloc(set,
- sizeof(bag_set)
- + sizeof(bag_entry)*newAvail);
- if (set == NULL) {
- return NO_MEMORY;
- }
- set->availAttrs = newAvail;
- entries = (bag_entry*)(set+1);
- TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
- }
- if (isInside) {
- // Going in the middle, need to make space.
- memmove(entries+curEntry+1, entries+curEntry,
- sizeof(bag_entry)*(set->numAttrs-curEntry));
- set->numAttrs++;
- }
- TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
- curEntry, newName));
- } else {
- TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
- curEntry, oldName));
- }
+ bag_entry* cur = entries+curEntry;
- bag_entry* cur = entries+curEntry;
+ cur->stringBlock = entry.package->header->index;
+ cur->map.name.ident = newName;
+ cur->map.value.copyFrom_dtoh(map->value);
+ status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
+ if (err != NO_ERROR) {
+ ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
+ return UNKNOWN_ERROR;
+ }
- cur->stringBlock = package->header->index;
- cur->map.name.ident = newName;
- cur->map.value.copyFrom_dtoh(map->value);
- TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
- curEntry, cur, cur->stringBlock, cur->map.name.ident,
- cur->map.value.dataType, cur->map.value.data));
+ TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
+ curEntry, cur, cur->stringBlock, cur->map.name.ident,
+ cur->map.value.dataType, cur->map.value.data));
- // On to the next!
- curEntry++;
- pos++;
- const size_t size = dtohs(map->value.size);
- curOff += size + sizeof(*map)-sizeof(map->value);
- };
- if (curEntry > set->numAttrs) {
- set->numAttrs = curEntry;
- }
+ // On to the next!
+ curEntry++;
+ pos++;
+ const size_t size = dtohs(map->value.size);
+ curOff += size + sizeof(*map)-sizeof(map->value);
+ };
+
+ if (curEntry > set->numAttrs) {
+ set->numAttrs = curEntry;
}
// And this is it...
@@ -4054,80 +4155,63 @@ nope:
continue;
}
- const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
+ const ssize_t ti = group->findType16(type, typeLen);
if (ti < 0) {
TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
continue;
}
- const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
- if (ei < 0) {
- TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
- continue;
- }
-
- TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
-
- const Type* const typeConfigs = group->packages[0]->getType(ti);
- if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) {
- TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
+ const TypeList& typeList = group->types[ti];
+ if (typeList.isEmpty()) {
+ TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n",
String8(group->name).string(), ti));
+ continue;
}
- size_t NTC = typeConfigs->configs.size();
- for (size_t tci=0; tci<NTC; tci++) {
- const ResTable_type* const ty = typeConfigs->configs[tci];
- const uint32_t typeOffset = dtohl(ty->entriesStart);
-
- const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)ty) + dtohs(ty->header.headerSize));
-
- const size_t NE = dtohl(ty->entryCount);
- for (size_t i=0; i<NE; i++) {
- uint32_t offset = dtohl(eindex[i]);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
-
- offset += typeOffset;
+ const size_t typeCount = typeList.size();
+ for (size_t i = 0; i < typeCount; i++) {
+ const Type* t = typeList[i];
+ const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
+ if (ei < 0) {
+ continue;
+ }
- if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
- ALOGW("ResTable_entry at %d is beyond type chunk data %d",
- offset, dtohl(ty->header.size));
- return 0;
- }
- if ((offset&0x3) != 0) {
- ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s",
- (int)offset, (int)group->id, (int)ti+1, (int)i,
- String8(package, packageLen).string(),
- String8(type, typeLen).string(),
- String8(name, nameLen).string());
- return 0;
- }
+ const size_t configCount = t->configs.size();
+ for (size_t j = 0; j < configCount; j++) {
+ const TypeVariant tv(t->configs[j]);
+ for (TypeVariant::iterator iter = tv.beginEntries();
+ iter != tv.endEntries();
+ iter++) {
+ const ResTable_entry* entry = *iter;
+ if (entry == NULL) {
+ continue;
+ }
- const ResTable_entry* const entry = (const ResTable_entry*)
- (((const uint8_t*)ty) + offset);
- if (dtohs(entry->size) < sizeof(*entry)) {
- ALOGW("ResTable_entry size %d is too small", dtohs(entry->size));
- return BAD_TYPE;
- }
+ if (dtohl(entry->key.index) == (size_t) ei) {
+ uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index());
+ if (outTypeSpecFlags) {
+ Entry result;
+ if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) {
+ ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)",
+ String8(group->name).string(),
+ String8(String16(type, typeLen)).string(),
+ String8(String16(name, nameLen)).string(),
+ resId);
+ return 0;
+ }
+ *outTypeSpecFlags = result.specFlags;
- TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n",
- i, ei, dtohl(entry->key.index)));
- if (dtohl(entry->key.index) == (size_t)ei) {
- if (outTypeSpecFlags) {
- *outTypeSpecFlags = typeConfigs->typeSpecFlags[i];
- if (fakePublic) {
- *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+ if (fakePublic) {
+ *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+ }
}
+ return resId;
}
- return Res_MAKEID(group->id-1, ti, i);
}
}
}
+ break;
}
-
return 0;
}
@@ -4564,23 +4648,29 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
type.size(), package.string(), package.size(), &specFlags);
if (rid != 0) {
if (enforcePrivate) {
- if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
- if (accessor != NULL) {
- accessor->reportError(accessorCookie, "Resource is not public.");
+ if (accessor == NULL || accessor->getAssetsPackage() != package) {
+ if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
+ if (accessor != NULL) {
+ accessor->reportError(accessorCookie, "Resource is not public.");
+ }
+ return false;
}
- return false;
}
}
- if (!accessor) {
- outValue->data = rid;
- return true;
+
+ if (accessor) {
+ rid = Res_MAKEID(
+ accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
+ Res_GETTYPE(rid), Res_GETENTRY(rid));
+ TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n",
+ String8(package).string(), String8(type).string(),
+ String8(name).string(), rid));
+ }
+
+ uint32_t packageId = Res_GETPACKAGE(rid) + 1;
+ if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
+ outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
}
- rid = Res_MAKEID(
- accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
- Res_GETTYPE(rid), Res_GETENTRY(rid));
- TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), rid));
outValue->data = rid;
return true;
}
@@ -4592,8 +4682,17 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n",
String8(package).string(), String8(type).string(),
String8(name).string(), rid));
- outValue->data = rid;
- return true;
+ uint32_t packageId = Res_GETPACKAGE(rid) + 1;
+ if (packageId == 0x00) {
+ outValue->data = rid;
+ outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ return true;
+ } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
+ // We accept packageId's generated as 0x01 in order to support
+ // building the android system resources
+ outValue->data = rid;
+ return true;
+ }
}
}
}
@@ -5125,15 +5224,15 @@ size_t ResTable::getBasePackageCount() const
return mPackageGroups.size();
}
-const char16_t* ResTable::getBasePackageName(size_t idx) const
+const String16 ResTable::getBasePackageName(size_t idx) const
{
if (mError != NO_ERROR) {
- return 0;
+ return String16();
}
LOG_FATAL_IF(idx >= mPackageGroups.size(),
"Requested package index %d past package count %d",
(int)idx, (int)mPackageGroups.size());
- return mPackageGroups[idx]->name.string();
+ return mPackageGroups[idx]->name;
}
uint32_t ResTable::getBasePackageId(size_t idx) const
@@ -5147,6 +5246,18 @@ uint32_t ResTable::getBasePackageId(size_t idx) const
return mPackageGroups[idx]->id;
}
+uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
+{
+ if (mError != NO_ERROR) {
+ return 0;
+ }
+ LOG_FATAL_IF(idx >= mPackageGroups.size(),
+ "Requested package index %d past package count %d",
+ (int)idx, (int)mPackageGroups.size());
+ const PackageGroup* const group = mPackageGroups[idx];
+ return group->largestTypeId;
+}
+
size_t ResTable::getTableCount() const
{
return mHeaders.size();
@@ -5162,34 +5273,48 @@ int32_t ResTable::getTableCookie(size_t index) const
return mHeaders[index]->cookie;
}
+const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) const
+{
+ const size_t N = mPackageGroups.size();
+ for (size_t i = 0; i < N; i++) {
+ const PackageGroup* pg = mPackageGroups[i];
+ size_t M = pg->packages.size();
+ for (size_t j = 0; j < M; j++) {
+ if (pg->packages[j]->header->cookie == cookie) {
+ return &pg->dynamicRefTable;
+ }
+ }
+ }
+ return NULL;
+}
+
void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
{
- const size_t I = mPackageGroups.size();
- for (size_t i=0; i<I; i++) {
+ const size_t packageCount = mPackageGroups.size();
+ for (size_t i = 0; i < packageCount; i++) {
const PackageGroup* packageGroup = mPackageGroups[i];
- const size_t J = packageGroup->packages.size();
- for (size_t j=0; j<J; j++) {
- const Package* package = packageGroup->packages[j];
- const size_t K = package->types.size();
- for (size_t k=0; k<K; k++) {
- const Type* type = package->types[k];
- if (type == NULL) continue;
- const size_t L = type->configs.size();
- for (size_t l=0; l<L; l++) {
- const ResTable_type* config = type->configs[l];
+ const size_t typeCount = packageGroup->types.size();
+ for (size_t j = 0; j < typeCount; j++) {
+ const TypeList& typeList = packageGroup->types[j];
+ const size_t numTypes = typeList.size();
+ for (size_t k = 0; k < numTypes; k++) {
+ const Type* type = typeList[k];
+ const size_t numConfigs = type->configs.size();
+ for (size_t m = 0; m < numConfigs; m++) {
+ const ResTable_type* config = type->configs[m];
ResTable_config cfg;
memset(&cfg, 0, sizeof(ResTable_config));
cfg.copyFromDtoH(config->config);
// only insert unique
- const size_t M = configs->size();
- size_t m;
- for (m=0; m<M; m++) {
- if (0 == (*configs)[m].compare(cfg)) {
+ const size_t N = configs->size();
+ size_t n;
+ for (n = 0; n < N; n++) {
+ if (0 == (*configs)[n].compare(cfg)) {
break;
}
}
// if we didn't find it
- if (m == M) {
+ if (n == N) {
configs->add(cfg);
}
}
@@ -5222,123 +5347,180 @@ void ResTable::getLocales(Vector<String8>* locales) const
}
}
-ssize_t ResTable::getEntry(
- const Package* package, int typeIndex, int entryIndex,
- const ResTable_config* config,
- const ResTable_type** outType, const ResTable_entry** outEntry,
- const Type** outTypeClass) const
-{
- ALOGV("Getting entry from package %p\n", package);
- const ResTable_package* const pkg = package->package;
+StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
+ : mPool(pool), mIndex(index) {}
- const Type* allTypes = package->getType(typeIndex);
- ALOGV("allTypes=%p\n", allTypes);
- if (allTypes == NULL) {
- ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
- return 0;
+StringPoolRef::StringPoolRef()
+ : mPool(NULL), mIndex(0) {}
+
+const char* StringPoolRef::string8(size_t* outLen) const {
+ if (mPool != NULL) {
+ return mPool->string8At(mIndex, outLen);
+ }
+ if (outLen != NULL) {
+ *outLen = 0;
}
+ return NULL;
+}
- if ((size_t)entryIndex >= allTypes->entryCount) {
- ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d",
- entryIndex, (int)allTypes->entryCount);
+const char16_t* StringPoolRef::string16(size_t* outLen) const {
+ if (mPool != NULL) {
+ return mPool->stringAt(mIndex, outLen);
+ }
+ if (outLen != NULL) {
+ *outLen = 0;
+ }
+ return NULL;
+}
+
+status_t ResTable::getEntry(
+ const PackageGroup* packageGroup, int typeIndex, int entryIndex,
+ const ResTable_config* config,
+ Entry* outEntry) const
+{
+ const TypeList& typeList = packageGroup->types[typeIndex];
+ if (typeList.isEmpty()) {
+ ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
return BAD_TYPE;
}
- const ResTable_type* type = NULL;
- uint32_t offset = ResTable_type::NO_ENTRY;
+ const ResTable_type* bestType = NULL;
+ uint32_t bestOffset = ResTable_type::NO_ENTRY;
+ const Package* bestPackage = NULL;
+ uint32_t specFlags = 0;
+ uint8_t actualTypeIndex = typeIndex;
ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
-
- const size_t NT = allTypes->configs.size();
- for (size_t i=0; i<NT; i++) {
- const ResTable_type* const thisType = allTypes->configs[i];
- if (thisType == NULL) continue;
-
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(thisType->config);
+ memset(&bestConfig, 0, sizeof(bestConfig));
- TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
- entryIndex, typeIndex+1, dtohl(thisType->config.size),
- thisConfig.toString().string()));
+ // Iterate over the Types of each package.
+ const size_t typeCount = typeList.size();
+ for (size_t i = 0; i < typeCount; i++) {
+ const Type* const typeSpec = typeList[i];
+
+ int realEntryIndex = entryIndex;
+ int realTypeIndex = typeIndex;
+ bool currentTypeIsOverlay = false;
+
+ // Runtime overlay packages provide a mapping of app resource
+ // ID to package resource ID.
+ if (typeSpec->idmapEntries.hasEntries()) {
+ uint16_t overlayEntryIndex;
+ if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
+ // No such mapping exists
+ continue;
+ }
+ realEntryIndex = overlayEntryIndex;
+ realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
+ currentTypeIsOverlay = true;
+ }
- // Check to make sure this one is valid for the current parameters.
- if (config && !thisConfig.match(*config)) {
- TABLE_GETENTRY(ALOGI("Does not match config!\n"));
+ if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
+ ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
+ Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
+ entryIndex, static_cast<int>(typeSpec->entryCount));
+ // We should normally abort here, but some legacy apps declare
+ // resources in the 'android' package (old bug in AAPT).
continue;
}
- // Check if there is the desired entry in this type.
+ // Aggregate all the flags for each package that defines this entry.
+ if (typeSpec->typeSpecFlags != NULL) {
+ specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
+ } else {
+ specFlags = -1;
+ }
- const uint8_t* const end = ((const uint8_t*)thisType)
- + dtohl(thisType->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
+ const size_t numConfigs = typeSpec->configs.size();
+ for (size_t c = 0; c < numConfigs; c++) {
+ const ResTable_type* const thisType = typeSpec->configs[c];
+ if (thisType == NULL) {
+ continue;
+ }
- uint32_t thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
- continue;
- }
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(thisType->config);
- if (type != NULL) {
- // Check if this one is less specific than the last found. If so,
- // we will skip it. We check starting with things we most care
- // about to those we least care about.
- if (!thisConfig.isBetterThan(bestConfig, config)) {
- TABLE_GETENTRY(ALOGI("This config is worse than last!\n"));
+ // Check to make sure this one is valid for the current parameters.
+ if (config != NULL && !thisConfig.match(*config)) {
continue;
}
- }
- type = thisType;
- offset = thisOffset;
- bestConfig = thisConfig;
- TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
- if (!config) break;
+ // Check if there is the desired entry in this type.
+ const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType)
+ + dtohl(thisType->header.size);
+ const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
+
+ uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
+ if (thisOffset == ResTable_type::NO_ENTRY) {
+ // There is no entry for this index and configuration.
+ continue;
+ }
+
+ if (bestType != NULL) {
+ // Check if this one is less specific than the last found. If so,
+ // we will skip it. We check starting with things we most care
+ // about to those we least care about.
+ if (!thisConfig.isBetterThan(bestConfig, config)) {
+ if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
+ continue;
+ }
+ }
+ }
+
+ bestType = thisType;
+ bestOffset = thisOffset;
+ bestConfig = thisConfig;
+ bestPackage = typeSpec->package;
+ actualTypeIndex = realTypeIndex;
+
+ // If no config was specified, any type will do, so skip
+ if (config == NULL) {
+ break;
+ }
+ }
}
- if (type == NULL) {
- TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
+ if (bestType == NULL) {
return BAD_INDEX;
}
- offset += dtohl(type->entriesStart);
- TABLE_NOISY(aout << "Looking in resource table " << package->header->header
- << ", typeOff="
- << (void*)(((const char*)type)-((const char*)package->header->header))
- << ", offset=" << (void*)offset << endl);
+ bestOffset += dtohl(bestType->entriesStart);
- if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) {
+ if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
- offset, dtohl(type->header.size));
+ bestOffset, dtohl(bestType->header.size));
return BAD_TYPE;
}
- if ((offset&0x3) != 0) {
- ALOGW("ResTable_entry at 0x%x is not on an integer boundary",
- offset);
+ if ((bestOffset & 0x3) != 0) {
+ ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
return BAD_TYPE;
}
- const ResTable_entry* const entry = (const ResTable_entry*)
- (((const uint8_t*)type) + offset);
+ const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
if (dtohs(entry->size) < sizeof(*entry)) {
ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
return BAD_TYPE;
}
- *outType = type;
- *outEntry = entry;
- if (outTypeClass != NULL) {
- *outTypeClass = allTypes;
+ if (outEntry != NULL) {
+ outEntry->entry = entry;
+ outEntry->config = bestConfig;
+ outEntry->type = bestType;
+ outEntry->specFlags = specFlags;
+ outEntry->package = bestPackage;
+ outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
+ outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
}
- return offset + dtohs(entry->size);
+ return NO_ERROR;
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header, uint32_t idmap_id)
+ const Header* const header)
{
const uint8_t* base = (const uint8_t*)pkg;
- status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
+ status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
header->dataEnd, "ResTable_package");
if (err != NO_ERROR) {
return (mError=err);
@@ -5367,84 +5549,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=BAD_TYPE);
}
- Package* package = NULL;
- PackageGroup* group = NULL;
- uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
- // If at this point id == 0, pkg is an overlay package without a
- // corresponding idmap. During regular usage, overlay packages are
- // always loaded alongside their idmaps, but during idmap creation
- // the package is temporarily loaded by itself.
- if (id < 256) {
-
- package = new Package(this, header, pkg);
- if (package == NULL) {
- return (mError=NO_MEMORY);
+ uint32_t id = dtohl(pkg->id);
+ KeyedVector<uint8_t, IdmapEntries> idmapEntries;
+
+ if (header->resourceIDMap != NULL) {
+ uint8_t targetPackageId = 0;
+ status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
+ if (err != NO_ERROR) {
+ ALOGW("Overlay is broken");
+ return (mError=err);
}
+ id = targetPackageId;
+ }
- size_t idx = mPackageMap[id];
- if (idx == 0) {
- idx = mPackageGroups.size()+1;
+ if (id >= 256) {
+ LOG_ALWAYS_FATAL("Package id out of range");
+ return NO_ERROR;
+ } else if (id == 0) {
+ // This is a library so assign an ID
+ id = mNextPackageId++;
+ }
- char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
- strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
- group = new PackageGroup(this, String16(tmpName), id);
- if (group == NULL) {
- delete package;
- return (mError=NO_MEMORY);
- }
+ PackageGroup* group = NULL;
+ Package* package = new Package(this, header, pkg);
+ if (package == NULL) {
+ return (mError=NO_MEMORY);
+ }
- err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
- header->dataEnd-(base+dtohl(pkg->typeStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
- err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
- header->dataEnd-(base+dtohl(pkg->keyStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
+ err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+ header->dataEnd-(base+dtohl(pkg->typeStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
- err = group->packages.add(package);
- if (err < NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
- group->basePackage = package;
+ err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+ header->dataEnd-(base+dtohl(pkg->keyStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
- //printf("Adding new package id %d at index %d\n", id, idx);
- err = mPackageGroups.add(group);
- if (err < NO_ERROR) {
- delete group;
- return (mError=err);
- }
+ size_t idx = mPackageMap[id];
+ if (idx == 0) {
+ idx = mPackageGroups.size() + 1;
- mPackageMap[id] = (uint8_t)idx;
- } else {
- group = mPackageGroups.itemAt(idx-1);
- if (group == NULL) {
- delete package;
- return (mError=UNKNOWN_ERROR);
- }
- err = group->packages.add(package);
- if (err < NO_ERROR) {
- delete package;
- return (mError=err);
- }
+ char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
+ strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
+ group = new PackageGroup(this, String16(tmpName), id);
+ if (group == NULL) {
+ delete package;
+ return (mError=NO_MEMORY);
+ }
+
+ //printf("Adding new package id %d at index %d\n", id, idx);
+ err = mPackageGroups.add(group);
+ if (err < NO_ERROR) {
+ return (mError=err);
+ }
+
+ mPackageMap[id] = static_cast<uint8_t>(idx);
+
+ // Find all packages that reference this package
+ size_t N = mPackageGroups.size();
+ for (size_t i = 0; i < N; i++) {
+ mPackageGroups[i]->dynamicRefTable.addMapping(
+ group->name, static_cast<uint8_t>(group->id));
}
} else {
- LOG_ALWAYS_FATAL("Package id out of range");
- return NO_ERROR;
+ group = mPackageGroups.itemAt(idx - 1);
+ if (group == NULL) {
+ return (mError=UNKNOWN_ERROR);
+ }
}
+ err = group->packages.add(package);
+ if (err < NO_ERROR) {
+ return (mError=err);
+ }
// Iterate through all chunks.
- size_t curPackage = 0;
-
const ResChunk_header* chunk =
(const ResChunk_header*)(((const uint8_t*)pkg)
+ dtohs(pkg->header.headerSize));
@@ -5465,20 +5651,20 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
}
const size_t typeSpecSize = dtohl(typeSpec->header.size);
+ const size_t newEntryCount = dtohl(typeSpec->entryCount);
LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(typeSpec->header.type),
dtohs(typeSpec->header.headerSize),
- (void*)typeSize));
+ (void*)typeSpecSize));
// look for block overrun or int overflow when multiplying by 4
if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
- || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount))
+ || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
> typeSpecSize)) {
ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
- (void*)(dtohs(typeSpec->header.headerSize)
- +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))),
- (void*)typeSpecSize);
+ (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+ (void*)typeSpecSize);
return (mError=BAD_TYPE);
}
@@ -5487,21 +5673,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=BAD_TYPE);
}
- while (package->types.size() < typeSpec->id) {
- package->types.add(NULL);
- }
- Type* t = package->types[typeSpec->id-1];
- if (t == NULL) {
- t = new Type(header, package, dtohl(typeSpec->entryCount));
- package->types.editItemAt(typeSpec->id-1) = t;
- } else if (dtohl(typeSpec->entryCount) != t->entryCount) {
- ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
- (int)dtohl(typeSpec->entryCount), (int)t->entryCount);
- return (mError=BAD_TYPE);
+ if (newEntryCount > 0) {
+ uint8_t typeIndex = typeSpec->id - 1;
+ ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
+ if (idmapIndex >= 0) {
+ typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+ }
+
+ TypeList& typeList = group->types.editItemAt(typeIndex);
+ if (!typeList.isEmpty()) {
+ const Type* existingType = typeList[0];
+ if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
+ ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
+ (int) newEntryCount, (int) existingType->entryCount);
+ // We should normally abort here, but some legacy apps declare
+ // resources in the 'android' package (old bug in AAPT).
+ }
+ }
+
+ Type* t = new Type(header, package, newEntryCount);
+ t->typeSpec = typeSpec;
+ t->typeSpecFlags = (const uint32_t*)(
+ ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
+ if (idmapIndex >= 0) {
+ t->idmapEntries = idmapEntries[idmapIndex];
+ }
+ typeList.add(t);
+ group->largestTypeId = max(group->largestTypeId, typeSpec->id);
+ } else {
+ ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
}
- t->typeSpecFlags = (const uint32_t*)(
- ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
- t->typeSpec = typeSpec;
} else if (ctype == RES_TABLE_TYPE_TYPE) {
const ResTable_type* type = (const ResTable_type*)(chunk);
@@ -5512,50 +5713,84 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
}
const uint32_t typeSize = dtohl(type->header.size);
+ const size_t newEntryCount = dtohl(type->entryCount);
LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(type->header.type),
dtohs(type->header.headerSize),
(void*)typeSize));
- if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
- > typeSize) {
+ if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
+ > typeSize) {
ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
- (void*)(dtohs(type->header.headerSize)
- +(sizeof(uint32_t)*dtohl(type->entryCount))),
- typeSize);
+ (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+ typeSize);
return (mError=BAD_TYPE);
}
- if (dtohl(type->entryCount) != 0
+
+ if (newEntryCount != 0
&& dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
dtohl(type->entriesStart), typeSize);
return (mError=BAD_TYPE);
}
+
if (type->id == 0) {
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
- while (package->types.size() < type->id) {
- package->types.add(NULL);
- }
- Type* t = package->types[type->id-1];
- if (t == NULL) {
- t = new Type(header, package, dtohl(type->entryCount));
- package->types.editItemAt(type->id-1) = t;
- } else if (dtohl(type->entryCount) != t->entryCount) {
- ALOGW("ResTable_type entry count inconsistent: given %d, previously %d",
- (int)dtohl(type->entryCount), (int)t->entryCount);
- return (mError=BAD_TYPE);
+ if (newEntryCount > 0) {
+ uint8_t typeIndex = type->id - 1;
+ ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
+ if (idmapIndex >= 0) {
+ typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+ }
+
+ TypeList& typeList = group->types.editItemAt(typeIndex);
+ if (typeList.isEmpty()) {
+ ALOGE("No TypeSpec for type %d", type->id);
+ return (mError=BAD_TYPE);
+ }
+
+ Type* t = typeList.editItemAt(typeList.size() - 1);
+ if (newEntryCount != t->entryCount) {
+ ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
+ (int)newEntryCount, (int)t->entryCount);
+ return (mError=BAD_TYPE);
+ }
+
+ if (t->package != package) {
+ ALOGE("No TypeSpec for type %d", type->id);
+ return (mError=BAD_TYPE);
+ }
+
+ t->configs.add(type);
+
+ TABLE_GETENTRY(
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(type->config);
+ ALOGI("Adding config to type %d: %s\n",
+ type->id, thisConfig.toString().string()));
+ } else {
+ ALOGV("Skipping empty ResTable_type for type %d", type->id);
}
- TABLE_GETENTRY(
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
- ALOGI("Adding config to type %d: %s\n",
- type->id, thisConfig.toString().string()));
- t->configs.add(type);
+ } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
+ if (group->dynamicRefTable.entries().size() == 0) {
+ status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk);
+ if (err != NO_ERROR) {
+ return (mError=err);
+ }
+
+ // Fill in the reference table with the entries we already know about.
+ size_t N = mPackageGroups.size();
+ for (size_t i = 0; i < N; i++) {
+ group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id);
+ }
+ } else {
+ ALOGW("Found multiple library tables, ignoring...");
+ }
} else {
status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
endPos, "ResTable_package:unknown");
@@ -5567,13 +5802,112 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(((const uint8_t*)chunk) + csize);
}
- if (group->typeCount == 0) {
- group->typeCount = package->types.size();
+ return NO_ERROR;
+}
+
+DynamicRefTable::DynamicRefTable(uint8_t packageId)
+ : mAssignedPackageId(packageId)
+{
+ memset(mLookupTable, 0, sizeof(mLookupTable));
+
+ // Reserved package ids
+ mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
+ mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
+}
+
+status_t DynamicRefTable::load(const ResTable_lib_header* const header)
+{
+ const uint32_t entryCount = dtohl(header->count);
+ const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount;
+ const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize);
+ if (sizeOfEntries > expectedSize) {
+ ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).",
+ expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry));
+ return UNKNOWN_ERROR;
+ }
+
+ const ResTable_lib_entry* entry = (const ResTable_lib_entry*)(((uint8_t*) header) +
+ dtohl(header->header.headerSize));
+ for (uint32_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
+ uint32_t packageId = dtohl(entry->packageId);
+ char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
+ strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
+ LIB_NOISY(ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
+ dtohl(entry->packageId)));
+ if (packageId >= 256) {
+ ALOGE("Bad package id 0x%08x", packageId);
+ return UNKNOWN_ERROR;
+ }
+ mEntries.replaceValueFor(String16(tmpName), (uint8_t) packageId);
+ entry = entry + 1;
+ }
+ return NO_ERROR;
+}
+
+status_t DynamicRefTable::addMapping(const String16& packageName, uint8_t packageId)
+{
+ ssize_t index = mEntries.indexOfKey(packageName);
+ if (index < 0) {
+ return UNKNOWN_ERROR;
+ }
+ mLookupTable[mEntries.valueAt(index)] = packageId;
+ return NO_ERROR;
+}
+
+status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
+ uint32_t res = *resId;
+ size_t packageId = Res_GETPACKAGE(res) + 1;
+
+ if (packageId == APP_PACKAGE_ID) {
+ // No lookup needs to be done, app package IDs are absolute.
+ return NO_ERROR;
+ }
+
+ if (packageId == 0) {
+ // The package ID is 0x00. That means that a shared library is accessing
+ // its own local resource, so we fix up the resource with the calling
+ // package ID.
+ *resId |= ((uint32_t) mAssignedPackageId) << 24;
+ return NO_ERROR;
+ }
+
+ // Do a proper lookup.
+ uint8_t translatedId = mLookupTable[packageId];
+ if (translatedId == 0) {
+ ALOGE("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
+ (uint8_t)mAssignedPackageId, (uint8_t)packageId);
+ for (size_t i = 0; i < 256; i++) {
+ if (mLookupTable[i] != 0) {
+ ALOGE("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
+ }
+ }
+ return UNKNOWN_ERROR;
+ }
+
+ *resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24);
+ return NO_ERROR;
+}
+
+status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
+ if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ return NO_ERROR;
+ }
+
+ status_t err = lookupResourceId(&value->data);
+ if (err != NO_ERROR) {
+ return err;
}
+ value->dataType = Res_value::TYPE_REFERENCE;
return NO_ERROR;
}
+struct IdmapTypeMap {
+ ssize_t overlayTypeId;
+ size_t entryOffset;
+ Vector<uint32_t> entryMap;
+};
+
status_t ResTable::createIdmap(const ResTable& overlay,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
@@ -5584,41 +5918,46 @@ status_t ResTable::createIdmap(const ResTable& overlay,
ALOGW("idmap: target package has no package groups, cannot create idmap\n");
return UNKNOWN_ERROR;
}
+
if (mPackageGroups[0]->packages.size() == 0) {
ALOGW("idmap: target package has no packages in its first package group, "
"cannot create idmap\n");
return UNKNOWN_ERROR;
}
- Vector<Vector<uint32_t> > map;
+ KeyedVector<uint8_t, IdmapTypeMap> map;
+
// overlaid packages are assumed to contain only one package group
const PackageGroup* pg = mPackageGroups[0];
- const Package* pkg = pg->packages[0];
- size_t typeCount = pkg->types.size();
- // starting size is header + first item (number of types in map)
- *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+
+ // starting size is header
+ *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
+
+ // target package id and number of types in map
+ *outSize += 2 * sizeof(uint16_t);
+
// overlay packages are assumed to contain only one package group
const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
- const uint32_t pkg_id = pkg->package->id << 24;
-
- for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
- ssize_t first = -1;
- ssize_t last = -1;
- const Type* typeConfigs = pkg->getType(typeIndex);
- ssize_t mapIndex = map.add();
- if (mapIndex < 0) {
- return NO_MEMORY;
+
+ for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
+ const TypeList& typeList = pg->types[typeIndex];
+ if (typeList.isEmpty()) {
+ continue;
}
- Vector<uint32_t>& vector = map.editItemAt(mapIndex);
+
+ const Type* typeConfigs = typeList[0];
+
+ IdmapTypeMap typeMap;
+ typeMap.overlayTypeId = -1;
+ typeMap.entryOffset = 0;
+
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = pkg_id
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
+ uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
resource_name resName;
if (!this->getResourceName(resID, false, &resName)) {
- ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
- // add dummy value, or trimming leading/trailing zeroes later will fail
- vector.push(0);
+ if (typeMap.entryMap.isEmpty()) {
+ typeMap.entryOffset++;
+ }
continue;
}
@@ -5630,49 +5969,55 @@ status_t ResTable::createIdmap(const ResTable& overlay,
overlayType.size(),
overlayPackage.string(),
overlayPackage.size());
- if (overlayResID != 0) {
- overlayResID = pkg_id | (0x00ffffff & overlayResID);
- last = Res_GETENTRY(resID);
- if (first == -1) {
- first = Res_GETENTRY(resID);
+ if (overlayResID == 0) {
+ if (typeMap.entryMap.isEmpty()) {
+ typeMap.entryOffset++;
}
+ continue;
}
- vector.push(overlayResID);
-#if 0
- if (overlayResID != 0) {
- ALOGD("%s/%s 0x%08x -> 0x%08x\n",
- String8(String16(resName.type)).string(),
- String8(String16(resName.name)).string(),
- resID, overlayResID);
+
+ if (typeMap.overlayTypeId == -1) {
+ typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
+ }
+
+ if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
+ ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
+ " but entries should map to resources of type %02x",
+ resID, overlayResID, typeMap.overlayTypeId);
+ return BAD_TYPE;
+ }
+
+ if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
+ // Resize to accomodate this entry and the 0's in between.
+ if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) {
+ return NO_MEMORY;
+ }
+ typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID);
+ } else {
+ typeMap.entryMap.add(Res_GETENTRY(overlayResID));
}
-#endif
}
- if (first != -1) {
- // shave off trailing entries which lack overlay values
- const size_t last_past_one = last + 1;
- if (last_past_one < vector.size()) {
- vector.removeItemsAt(last_past_one, vector.size() - last_past_one);
+ if (!typeMap.entryMap.isEmpty()) {
+ if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
+ return NO_MEMORY;
}
- // shave off leading entries which lack overlay values
- vector.removeItemsAt(0, first);
- // store offset to first overlaid resource ID of this type
- vector.insertAt((uint32_t)first, 0, 1);
- // reserve space for number and offset of entries, and the actual entries
- *outSize += (2 + vector.size()) * sizeof(uint32_t);
- } else {
- // no entries of current type defined in overlay package
- vector.clear();
- // reserve space for type offset
- *outSize += 1 * sizeof(uint32_t);
+ *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
}
}
+ if (map.isEmpty()) {
+ ALOGW("idmap: no resources in overlay package present in base package");
+ return UNKNOWN_ERROR;
+ }
+
if ((*outData = malloc(*outSize)) == NULL) {
return NO_MEMORY;
}
+
uint32_t* data = (uint32_t*)*outData;
*data++ = htodl(IDMAP_MAGIC);
+ *data++ = htodl(IDMAP_CURRENT_VERSION);
*data++ = htodl(targetCrc);
*data++ = htodl(overlayCrc);
const char* paths[] = { targetPath, overlayPath };
@@ -5690,44 +6035,30 @@ status_t ResTable::createIdmap(const ResTable& overlay,
data += 256 / sizeof(uint32_t);
}
const size_t mapSize = map.size();
- *data++ = htodl(mapSize);
- size_t offset = mapSize;
- for (size_t i = 0; i < mapSize; ++i) {
- const Vector<uint32_t>& vector = map.itemAt(i);
- const size_t N = vector.size();
- if (N == 0) {
- *data++ = htodl(0);
- } else {
- offset++;
- *data++ = htodl(offset);
- offset += N;
- }
- }
- if (offset == mapSize) {
- ALOGW("idmap: no resources in overlay package present in base package\n");
- return UNKNOWN_ERROR;
- }
+ uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+ *typeData++ = htods(pg->id);
+ *typeData++ = htods(mapSize);
for (size_t i = 0; i < mapSize; ++i) {
- const Vector<uint32_t>& vector = map.itemAt(i);
- const size_t N = vector.size();
- if (N == 0) {
- continue;
- }
- if (N == 1) { // vector expected to hold (offset) + (N > 0 entries)
- ALOGW("idmap: type %d supposedly has entries, but no entries found\n", i);
- return UNKNOWN_ERROR;
- }
- *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
- for (size_t j = 0; j < N; ++j) {
- const uint32_t& overlayResID = vector.itemAt(j);
- *data++ = htodl(overlayResID);
+ uint8_t targetTypeId = map.keyAt(i);
+ const IdmapTypeMap& typeMap = map[i];
+ *typeData++ = htods(targetTypeId + 1);
+ *typeData++ = htods(typeMap.overlayTypeId);
+ *typeData++ = htods(typeMap.entryMap.size());
+ *typeData++ = htods(typeMap.entryOffset);
+
+ const size_t entryCount = typeMap.entryMap.size();
+ uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
+ for (size_t j = 0; j < entryCount; j++) {
+ entries[j] = htodl(typeMap.entryMap[j]);
}
+ typeData += entryCount * 2;
}
return NO_ERROR;
}
bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
+ uint32_t* pVersion,
uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
String8* pTargetPath, String8* pOverlayPath)
{
@@ -5735,17 +6066,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
if (!assertIdmapHeader(map, sizeBytes)) {
return false;
}
+ if (pVersion) {
+ *pVersion = dtohl(map[1]);
+ }
if (pTargetCrc) {
- *pTargetCrc = map[1];
+ *pTargetCrc = dtohl(map[2]);
}
if (pOverlayCrc) {
- *pOverlayCrc = map[2];
+ *pOverlayCrc = dtohl(map[3]);
}
if (pTargetPath) {
- pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+ pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
}
if (pOverlayPath) {
- pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+ pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
}
return true;
}
@@ -5828,6 +6162,8 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const
printf("(null)\n");
} else if (value.dataType == Res_value::TYPE_REFERENCE) {
printf("(reference) 0x%08x\n", value.data);
+ } else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {
+ printf("(dynamic reference) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
printf("(attribute) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_STRING) {
@@ -5876,11 +6212,6 @@ void ResTable::print(bool inclValues) const
if (mError != 0) {
printf("mError=0x%x (%s)\n", mError, strerror(mError));
}
-#if 0
- char localeStr[RESTABLE_MAX_LOCALE_LEN];
- mParams.getBcp47Locale(localeStr);
- printf("mParams=%s,\n" localeStr);
-#endif
size_t pgCount = mPackageGroups.size();
printf("Package Groups (%d)\n", (int)pgCount);
for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) {
@@ -5892,172 +6223,184 @@ void ResTable::print(bool inclValues) const
size_t pkgCount = pg->packages.size();
for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
const Package* pkg = pg->packages[pkgIndex];
- size_t typeCount = pkg->types.size();
- printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex,
- pkg->package->id, String8(String16(pkg->package->name)).string(),
- (int)typeCount);
- for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) {
- const Type* typeConfigs = pkg->getType(typeIndex);
- if (typeConfigs == NULL) {
- printf(" type %d NULL\n", (int)typeIndex);
+ printf(" Package %d id=%d name=%s\n", (int)pkgIndex,
+ pkg->package->id, String8(String16(pkg->package->name)).string());
+ }
+
+ for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
+ const TypeList& typeList = pg->types[typeIndex];
+ if (typeList.isEmpty()) {
+ //printf(" type %d NULL\n", (int)typeIndex);
+ continue;
+ }
+ const Type* typeConfigs = typeList[0];
+ const size_t NTC = typeConfigs->configs.size();
+ printf(" type %d configCount=%d entryCount=%d\n",
+ (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
+ if (typeConfigs->typeSpecFlags != NULL) {
+ for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
+ uint32_t resID = (0xff000000 & ((pg->id)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryIndex));
+ // Since we are creating resID without actually
+ // iterating over them, we have no idea which is a
+ // dynamic reference. We must check.
+ pg->dynamicRefTable.lookupResourceId(&resID);
+
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
+ }
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
+ } else {
+ name8 = String8(resName.name, resName.nameLen);
+ }
+ printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+ resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string(),
+ dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ } else {
+ printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+ }
+ }
+ }
+ for (size_t configIndex=0; configIndex<NTC; configIndex++) {
+ const ResTable_type* type = typeConfigs->configs[configIndex];
+ if ((((uint64_t)type)&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
continue;
}
- const size_t NTC = typeConfigs->configs.size();
- printf(" type %d configCount=%d entryCount=%d\n",
- (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
- if (typeConfigs->typeSpecFlags != NULL) {
- for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
- uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
- } else {
- name8 = String8(resName.name, resName.nameLen);
- }
- printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
- resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string(),
- dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ String8 configStr = type->config.toString();
+ printf(" config %s:\n", configStr.size() > 0
+ ? configStr.string() : "(default)");
+ size_t entryCount = dtohl(type->entryCount);
+ uint32_t entriesStart = dtohl(type->entriesStart);
+ if ((entriesStart&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+ continue;
+ }
+ uint32_t typeSize = dtohl(type->header.size);
+ if ((typeSize&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+ continue;
+ }
+ for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
+
+ const uint8_t* const end = ((const uint8_t*)type)
+ + dtohl(type->header.size);
+ const uint32_t* const eindex = (const uint32_t*)
+ (((const uint8_t*)type) + dtohs(type->header.headerSize));
+
+ uint32_t thisOffset = dtohl(eindex[entryIndex]);
+ if (thisOffset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+
+ uint32_t resID = (0xff000000 & ((pg->id)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryIndex));
+ pg->dynamicRefTable.lookupResourceId(&resID);
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
+ }
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
} else {
- printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+ name8 = String8(resName.name, resName.nameLen);
}
+ printf(" resource 0x%08x %s:%s/%s: ", resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string());
+ } else {
+ printf(" INVALID RESOURCE 0x%08x: ", resID);
}
- }
- for (size_t configIndex=0; configIndex<NTC; configIndex++) {
- const ResTable_type* type = typeConfigs->configs[configIndex];
- if ((((uint64_t)type)&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
+ if ((thisOffset&0x3) != 0) {
+ printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
continue;
}
- String8 configStr = type->config.toString();
- printf(" config %s:\n", configStr.size() > 0
- ? configStr.string() : "(default)");
- size_t entryCount = dtohl(type->entryCount);
- uint32_t entriesStart = dtohl(type->entriesStart);
- if ((entriesStart&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+ if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
+ printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+ entriesStart, thisOffset, typeSize);
continue;
}
- uint32_t typeSize = dtohl(type->header.size);
- if ((typeSize&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+
+ const ResTable_entry* ent = (const ResTable_entry*)
+ (((const uint8_t*)type) + entriesStart + thisOffset);
+ if (((entriesStart + thisOffset)&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+ (entriesStart + thisOffset));
continue;
}
- for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
- const uint8_t* const end = ((const uint8_t*)type)
- + dtohl(type->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)type) + dtohs(type->header.headerSize));
+ uintptr_t esize = dtohs(ent->size);
+ if ((esize&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+ continue;
+ }
+ if ((thisOffset+esize) > typeSize) {
+ printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+ entriesStart, thisOffset, (void *)esize, typeSize);
+ continue;
+ }
- uint32_t thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- continue;
- }
+ const Res_value* valuePtr = NULL;
+ const ResTable_map_entry* bagPtr = NULL;
+ Res_value value;
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+ printf("<bag>");
+ bagPtr = (const ResTable_map_entry*)ent;
+ } else {
+ valuePtr = (const Res_value*)
+ (((const uint8_t*)ent) + esize);
+ value.copyFrom_dtoh(*valuePtr);
+ printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
+ (int)value.dataType, (int)value.data,
+ (int)value.size, (int)value.res0);
+ }
- uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
- } else {
- name8 = String8(resName.name, resName.nameLen);
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+ printf(" (PUBLIC)");
+ }
+ printf("\n");
+
+ if (inclValues) {
+ if (valuePtr != NULL) {
+ printf(" ");
+ print_value(typeConfigs->package, value);
+ } else if (bagPtr != NULL) {
+ const int N = dtohl(bagPtr->count);
+ const uint8_t* baseMapPtr = (const uint8_t*)ent;
+ size_t mapOffset = esize;
+ const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+ const uint32_t parent = dtohl(bagPtr->parent.ident);
+ uint32_t resolvedParent = parent;
+ status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
+ if (err != NO_ERROR) {
+ resolvedParent = 0;
}
- printf(" resource 0x%08x %s:%s/%s: ", resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string());
- } else {
- printf(" INVALID RESOURCE 0x%08x: ", resID);
- }
- if ((thisOffset&0x3) != 0) {
- printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
- continue;
- }
- if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
- printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
- entriesStart, thisOffset, typeSize);
- continue;
- }
-
- const ResTable_entry* ent = (const ResTable_entry*)
- (((const uint8_t*)type) + entriesStart + thisOffset);
- if (((entriesStart + thisOffset)&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
- (entriesStart + thisOffset));
- continue;
- }
-
- uintptr_t esize = dtohs(ent->size);
- if ((esize&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
- continue;
- }
- if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
- entriesStart, thisOffset, (void *)esize, typeSize);
- continue;
- }
-
- const Res_value* valuePtr = NULL;
- const ResTable_map_entry* bagPtr = NULL;
- Res_value value;
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
- printf("<bag>");
- bagPtr = (const ResTable_map_entry*)ent;
- } else {
- valuePtr = (const Res_value*)
- (((const uint8_t*)ent) + esize);
- value.copyFrom_dtoh(*valuePtr);
- printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
- (int)value.dataType, (int)value.data,
- (int)value.size, (int)value.res0);
- }
-
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
- printf(" (PUBLIC)");
- }
- printf("\n");
-
- if (inclValues) {
- if (valuePtr != NULL) {
- printf(" ");
- print_value(pkg, value);
- } else if (bagPtr != NULL) {
- const int N = dtohl(bagPtr->count);
- const uint8_t* baseMapPtr = (const uint8_t*)ent;
- size_t mapOffset = esize;
- const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
- printf(" Parent=0x%08x, Count=%d\n",
- dtohl(bagPtr->parent.ident), N);
- for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
- printf(" #%i (Key=0x%08x): ",
- i, dtohl(mapPtr->name.ident));
- value.copyFrom_dtoh(mapPtr->value);
- print_value(pkg, value);
- const size_t size = dtohs(mapPtr->value.size);
- mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
- mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
- }
+ printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
+ parent, resolvedParent, N);
+ for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
+ printf(" #%i (Key=0x%08x): ",
+ i, dtohl(mapPtr->name.ident));
+ value.copyFrom_dtoh(mapPtr->value);
+ print_value(typeConfigs->package, value);
+ const size_t size = dtohs(mapPtr->value.size);
+ mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+ mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
}
}
}
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
new file mode 100644
index 0000000..06b4040
--- /dev/null
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/TypeWrappers.h>
+
+namespace android {
+
+TypeVariant::iterator& TypeVariant::iterator::operator++() {
+ mIndex++;
+ if (mIndex > dtohl(mTypeVariant->data->entryCount)) {
+ mIndex = dtohl(mTypeVariant->data->entryCount);
+ }
+ return *this;
+}
+
+const ResTable_entry* TypeVariant::iterator::operator*() const {
+ const ResTable_type* type = mTypeVariant->data;
+ const uint32_t entryCount = dtohl(type->entryCount);
+ if (mIndex >= entryCount) {
+ return NULL;
+ }
+
+ const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+ + dtohl(type->header.size);
+ const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+ if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
+ ALOGE("Type's entry indices extend beyond its boundaries");
+ return NULL;
+ }
+
+ const uint32_t entryOffset = dtohl(entryIndices[mIndex]);
+ if (entryOffset == ResTable_type::NO_ENTRY) {
+ return NULL;
+ }
+
+ if ((entryOffset & 0x3) != 0) {
+ ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
+ return NULL;
+ }
+
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
+ if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
+ ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
+ return NULL;
+ } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
+ ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
+ return NULL;
+ } else if (dtohs(entry->size) < sizeof(*entry)) {
+ ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
+ return NULL;
+ }
+ return entry;
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 977ba80..4ff6eec 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -1,32 +1,66 @@
-# Build the unit tests.
+#
+# Copyright (C) 2014 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.
+#
+
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
LOCAL_PATH:= $(call my-dir)
+testFiles := \
+ ByteBucketArray_test.cpp \
+ Idmap_test.cpp \
+ ResourceTypes_test.cpp \
+ ResTable_test.cpp \
+ Split_test.cpp \
+ TypeWrappers_test.cpp \
+ ZipUtils_test.cpp
+
+# ==========================================================
+# Build the host tests: libandroidfw_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles)
+LOCAL_STATIC_LIBRARIES := \
+ libandroidfw \
+ libutils \
+ libcutils \
+ liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device tests: libandroidfw_tests
+# ==========================================================
include $(CLEAR_VARS)
-# Build the unit tests.
-test_src_files := \
- ObbFile_test.cpp \
- ZipUtils_test.cpp \
- ResourceTypes_test.cpp
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles) \
+ BackupData_test.cpp \
+ ObbFile_test.cpp
-shared_libraries := \
+LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libcutils \
libutils \
libui \
libstlport
-static_libraries := \
- libgtest \
- libgtest_main
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp
new file mode 100644
index 0000000..17f91ca
--- /dev/null
+++ b/libs/androidfw/tests/BackupData_test.cpp
@@ -0,0 +1,438 @@
+/*
+ * 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 "ObbFile_test"
+#include <androidfw/BackupHelpers.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+#define TEST_FILENAME "/test.bd"
+
+// keys of different lengths to test padding
+#define KEY1 "key1"
+#define KEY2 "key2a"
+#define KEY3 "key3bc"
+#define KEY4 "key4def"
+
+// payloads of different lengths to test padding
+#define DATA1 "abcdefg"
+#define DATA2 "hijklmnopq"
+#define DATA3 "rstuvwxyz"
+// KEY4 is only ever deleted
+
+class BackupDataTest : public testing::Test {
+protected:
+ char* m_external_storage;
+ char* m_filename;
+ String8 mKey1;
+ String8 mKey2;
+ String8 mKey3;
+ String8 mKey4;
+
+ virtual void SetUp() {
+ m_external_storage = getenv("EXTERNAL_STORAGE");
+
+ const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1;
+ m_filename = new char[totalLen];
+ snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME);
+
+ ::unlink(m_filename);
+ int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ FAIL() << "Couldn't create " << m_filename << " for writing";
+ }
+ mKey1 = String8(KEY1);
+ mKey2 = String8(KEY2);
+ mKey3 = String8(KEY3);
+ mKey4 = String8(KEY4);
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(BackupDataTest, WriteAndReadSingle) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+
+ EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
+ << "WriteEntityHeader returned an error";
+ EXPECT_EQ(NO_ERROR, writer->WriteEntityData(DATA1, sizeof(DATA1)))
+ << "WriteEntityData returned an error";
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+ EXPECT_EQ(NO_ERROR, reader->Status())
+ << "Reader ctor failed";
+
+ bool done;
+ int type;
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader";
+
+ String8 key;
+ size_t dataSize;
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error";
+ EXPECT_EQ(mKey1, key)
+ << "wrong key from ReadEntityHeader";
+ EXPECT_EQ(sizeof(DATA1), dataSize)
+ << "wrong size from ReadEntityHeader";
+
+ char* dataBytes = new char[dataSize];
+ EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
+ << "ReadEntityData returned an error";
+ for (unsigned int i = 0; i < sizeof(DATA1); i++) {
+ EXPECT_EQ(DATA1[i], dataBytes[i])
+ << "data character " << i << " should be equal";
+ }
+ delete dataBytes;
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, WriteAndReadMultiple) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, sizeof(DATA1));
+ writer->WriteEntityData(DATA1, sizeof(DATA1));
+ writer->WriteEntityHeader(mKey2, sizeof(DATA2));
+ writer->WriteEntityData(DATA2, sizeof(DATA2));
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ char* dataBytes;
+ // read first entity
+ reader->ReadNextHeader(&done, &type);
+ reader->ReadEntityHeader(&key, &dataSize);
+ dataBytes = new char[dataSize];
+ reader->ReadEntityData(dataBytes, dataSize);
+ delete dataBytes;
+
+ // read and verify second entity
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on second entity";
+ EXPECT_EQ(mKey2, key)
+ << "wrong key from ReadEntityHeader on second entity";
+ EXPECT_EQ(sizeof(DATA2), dataSize)
+ << "wrong size from ReadEntityHeader on second entity";
+
+ dataBytes = new char[dataSize];
+ EXPECT_EQ((int)dataSize, reader->ReadEntityData(dataBytes, dataSize))
+ << "ReadEntityData returned an error on second entity";
+ for (unsigned int i = 0; i < sizeof(DATA2); i++) {
+ EXPECT_EQ(DATA2[i], dataBytes[i])
+ << "data character " << i << " should be equal";
+ }
+ delete dataBytes;
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, SkipEntity) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, sizeof(DATA1));
+ writer->WriteEntityData(DATA1, sizeof(DATA1));
+ writer->WriteEntityHeader(mKey2, sizeof(DATA2));
+ writer->WriteEntityData(DATA2, sizeof(DATA2));
+ writer->WriteEntityHeader(mKey3, sizeof(DATA3));
+ writer->WriteEntityData(DATA3, sizeof(DATA3));
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ char* dataBytes;
+ // read first entity
+ reader->ReadNextHeader(&done, &type);
+ reader->ReadEntityHeader(&key, &dataSize);
+ dataBytes = new char[dataSize];
+ reader->ReadEntityData(dataBytes, dataSize);
+ delete dataBytes;
+
+ // skip second entity
+ reader->ReadNextHeader(&done, &type);
+ reader->ReadEntityHeader(&key, &dataSize);
+ reader->SkipEntityData();
+
+ // read and verify third entity
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader after skip";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on third entity";
+ EXPECT_EQ(mKey3, key)
+ << "wrong key from ReadEntityHeader on third entity";
+ EXPECT_EQ(sizeof(DATA3), dataSize)
+ << "wrong size from ReadEntityHeader on third entity";
+
+ dataBytes = new char[dataSize];
+ EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
+ << "ReadEntityData returned an error on third entity";
+ for (unsigned int i = 0; i < sizeof(DATA3); i++) {
+ EXPECT_EQ(DATA3[i], dataBytes[i])
+ << "data character " << i << " should be equal";
+ }
+ delete dataBytes;
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, DeleteEntity) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, sizeof(DATA1));
+ writer->WriteEntityData(DATA1, sizeof(DATA1));
+ writer->WriteEntityHeader(mKey2, -1);
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ char* dataBytes;
+ // read first entity
+ reader->ReadNextHeader(&done, &type);
+ reader->ReadEntityHeader(&key, &dataSize);
+ dataBytes = new char[dataSize];
+ reader->ReadEntityData(dataBytes, dataSize);
+ delete dataBytes;
+
+ // read and verify deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader on deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on second entity";
+ EXPECT_EQ(mKey2, key)
+ << "wrong key from ReadEntityHeader on second entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on second entity";
+
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, EneityAfterDelete) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, sizeof(DATA1));
+ writer->WriteEntityData(DATA1, sizeof(DATA1));
+ writer->WriteEntityHeader(mKey2, -1);
+ writer->WriteEntityHeader(mKey3, sizeof(DATA3));
+ writer->WriteEntityData(DATA3, sizeof(DATA3));
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ char* dataBytes;
+ // read first entity
+ reader->ReadNextHeader(&done, &type);
+ reader->ReadEntityHeader(&key, &dataSize);
+ dataBytes = new char[dataSize];
+ reader->ReadEntityData(dataBytes, dataSize);
+ delete dataBytes;
+
+ // read and verify deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader on deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on second entity";
+ EXPECT_EQ(mKey2, key)
+ << "wrong key from ReadEntityHeader on second entity";
+ EXPECT_EQ(-1, (int)dataSize)
+ << "not recognizing deletion on second entity";
+
+ // read and verify third entity
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader after deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on third entity";
+ EXPECT_EQ(mKey3, key)
+ << "wrong key from ReadEntityHeader on third entity";
+ EXPECT_EQ(sizeof(DATA3), dataSize)
+ << "wrong size from ReadEntityHeader on third entity";
+
+ dataBytes = new char[dataSize];
+ EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
+ << "ReadEntityData returned an error on third entity";
+ for (unsigned int i = 0; i < sizeof(DATA3); i++) {
+ EXPECT_EQ(DATA3[i], dataBytes[i])
+ << "data character " << i << " should be equal";
+ }
+ delete dataBytes;
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, OnlyDeleteEntities) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, -1);
+ writer->WriteEntityHeader(mKey2, -1);
+ writer->WriteEntityHeader(mKey3, -1);
+ writer->WriteEntityHeader(mKey4, -1);
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ // read and verify first deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader first deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on first entity";
+ EXPECT_EQ(mKey1, key)
+ << "wrong key from ReadEntityHeader on first entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on first entity";
+
+ // read and verify second deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader second deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on second entity";
+ EXPECT_EQ(mKey2, key)
+ << "wrong key from ReadEntityHeader on second entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on second entity";
+
+ // read and verify third deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader third deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on third entity";
+ EXPECT_EQ(mKey3, key)
+ << "wrong key from ReadEntityHeader on third entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on third entity";
+
+ // read and verify fourth deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader fourth deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on fourth entity";
+ EXPECT_EQ(mKey4, key)
+ << "wrong key from ReadEntityHeader on fourth entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on fourth entity";
+
+ delete writer;
+ delete reader;
+}
+
+TEST_F(BackupDataTest, ReadDeletedEntityData) {
+ int fd = ::open(m_filename, O_WRONLY);
+ BackupDataWriter* writer = new BackupDataWriter(fd);
+ writer->WriteEntityHeader(mKey1, -1);
+ writer->WriteEntityHeader(mKey2, -1);
+
+ ::close(fd);
+ fd = ::open(m_filename, O_RDONLY);
+ BackupDataReader* reader = new BackupDataReader(fd);
+
+ bool done;
+ int type;
+ String8 key;
+ size_t dataSize;
+ // read and verify first deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader first deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on first entity";
+ EXPECT_EQ(mKey1, key)
+ << "wrong key from ReadEntityHeader on first entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on first entity";
+
+ // erroneously try to read first entity data
+ char* dataBytes = new char[10];
+ dataBytes[0] = 'A';
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityData(dataBytes, dataSize));
+ // expect dataBytes to be unmodofied
+ EXPECT_EQ('A', dataBytes[0]);
+
+ // read and verify second deletion
+ reader->ReadNextHeader(&done, &type);
+ EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
+ << "wrong type from ReadNextHeader second deletion";
+
+ EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
+ << "ReadEntityHeader returned an error on second entity";
+ EXPECT_EQ(mKey2, key)
+ << "wrong key from ReadEntityHeader on second entity";
+ EXPECT_EQ(-1, (int) dataSize)
+ << "not recognizing deletion on second entity";
+
+ delete writer;
+ delete reader;
+}
+
+}
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
new file mode 100644
index 0000000..376e79c
--- /dev/null
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ByteBucketArray.h>
+
+#include <gtest/gtest.h>
+
+using android::ByteBucketArray;
+
+TEST(ByteBucketArrayTest, TestSparseInsertion) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ for (size_t i = 0; i < bba.size(); i++) {
+ switch (i) {
+ case 0: EXPECT_EQ(1, bba[i]); break;
+ case 10: EXPECT_EQ(2, bba[i]); break;
+ case 26: EXPECT_EQ(3, bba[i]); break;
+ case 129: EXPECT_EQ(4, bba[i]); break;
+ case 234: EXPECT_EQ(5, bba[i]); break;
+ default: EXPECT_EQ(0, bba[i]); break;
+ }
+ }
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
new file mode 100644
index 0000000..f50c178
--- /dev/null
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+/**
+ * Include a binary resource table.
+ * This table is an overlay.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/overlay/overlay_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+class IdmapTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len));
+ char targetName[256] = "com.android.test.basic";
+ ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0,
+ targetName, targetName, &mData, &mDataSize));
+ }
+
+ virtual void TearDown() {
+ free(mData);
+ }
+
+ ResTable mTargetTable;
+ ResTable mOverlayTable;
+ void* mData;
+ size_t mDataSize;
+};
+
+TEST_F(IdmapTest, canLoadIdmap) {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+}
+
+TEST_F(IdmapTest, overlayOverridesResourceValue) {
+ Res_value val;
+ ssize_t block = mTargetTable.getResource(base::R::string::test2, &val, false);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ size_t strLen;
+ const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
+
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+ ssize_t newBlock = mTargetTable.getResource(base::R::string::test2, &val, false);
+ ASSERT_GE(newBlock, 0);
+ ASSERT_NE(block, newBlock);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ pool = mTargetTable.getTableStringBlock(newBlock);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
+}
+
+TEST_F(IdmapTest, overlaidResourceHasSameName) {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+ ResTable::resource_name resName;
+ ASSERT_TRUE(mTargetTable.getResourceName(base::R::array::integerArray1, false, &resName));
+
+ ASSERT_TRUE(resName.package != NULL);
+ ASSERT_TRUE(resName.type != NULL);
+ ASSERT_TRUE(resName.name != NULL);
+
+ EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen));
+ EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
+ EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
+}
+
+} // namespace
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 2c9f650..7a4dd13 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -91,7 +91,7 @@ TEST_F(ObbFileTest, WriteThenRead) {
EXPECT_EQ(sizeof(salt), saltLen)
<< "salt sizes were not the same";
- for (int i = 0; i < sizeof(salt); i++) {
+ for (size_t i = 0; i < sizeof(salt); i++) {
EXPECT_EQ(salt[i], newSalt[i])
<< "salt character " << i << " should be equal";
}
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
new file mode 100644
index 0000000..89d271d0
--- /dev/null
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/lib/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+#include "data/lib/lib_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+TEST(ResTableTest, shouldLoadSuccessfully) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+}
+
+TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG);
+
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+
+ const ResStringPool* pool = table.getTableStringBlock(block);
+ ASSERT_TRUE(NULL != pool);
+ ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data));
+}
+
+TEST(ResTableTest, resourceNameIsResolved) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ String16 defPackage("com.android.test.basic");
+ String16 testName("@string/test1");
+ uint32_t resID = table.identifierForName(testName.string(), testName.size(),
+ 0, 0,
+ defPackage.string(), defPackage.size());
+ ASSERT_NE(uint32_t(0x00000000), resID);
+ ASSERT_EQ(base::R::string::test1, resID);
+}
+
+TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme1));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(100), val.data);
+
+ index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::integer::number1, val.data);
+}
+
+TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme2));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(300), val.data);
+
+ index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::integer::number1, val.data);
+}
+
+TEST(ResTableTest, libraryThemeIsAppliedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(lib_arsc, lib_arsc_len));
+
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(700), val.data);
+}
+
+TEST(ResTableTest, referenceToBagIsNotResolved) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number2, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::array::integerArray1, val.data);
+
+ ssize_t newBlock = table.resolveReference(&val, block);
+ EXPECT_EQ(block, newBlock);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ EXPECT_EQ(base::R::array::integerArray1, val.data);
+}
+
+TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ const ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(base::R::array::integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.density = 320;
+ table.setParameters(&param);
+
+ block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ count = table.lockBag(base::R::array::integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
+}
+
+TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(200), val.data);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(&param);
+
+ block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
+}
+
+TEST(ResTableTest, emptyTableHasSensibleDefaults) {
+ const int32_t assetCookie = 1;
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
+
+ // Adding an empty table gives us one table!
+ ASSERT_EQ(uint32_t(1), table.getTableCount());
+
+ // Adding an empty table doesn't mean we get packages.
+ ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
+
+ Res_value val;
+ ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0);
+}
+
+}
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index 139f868..f00a2d9 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -64,8 +64,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) {
config.packLanguage("eng");
// 1-00110-01 101-00100
- EXPECT_EQ(0x99, config.language[0]);
- EXPECT_EQ(0xa4, config.language[1]);
+ EXPECT_EQ('\x99', config.language[0]);
+ EXPECT_EQ('\xA4', config.language[1]);
char out[4] = { 1, 1, 1, 1};
config.unpackLanguage(out);
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
new file mode 100644
index 0000000..82703f9
--- /dev/null
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table. This table
+ * is a base table for an APK split.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+/**
+ * Include a binary resource table. This table
+ * is a configuration split table for an APK split.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/split_de_fr_arsc.h"
+
+/**
+ * Include a binary resource table. This table
+ * is a feature split table for an APK split.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/feature/feature_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+void makeConfigFrench(ResTable_config* config) {
+ memset(config, 0, sizeof(*config));
+ config->language[0] = 'f';
+ config->language[1] = 'r';
+}
+
+TEST(SplitTest, TestLoadBase) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+}
+
+TEST(SplitTest, TestGetResourceFromBase) {
+ ResTable_config frenchConfig;
+ makeConfigFrench(&frenchConfig);
+
+ ResTable table;
+ table.setParameters(&frenchConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable_config expectedConfig;
+ memset(&expectedConfig, 0, sizeof(expectedConfig));
+
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+ // The returned block should tell us which string pool to get the value, if it is a string.
+ EXPECT_GE(block, 0);
+
+ // We expect the default resource to be selected since it is the only resource configuration.
+ EXPECT_EQ(0, expectedConfig.compare(config));
+
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, TestGetResourceFromSplit) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
+
+ ResTable table;
+ table.setParameters(&expectedConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(0, expectedConfig.compare(config));
+
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
+
+ ResTable table;
+ table.setParameters(&expectedConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::resource_name baseName;
+ EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &baseName));
+
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ ResTable::resource_name frName;
+ EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &frName));
+
+ EXPECT_EQ(
+ String16(baseName.package, baseName.packageLen),
+ String16(frName.package, frName.packageLen));
+
+ EXPECT_EQ(
+ String16(baseName.type, baseName.typeLen),
+ String16(frName.type, frName.typeLen));
+
+ EXPECT_EQ(
+ String16(baseName.name, baseName.nameLen),
+ String16(frName.name, frName.nameLen));
+}
+
+TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
+
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ uint32_t frSpecFlags = 0;
+ block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
+}
+
+TEST(SplitFeatureTest, TestNewResourceIsAccessible) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG);
+ EXPECT_LT(block, 0);
+
+ ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
+
+ block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG);
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitFeatureTest, TestNewResourceIsAccessibleByName) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::resource_name name;
+ EXPECT_FALSE(table.getResourceName(base::R::string::test3, false, &name));
+
+ ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
+
+ EXPECT_TRUE(table.getResourceName(base::R::string::test3, false, &name));
+
+ EXPECT_EQ(String16("com.android.test.basic"),
+ String16(name.package, name.packageLen));
+
+ EXPECT_EQ(String16("string"),
+ String16(name.type, name.typeLen));
+
+ EXPECT_EQ(String16("test3"),
+ String16(name.name, name.nameLen));
+}
+
+} // namespace
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
new file mode 100644
index 0000000..75a233a
--- /dev/null
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -0,0 +1,17 @@
+#ifndef __TEST_HELPERS_H
+#define __TEST_HELPERS_H
+
+#include <ostream>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
+ return out << str.string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
+ return out << android::String8(str).string();
+}
+
+#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
new file mode 100644
index 0000000..d69abe5
--- /dev/null
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+void* createTypeData() {
+ ResTable_type t;
+ memset(&t, 0, sizeof(t));
+ t.header.type = RES_TABLE_TYPE_TYPE;
+ t.header.headerSize = sizeof(t);
+ t.id = 1;
+ t.entryCount = 3;
+
+ uint32_t offsets[3];
+ t.entriesStart = t.header.headerSize + sizeof(offsets);
+ t.header.size = t.entriesStart;
+
+ offsets[0] = 0;
+ ResTable_entry e1;
+ memset(&e1, 0, sizeof(e1));
+ e1.size = sizeof(e1);
+ e1.key.index = 0;
+ t.header.size += sizeof(e1);
+
+ Res_value v1;
+ memset(&v1, 0, sizeof(v1));
+ t.header.size += sizeof(v1);
+
+ offsets[1] = ResTable_type::NO_ENTRY;
+
+ offsets[2] = sizeof(e1) + sizeof(v1);
+ ResTable_entry e2;
+ memset(&e2, 0, sizeof(e2));
+ e2.size = sizeof(e2);
+ e2.key.index = 1;
+ t.header.size += sizeof(e2);
+
+ Res_value v2;
+ memset(&v2, 0, sizeof(v2));
+ t.header.size += sizeof(v2);
+
+ uint8_t* data = (uint8_t*)malloc(t.header.size);
+ uint8_t* p = data;
+ memcpy(p, &t, sizeof(t));
+ p += sizeof(t);
+ memcpy(p, offsets, sizeof(offsets));
+ p += sizeof(offsets);
+ memcpy(p, &e1, sizeof(e1));
+ p += sizeof(e1);
+ memcpy(p, &v1, sizeof(v1));
+ p += sizeof(v1);
+ memcpy(p, &e2, sizeof(e2));
+ p += sizeof(e2);
+ memcpy(p, &v2, sizeof(v2));
+ p += sizeof(v2);
+ return data;
+}
+
+TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
+ ResTable_type* data = (ResTable_type*) createTypeData();
+
+ TypeVariant v(data);
+
+ TypeVariant::iterator iter = v.beginEntries();
+ ASSERT_EQ(uint32_t(0), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(0), iter->key.index);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(1), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(1), iter->key.index);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(v.endEntries(), iter);
+
+ free(data);
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
new file mode 100644
index 0000000..c05cfb0
--- /dev/null
+++ b/libs/androidfw/tests/data/.gitignore
@@ -0,0 +1,2 @@
+*.apk
+*.arsc
diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
new file mode 100644
index 0000000..363dcb9
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -0,0 +1,55 @@
+#ifndef __BASE_R_H
+#define __BASE_R_H
+
+namespace base {
+namespace R {
+
+namespace attr {
+ enum {
+ attr1 = 0x7f010000, // default
+ attr2 = 0x7f010001, // default
+ };
+}
+
+namespace layout {
+ enum {
+ main = 0x7f020000, // default, fr-sw600dp-v13
+ };
+}
+
+namespace string {
+ enum {
+ test1 = 0x7f030000, // default
+ test2 = 0x7f030001, // default
+
+ test3 = 0x7f070000, // default (in feature)
+ test4 = 0x7f070001, // default (in feature)
+ };
+}
+
+namespace integer {
+ enum {
+ number1 = 0x7f040000, // default, sv
+ number2 = 0x7f040001, // default
+
+ test3 = 0x7f080000, // default (in feature)
+ };
+}
+
+namespace style {
+ enum {
+ Theme1 = 0x7f050000, // default
+ Theme2 = 0x7f050001, // default
+ };
+}
+
+namespace array {
+ enum {
+ integerArray1 = 0x7f060000, // default
+ };
+}
+
+} // namespace R
+} // namespace base
+
+#endif // __BASE_R_H
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
new file mode 100644
index 0000000..61cb94c
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -0,0 +1,161 @@
+unsigned char basic_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x60, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00,
+ 0x00, 0x00, 0x22, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00,
+ 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00,
+ 0x2d, 0x00, 0x66, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00,
+ 0x36, 0x00, 0x30, 0x00, 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00,
+ 0x76, 0x00, 0x31, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
+ 0x98, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+ 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
+ 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+ 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0xec, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00,
+ 0x74, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6d, 0x00,
+ 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00,
+ 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00,
+ 0x6d, 0x00, 0x65, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x44, 0x00,
+ 0x5c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x7f,
+ 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10,
+ 0x2c, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x03, 0x00, 0x00, 0x00
+};
+unsigned int basic_arsc_len = 1888;
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
new file mode 100755
index 0000000..fa4a9fe
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/build
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res --split fr,de -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc basic.arsc && \
+xxd -i basic.arsc > basic_arsc.h && \
+unzip bundle_de_fr.apk resources.arsc && \
+mv resources.arsc split_de_fr.arsc && \
+xxd -i split_de_fr.arsc > split_de_fr_arsc.h
diff --git a/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml b/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml
new file mode 100644
index 0000000..05ffd58
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge>
+</merge>
diff --git a/libs/androidfw/tests/data/basic/res/layout/main.xml b/libs/androidfw/tests/data/basic/res/layout/main.xml
new file mode 100644
index 0000000..05ffd58
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/layout/main.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge>
+</merge>
diff --git a/libs/androidfw/tests/data/basic/res/values-de/values.xml b/libs/androidfw/tests/data/basic/res/values-de/values.xml
new file mode 100644
index 0000000..103c6a3
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-de/values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test1">versuch 1</string>
+ <string name="test2">versuch 2</string>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values-fr/values.xml b/libs/androidfw/tests/data/basic/res/values-fr/values.xml
new file mode 100644
index 0000000..1806a2d
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-fr/values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test1">essai 1</string>
+ <string name="test2">essai 2</string>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
new file mode 100644
index 0000000..9d52307
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="number1">400</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
new file mode 100644
index 0000000..662eda6
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="attr1" format="reference|integer" />
+ <attr name="attr2" format="reference|integer" />
+
+ <string name="test1">test1</string>
+ <string name="test2">test2</string>
+
+ <integer name="number1">200</integer>
+ <integer name="number2">@array/integerArray1</integer>
+
+ <style name="Theme1">
+ <item name="com.android.test.basic:attr1">100</item>
+ <item name="com.android.test.basic:attr2">@integer/number1</item>
+ </style>
+
+ <style name="Theme2" parent="@com.android.test.basic:style/Theme1">
+ <item name="com.android.test.basic:attr1">300</item>
+ </style>
+
+ <integer-array name="integerArray1">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
new file mode 100644
index 0000000..a8eaf0b
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
@@ -0,0 +1,85 @@
+unsigned char split_de_fr_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0xd8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00,
+ 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00, 0x65, 0x00,
+ 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00,
+ 0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x50, 0x03, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00,
+ 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_de_fr_arsc_len = 984;
diff --git a/libs/androidfw/tests/data/feature/AndroidManifest.xml b/libs/androidfw/tests/data/feature/AndroidManifest.xml
new file mode 100644
index 0000000..c2343b7
--- /dev/null
+++ b/libs/androidfw/tests/data/feature/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+</manifest>
diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build
new file mode 100755
index 0000000..b547dc2
--- /dev/null
+++ b/libs/androidfw/tests/data/feature/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res --feature-of ../basic/bundle.apk -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc feature.arsc && \
+xxd -i feature.arsc > feature_arsc.h
diff --git a/libs/androidfw/tests/data/feature/feature_arsc.h b/libs/androidfw/tests/data/feature/feature_arsc.h
new file mode 100644
index 0000000..cf7647d
--- /dev/null
+++ b/libs/androidfw/tests/data/feature/feature_arsc.h
@@ -0,0 +1,73 @@
+unsigned char feature_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x40, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf4, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x3c, 0x00, 0x65, 0x00, 0x6d, 0x00,
+ 0x70, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0xc8, 0x00, 0x00, 0x00
+};
+unsigned int feature_arsc_len = 832;
diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml
new file mode 100644
index 0000000..d03445a
--- /dev/null
+++ b/libs/androidfw/tests/data/feature/res/values/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test3">test3</string>
+ <string name="test4">test4</string>
+
+ <integer name="number3">200</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib/R.h
new file mode 100644
index 0000000..13bf095
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/R.h
@@ -0,0 +1,22 @@
+#ifndef __LIB_R_H
+#define __LIB_R_H
+
+namespace lib {
+namespace R {
+
+namespace attr {
+ enum {
+ attr1 = 0x02010000, // default
+ };
+}
+
+namespace style {
+ enum {
+ Theme = 0x02020000, // default
+ };
+}
+
+} // namespace R
+} // namespace lib
+
+#endif // __LIB_R_H
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib/build
new file mode 100755
index 0000000..8e6e70c
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f --shared-lib && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc lib.arsc && \
+xxd -i lib.arsc > lib_arsc.h
diff --git a/libs/androidfw/tests/data/lib/lib_arsc.h b/libs/androidfw/tests/data/lib/lib_arsc.h
new file mode 100644
index 0000000..d670c5b
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/lib_arsc.h
@@ -0,0 +1,84 @@
+unsigned char lib_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0xc8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xa0, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x0c, 0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x08, 0x00, 0x00, 0x10, 0xbc, 0x02, 0x00, 0x00
+};
+unsigned int lib_arsc_len = 968;
diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib/res/values/values.xml
new file mode 100644
index 0000000..a77f0c7
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/res/values/values.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="attr1" format="integer" />
+
+ <style name="Theme">
+ <item name="com.android.test.basic:attr1">700</item>
+ </style>
+</resources>
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
new file mode 100755
index 0000000..87cf6de
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc overlay.arsc && \
+xxd -i overlay.arsc > overlay_arsc.h
diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h
new file mode 100644
index 0000000..5bd98b2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h
@@ -0,0 +1,69 @@
+unsigned char overlay_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00,
+ 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+ 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00,
+ 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x0b, 0x00, 0x00, 0x00
+};
+unsigned int overlay_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..227e889
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test2">test2-overlay</string>
+ <integer-array name="integerArray1">
+ <item>10</item>
+ <item>11</item>
+ </integer-array>
+</resources>
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
new file mode 100644
index 0000000..9cc83ed
--- /dev/null
+++ b/libs/hwui/AmbientShadow.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2013 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 "OpenGLRenderer"
+
+#include <math.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
+
+#include "AmbientShadow.h"
+#include "ShadowTessellator.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Calculate the shadows as a triangle strips while alpha value as the
+ * shadow values.
+ *
+ * @param isCasterOpaque Whether the caster is opaque.
+ * @param vertices The shadow caster's polygon, which is represented in a Vector3
+ * array.
+ * @param vertexCount The length of caster's polygon in terms of number of
+ * vertices.
+ * @param centroid3d The centroid of the shadow caster.
+ * @param heightFactor The factor showing the higher the object, the lighter the
+ * shadow.
+ * @param geomFactor The factor scaling the geometry expansion along the normal.
+ *
+ * @param shadowVertexBuffer Return an floating point array of (x, y, a)
+ * triangle strips mode.
+ */
+void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
+ const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
+ float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
+ const int rays = SHADOW_RAY_COUNT;
+ // Validate the inputs.
+ if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
+ || geomFactor <= 0) {
+#if DEBUG_SHADOW
+ ALOGW("Invalid input for createAmbientShadow(), early return!");
+#endif
+ return;
+ }
+
+ Vector<Vector2> dir; // TODO: use C++11 unique_ptr
+ dir.setCapacity(rays);
+ float rayDist[rays];
+ float rayHeight[rays];
+ calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray());
+
+ // Calculate the length and height of the points along the edge.
+ //
+ // The math here is:
+ // Intersect each ray (starting from the centroid) with the polygon.
+ for (int i = 0; i < rays; i++) {
+ int edgeIndex;
+ float edgeFraction;
+ float rayDistance;
+ calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex,
+ edgeFraction, rayDistance);
+ rayDist[i] = rayDistance;
+ if (edgeIndex < 0 || edgeIndex >= vertexCount) {
+#if DEBUG_SHADOW
+ ALOGW("Invalid edgeIndex!");
+#endif
+ edgeIndex = 0;
+ }
+ float h1 = vertices[edgeIndex].z;
+ float h2 = vertices[((edgeIndex + 1) % vertexCount)].z;
+ rayHeight[i] = h1 + edgeFraction * (h2 - h1);
+ }
+
+ // The output buffer length basically is roughly rays * layers, but since we
+ // need triangle strips, so we need to duplicate vertices to accomplish that.
+ AlphaVertex* shadowVertices =
+ shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+
+ // Calculate the vertex of the shadows.
+ //
+ // The math here is:
+ // Along the edges of the polygon, for each intersection point P (generated above),
+ // calculate the normal N, which should be perpendicular to the edge of the
+ // polygon (represented by the neighbor intersection points) .
+ // Shadow's vertices will be generated as : P + N * scale.
+ const Vector2 centroid2d = {centroid3d.x, centroid3d.y};
+ for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+ Vector2 normal = {1.0f, 0.0f};
+ calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
+
+ // The vertex should be start from rayDist[i] then scale the
+ // normalizeNormal!
+ Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
+ centroid2d;
+
+ // outer ring of points, expanded based upon height of each ray intersection
+ float expansionDist = rayHeight[rayIndex] * heightFactor *
+ geomFactor;
+ AlphaVertex::set(&shadowVertices[rayIndex],
+ intersection.x + normal.x * expansionDist,
+ intersection.y + normal.y * expansionDist,
+ 0.0f);
+
+ // inner ring of points
+ float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
+ // NOTE: Shadow alpha values are transformed when stored in alphavertices,
+ // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
+ float transformedOpacity = acos(1.0f - 2.0f * opacity);
+ AlphaVertex::set(&shadowVertices[rays + rayIndex],
+ intersection.x,
+ intersection.y,
+ transformedOpacity);
+ }
+
+ if (isCasterOpaque) {
+ // skip inner ring, calc bounds over filled portion of buffer
+ shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
+ shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
+ } else {
+ // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
+ // centroid in the innermost ring of vertices.
+ float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
+ AlphaVertex centroidXYA;
+ AlphaVertex::set(&centroidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
+ for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+ shadowVertices[2 * rays + rayIndex] = centroidXYA;
+ }
+ // calc bounds over entire buffer
+ shadowVertexBuffer.computeBounds<AlphaVertex>();
+ shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
+ }
+
+#if DEBUG_SHADOW
+ for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
+ ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
+ shadowVertices[i].y, shadowVertices[i].alpha);
+ }
+#endif
+}
+
+/**
+ * Generate an array of rays' direction vectors.
+ * To make sure the vertices generated are clockwise, the directions are from PI
+ * to -PI.
+ *
+ * @param rays The number of rays shooting out from the centroid.
+ * @param vertices Vertices of the polygon.
+ * @param vertexCount The number of vertices.
+ * @param centroid3d The centroid of the polygon.
+ * @param dir Return the array of ray vectors.
+ */
+void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices,
+ const int vertexCount, const Vector3& centroid3d, Vector2* dir) {
+ // If we don't have enough rays, then fall back to the uniform distribution.
+ if (vertexCount * 2 > rays) {
+ float deltaAngle = 2 * M_PI / rays;
+ for (int i = 0; i < rays; i++) {
+ dir[i].x = cosf(M_PI - deltaAngle * i);
+ dir[i].y = sinf(M_PI - deltaAngle * i);
+ }
+ return;
+ }
+
+ // If we have enough rays, then we assign each vertices a ray, and distribute
+ // the rest uniformly.
+ float rayThetas[rays];
+
+ const int uniformRayCount = rays - vertexCount;
+ const float deltaAngle = 2 * M_PI / uniformRayCount;
+
+ // We have to generate all the vertices' theta anyway and we also need to
+ // find the minimal, so let's precompute it first.
+ // Since the incoming polygon is clockwise, we can find the dip to identify
+ // the minimal theta.
+ float polyThetas[vertexCount];
+ int maxPolyThetaIndex = 0;
+ for (int i = 0; i < vertexCount; i++) {
+ polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
+ vertices[i].x - centroid3d.x);
+ if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
+ maxPolyThetaIndex = i;
+ }
+ }
+
+ // Both poly's thetas and uniform thetas are in decrease order(clockwise)
+ // from PI to -PI.
+ int polyThetaIndex = maxPolyThetaIndex;
+ float polyTheta = polyThetas[maxPolyThetaIndex];
+ int uniformThetaIndex = 0;
+ float uniformTheta = M_PI;
+ for (int i = 0; i < rays; i++) {
+ // Compare both thetas and pick the smaller one and move on.
+ bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
+ if (polyTheta > uniformTheta || hasThetaCollision) {
+ if (hasThetaCollision) {
+ // Shift the uniformTheta to middle way between current polyTheta
+ // and next uniform theta. The next uniform theta can wrap around
+ // to exactly PI safely here.
+ // Note that neither polyTheta nor uniformTheta can be FLT_MAX
+ // due to the hasThetaCollision is true.
+ uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
+#if DEBUG_SHADOW
+ ALOGD("Shifted uniformTheta to %f", uniformTheta);
+#endif
+ }
+ rayThetas[i] = polyTheta;
+ polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
+ if (polyThetaIndex != maxPolyThetaIndex) {
+ polyTheta = polyThetas[polyThetaIndex];
+ } else {
+ // out of poly points.
+ polyTheta = - FLT_MAX;
+ }
+ } else {
+ rayThetas[i] = uniformTheta;
+ uniformThetaIndex++;
+ if (uniformThetaIndex < uniformRayCount) {
+ uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
+ } else {
+ // out of uniform points.
+ uniformTheta = - FLT_MAX;
+ }
+ }
+ }
+
+ for (int i = 0; i < rays; i++) {
+#if DEBUG_SHADOW
+ ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI);
+#endif
+ // TODO: Fix the intersection precision problem and remvoe the delta added
+ // here.
+ dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
+ dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
+ }
+}
+
+/**
+ * Calculate the intersection of a ray hitting the polygon.
+ *
+ * @param vertices The shadow caster's polygon, which is represented in a
+ * Vector3 array.
+ * @param vertexCount The length of caster's polygon in terms of number of vertices.
+ * @param start The starting point of the ray.
+ * @param dir The direction vector of the ray.
+ *
+ * @param outEdgeIndex Return the index of the segment (or index of the starting
+ * vertex) that ray intersect with.
+ * @param outEdgeFraction Return the fraction offset from the segment starting
+ * index.
+ * @param outRayDist Return the ray distance from centroid to the intersection.
+ */
+void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount,
+ const Vector3& start, const Vector2& dir, int& outEdgeIndex,
+ float& outEdgeFraction, float& outRayDist) {
+ float startX = start.x;
+ float startY = start.y;
+ float dirX = dir.x;
+ float dirY = dir.y;
+ // Start the search from the last edge from poly[len-1] to poly[0].
+ int p1 = vertexCount - 1;
+
+ for (int p2 = 0; p2 < vertexCount; p2++) {
+ float p1x = vertices[p1].x;
+ float p1y = vertices[p1].y;
+ float p2x = vertices[p2].x;
+ float p2y = vertices[p2].y;
+
+ // The math here is derived from:
+ // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0;
+ // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0;
+ float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x);
+ if (div != 0) {
+ float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div);
+ if (t > 0 && t <= 1) {
+ float t2 = (p1x * (startY - p2y)
+ + p2x * (p1y - startY)
+ + startX * (p2y - p1y)) / div;
+ if (t2 > 0) {
+ outEdgeIndex = p1;
+ outRayDist = t2;
+ outEdgeFraction = t;
+ return;
+ }
+ }
+ }
+ p1 = p2;
+ }
+ return;
+};
+
+/**
+ * Calculate the normal at the intersection point between a ray and the polygon.
+ *
+ * @param rays The total number of rays.
+ * @param currentRayIndex The index of the ray which the normal is based on.
+ * @param dir The array of the all the rays directions.
+ * @param rayDist The pre-computed ray distances array.
+ *
+ * @param normal Return the normal.
+ */
+void AmbientShadow::calculateNormal(int rays, int currentRayIndex,
+ const Vector2* dir, const float* rayDist, Vector2& normal) {
+ int preIndex = (currentRayIndex - 1 + rays) % rays;
+ int postIndex = (currentRayIndex + 1) % rays;
+ Vector2 p1 = dir[preIndex] * rayDist[preIndex];
+ Vector2 p2 = dir[postIndex] * rayDist[postIndex];
+
+ // Now the rays are going CW around the poly.
+ Vector2 delta = p2 - p1;
+ if (delta.length() != 0) {
+ delta.normalize();
+ // Calculate the normal , which is CCW 90 rotate to the delta.
+ normal.x = - delta.y;
+ normal.y = delta.x;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
new file mode 100644
index 0000000..68df246
--- /dev/null
+++ b/libs/hwui/AmbientShadow.h
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_AMBIENT_SHADOW_H
+#define ANDROID_HWUI_AMBIENT_SHADOW_H
+
+#include "Debug.h"
+#include "OpenGLRenderer.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * AmbientShadow is used to calculate the ambient shadow value around a polygon.
+ *
+ * TODO: calculateIntersection() now is O(N*M), where N is the number of
+ * polygon's vertics and M is the number of rays. In fact, by staring tracing
+ * the vertex from the previous intersection, the algorithm can be O(N + M);
+ */
+class AmbientShadow {
+public:
+ static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
+ int polyLength, const Vector3& centroid3d, float heightFactor,
+ float geomFactor, VertexBuffer& shadowVertexBuffer);
+
+private:
+ static void calculateRayDirections(const int rays, const Vector3* vertices,
+ const int vertexCount, const Vector3& centroid3d, Vector2* dir);
+
+ static void calculateIntersection(const Vector3* poly, int nbVertices,
+ const Vector3& start, const Vector2& dir, int& outEdgeIndex,
+ float& outEdgeFraction, float& outRayDist);
+
+ static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir,
+ const float* rayDist, Vector2& normal);
+}; // AmbientShadow
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_AMBIENT_SHADOW_H
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 411c133..d9f7941 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -4,25 +4,33 @@ include $(CLEAR_VARS)
# Only build libhwui when USE_OPENGL_RENDERER is
# defined in the current device/board configuration
ifeq ($(USE_OPENGL_RENDERER),true)
- LOCAL_SRC_FILES:= \
+ LOCAL_SRC_FILES := \
utils/Blur.cpp \
+ utils/GLUtils.cpp \
utils/SortedListImpl.cpp \
thread/TaskManager.cpp \
font/CacheTexture.cpp \
font/Font.cpp \
+ AmbientShadow.cpp \
+ Animator.cpp \
+ AnimatorManager.cpp \
AssetAtlas.cpp \
+ DamageAccumulator.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
DisplayList.cpp \
DeferredDisplayList.cpp \
+ DeferredLayerUpdater.cpp \
DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
Dither.cpp \
+ DrawProfiler.cpp \
Extensions.cpp \
FboCache.cpp \
GradientCache.cpp \
Image.cpp \
+ Interpolator.cpp \
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
@@ -36,43 +44,52 @@ ifeq ($(USE_OPENGL_RENDERER),true)
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \
+ RenderNode.cpp \
+ RenderProperties.cpp \
+ RenderState.cpp \
ResourceCache.cpp \
- SkiaColorFilter.cpp \
+ ShadowTessellator.cpp \
SkiaShader.cpp \
Snapshot.cpp \
+ SpotShadow.cpp \
+ StatefulBaseRenderer.cpp \
Stencil.cpp \
+ TessellationCache.cpp \
Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
+# RenderThread stuff
+ LOCAL_SRC_FILES += \
+ renderthread/CanvasContext.cpp \
+ renderthread/DrawFrameTask.cpp \
+ renderthread/EglManager.cpp \
+ renderthread/RenderProxy.cpp \
+ renderthread/RenderTask.cpp \
+ renderthread/RenderThread.cpp \
+ renderthread/TimeLord.cpp
+
intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- $(LOCAL_PATH)/../../include/utils \
- external/skia/include/core \
- external/skia/include/effects \
- external/skia/include/images \
- external/skia/src/core \
- external/skia/src/ports \
- external/skia/include/utils
+ external/skia/src/core
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
+ LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
+ include external/stlport/libstlport.mk
+
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
LOCAL_C_INCLUDES += \
$(intermediates) \
frameworks/rs/cpp \
- frameworks/rs \
- external/stlport/stlport \
- bionic/ \
- bionic/libstdc++/include
+ frameworks/rs
endif
ifndef HWUI_COMPILE_SYMBOLS
@@ -83,6 +100,9 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
endif
+ # Defaults for ATRACE_TAG and LOG_TAG for libhwui
+ LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
+
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
new file mode 100644
index 0000000..78d569d
--- /dev/null
+++ b/libs/hwui/Animator.cpp
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "Animator.h"
+
+#include <inttypes.h>
+#include <set>
+
+#include "RenderNode.h"
+#include "RenderProperties.h"
+
+namespace android {
+namespace uirenderer {
+
+/************************************************************
+ * BaseRenderNodeAnimator
+ ************************************************************/
+
+BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
+ : mTarget(NULL)
+ , mFinalValue(finalValue)
+ , mDeltaValue(0)
+ , mFromValue(0)
+ , mInterpolator(0)
+ , mStagingPlayState(NOT_STARTED)
+ , mPlayState(NOT_STARTED)
+ , mHasStartValue(false)
+ , mStartTime(0)
+ , mDuration(300)
+ , mStartDelay(0) {
+}
+
+BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {
+ delete mInterpolator;
+}
+
+void BaseRenderNodeAnimator::checkMutable() {
+ // Should be impossible to hit as the Java-side also has guards for this
+ LOG_ALWAYS_FATAL_IF(mStagingPlayState != NOT_STARTED,
+ "Animator has already been started!");
+}
+
+void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) {
+ checkMutable();
+ delete mInterpolator;
+ mInterpolator = interpolator;
+}
+
+void BaseRenderNodeAnimator::setStartValue(float value) {
+ checkMutable();
+ doSetStartValue(value);
+}
+
+void BaseRenderNodeAnimator::doSetStartValue(float value) {
+ mFromValue = value;
+ mDeltaValue = (mFinalValue - mFromValue);
+ mHasStartValue = true;
+}
+
+void BaseRenderNodeAnimator::setDuration(nsecs_t duration) {
+ checkMutable();
+ mDuration = duration;
+}
+
+void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) {
+ checkMutable();
+ mStartDelay = startDelay;
+}
+
+void BaseRenderNodeAnimator::attach(RenderNode* target) {
+ mTarget = target;
+ onAttached();
+}
+
+void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
+ if (!mHasStartValue) {
+ doSetStartValue(getValue(mTarget));
+ }
+ if (mStagingPlayState > mPlayState) {
+ mPlayState = mStagingPlayState;
+ // Oh boy, we're starting! Man the battle stations!
+ if (mPlayState == RUNNING) {
+ transitionToRunning(info);
+ }
+ }
+}
+
+void BaseRenderNodeAnimator::transitionToRunning(TreeInfo& info) {
+ LOG_ALWAYS_FATAL_IF(info.frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", info.frameTimeMs);
+ if (mStartDelay < 0 || mStartDelay > 50000) {
+ ALOGW("Your start delay is strange and confusing: %" PRId64, mStartDelay);
+ }
+ mStartTime = info.frameTimeMs + mStartDelay;
+ if (mStartTime < 0) {
+ ALOGW("Ended up with a really weird start time of %" PRId64
+ " with frame time %" PRId64 " and start delay %" PRId64,
+ mStartTime, info.frameTimeMs, mStartDelay);
+ // Set to 0 so that the animate() basically instantly finishes
+ mStartTime = 0;
+ }
+ // No interpolator was set, use the default
+ if (!mInterpolator) {
+ mInterpolator = Interpolator::createDefaultInterpolator();
+ }
+ if (mDuration < 0 || mDuration > 50000) {
+ ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
+ }
+}
+
+bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
+ if (mPlayState < RUNNING) {
+ return false;
+ }
+ if (mPlayState == FINISHED) {
+ return true;
+ }
+
+ // If BaseRenderNodeAnimator is handling the delay (not typical), then
+ // because the staging properties reflect the final value, we always need
+ // to call setValue even if the animation isn't yet running or is still
+ // being delayed as we need to override the staging value
+ if (mStartTime > info.frameTimeMs) {
+ info.out.hasAnimations |= true;
+ setValue(mTarget, mFromValue);
+ return false;
+ }
+
+ float fraction = 1.0f;
+ if (mPlayState == RUNNING && mDuration > 0) {
+ fraction = (float)(info.frameTimeMs - mStartTime) / mDuration;
+ }
+ if (fraction >= 1.0f) {
+ fraction = 1.0f;
+ mPlayState = FINISHED;
+ }
+
+ fraction = mInterpolator->interpolate(fraction);
+ setValue(mTarget, mFromValue + (mDeltaValue * fraction));
+
+ if (mPlayState == FINISHED) {
+ callOnFinishedListener(info);
+ return true;
+ }
+
+ info.out.hasAnimations |= true;
+ return false;
+}
+
+void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) {
+ if (mListener.get()) {
+ if (!info.animationHook) {
+ mListener->onAnimationFinished(this);
+ } else {
+ info.animationHook->callOnFinished(this, mListener.get());
+ }
+ }
+}
+
+/************************************************************
+ * RenderPropertyAnimator
+ ************************************************************/
+
+struct RenderPropertyAnimator::PropertyAccessors {
+ RenderNode::DirtyPropertyMask dirtyMask;
+ GetFloatProperty getter;
+ SetFloatProperty setter;
+};
+
+// Maps RenderProperty enum to accessors
+const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
+ {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
+ {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
+ {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
+ {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX },
+ {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY },
+ {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation },
+ {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX },
+ {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY },
+ {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX },
+ {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY },
+ {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ },
+ {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha },
+};
+
+RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue)
+ : BaseRenderNodeAnimator(finalValue)
+ , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
+}
+
+void RenderPropertyAnimator::onAttached() {
+ if (!mHasStartValue
+ && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+ setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
+ }
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+ if (mStagingPlayState == RUNNING) {
+ (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+ } else if (mStagingPlayState == FINISHED) {
+ // We're being canceled, so make sure that whatever values the UI thread
+ // is observing for us is pushed over
+ mTarget->setPropertyFieldsDirty(dirtyMask());
+ }
+}
+
+uint32_t RenderPropertyAnimator::dirtyMask() {
+ return mPropertyAccess->dirtyMask;
+}
+
+float RenderPropertyAnimator::getValue(RenderNode* target) const {
+ return (target->properties().*mPropertyAccess->getter)();
+}
+
+void RenderPropertyAnimator::setValue(RenderNode* target, float value) {
+ (target->animatorProperties().*mPropertyAccess->setter)(value);
+}
+
+/************************************************************
+ * CanvasPropertyPrimitiveAnimator
+ ************************************************************/
+
+CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(
+ CanvasPropertyPrimitive* property, float finalValue)
+ : BaseRenderNodeAnimator(finalValue)
+ , mProperty(property) {
+}
+
+float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const {
+ return mProperty->value;
+}
+
+void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) {
+ mProperty->value = value;
+}
+
+uint32_t CanvasPropertyPrimitiveAnimator::dirtyMask() {
+ return RenderNode::DISPLAY_LIST;
+}
+
+/************************************************************
+ * CanvasPropertySkPaintAnimator
+ ************************************************************/
+
+CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(
+ CanvasPropertyPaint* property, PaintField field, float finalValue)
+ : BaseRenderNodeAnimator(finalValue)
+ , mProperty(property)
+ , mField(field) {
+}
+
+float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const {
+ switch (mField) {
+ case STROKE_WIDTH:
+ return mProperty->value.getStrokeWidth();
+ case ALPHA:
+ return mProperty->value.getAlpha();
+ }
+ LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
+ return -1;
+}
+
+static uint8_t to_uint8(float value) {
+ int c = (int) (value + .5f);
+ return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c );
+}
+
+void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) {
+ switch (mField) {
+ case STROKE_WIDTH:
+ mProperty->value.setStrokeWidth(value);
+ return;
+ case ALPHA:
+ mProperty->value.setAlpha(to_uint8(value));
+ return;
+ }
+ LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
+}
+
+uint32_t CanvasPropertyPaintAnimator::dirtyMask() {
+ return RenderNode::DISPLAY_LIST;
+}
+
+RevealAnimator::RevealAnimator(int centerX, int centerY,
+ float startValue, float finalValue)
+ : BaseRenderNodeAnimator(finalValue)
+ , mCenterX(centerX)
+ , mCenterY(centerY) {
+ setStartValue(startValue);
+}
+
+float RevealAnimator::getValue(RenderNode* target) const {
+ return target->properties().getRevealClip().getRadius();
+}
+
+void RevealAnimator::setValue(RenderNode* target, float value) {
+ target->animatorProperties().mutableRevealClip().set(true,
+ mCenterX, mCenterY, value);
+}
+
+uint32_t RevealAnimator::dirtyMask() {
+ return RenderNode::GENERIC;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
new file mode 100644
index 0000000..6dfe7b4
--- /dev/null
+++ b/libs/hwui/Animator.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef ANIMATOR_H
+#define ANIMATOR_H
+
+#include <cutils/compiler.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+#include "CanvasProperty.h"
+#include "Interpolator.h"
+#include "TreeInfo.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+class AnimationListener : public VirtualLightRefBase {
+public:
+ ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0;
+protected:
+ ANDROID_API virtual ~AnimationListener() {}
+};
+
+class BaseRenderNodeAnimator : public VirtualLightRefBase {
+ PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator);
+public:
+ ANDROID_API void setStartValue(float value);
+ ANDROID_API void setInterpolator(Interpolator* interpolator);
+ ANDROID_API void setDuration(nsecs_t durationInMs);
+ ANDROID_API nsecs_t duration() { return mDuration; }
+ ANDROID_API void setStartDelay(nsecs_t startDelayInMs);
+ ANDROID_API nsecs_t startDelay() { return mStartDelay; }
+ ANDROID_API void setListener(AnimationListener* listener) {
+ mListener = listener;
+ }
+ ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
+ ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
+
+ void attach(RenderNode* target);
+ virtual void onAttached() {}
+ void detach() { mTarget = 0; }
+ void pushStaging(TreeInfo& info);
+ bool animate(TreeInfo& info);
+
+ bool isFinished() { return mPlayState == FINISHED; }
+ float finalValue() { return mFinalValue; }
+
+ ANDROID_API virtual uint32_t dirtyMask() = 0;
+
+protected:
+ BaseRenderNodeAnimator(float finalValue);
+ virtual ~BaseRenderNodeAnimator();
+
+ virtual float getValue(RenderNode* target) const = 0;
+ virtual void setValue(RenderNode* target, float value) = 0;
+ RenderNode* target() { return mTarget; }
+
+ void callOnFinishedListener(TreeInfo& info);
+
+ virtual void onStagingPlayStateChanged() {}
+
+ enum PlayState {
+ NOT_STARTED,
+ RUNNING,
+ FINISHED,
+ };
+
+ RenderNode* mTarget;
+
+ float mFinalValue;
+ float mDeltaValue;
+ float mFromValue;
+
+ Interpolator* mInterpolator;
+ PlayState mStagingPlayState;
+ PlayState mPlayState;
+ bool mHasStartValue;
+ nsecs_t mStartTime;
+ nsecs_t mDuration;
+ nsecs_t mStartDelay;
+
+ sp<AnimationListener> mListener;
+
+private:
+ inline void checkMutable();
+ virtual void transitionToRunning(TreeInfo& info);
+ void doSetStartValue(float value);
+};
+
+class RenderPropertyAnimator : public BaseRenderNodeAnimator {
+public:
+ enum RenderProperty {
+ TRANSLATION_X = 0,
+ TRANSLATION_Y,
+ TRANSLATION_Z,
+ SCALE_X,
+ SCALE_Y,
+ ROTATION,
+ ROTATION_X,
+ ROTATION_Y,
+ X,
+ Y,
+ Z,
+ ALPHA,
+ };
+
+ ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);
+
+ ANDROID_API virtual uint32_t dirtyMask();
+
+protected:
+ virtual float getValue(RenderNode* target) const;
+ virtual void setValue(RenderNode* target, float value);
+ virtual void onAttached();
+ virtual void onStagingPlayStateChanged();
+
+private:
+ typedef bool (RenderProperties::*SetFloatProperty)(float value);
+ typedef float (RenderProperties::*GetFloatProperty)() const;
+
+ struct PropertyAccessors;
+ const PropertyAccessors* mPropertyAccess;
+
+ static const PropertyAccessors PROPERTY_ACCESSOR_LUT[];
+};
+
+class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator {
+public:
+ ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property,
+ float finalValue);
+
+ ANDROID_API virtual uint32_t dirtyMask();
+
+protected:
+ virtual float getValue(RenderNode* target) const;
+ virtual void setValue(RenderNode* target, float value);
+private:
+ sp<CanvasPropertyPrimitive> mProperty;
+};
+
+class CanvasPropertyPaintAnimator : public BaseRenderNodeAnimator {
+public:
+ enum PaintField {
+ STROKE_WIDTH = 0,
+ ALPHA,
+ };
+
+ ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property,
+ PaintField field, float finalValue);
+
+ ANDROID_API virtual uint32_t dirtyMask();
+
+protected:
+ virtual float getValue(RenderNode* target) const;
+ virtual void setValue(RenderNode* target, float value);
+private:
+ sp<CanvasPropertyPaint> mProperty;
+ PaintField mField;
+};
+
+class RevealAnimator : public BaseRenderNodeAnimator {
+public:
+ ANDROID_API RevealAnimator(int centerX, int centerY,
+ float startValue, float finalValue);
+
+ ANDROID_API virtual uint32_t dirtyMask();
+
+protected:
+ virtual float getValue(RenderNode* target) const;
+ virtual void setValue(RenderNode* target, float value);
+
+private:
+ int mCenterX, mCenterY;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* ANIMATOR_H */
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
new file mode 100644
index 0000000..7221295a4
--- /dev/null
+++ b/libs/hwui/AnimatorManager.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "AnimatorManager.h"
+
+#include <algorithm>
+
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+using namespace std;
+
+static void unref(BaseRenderNodeAnimator* animator) {
+ animator->detach();
+ animator->decStrong(0);
+}
+
+AnimatorManager::AnimatorManager(RenderNode& parent)
+ : mParent(parent) {
+}
+
+AnimatorManager::~AnimatorManager() {
+ for_each(mNewAnimators.begin(), mNewAnimators.end(), unref);
+ for_each(mAnimators.begin(), mAnimators.end(), unref);
+}
+
+void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
+ animator->incStrong(0);
+ animator->attach(&mParent);
+ mNewAnimators.push_back(animator.get());
+}
+
+template<typename T>
+static void move_all(T& source, T& dest) {
+ dest.reserve(source.size() + dest.size());
+ for (typename T::iterator it = source.begin(); it != source.end(); it++) {
+ dest.push_back(*it);
+ }
+ source.clear();
+}
+
+void AnimatorManager::pushStaging(TreeInfo& info) {
+ if (mNewAnimators.size()) {
+ // Since this is a straight move, we don't need to inc/dec the ref count
+ move_all(mNewAnimators, mAnimators);
+ }
+ for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
+ (*it)->pushStaging(info);
+ }
+}
+
+class AnimateFunctor {
+public:
+ AnimateFunctor(RenderNode& target, TreeInfo& info)
+ : dirtyMask(0), mTarget(target), mInfo(info) {}
+
+ bool operator() (BaseRenderNodeAnimator* animator) {
+ dirtyMask |= animator->dirtyMask();
+ bool remove = animator->animate(mInfo);
+ if (remove) {
+ animator->decStrong(0);
+ }
+ return remove;
+ }
+
+ uint32_t dirtyMask;
+
+private:
+ RenderNode& mTarget;
+ TreeInfo& mInfo;
+};
+
+uint32_t AnimatorManager::animate(TreeInfo& info) {
+ if (!mAnimators.size()) return 0;
+
+ // TODO: Can we target this better? For now treat it like any other staging
+ // property push and just damage self before and after animators are run
+
+ mParent.damageSelf(info);
+ info.damageAccumulator->popTransform();
+
+ AnimateFunctor functor(mParent, info);
+ std::vector< BaseRenderNodeAnimator* >::iterator newEnd;
+ newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
+ mAnimators.erase(newEnd, mAnimators.end());
+
+ mParent.mProperties.updateMatrix();
+ info.damageAccumulator->pushTransform(&mParent);
+ mParent.damageSelf(info);
+
+ return functor.dirtyMask;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h
new file mode 100644
index 0000000..0d177c5
--- /dev/null
+++ b/libs/hwui/AnimatorManager.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef ANIMATORMANAGER_H
+#define ANIMATORMANAGER_H
+
+#include <vector>
+
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+#include "TreeInfo.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class BaseRenderNodeAnimator;
+class RenderNode;
+
+// Responsible for managing the animators for a single RenderNode
+class AnimatorManager {
+ PREVENT_COPY_AND_ASSIGN(AnimatorManager);
+public:
+ AnimatorManager(RenderNode& parent);
+ ~AnimatorManager();
+
+ void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
+
+ void pushStaging(TreeInfo& info);
+ // Returns the combined dirty mask of all animators run
+ uint32_t animate(TreeInfo& info);
+
+private:
+ RenderNode& mParent;
+
+ // To improve the efficiency of resizing & removing from the vector
+ // use manual ref counting instead of sp<>.
+ std::vector<BaseRenderNodeAnimator*> mNewAnimators;
+ std::vector<BaseRenderNodeAnimator*> mAnimators;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* ANIMATORMANAGER_H */
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index e8c3d3c..fc86e4f 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -74,12 +74,12 @@ void AssetAtlas::terminate() {
// Entries
///////////////////////////////////////////////////////////////////////////////
-AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const {
+AssetAtlas::Entry* AssetAtlas::getEntry(const SkBitmap* bitmap) const {
ssize_t index = mEntries.indexOfKey(bitmap);
return index >= 0 ? mEntries.valueAt(index) : NULL;
}
-Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const {
+Texture* AssetAtlas::getEntryTexture(const SkBitmap* bitmap) const {
ssize_t index = mEntries.indexOfKey(bitmap);
return index >= 0 ? mEntries.valueAt(index)->texture : NULL;
}
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index 163bdbc..2ec556e 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -160,13 +160,13 @@ public:
* Returns the entry in the atlas associated with the specified
* bitmap. If the bitmap is not in the atlas, return NULL.
*/
- Entry* getEntry(SkBitmap* const bitmap) const;
+ Entry* getEntry(const SkBitmap* bitmap) const;
/**
* Returns the texture for the atlas entry associated with the
* specified bitmap. If the bitmap is not in the atlas, return NULL.
*/
- Texture* getEntryTexture(SkBitmap* const bitmap) const;
+ Texture* getEntryTexture(const SkBitmap* bitmap) const;
/**
* Returns the current generation id of the atlas.
@@ -186,7 +186,7 @@ private:
const bool mBlendKey;
const bool mOpaqueKey;
- KeyedVector<SkBitmap*, Entry*> mEntries;
+ KeyedVector<const SkBitmap*, Entry*> mEntries;
}; // class AssetAtlas
}; // namespace uirenderer
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index b0f4c2c..5689e17 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -23,6 +23,7 @@
#include "DisplayListRenderer.h"
#include "Properties.h"
#include "LayerRenderer.h"
+#include "ShadowTessellator.h"
namespace android {
@@ -55,6 +56,7 @@ Caches::Caches(): Singleton<Caches>(),
initProperties();
initStaticProperties();
initExtensions();
+ initTempProperties();
mDebugLevel = readDebugLevel();
ALOGD("Enabling debug mode %d", mDebugLevel);
@@ -85,7 +87,7 @@ bool Caches::init() {
mRegionMesh = NULL;
mMeshIndices = 0;
-
+ mShadowStripsIndices = 0;
blend = false;
lastSrcMode = GL_ZERO;
lastDstMode = GL_ZERO;
@@ -196,6 +198,7 @@ bool Caches::initProperties() {
drawDeferDisabled = !strcasecmp(property, "true");
INIT_LOGD(" Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
} else {
+ drawDeferDisabled = false;
INIT_LOGD(" Draw defer enabled");
}
@@ -203,6 +206,7 @@ bool Caches::initProperties() {
drawReorderDisabled = !strcasecmp(property, "true");
INIT_LOGD(" Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
} else {
+ drawReorderDisabled = false;
INIT_LOGD(" Draw reorder enabled");
}
@@ -222,6 +226,9 @@ void Caches::terminate() {
mMeshIndices = 0;
mRegionMesh = NULL;
+ glDeleteBuffers(1, &mShadowStripsIndices);
+ mShadowStripsIndices = 0;
+
fboCache.clear();
programCache.clear();
@@ -268,6 +275,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
pathCache.getSize(), pathCache.getMaxSize());
+ log.appendFormat(" TessellationCache %8d / %8d\n",
+ tessellationCache.getSize(), tessellationCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",
@@ -290,6 +299,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
+ total += tessellationCache.getSize();
total += dropShadowCache.getSize();
total += patchCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -310,24 +320,15 @@ void Caches::clearGarbage() {
pathCache.clearGarbage();
patchCache.clearGarbage();
- Vector<DisplayList*> displayLists;
Vector<Layer*> layers;
{ // scope for the lock
Mutex::Autolock _l(mGarbageLock);
- displayLists = mDisplayListGarbage;
layers = mLayerGarbage;
- mDisplayListGarbage.clear();
mLayerGarbage.clear();
}
- size_t count = displayLists.size();
- for (size_t i = 0; i < count; i++) {
- DisplayList* displayList = displayLists.itemAt(i);
- delete displayList;
- }
-
- count = layers.size();
+ size_t count = layers.size();
for (size_t i = 0; i < count; i++) {
Layer* layer = layers.itemAt(i);
delete layer;
@@ -340,11 +341,6 @@ void Caches::deleteLayerDeferred(Layer* layer) {
mLayerGarbage.push(layer);
}
-void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
- Mutex::Autolock _l(mGarbageLock);
- mDisplayListGarbage.push(displayList);
-}
-
void Caches::flush(FlushMode mode) {
FLUSH_LOGD("Flushing caches (mode %d)", mode);
@@ -367,6 +363,7 @@ void Caches::flush(FlushMode mode) {
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
+ tessellationCache.clear();
// fall through
case kFlushMode_Layers:
layerCache.clear();
@@ -403,7 +400,7 @@ bool Caches::unbindMeshBuffer() {
return false;
}
-bool Caches::bindIndicesBuffer(const GLuint buffer) {
+bool Caches::bindIndicesBufferInternal(const GLuint buffer) {
if (mCurrentIndicesBuffer != buffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
mCurrentIndicesBuffer = buffer;
@@ -412,7 +409,7 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) {
return false;
}
-bool Caches::bindIndicesBuffer() {
+bool Caches::bindQuadIndicesBuffer() {
if (!mMeshIndices) {
uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
@@ -427,7 +424,7 @@ bool Caches::bindIndicesBuffer() {
}
glGenBuffers(1, &mMeshIndices);
- bool force = bindIndicesBuffer(mMeshIndices);
+ bool force = bindIndicesBufferInternal(mMeshIndices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
regionIndices, GL_STATIC_DRAW);
@@ -435,7 +432,23 @@ bool Caches::bindIndicesBuffer() {
return force;
}
- return bindIndicesBuffer(mMeshIndices);
+ return bindIndicesBufferInternal(mMeshIndices);
+}
+
+bool Caches::bindShadowIndicesBuffer() {
+ if (!mShadowStripsIndices) {
+ uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT];
+ ShadowTessellator::generateShadowIndices(shadowIndices);
+ glGenBuffers(1, &mShadowStripsIndices);
+ bool force = bindIndicesBufferInternal(mShadowStripsIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
+ shadowIndices, GL_STATIC_DRAW);
+
+ delete[] shadowIndices;
+ return force;
+ }
+
+ return bindIndicesBufferInternal(mShadowStripsIndices);
}
bool Caches::unbindIndicesBuffer() {
@@ -473,7 +486,7 @@ bool Caches::unbindPixelBuffer() {
// Meshes and textures
///////////////////////////////////////////////////////////////////////////////
-void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
+void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
GLuint slot = currentProgram->position;
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
@@ -482,7 +495,7 @@ void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei str
}
}
-void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
+void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
GLuint slot = currentProgram->texCoords;
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
@@ -681,5 +694,48 @@ TextureVertex* Caches::getRegionMesh() {
return mRegionMesh;
}
+///////////////////////////////////////////////////////////////////////////////
+// Temporary Properties
+///////////////////////////////////////////////////////////////////////////////
+
+void Caches::initTempProperties() {
+ propertyLightDiameter = -1.0f;
+ propertyLightPosY = -1.0f;
+ propertyLightPosZ = -1.0f;
+ propertyAmbientRatio = -1.0f;
+ propertyAmbientShadowStrength = -1;
+ propertySpotShadowStrength = -1;
+}
+
+void Caches::setTempProperty(const char* name, const char* value) {
+ ALOGD("setting property %s to %s", name, value);
+ if (!strcmp(name, "ambientRatio")) {
+ propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
+ ALOGD("ambientRatio = %.2f", propertyAmbientRatio);
+ return;
+ } else if (!strcmp(name, "lightDiameter")) {
+ propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightDiameter = %.2f", propertyLightDiameter);
+ return;
+ } else if (!strcmp(name, "lightPosY")) {
+ propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightPos Y = %.2f", propertyLightPosY);
+ return;
+ } else if (!strcmp(name, "lightPosZ")) {
+ propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightPos Z = %.2f", propertyLightPosZ);
+ return;
+ } else if (!strcmp(name, "ambientShadowStrength")) {
+ propertyAmbientShadowStrength = atoi(value);
+ ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength);
+ return;
+ } else if (!strcmp(name, "spotShadowStrength")) {
+ propertySpotShadowStrength = atoi(value);
+ ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength);
+ return;
+ }
+ ALOGD(" failed");
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 544757a..0482430 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,6 +21,8 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <vector>
+
#include <GLES3/gl3.h>
#include <utils/KeyedVector.h>
@@ -33,6 +35,7 @@
#include "thread/TaskManager.h"
#include "AssetAtlas.h"
+#include "Extensions.h"
#include "FontRenderer.h"
#include "GammaFontRenderer.h"
#include "TextureCache.h"
@@ -42,6 +45,7 @@
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
+#include "TessellationCache.h"
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
@@ -62,7 +66,7 @@ namespace uirenderer {
static const uint32_t gMaxNumberOfQuads = 2048;
// Generates simple and textured vertices
-#define FV(x, y, u, v) { { x, y }, { u, v } }
+#define FV(x, y, u, v) { x, y, u, v }
// This array is never used directly but used as a memcpy source in the
// OpenGLRenderer constructor
@@ -102,7 +106,7 @@ struct CacheLogger {
// Caches
///////////////////////////////////////////////////////////////////////////////
-class DisplayList;
+class RenderNode;
class ANDROID_API Caches: public Singleton<Caches> {
Caches();
@@ -166,11 +170,6 @@ public:
*/
void deleteLayerDeferred(Layer* layer);
- /*
- * Can be used to delete a display list from a non EGL thread.
- */
- void deleteDisplayListDeferred(DisplayList* layer);
-
/**
* Binds the VBO used to render simple textured quads.
*/
@@ -190,8 +189,8 @@ public:
* Binds a global indices buffer that can draw up to
* gMaxNumberOfQuads quads.
*/
- bool bindIndicesBuffer();
- bool bindIndicesBuffer(const GLuint buffer);
+ bool bindQuadIndicesBuffer();
+ bool bindShadowIndicesBuffer();
bool unbindIndicesBuffer();
/**
@@ -208,13 +207,13 @@ public:
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
- void bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride);
+ void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
/**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
- void bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride);
+ void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
/**
* Resets the vertex pointers.
@@ -335,6 +334,7 @@ public:
ProgramCache programCache;
PathCache pathCache;
PatchCache patchCache;
+ TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
ResourceCache resourceCache;
@@ -358,6 +358,17 @@ public:
PFNGLLABELOBJECTEXTPROC setLabel;
PFNGLGETOBJECTLABELEXTPROC getLabel;
+ // TEMPORARY properties
+ void initTempProperties();
+ void setTempProperty(const char* name, const char* value);
+
+ float propertyLightDiameter;
+ float propertyLightPosY;
+ float propertyLightPosZ;
+ float propertyAmbientRatio;
+ int propertyAmbientShadowStrength;
+ int propertySpotShadowStrength;
+
private:
enum OverdrawColorSet {
kColorSet_Default = 0,
@@ -369,6 +380,8 @@ private:
void initConstraints();
void initStaticProperties();
+ bool bindIndicesBufferInternal(const GLuint buffer);
+
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
static void endMarkNull() { }
@@ -384,9 +397,9 @@ private:
GLuint mCurrentBuffer;
GLuint mCurrentIndicesBuffer;
GLuint mCurrentPixelBuffer;
- void* mCurrentPositionPointer;
+ const void* mCurrentPositionPointer;
GLsizei mCurrentPositionStride;
- void* mCurrentTexCoordsPointer;
+ const void* mCurrentTexCoordsPointer;
GLsizei mCurrentTexCoordsStride;
bool mTexCoordsArrayEnabled;
@@ -405,10 +418,10 @@ private:
// Global index buffer
GLuint mMeshIndices;
+ GLuint mShadowStripsIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
- Vector<DisplayList*> mDisplayListGarbage;
DebugLevel mDebugLevel;
bool mInitialized;
diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h
new file mode 100644
index 0000000..6074394
--- /dev/null
+++ b/libs/hwui/CanvasProperty.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef CANVASPROPERTY_H
+#define CANVASPROPERTY_H
+
+#include <utils/RefBase.h>
+
+#include "utils/Macros.h"
+
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+class CanvasPropertyPrimitive : public VirtualLightRefBase {
+ PREVENT_COPY_AND_ASSIGN(CanvasPropertyPrimitive);
+public:
+ CanvasPropertyPrimitive(float initialValue) : value(initialValue) {}
+
+ float value;
+};
+
+class CanvasPropertyPaint : public VirtualLightRefBase {
+ PREVENT_COPY_AND_ASSIGN(CanvasPropertyPaint);
+public:
+ CanvasPropertyPaint(const SkPaint& initialValue) : value(initialValue) {}
+
+ SkPaint value;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* CANVASPROPERTY_H */
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
new file mode 100644
index 0000000..054a164
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "DamageAccumulator.h"
+
+#include <cutils/log.h>
+
+#include "RenderNode.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+enum TransformType {
+ TransformInvalid = 0,
+ TransformRenderNode,
+ TransformMatrix4,
+ TransformNone,
+};
+
+struct DirtyStack {
+ TransformType type;
+ union {
+ const RenderNode* renderNode;
+ const Matrix4* matrix4;
+ };
+ // When this frame is pop'd, this rect is mapped through the above transform
+ // and applied to the previous (aka parent) frame
+ SkRect pendingDirty;
+ DirtyStack* prev;
+ DirtyStack* next;
+};
+
+DamageAccumulator::DamageAccumulator() {
+ mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ memset(mHead, 0, sizeof(DirtyStack));
+ // Create a root that we will not pop off
+ mHead->prev = mHead;
+ mHead->type = TransformNone;
+}
+
+static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
+ if (currentFrame->prev != currentFrame) {
+ computeTransformImpl(currentFrame->prev, outMatrix);
+ }
+ switch (currentFrame->type) {
+ case TransformRenderNode:
+ currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+ break;
+ case TransformMatrix4:
+ outMatrix->multiply(*currentFrame->matrix4);
+ break;
+ case TransformNone:
+ // nothing to be done
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
+ }
+}
+
+void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
+ outMatrix->loadIdentity();
+ computeTransformImpl(mHead, outMatrix);
+}
+
+void DamageAccumulator::pushCommon() {
+ if (!mHead->next) {
+ DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ nextFrame->next = 0;
+ nextFrame->prev = mHead;
+ mHead->next = nextFrame;
+ }
+ mHead = mHead->next;
+ mHead->pendingDirty.setEmpty();
+}
+
+void DamageAccumulator::pushTransform(const RenderNode* transform) {
+ pushCommon();
+ mHead->type = TransformRenderNode;
+ mHead->renderNode = transform;
+}
+
+void DamageAccumulator::pushTransform(const Matrix4* transform) {
+ pushCommon();
+ mHead->type = TransformMatrix4;
+ mHead->matrix4 = transform;
+}
+
+void DamageAccumulator::popTransform() {
+ LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
+ DirtyStack* dirtyFrame = mHead;
+ mHead = mHead->prev;
+ switch (dirtyFrame->type) {
+ case TransformRenderNode:
+ applyRenderNodeTransform(dirtyFrame);
+ break;
+ case TransformMatrix4:
+ applyMatrix4Transform(dirtyFrame);
+ break;
+ case TransformNone:
+ mHead->pendingDirty.join(dirtyFrame->pendingDirty);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
+ }
+}
+
+static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ Rect temp(in);
+ matrix->mapRect(temp);
+ out->join(RECT_ARGS(temp));
+}
+
+void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
+ mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
+}
+
+static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ const SkMatrix* transform = props.getTransformMatrix();
+ SkRect temp(in);
+ if (transform && !transform->isIdentity()) {
+ transform->mapRect(&temp);
+ }
+ temp.offset(props.getLeft(), props.getTop());
+ out->join(temp);
+}
+
+static DirtyStack* findParentRenderNode(DirtyStack* frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode) {
+ return frame;
+ }
+ }
+ return NULL;
+}
+
+static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
+ if (frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode
+ && frame->renderNode->hasProjectionReceiver()) {
+ return frame;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
+ SkRect* rect = &frame->pendingDirty;
+ while (frame != end) {
+ if (frame->type == TransformRenderNode) {
+ mapRect(frame->renderNode->properties(), *rect, rect);
+ } else {
+ mapRect(frame->matrix4, *rect, rect);
+ }
+ frame = frame->prev;
+ }
+}
+
+void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
+ if (frame->pendingDirty.isEmpty()) {
+ return;
+ }
+
+ const RenderProperties& props = frame->renderNode->properties();
+ if (props.getAlpha() <= 0) {
+ return;
+ }
+
+ // Perform clipping
+ if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
+ frame->pendingDirty.setEmpty();
+ }
+ }
+
+ // apply all transforms
+ mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
+
+ // project backwards if necessary
+ if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
+ // First, find our parent RenderNode:
+ DirtyStack* parentNode = findParentRenderNode(frame);
+ // Find our parent's projection receiver, which is what we project onto
+ DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
+ if (projectionReceiver) {
+ applyTransforms(frame, projectionReceiver);
+ projectionReceiver->pendingDirty.join(frame->pendingDirty);
+ }
+
+ frame->pendingDirty.setEmpty();
+ }
+}
+
+void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
+ mHead->pendingDirty.join(left, top, right, bottom);
+}
+
+void DamageAccumulator::peekAtDirty(SkRect* dest) {
+ *dest = mHead->pendingDirty;
+}
+
+void DamageAccumulator::finish(SkRect* totalDirty) {
+ LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
+ // Root node never has a transform, so this is the fully mapped dirty rect
+ *totalDirty = mHead->pendingDirty;
+ totalDirty->roundOut();
+ mHead->pendingDirty.setEmpty();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
new file mode 100644
index 0000000..6f0bd8c
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef DAMAGEACCUMULATOR_H
+#define DAMAGEACCUMULATOR_H
+
+#include <cutils/compiler.h>
+#include <utils/LinearAllocator.h>
+
+#include <SkMatrix.h>
+#include <SkRect.h>
+
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+struct DirtyStack;
+class RenderNode;
+class Matrix4;
+
+class DamageAccumulator {
+ PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
+public:
+ DamageAccumulator();
+ // mAllocator will clean everything up for us, no need for a dtor
+
+ // Push a transform node onto the stack. This should be called prior
+ // to any dirty() calls. Subsequent calls to dirty()
+ // will be affected by the transform when popTransform() is called.
+ void pushTransform(const RenderNode* transform);
+ void pushTransform(const Matrix4* transform);
+
+ // Pops a transform node from the stack, propagating the dirty rect
+ // up to the parent node. Returns the IDamageTransform that was just applied
+ void popTransform();
+
+ void dirty(float left, float top, float right, float bottom);
+
+ // Returns the current dirty area, *NOT* transformed by pushed transforms
+ void peekAtDirty(SkRect* dest);
+
+ void computeCurrentTransform(Matrix4* outMatrix) const;
+
+ void finish(SkRect* totalDirty);
+
+private:
+ void pushCommon();
+ void applyMatrix4Transform(DirtyStack* frame);
+ void applyRenderNodeTransform(DirtyStack* frame);
+
+ LinearAllocator mAllocator;
+ DirtyStack* mHead;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DAMAGEACCUMULATOR_H */
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 786f12a..5808aac 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -82,9 +82,15 @@
// Turn on to insert an event marker for each display list op
#define DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 0
+// Turn on to insert detailed event markers
+#define DEBUG_DETAILED_EVENTS 0
+
// Turn on to highlight drawing batches and merged batches with different colors
#define DEBUG_MERGE_BEHAVIOR 0
+// Turn on to enable debugging shadow
+#define DEBUG_SHADOW 0
+
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 7eb7028..a998594 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -28,6 +28,7 @@
#include "DeferredDisplayList.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
+#include "utils/MathUtils.h"
#if DEBUG_DEFER
#define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
@@ -146,10 +147,6 @@ private:
mergeid_t mMergeId;
};
-// compare alphas approximately, with a small margin
-#define NEQ_FALPHA(lhs, rhs) \
- fabs((float)lhs - (float)rhs) > 0.001f
-
class MergingDrawBatch : public DrawBatch {
public:
MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
@@ -190,13 +187,17 @@ public:
// Overlapping other operations is only allowed for text without shadow. For other ops,
// multiDraw isn't guaranteed to overdraw correctly
- if (!isTextBatch || state->mDrawModifiers.mHasShadow) {
+ if (!isTextBatch || op->hasTextShadow()) {
if (intersects(state->mBounds)) return false;
}
const DeferredDisplayState* lhs = state;
const DeferredDisplayState* rhs = mOps[0].state;
- if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false;
+ if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false;
+
+ // Identical round rect clip state means both ops will clip in the same way, or not at all.
+ // As the state objects are const, we can compare their pointers to determine mergeability
+ if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
/* Clipping compatibility check
*
@@ -224,6 +225,16 @@ public:
if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+ if (op->mPaint && mOps[0].op->mPaint &&
+ op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
+ return false;
+ }
+
+ if (op->mPaint && mOps[0].op->mPaint &&
+ op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
+ return false;
+ }
+
/* Draw Modifiers compatibility check
*
* Shadows are ignored, as only text uses them, and in that case they are drawn
@@ -238,8 +249,6 @@ public:
*/
const DrawModifiers& lhsMod = lhs->mDrawModifiers;
const DrawModifiers& rhsMod = rhs->mDrawModifiers;
- if (lhsMod.mShader != rhsMod.mShader) return false;
- if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false;
// Draw filter testing expects bit fields to be clear if filter not set.
if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
@@ -491,7 +500,7 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
/* 1: op calculates local bounds */
DeferredDisplayState* const state = createState();
- if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) {
+ if (op->getLocalBounds(state->mBounds)) {
if (state->mBounds.isEmpty()) {
// valid empty bounds, don't bother deferring
tryRecycleState(state);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 3dcbd0b..8a015b2 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -38,7 +38,6 @@ class SaveLayerOp;
class StateOp;
class DeferredDisplayState;
-class OpenGLRenderer;
class Batch;
class DrawBatch;
@@ -64,6 +63,7 @@ public:
mat4 mMatrix;
DrawModifiers mDrawModifiers;
float mAlpha;
+ const RoundRectClipState* mRoundRectClipState;
};
class OpStatePair {
@@ -79,13 +79,13 @@ public:
};
class DeferredDisplayList {
+ friend class DeferStateStruct; // used to give access to allocator
public:
DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
clear();
}
~DeferredDisplayList() { clear(); }
- void reset(const Rect& bounds) { mBounds.set(bounds); }
enum OpBatchId {
kOpBatch_None = 0, // Don't batch
@@ -120,6 +120,8 @@ public:
void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);
private:
+ DeferredDisplayList(const DeferredDisplayList& other); // disallow copy
+
DeferredDisplayState* createState() {
return new (mAllocator) DeferredDisplayState();
}
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
new file mode 100644
index 0000000..836de45
--- /dev/null
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "DeferredLayerUpdater.h"
+
+#include "OpenGLRenderer.h"
+
+#include "LayerRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+static void defaultLayerDestroyer(Layer* layer) {
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
+}
+
+DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
+ : mSurfaceTexture(0)
+ , mTransform(0)
+ , mNeedsGLContextAttach(false)
+ , mUpdateTexImage(false)
+ , mLayer(layer)
+ , mCaches(Caches::getInstance())
+ , mDestroyer(destroyer) {
+ mWidth = mLayer->layer.getWidth();
+ mHeight = mLayer->layer.getHeight();
+ mBlend = mLayer->isBlend();
+ mColorFilter = SkSafeRef(mLayer->getColorFilter());
+ mAlpha = mLayer->getAlpha();
+ mMode = mLayer->getMode();
+
+ if (!mDestroyer) {
+ mDestroyer = defaultLayerDestroyer;
+ }
+}
+
+DeferredLayerUpdater::~DeferredLayerUpdater() {
+ SkSafeUnref(mColorFilter);
+ setTransform(0);
+ mDestroyer(mLayer);
+}
+
+void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+ SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : NULL;
+ SkRefCnt_SafeAssign(mColorFilter, colorFilter);
+}
+
+bool DeferredLayerUpdater::apply() {
+ bool success = true;
+ // These properties are applied the same to both layer types
+ mLayer->setColorFilter(mColorFilter);
+ mLayer->setAlpha(mAlpha, mMode);
+
+ if (mSurfaceTexture.get()) {
+ if (mNeedsGLContextAttach) {
+ mNeedsGLContextAttach = false;
+ mSurfaceTexture->attachToContext(mLayer->getTexture());
+ }
+ if (mUpdateTexImage) {
+ mUpdateTexImage = false;
+ doUpdateTexImage();
+ }
+ if (mTransform) {
+ mLayer->getTransform().load(*mTransform);
+ setTransform(0);
+ }
+ }
+ return success;
+}
+
+void DeferredLayerUpdater::doUpdateTexImage() {
+ if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
+ float transform[16];
+
+ int64_t frameNumber = mSurfaceTexture->getFrameNumber();
+ // If the GLConsumer queue is in synchronous mode, need to discard all
+ // but latest frame, using the frame number to tell when we no longer
+ // have newer frames to target. Since we can't tell which mode it is in,
+ // do this unconditionally.
+ int dropCounter = 0;
+ while (mSurfaceTexture->updateTexImage() == NO_ERROR) {
+ int64_t newFrameNumber = mSurfaceTexture->getFrameNumber();
+ if (newFrameNumber == frameNumber) break;
+ frameNumber = newFrameNumber;
+ dropCounter++;
+ }
+
+ bool forceFilter = false;
+ sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer();
+ if (buffer != NULL) {
+ // force filtration if buffer size != layer size
+ forceFilter = mWidth != buffer->getWidth()
+ || mHeight != buffer->getHeight();
+ }
+
+ #if DEBUG_RENDERER
+ if (dropCounter > 0) {
+ RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter);
+ }
+ #endif
+ mSurfaceTexture->getTransformMatrix(transform);
+ GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
+
+ LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight,
+ !mBlend, forceFilter, renderTarget, transform);
+ }
+}
+
+void DeferredLayerUpdater::detachSurfaceTexture() {
+ if (mSurfaceTexture.get()) {
+ mSurfaceTexture->detachFromContext();
+ mSurfaceTexture = 0;
+ mLayer->clearTexture();
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
new file mode 100644
index 0000000..c838c32
--- /dev/null
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef DEFERREDLAYERUPDATE_H_
+#define DEFERREDLAYERUPDATE_H_
+
+#include <cutils/compiler.h>
+#include <gui/GLConsumer.h>
+#include <SkColorFilter.h>
+#include <SkMatrix.h>
+#include <utils/StrongPointer.h>
+
+#include "Layer.h"
+#include "Rect.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+typedef void (*LayerDestroyer)(Layer* layer);
+
+// Container to hold the properties a layer should be set to at the start
+// of a render pass
+class DeferredLayerUpdater : public VirtualLightRefBase {
+public:
+ // Note that DeferredLayerUpdater assumes it is taking ownership of the layer
+ // and will not call incrementRef on it as a result.
+ ANDROID_API DeferredLayerUpdater(Layer* layer, LayerDestroyer = 0);
+ ANDROID_API ~DeferredLayerUpdater();
+
+ ANDROID_API bool setSize(uint32_t width, uint32_t height) {
+ if (mWidth != width || mHeight != height) {
+ mWidth = width;
+ mHeight = height;
+ return true;
+ }
+ return false;
+ }
+
+ ANDROID_API bool setBlend(bool blend) {
+ if (blend != mBlend) {
+ mBlend = blend;
+ return true;
+ }
+ return false;
+ }
+
+ ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture, bool needsAttach) {
+ if (texture.get() != mSurfaceTexture.get()) {
+ mNeedsGLContextAttach = needsAttach;
+ mSurfaceTexture = texture;
+ }
+ }
+
+ ANDROID_API void updateTexImage() {
+ mUpdateTexImage = true;
+ }
+
+ ANDROID_API void setTransform(const SkMatrix* matrix) {
+ delete mTransform;
+ mTransform = matrix ? new SkMatrix(*matrix) : 0;
+ }
+
+ ANDROID_API void setPaint(const SkPaint* paint);
+
+ ANDROID_API bool apply();
+
+ ANDROID_API Layer* backingLayer() {
+ return mLayer;
+ }
+
+ ANDROID_API void detachSurfaceTexture();
+
+private:
+ // Generic properties
+ uint32_t mWidth;
+ uint32_t mHeight;
+ bool mBlend;
+ SkColorFilter* mColorFilter;
+ int mAlpha;
+ SkXfermode::Mode mMode;
+
+ sp<GLConsumer> mSurfaceTexture;
+ SkMatrix* mTransform;
+ bool mNeedsGLContextAttach;
+ bool mUpdateTexImage;
+
+ Layer* mLayer;
+ Caches& mCaches;
+
+ LayerDestroyer mDestroyer;
+
+ void doUpdateTexImage();
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DEFERREDLAYERUPDATE_H_ */
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index bb6526e..6461ee7 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,7 +14,12 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
#include "Debug.h"
#include "DisplayList.h"
@@ -24,533 +29,71 @@
namespace android {
namespace uirenderer {
-void DisplayList::outputLogBuffer(int fd) {
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- if (logBuffer.isEmpty()) {
- return;
- }
-
- FILE *file = fdopen(fd, "a");
-
- fprintf(file, "\nRecent DisplayList operations\n");
- logBuffer.outputCommands(file);
-
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s", cachesLog.string());
- fprintf(file, "\n");
-
- fflush(file);
-}
-
-DisplayList::DisplayList(const DisplayListRenderer& recorder) :
- mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
- mStaticMatrix(NULL), mAnimationMatrix(NULL) {
-
- initFromDisplayListRenderer(recorder);
-}
-
-DisplayList::~DisplayList() {
- mDestroyed = true;
- clearResources();
-}
-
-void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
- if (displayList) {
- DISPLAY_LIST_LOGD("Deferring display list destruction");
- Caches::getInstance().deleteDisplayListDeferred(displayList);
- }
-}
-
-void DisplayList::clearResources() {
- mDisplayListData = NULL;
-
- mClipRectOp = NULL;
- mSaveLayerOp = NULL;
- mSaveOp = NULL;
- mRestoreToCountOp = NULL;
-
- delete mTransformMatrix;
- delete mTransformCamera;
- delete mTransformMatrix3D;
- delete mStaticMatrix;
- delete mAnimationMatrix;
-
- mTransformMatrix = NULL;
- mTransformCamera = NULL;
- mTransformMatrix3D = NULL;
- mStaticMatrix = NULL;
- mAnimationMatrix = NULL;
-
- Caches& caches = Caches::getInstance();
- caches.unregisterFunctors(mFunctorCount);
- caches.resourceCache.lock();
-
- for (size_t i = 0; i < mBitmapResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
- SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
- caches.resourceCache.decrementRefcountLocked(bitmap);
- caches.resourceCache.destructorLocked(bitmap);
- }
-
- for (size_t i = 0; i < mFilterResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mPatchResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mShaders.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
- caches.resourceCache.destructorLocked(mShaders.itemAt(i));
- }
-
- for (size_t i = 0; i < mSourcePaths.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
- }
-
- for (size_t i = 0; i < mLayers.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
- }
-
- caches.resourceCache.unlock();
-
- for (size_t i = 0; i < mPaints.size(); i++) {
- delete mPaints.itemAt(i);
- }
-
- for (size_t i = 0; i < mRegions.size(); i++) {
- delete mRegions.itemAt(i);
- }
-
- for (size_t i = 0; i < mPaths.size(); i++) {
- delete mPaths.itemAt(i);
- }
-
- for (size_t i = 0; i < mMatrices.size(); i++) {
- delete mMatrices.itemAt(i);
- }
-
- mBitmapResources.clear();
- mOwnedBitmapResources.clear();
- mFilterResources.clear();
- mPatchResources.clear();
- mShaders.clear();
- mSourcePaths.clear();
- mPaints.clear();
- mRegions.clear();
- mPaths.clear();
- mMatrices.clear();
- mLayers.clear();
+DisplayListData::DisplayListData()
+ : projectionReceiveIndex(-1)
+ , hasDrawOps(false) {
}
-void DisplayList::reset() {
- clearResources();
- init();
+DisplayListData::~DisplayListData() {
+ cleanupResources();
}
-void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
- if (reusing) {
- // re-using display list - clear out previous allocations
- clearResources();
- }
-
- init();
-
- mDisplayListData = recorder.getDisplayListData();
- mSize = mDisplayListData->allocator.usedSize();
-
- if (mSize == 0) {
- return;
- }
-
- // allocate reusable ops for state-deferral
- LinearAllocator& alloc = mDisplayListData->allocator;
- mClipRectOp = new (alloc) ClipRectOp();
- mSaveLayerOp = new (alloc) SaveLayerOp();
- mSaveOp = new (alloc) SaveOp();
- mRestoreToCountOp = new (alloc) RestoreToCountOp();
- if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging
- ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize);
- CRASH();
- }
-
- mFunctorCount = recorder.getFunctorCount();
-
+void DisplayListData::cleanupResources() {
Caches& caches = Caches::getInstance();
- caches.registerFunctors(mFunctorCount);
+ caches.unregisterFunctors(functors.size());
caches.resourceCache.lock();
- const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
for (size_t i = 0; i < bitmapResources.size(); i++) {
- SkBitmap* resource = bitmapResources.itemAt(i);
- mBitmapResources.add(resource);
- caches.resourceCache.incrementRefcountLocked(resource);
+ caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
}
- const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
- SkBitmap* resource = ownedBitmapResources.itemAt(i);
- mOwnedBitmapResources.add(resource);
- caches.resourceCache.incrementRefcountLocked(resource);
- }
-
- const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
- for (size_t i = 0; i < filterResources.size(); i++) {
- SkiaColorFilter* resource = filterResources.itemAt(i);
- mFilterResources.add(resource);
- caches.resourceCache.incrementRefcountLocked(resource);
+ const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
+ caches.resourceCache.decrementRefcountLocked(bitmap);
+ caches.resourceCache.destructorLocked(bitmap);
}
- const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources();
for (size_t i = 0; i < patchResources.size(); i++) {
- Res_png_9patch* resource = patchResources.itemAt(i);
- mPatchResources.add(resource);
- caches.resourceCache.incrementRefcountLocked(resource);
- }
-
- const Vector<SkiaShader*>& shaders = recorder.getShaders();
- for (size_t i = 0; i < shaders.size(); i++) {
- SkiaShader* resource = shaders.itemAt(i);
- mShaders.add(resource);
- caches.resourceCache.incrementRefcountLocked(resource);
+ caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
- const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
for (size_t i = 0; i < sourcePaths.size(); i++) {
- mSourcePaths.add(sourcePaths.itemAt(i));
- caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
+ caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
}
- const Vector<Layer*>& layers = recorder.getLayers();
for (size_t i = 0; i < layers.size(); i++) {
- mLayers.add(layers.itemAt(i));
- caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
+ caches.resourceCache.decrementRefcountLocked(layers.itemAt(i));
}
caches.resourceCache.unlock();
- mPaints.appendVector(recorder.getPaints());
- mRegions.appendVector(recorder.getRegions());
- mPaths.appendVector(recorder.getPaths());
- mMatrices.appendVector(recorder.getMatrices());
-}
-
-void DisplayList::init() {
- mSize = 0;
- mIsRenderable = true;
- mFunctorCount = 0;
- mLeft = 0;
- mTop = 0;
- mRight = 0;
- mBottom = 0;
- mClipToBounds = true;
- mAlpha = 1;
- mHasOverlappingRendering = true;
- mTranslationX = 0;
- mTranslationY = 0;
- mRotation = 0;
- mRotationX = 0;
- mRotationY= 0;
- mScaleX = 1;
- mScaleY = 1;
- mPivotX = 0;
- mPivotY = 0;
- mCameraDistance = 0;
- mMatrixDirty = false;
- mMatrixFlags = 0;
- mPrevWidth = -1;
- mPrevHeight = -1;
- mWidth = 0;
- mHeight = 0;
- mPivotExplicitlySet = false;
- mCaching = false;
-}
-
-size_t DisplayList::getSize() {
- return mSize;
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void DisplayList::output(uint32_t level) {
- ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
- mName.string(), isRenderable());
- ALOGD("%*s%s %d", level * 2, "", "Save",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-
- outputViewProperties(level);
- int flags = DisplayListOp::kOpLogFlag_Recurse;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- mDisplayListData->displayListOps[i]->output(level, flags);
+ for (size_t i = 0; i < paints.size(); i++) {
+ delete paints.itemAt(i);
}
- ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
-}
-
-float DisplayList::getPivotX() {
- updateMatrix();
- return mPivotX;
-}
-
-float DisplayList::getPivotY() {
- updateMatrix();
- return mPivotY;
-}
-
-void DisplayList::updateMatrix() {
- if (mMatrixDirty) {
- if (!mTransformMatrix) {
- mTransformMatrix = new SkMatrix();
- }
- if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) {
- mTransformMatrix->reset();
- } else {
- if (!mPivotExplicitlySet) {
- if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
- mPrevWidth = mWidth;
- mPrevHeight = mHeight;
- mPivotX = mPrevWidth / 2.0f;
- mPivotY = mPrevHeight / 2.0f;
- }
- }
- if ((mMatrixFlags & ROTATION_3D) == 0) {
- mTransformMatrix->setTranslate(mTranslationX, mTranslationY);
- mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY);
- mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
- } else {
- if (!mTransformCamera) {
- mTransformCamera = new Sk3DView();
- mTransformMatrix3D = new SkMatrix();
- }
- mTransformMatrix->reset();
- mTransformCamera->save();
- mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
- mTransformCamera->rotateX(mRotationX);
- mTransformCamera->rotateY(mRotationY);
- mTransformCamera->rotateZ(-mRotation);
- mTransformCamera->getMatrix(mTransformMatrix3D);
- mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
- mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
- mPivotY + mTranslationY);
- mTransformMatrix->postConcat(*mTransformMatrix3D);
- mTransformCamera->restore();
- }
- }
- mMatrixDirty = false;
+ for (size_t i = 0; i < regions.size(); i++) {
+ delete regions.itemAt(i);
}
-}
-void DisplayList::outputViewProperties(const int level) {
- updateMatrix();
- if (mLeft != 0 || mTop != 0) {
- ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
- }
- if (mStaticMatrix) {
- ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
- level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
- }
- if (mAnimationMatrix) {
- ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
- level * 2, "", mAnimationMatrix, MATRIX_ARGS(mAnimationMatrix));
- }
- if (mMatrixFlags != 0) {
- if (mMatrixFlags == TRANSLATION) {
- ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
- } else {
- ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
- level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
- }
+ for (size_t i = 0; i < paths.size(); i++) {
+ delete paths.itemAt(i);
}
- bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
- if (mAlpha < 1) {
- if (mCaching) {
- ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
- } else if (!mHasOverlappingRendering) {
- ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
- } else {
- int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- flags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by save layer
- }
- ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
- (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
- (int)(mAlpha * 255), flags);
- }
- }
- if (clipToBoundsNeeded) {
- ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
- (float) mRight - mLeft, (float) mBottom - mTop);
- }
+ bitmapResources.clear();
+ ownedBitmapResources.clear();
+ patchResources.clear();
+ sourcePaths.clear();
+ paints.clear();
+ regions.clear();
+ paths.clear();
+ layers.clear();
}
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + mCount)
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
- const int level) {
-#if DEBUG_DISPLAY_LIST
- outputViewProperties(level);
-#endif
- updateMatrix();
- if (mLeft != 0 || mTop != 0) {
- renderer.translate(mLeft, mTop);
- }
- if (mStaticMatrix) {
- renderer.concatMatrix(mStaticMatrix);
- } else if (mAnimationMatrix) {
- renderer.concatMatrix(mAnimationMatrix);
- }
- if (mMatrixFlags != 0) {
- if (mMatrixFlags == TRANSLATION) {
- renderer.translate(mTranslationX, mTranslationY);
- } else {
- renderer.concatMatrix(mTransformMatrix);
- }
- }
- bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
- if (mAlpha < 1) {
- if (mCaching) {
- renderer.setOverrideLayerAlpha(mAlpha);
- } else if (!mHasOverlappingRendering) {
- renderer.scaleAlpha(mAlpha);
- } else {
- // TODO: should be able to store the size of a DL at record time and not
- // have to pass it into this call. In fact, this information might be in the
- // location/size info that we store with the new native transform data.
- int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by saveLayer
- }
- handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
- mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT,
- mClipToBounds);
- }
- }
- if (clipToBoundsNeeded) {
- handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
- PROPERTY_SAVECOUNT, mClipToBounds);
- }
-}
-
-class DeferOperationHandler {
-public:
- DeferOperationHandler(DeferStateStruct& deferStruct, int level)
- : mDeferStruct(deferStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
- operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
- }
-private:
- DeferStateStruct& mDeferStruct;
- const int mLevel;
-};
-
-void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
- DeferOperationHandler handler(deferStruct, level);
- iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
-}
-
-class ReplayOperationHandler {
-public:
- ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
- : mReplayStruct(replayStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
- operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
- }
-private:
- ReplayStateStruct& mReplayStruct;
- const int mLevel;
-};
-
-void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
- ReplayOperationHandler handler(replayStruct, level);
-
- replayStruct.mRenderer.startMark(mName.string());
- iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
- replayStruct.mRenderer.endMark();
-
- DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
- replayStruct.mDrawGlStatus);
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
- if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
- ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize);
- CRASH();
- }
- if (mSize == 0 || mAlpha <= 0) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
- return;
- }
-
-#if DEBUG_DISPLAY_LIST
- Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
- level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
- clipRect->right, clipRect->bottom);
-#endif
-
- int restoreTo = renderer.getSaveCount();
- handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
- PROPERTY_SAVECOUNT, mClipToBounds);
-
- DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-
- setViewProperties<T>(renderer, handler, level + 1);
-
- if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
- DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
- handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
- renderer.restoreToCount(restoreTo);
- renderer.setOverrideLayerAlpha(1.0f);
- return;
- }
-
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- int saveCount = renderer.getSaveCount() - 1;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- DisplayListOp *op = mDisplayListData->displayListOps[i];
-
-#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
-#endif
-
- logBuffer.writeCommand(level, op->name());
- handler(op, saveCount, mClipToBounds);
- }
+void DisplayListData::addChild(DrawRenderNodeOp* op) {
+ LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!");
- DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
- handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
- renderer.restoreToCount(restoreTo);
- renderer.setOverrideLayerAlpha(1.0f);
+ mChildren.push(op);
+ mReferenceHolders.push(op->renderNode());
}
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 1cd5f1c..acfa98e 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -26,6 +26,7 @@
#include <private/hwui/DrawGlInfo.h>
+#include <utils/KeyedVector.h>
#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
@@ -37,12 +38,9 @@
#include <androidfw/ResourceTypes.h>
#include "Debug.h"
-
-#define TRANSLATION 0x0001
-#define ROTATION 0x0002
-#define ROTATION_3D 0x0004
-#define SCALE 0x0008
-#define PIVOT 0x0010
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "RenderProperties.h"
class SkBitmap;
class SkPaint;
@@ -58,492 +56,113 @@ class DisplayListRenderer;
class OpenGLRenderer;
class Rect;
class Layer;
-class SkiaColorFilter;
-class SkiaShader;
class ClipRectOp;
class SaveLayerOp;
class SaveOp;
class RestoreToCountOp;
-
-struct DeferStateStruct {
- DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags)
- : mDeferredList(deferredList), mRenderer(renderer), mReplayFlags(replayFlags) {}
- DeferredDisplayList& mDeferredList;
- OpenGLRenderer& mRenderer;
- const int mReplayFlags;
-};
-
-struct ReplayStateStruct {
- ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags)
- : mRenderer(renderer), mDirty(dirty), mReplayFlags(replayFlags),
- mDrawGlStatus(DrawGlInfo::kStatusDone) {}
- OpenGLRenderer& mRenderer;
- Rect& mDirty;
- const int mReplayFlags;
- status_t mDrawGlStatus;
-};
+class DrawRenderNodeOp;
/**
- * Refcounted structure that holds data used in display list stream
+ * Holds data used in the playback a tree of DisplayLists.
*/
-class DisplayListData : public LightRefBase<DisplayListData> {
-public:
- LinearAllocator allocator;
- Vector<DisplayListOp*> displayListOps;
-};
+class PlaybackStateStruct {
+protected:
+ PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator)
+ : mRenderer(renderer)
+ , mReplayFlags(replayFlags)
+ , mAllocator(allocator) {}
-/**
- * Replays recorded drawing commands.
- */
-class DisplayList {
public:
- DisplayList(const DisplayListRenderer& recorder);
- ANDROID_API ~DisplayList();
-
- // See flags defined in DisplayList.java
- enum ReplayFlag {
- kReplayFlag_ClipChildren = 0x1
- };
-
-
- ANDROID_API size_t getSize();
- ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
- ANDROID_API static void outputLogBuffer(int fd);
-
- void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
-
- void defer(DeferStateStruct& deferStruct, const int level);
- void replay(ReplayStateStruct& replayStruct, const int level);
-
- void output(uint32_t level = 0);
-
- ANDROID_API void reset();
-
- void setRenderable(bool renderable) {
- mIsRenderable = renderable;
- }
-
- bool isRenderable() const {
- return mIsRenderable;
- }
-
- void setName(const char* name) {
- if (name) {
- char* lastPeriod = strrchr(name, '.');
- if (lastPeriod) {
- mName.setTo(lastPeriod + 1);
- } else {
- mName.setTo(name);
- }
- }
- }
-
- const char* getName() const {
- return mName.string();
- }
-
- void setClipToBounds(bool clipToBounds) {
- mClipToBounds = clipToBounds;
- }
-
- void setStaticMatrix(SkMatrix* matrix) {
- delete mStaticMatrix;
- mStaticMatrix = new SkMatrix(*matrix);
- }
-
- // Can return NULL
- SkMatrix* getStaticMatrix() {
- return mStaticMatrix;
- }
-
- void setAnimationMatrix(SkMatrix* matrix) {
- delete mAnimationMatrix;
- if (matrix) {
- mAnimationMatrix = new SkMatrix(*matrix);
- } else {
- mAnimationMatrix = NULL;
- }
- }
-
- void setAlpha(float alpha) {
- alpha = fminf(1.0f, fmaxf(0.0f, alpha));
- if (alpha != mAlpha) {
- mAlpha = alpha;
- }
- }
-
- float getAlpha() const {
- return mAlpha;
- }
-
- void setHasOverlappingRendering(bool hasOverlappingRendering) {
- mHasOverlappingRendering = hasOverlappingRendering;
- }
-
- bool hasOverlappingRendering() const {
- return mHasOverlappingRendering;
- }
-
- void setTranslationX(float translationX) {
- if (translationX != mTranslationX) {
- mTranslationX = translationX;
- mMatrixDirty = true;
- if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
- mMatrixFlags &= ~TRANSLATION;
- } else {
- mMatrixFlags |= TRANSLATION;
- }
- }
- }
-
- float getTranslationX() const {
- return mTranslationX;
- }
-
- void setTranslationY(float translationY) {
- if (translationY != mTranslationY) {
- mTranslationY = translationY;
- mMatrixDirty = true;
- if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
- mMatrixFlags &= ~TRANSLATION;
- } else {
- mMatrixFlags |= TRANSLATION;
- }
- }
- }
-
- float getTranslationY() const {
- return mTranslationY;
- }
-
- void setRotation(float rotation) {
- if (rotation != mRotation) {
- mRotation = rotation;
- mMatrixDirty = true;
- if (mRotation == 0.0f) {
- mMatrixFlags &= ~ROTATION;
- } else {
- mMatrixFlags |= ROTATION;
- }
- }
- }
-
- float getRotation() const {
- return mRotation;
- }
-
- void setRotationX(float rotationX) {
- if (rotationX != mRotationX) {
- mRotationX = rotationX;
- mMatrixDirty = true;
- if (mRotationX == 0.0f && mRotationY == 0.0f) {
- mMatrixFlags &= ~ROTATION_3D;
- } else {
- mMatrixFlags |= ROTATION_3D;
- }
- }
- }
-
- float getRotationX() const {
- return mRotationX;
- }
-
- void setRotationY(float rotationY) {
- if (rotationY != mRotationY) {
- mRotationY = rotationY;
- mMatrixDirty = true;
- if (mRotationX == 0.0f && mRotationY == 0.0f) {
- mMatrixFlags &= ~ROTATION_3D;
- } else {
- mMatrixFlags |= ROTATION_3D;
- }
- }
- }
-
- float getRotationY() const {
- return mRotationY;
- }
-
- void setScaleX(float scaleX) {
- if (scaleX != mScaleX) {
- mScaleX = scaleX;
- mMatrixDirty = true;
- if (mScaleX == 1.0f && mScaleY == 1.0f) {
- mMatrixFlags &= ~SCALE;
- } else {
- mMatrixFlags |= SCALE;
- }
- }
- }
-
- float getScaleX() const {
- return mScaleX;
- }
-
- void setScaleY(float scaleY) {
- if (scaleY != mScaleY) {
- mScaleY = scaleY;
- mMatrixDirty = true;
- if (mScaleX == 1.0f && mScaleY == 1.0f) {
- mMatrixFlags &= ~SCALE;
- } else {
- mMatrixFlags |= SCALE;
- }
- }
- }
-
- float getScaleY() const {
- return mScaleY;
- }
+ OpenGLRenderer& mRenderer;
+ const int mReplayFlags;
- void setPivotX(float pivotX) {
- mPivotX = pivotX;
- mMatrixDirty = true;
- if (mPivotX == 0.0f && mPivotY == 0.0f) {
- mMatrixFlags &= ~PIVOT;
- } else {
- mMatrixFlags |= PIVOT;
- }
- mPivotExplicitlySet = true;
- }
+ // Allocator with the lifetime of a single frame.
+ // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator
+ LinearAllocator * const mAllocator;
- ANDROID_API float getPivotX();
-
- void setPivotY(float pivotY) {
- mPivotY = pivotY;
- mMatrixDirty = true;
- if (mPivotX == 0.0f && mPivotY == 0.0f) {
- mMatrixFlags &= ~PIVOT;
- } else {
- mMatrixFlags |= PIVOT;
- }
- mPivotExplicitlySet = true;
+ SkPath* allocPathForFrame() {
+ mTempPaths.push_back();
+ return &mTempPaths.back();
}
- ANDROID_API float getPivotY();
-
- void setCameraDistance(float distance) {
- if (distance != mCameraDistance) {
- mCameraDistance = distance;
- mMatrixDirty = true;
- if (!mTransformCamera) {
- mTransformCamera = new Sk3DView();
- mTransformMatrix3D = new SkMatrix();
- }
- mTransformCamera->setCameraLocation(0, 0, distance);
- }
- }
+private:
+ // Paths kept alive for the duration of the frame
+ std::vector<SkPath> mTempPaths;
+};
- float getCameraDistance() const {
- return mCameraDistance;
- }
+class DeferStateStruct : public PlaybackStateStruct {
+public:
+ DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags)
+ : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)),
+ mDeferredList(deferredList) {}
- void setLeft(int left) {
- if (left != mLeft) {
- mLeft = left;
- mWidth = mRight - mLeft;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ DeferredDisplayList& mDeferredList;
+};
- float getLeft() const {
- return mLeft;
- }
+class ReplayStateStruct : public PlaybackStateStruct {
+public:
+ ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags)
+ : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator),
+ mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {}
- void setTop(int top) {
- if (top != mTop) {
- mTop = top;
- mHeight = mBottom - mTop;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ Rect& mDirty;
+ status_t mDrawGlStatus;
+ LinearAllocator mReplayAllocator;
+};
- float getTop() const {
- return mTop;
- }
+/**
+ * Data structure that holds the list of commands used in display list stream
+ */
+class DisplayListData {
+public:
+ DisplayListData();
+ ~DisplayListData();
- void setRight(int right) {
- if (right != mRight) {
- mRight = right;
- mWidth = mRight - mLeft;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ // allocator into which all ops were allocated
+ LinearAllocator allocator;
- float getRight() const {
- return mRight;
- }
+ // pointers to all ops within display list, pointing into allocator data
+ Vector<DisplayListOp*> displayListOps;
- void setBottom(int bottom) {
- if (bottom != mBottom) {
- mBottom = bottom;
- mHeight = mBottom - mTop;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ // index of DisplayListOp restore, after which projected descendents should be drawn
+ int projectionReceiveIndex;
- float getBottom() const {
- return mBottom;
- }
+ Vector<const SkBitmap*> bitmapResources;
+ Vector<const SkBitmap*> ownedBitmapResources;
+ Vector<const Res_png_9patch*> patchResources;
- void setLeftTop(int left, int top) {
- if (left != mLeft || top != mTop) {
- mLeft = left;
- mTop = top;
- mWidth = mRight - mLeft;
- mHeight = mBottom - mTop;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ Vector<const SkPaint*> paints;
+ Vector<const SkPath*> paths;
+ SortedVector<const SkPath*> sourcePaths;
+ Vector<const SkRegion*> regions;
+ Vector<Layer*> layers;
+ Vector<Functor*> functors;
+ bool hasDrawOps;
- void setLeftTopRightBottom(int left, int top, int right, int bottom) {
- if (left != mLeft || top != mTop || right != mRight || bottom != mBottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mWidth = mRight - mLeft;
- mHeight = mBottom - mTop;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
+ bool isEmpty() {
+ return !displayListOps.size();
}
- void offsetLeftRight(float offset) {
- if (offset != 0) {
- mLeft += offset;
- mRight += offset;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
- }
+ void addChild(DrawRenderNodeOp* childOp);
+ const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
- void offsetTopBottom(float offset) {
- if (offset != 0) {
- mTop += offset;
- mBottom += offset;
- if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
- mMatrixDirty = true;
- }
- }
+ void refProperty(CanvasPropertyPrimitive* prop) {
+ mReferenceHolders.push(prop);
}
- void setCaching(bool caching) {
- mCaching = caching;
+ void refProperty(CanvasPropertyPaint* prop) {
+ mReferenceHolders.push(prop);
}
- int getWidth() {
- return mWidth;
- }
+private:
+ Vector< sp<VirtualLightRefBase> > mReferenceHolders;
- int getHeight() {
- return mHeight;
- }
+ // list of children display lists for quick, non-drawing traversal
+ Vector<DrawRenderNodeOp*> mChildren;
-private:
- void outputViewProperties(const int level);
-
- template <class T>
- inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
-
- template <class T>
- inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
-
- void init();
-
- void clearResources();
-
- void updateMatrix();
-
- class TextContainer {
- public:
- size_t length() const {
- return mByteLength;
- }
-
- const char* text() const {
- return (const char*) mText;
- }
-
- size_t mByteLength;
- const char* mText;
- };
-
- Vector<SkBitmap*> mBitmapResources;
- Vector<SkBitmap*> mOwnedBitmapResources;
- Vector<SkiaColorFilter*> mFilterResources;
- Vector<Res_png_9patch*> mPatchResources;
-
- Vector<SkPaint*> mPaints;
- Vector<SkPath*> mPaths;
- SortedVector<SkPath*> mSourcePaths;
- Vector<SkRegion*> mRegions;
- Vector<SkMatrix*> mMatrices;
- Vector<SkiaShader*> mShaders;
- Vector<Layer*> mLayers;
-
- sp<DisplayListData> mDisplayListData;
-
- size_t mSize;
-
- bool mIsRenderable;
- uint32_t mFunctorCount;
-
- String8 mName;
- bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
-
- // View properties
- bool mClipToBounds;
- float mAlpha;
- bool mHasOverlappingRendering;
- float mTranslationX, mTranslationY;
- float mRotation, mRotationX, mRotationY;
- float mScaleX, mScaleY;
- float mPivotX, mPivotY;
- float mCameraDistance;
- int mLeft, mTop, mRight, mBottom;
- int mWidth, mHeight;
- int mPrevWidth, mPrevHeight;
- bool mPivotExplicitlySet;
- bool mMatrixDirty;
- bool mMatrixIsIdentity;
- uint32_t mMatrixFlags;
- SkMatrix* mTransformMatrix;
- Sk3DView* mTransformCamera;
- SkMatrix* mTransformMatrix3D;
- SkMatrix* mStaticMatrix;
- SkMatrix* mAnimationMatrix;
- bool mCaching;
-
- /**
- * State operations - needed to defer displayList property operations (for example, when setting
- * an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's
- * allocation, or null if uninitialized.
- *
- * These are initialized (via friend re-constructors) when a displayList is issued in either
- * replay or deferred mode. If replaying, the ops are not used until the next frame. If
- * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time.
- *
- * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data
- */
- ClipRectOp* mClipRectOp;
- SaveLayerOp* mSaveLayerOp;
- SaveOp* mSaveOp;
- RestoreToCountOp* mRestoreToCountOp;
-}; // class DisplayList
+ void cleanupResources();
+};
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 842e028..c6d3db7 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -21,6 +21,9 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <SkColor.h>
+#include <SkPath.h>
+#include <SkPathOps.h>
#include <SkXfermode.h>
#include <private/hwui/DrawGlInfo.h>
@@ -111,7 +114,7 @@ public:
class DrawOp : public DisplayListOp {
friend class MergingDrawBatch;
public:
- DrawOp(SkPaint* paint)
+ DrawOp(const SkPaint* paint)
: mPaint(paint), mQuickRejected(false) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
@@ -169,7 +172,7 @@ public:
*
* returns true if bounds exist
*/
- virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) {
return false;
}
@@ -181,14 +184,21 @@ public:
return OpenGLRenderer::getAlphaDirect(mPaint);
}
+ virtual bool hasTextShadow() const {
+ return false;
+ }
+
inline float strokeWidthOutset() {
- float width = mPaint->getStrokeWidth();
- if (width == 0) return 0.5f; // account for hairline
- return width * 0.5f;
+ // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
+ // 1.0 stroke, treat 1.0 as minimum.
+
+ // TODO: it would be nice if this could take scale into account, but scale isn't stable
+ // since higher levels of the view hierarchy can change scale out from underneath it.
+ return fmaxf(mPaint->getStrokeWidth(), 1) * 0.5f;
}
protected:
- SkPaint* getPaint(OpenGLRenderer& renderer) {
+ const SkPaint* getPaint(OpenGLRenderer& renderer) {
return renderer.filterPaint(mPaint);
}
@@ -199,9 +209,16 @@ protected:
if (!state.mMatrix.isSimple()) return false;
// check state/paint for transparency
- if (state.mDrawModifiers.mShader ||
- state.mAlpha != 1.0f ||
- (mPaint && mPaint->getAlpha() != 0xFF)) return false;
+ if (mPaint) {
+ if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
+ return false;
+ }
+ if (mPaint->getAlpha() != 0xFF) {
+ return false;
+ }
+ }
+
+ if (state.mAlpha != 1.0f) return false;
SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
return (mode == SkXfermode::kSrcOver_Mode ||
@@ -209,22 +226,22 @@ protected:
}
- SkPaint* mPaint; // should be accessed via getPaint() when applying
+ const SkPaint* mPaint; // should be accessed via getPaint() when applying
bool mQuickRejected;
};
class DrawBoundedOp : public DrawOp {
public:
- DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
+ DrawBoundedOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
- DrawBoundedOp(const Rect& localBounds, SkPaint* paint)
+ DrawBoundedOp(const Rect& localBounds, const SkPaint* paint)
: DrawOp(paint), mLocalBounds(localBounds) {}
// Calculates bounds as smallest rect encompassing all points
// NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
// subclass' constructor)
- DrawBoundedOp(const float* points, int count, SkPaint* paint)
+ DrawBoundedOp(const float* points, int count, const SkPaint* paint)
: DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) {
for (int i = 2; i < count; i += 2) {
mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
@@ -235,15 +252,15 @@ public:
}
// default empty constructor for bounds, to be overridden in child constructor body
- DrawBoundedOp(SkPaint* paint): DrawOp(paint) { }
+ DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { }
- bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) {
localBounds.set(mLocalBounds);
- if (drawModifiers.mHasShadow) {
- // TODO: inspect paint's looper directly
+ OpenGLRenderer::TextShadow textShadow;
+ if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
Rect shadow(mLocalBounds);
- shadow.translate(drawModifiers.mShadowDx, drawModifiers.mShadowDy);
- shadow.outset(drawModifiers.mShadowRadius);
+ shadow.translate(textShadow.dx, textShadow.dx);
+ shadow.outset(textShadow.radius);
localBounds.unionWith(shadow);
}
return true;
@@ -259,7 +276,6 @@ protected:
///////////////////////////////////////////////////////////////////////////////
class SaveOp : public StateOp {
- friend class DisplayList; // give DisplayList private constructor/reinit access
public:
SaveOp(int flags)
: mFlags(flags) {}
@@ -282,17 +298,10 @@ public:
int getFlags() const { return mFlags; }
private:
- SaveOp() {}
- DisplayListOp* reinit(int flags) {
- mFlags = flags;
- return this;
- }
-
int mFlags;
};
class RestoreToCountOp : public StateOp {
- friend class DisplayList; // give DisplayList private constructor/reinit access
public:
RestoreToCountOp(int count)
: mCount(count) {}
@@ -315,21 +324,25 @@ public:
virtual const char* name() { return "RestoreToCount"; }
private:
- RestoreToCountOp() {}
- DisplayListOp* reinit(int count) {
- mCount = count;
- return this;
- }
-
int mCount;
};
class SaveLayerOp : public StateOp {
- friend class DisplayList; // give DisplayList private constructor/reinit access
public:
- SaveLayerOp(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags)
- : mArea(left, top, right, bottom), mAlpha(alpha), mMode(mode), mFlags(flags) {}
+ SaveLayerOp(float left, float top, float right, float bottom, int alpha, int flags)
+ : mArea(left, top, right, bottom)
+ , mPaint(&mCachedPaint)
+ , mFlags(flags)
+ , mConvexMask(NULL) {
+ mCachedPaint.setAlpha(alpha);
+ }
+
+ SaveLayerOp(float left, float top, float right, float bottom, const SkPaint* paint, int flags)
+ : mArea(left, top, right, bottom)
+ , mPaint(paint)
+ , mFlags(flags)
+ , mConvexMask(NULL)
+ {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
@@ -340,11 +353,12 @@ public:
// NOTE: don't issue full saveLayer, since that has side effects/is costly. instead just
// setup the snapshot for deferral, and re-issue the op at flush time
deferStruct.mRenderer.saveLayerDeferred(mArea.left, mArea.top, mArea.right, mArea.bottom,
- mAlpha, mMode, mFlags);
+ mPaint, mFlags);
}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
+ renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom,
+ mPaint, mFlags, mConvexMask);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -356,23 +370,26 @@ public:
int getFlags() { return mFlags; }
+ // Called to make SaveLayerOp clip to the provided mask when drawing back/restored
+ void setMask(const SkPath* convexMask) {
+ mConvexMask = convexMask;
+ }
+
private:
- // Special case, reserved for direct DisplayList usage
- SaveLayerOp() {}
- DisplayListOp* reinit(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags) {
- mArea.set(left, top, right, bottom);
- mAlpha = alpha;
- mMode = mode;
- mFlags = flags;
- return this;
- }
-
- bool isSaveLayerAlpha() const { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
+ bool isSaveLayerAlpha() const {
+ SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+ return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
+ }
+
Rect mArea;
- int mAlpha;
- SkXfermode::Mode mMode;
+ const SkPaint* mPaint;
+ SkPaint mCachedPaint;
int mFlags;
+
+ // Convex path, points at data in RenderNode, valid for the duration of the frame only
+ // Only used for masking the SaveLayer which wraps projected RenderNodes
+ const SkPath* mConvexMask;
};
class TranslateOp : public StateOp {
@@ -456,7 +473,7 @@ private:
class SetMatrixOp : public StateOp {
public:
- SetMatrixOp(SkMatrix* matrix)
+ SetMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -464,22 +481,22 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- if (mMatrix) {
- OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
- } else {
+ if (mMatrix.isIdentity()) {
OP_LOGS("SetMatrix (reset)");
+ } else {
+ OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
}
virtual const char* name() { return "SetMatrix"; }
private:
- SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ConcatMatrixOp : public StateOp {
public:
- ConcatMatrixOp(SkMatrix* matrix)
+ ConcatMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -487,13 +504,13 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+ OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
virtual const char* name() { return "ConcatMatrix"; }
private:
- SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ClipOp : public StateOp {
@@ -514,14 +531,12 @@ public:
}
protected:
- ClipOp() {}
virtual bool isRect() { return false; }
SkRegion::Op mOp;
};
class ClipRectOp : public ClipOp {
- friend class DisplayList; // give DisplayList private constructor/reinit access
public:
ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
: ClipOp(op), mArea(left, top, right, bottom) {}
@@ -540,19 +555,12 @@ protected:
virtual bool isRect() { return true; }
private:
- ClipRectOp() {}
- DisplayListOp* reinit(float left, float top, float right, float bottom, SkRegion::Op op) {
- mOp = op;
- mArea.set(left, top, right, bottom);
- return this;
- }
-
Rect mArea;
};
class ClipPathOp : public ClipOp {
public:
- ClipPathOp(SkPath* path, SkRegion::Op op)
+ ClipPathOp(const SkPath* path, SkRegion::Op op)
: ClipOp(op), mPath(path) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -568,12 +576,12 @@ public:
virtual const char* name() { return "ClipPath"; }
private:
- SkPath* mPath;
+ const SkPath* mPath;
};
class ClipRegionOp : public ClipOp {
public:
- ClipRegionOp(SkRegion* region, SkRegion::Op op)
+ ClipRegionOp(const SkRegion* region, SkRegion::Op op)
: ClipOp(op), mRegion(region) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -589,105 +597,7 @@ public:
virtual const char* name() { return "ClipRegion"; }
private:
- SkRegion* mRegion;
-};
-
-class ResetShaderOp : public StateOp {
-public:
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.resetShader();
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOGS("ResetShader");
- }
-
- virtual const char* name() { return "ResetShader"; }
-};
-
-class SetupShaderOp : public StateOp {
-public:
- SetupShaderOp(SkiaShader* shader)
- : mShader(shader) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.setupShader(mShader);
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("SetupShader, shader %p", mShader);
- }
-
- virtual const char* name() { return "SetupShader"; }
-
-private:
- SkiaShader* mShader;
-};
-
-class ResetColorFilterOp : public StateOp {
-public:
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.resetColorFilter();
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOGS("ResetColorFilter");
- }
-
- virtual const char* name() { return "ResetColorFilter"; }
-};
-
-class SetupColorFilterOp : public StateOp {
-public:
- SetupColorFilterOp(SkiaColorFilter* colorFilter)
- : mColorFilter(colorFilter) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.setupColorFilter(mColorFilter);
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("SetupColorFilter, filter %p", mColorFilter);
- }
-
- virtual const char* name() { return "SetupColorFilter"; }
-
-private:
- SkiaColorFilter* mColorFilter;
-};
-
-class ResetShadowOp : public StateOp {
-public:
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.resetShadow();
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOGS("ResetShadow");
- }
-
- virtual const char* name() { return "ResetShadow"; }
-};
-
-class SetupShadowOp : public StateOp {
-public:
- SetupShadowOp(float radius, float dx, float dy, int color)
- : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.setupShadow(mRadius, mDx, mDy, mColor);
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
- }
-
- virtual const char* name() { return "SetupShadow"; }
-
-private:
- float mRadius;
- float mDx;
- float mDy;
- int mColor;
+ const SkRegion* mRegion;
};
class ResetPaintFilterOp : public StateOp {
@@ -729,9 +639,10 @@ private:
class DrawBitmapOp : public DrawBoundedOp {
public:
- DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
- : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint),
- mBitmap(bitmap), mAtlas(Caches::getInstance().assetAtlas) {
+ DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint)
+ : DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint)
+ , mBitmap(bitmap)
+ , mAtlas(Caches::getInstance().assetAtlas) {
mEntry = mAtlas.getEntry(bitmap);
if (mEntry) {
mEntryGenerationId = mAtlas.getGenerationId();
@@ -740,8 +651,7 @@ public:
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
- getPaint(renderer));
+ return renderer.drawBitmap(mBitmap, getPaint(renderer));
}
AssetAtlas::Entry* getAtlasEntry() {
@@ -823,51 +733,23 @@ public:
deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
!state.mClipSideFlags &&
OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
- (mBitmap->getConfig() != SkBitmap::kA8_Config);
+ (mBitmap->colorType() != kAlpha_8_SkColorType);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
- SkBitmap* mBitmap;
+ const SkBitmap* mBitmap;
const AssetAtlas& mAtlas;
uint32_t mEntryGenerationId;
AssetAtlas::Entry* mEntry;
UvMapper mUvMapper;
};
-class DrawBitmapMatrixOp : public DrawBoundedOp {
-public:
- DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint)
- : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
- mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
- transform.mapRect(mLocalBounds);
- }
-
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
- }
-
- virtual const char* name() { return "DrawBitmapMatrix"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
- }
-
-private:
- SkBitmap* mBitmap;
- SkMatrix* mMatrix;
-};
-
class DrawBitmapRectOp : public DrawBoundedOp {
public:
- DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
- float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint)
+ DrawBitmapRectOp(const SkBitmap* bitmap,
+ float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint)
: DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
@@ -878,7 +760,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+ OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
@@ -890,18 +772,17 @@ public:
}
private:
- SkBitmap* mBitmap;
+ const SkBitmap* mBitmap;
Rect mSrc;
};
class DrawBitmapDataOp : public DrawBitmapOp {
public:
- DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
- : DrawBitmapOp(bitmap, left, top, paint) {}
+ DrawBitmapDataOp(const SkBitmap* bitmap, const SkPaint* paint)
+ : DrawBitmapOp(bitmap, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
- mLocalBounds.top, getPaint(renderer));
+ return renderer.drawBitmapData(mBitmap, getPaint(renderer));
}
virtual void output(int level, uint32_t logFlags) const {
@@ -918,8 +799,8 @@ public:
class DrawBitmapMeshOp : public DrawBoundedOp {
public:
- DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
- float* vertices, int* colors, SkPaint* paint)
+ DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint)
: DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint),
mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
mVertices(vertices), mColors(colors) {}
@@ -941,17 +822,17 @@ public:
}
private:
- SkBitmap* mBitmap;
+ const SkBitmap* mBitmap;
int mMeshWidth;
int mMeshHeight;
- float* mVertices;
- int* mColors;
+ const float* mVertices;
+ const int* mColors;
};
class DrawPatchOp : public DrawBoundedOp {
public:
- DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
- float left, float top, float right, float bottom, SkPaint* paint)
+ DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint)
: DrawBoundedOp(left, top, right, bottom, paint),
mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL),
mAtlas(Caches::getInstance().assetAtlas) {
@@ -1028,8 +909,8 @@ public:
TextureVertex* opVertices = opMesh->vertices;
for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
TextureVertex::set(vertex++,
- opVertices->position[0] + tx, opVertices->position[1] + ty,
- opVertices->texture[0], opVertices->texture[1]);
+ opVertices->x + tx, opVertices->y + ty,
+ opVertices->u, opVertices->v);
}
// Dirty the current layer if possible. When the 9-patch does not
@@ -1068,7 +949,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPatch"; }
@@ -1083,8 +964,8 @@ public:
}
private:
- SkBitmap* mBitmap;
- Res_png_9patch* mPatch;
+ const SkBitmap* mBitmap;
+ const Res_png_9patch* mPatch;
uint32_t mGenerationId;
const Patch* mMesh;
@@ -1097,7 +978,7 @@ private:
class DrawColorOp : public DrawOp {
public:
DrawColorOp(int color, SkXfermode::Mode mode)
- : DrawOp(0), mColor(color), mMode(mode) {};
+ : DrawOp(NULL), mColor(color), mMode(mode) {};
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawColor(mColor, mMode);
@@ -1116,10 +997,10 @@ private:
class DrawStrokableOp : public DrawBoundedOp {
public:
- DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint)
+ DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawBoundedOp(left, top, right, bottom, paint) {};
- bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) {
localBounds.set(mLocalBounds);
if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
localBounds.outset(strokeWidthOutset());
@@ -1141,7 +1022,7 @@ public:
class DrawRectOp : public DrawStrokableOp {
public:
- DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
+ DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
@@ -1150,7 +1031,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1165,7 +1046,7 @@ public:
class DrawRectsOp : public DrawBoundedOp {
public:
- DrawRectsOp(const float* rects, int count, SkPaint* paint)
+ DrawRectsOp(const float* rects, int count, const SkPaint* paint)
: DrawBoundedOp(rects, count, paint),
mRects(rects), mCount(count) {}
@@ -1192,7 +1073,7 @@ private:
class DrawRoundRectOp : public DrawStrokableOp {
public:
DrawRoundRectOp(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint)
+ float rx, float ry, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
@@ -1201,7 +1082,16 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+ OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+ }
+
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ DrawStrokableOp::onDefer(renderer, deferInfo, state);
+ if (!mPaint->getPathEffect()) {
+ renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy);
+ }
}
virtual const char* name() { return "DrawRoundRect"; }
@@ -1213,7 +1103,7 @@ private:
class DrawCircleOp : public DrawStrokableOp {
public:
- DrawCircleOp(float x, float y, float radius, SkPaint* paint)
+ DrawCircleOp(float x, float y, float radius, const SkPaint* paint)
: DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
mX(x), mY(y), mRadius(radius) {}
@@ -1233,9 +1123,30 @@ private:
float mRadius;
};
+class DrawCirclePropsOp : public DrawOp {
+public:
+ DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint)
+ : DrawOp(paint), mX(x), mY(y), mRadius(radius) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t logFlags) const {
+ OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius);
+ }
+
+ virtual const char* name() { return "DrawCircleProps"; }
+
+private:
+ float* mX;
+ float* mY;
+ float* mRadius;
+};
+
class DrawOvalOp : public DrawStrokableOp {
public:
- DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
+ DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
@@ -1244,7 +1155,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawOval"; }
@@ -1253,7 +1164,7 @@ public:
class DrawArcOp : public DrawStrokableOp {
public:
DrawArcOp(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint)
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint),
mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
@@ -1264,7 +1175,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+ OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
@@ -1278,7 +1189,7 @@ private:
class DrawPathOp : public DrawBoundedOp {
public:
- DrawPathOp(SkPath* path, SkPaint* paint)
+ DrawPathOp(const SkPath* path, const SkPaint* paint)
: DrawBoundedOp(paint), mPath(path) {
float left, top, offset;
uint32_t width, height;
@@ -1294,25 +1205,25 @@ public:
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) {
- SkPaint* paint = getPaint(renderer);
+ const SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPath"; }
private:
- SkPath* mPath;
+ const SkPath* mPath;
};
class DrawLinesOp : public DrawBoundedOp {
public:
- DrawLinesOp(float* points, int count, SkPaint* paint)
+ DrawLinesOp(const float* points, int count, const SkPaint* paint)
: DrawBoundedOp(points, count, paint),
mPoints(points), mCount(count) {
mLocalBounds.outset(strokeWidthOutset());
@@ -1336,13 +1247,13 @@ public:
}
protected:
- float* mPoints;
+ const float* mPoints;
int mCount;
};
class DrawPointsOp : public DrawLinesOp {
public:
- DrawPointsOp(float* points, int count, SkPaint* paint)
+ DrawPointsOp(const float* points, int count, const SkPaint* paint)
: DrawLinesOp(points, count, paint) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
@@ -1358,20 +1269,24 @@ public:
class DrawSomeTextOp : public DrawOp {
public:
- DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
+ DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
+ virtual bool hasTextShadow() const {
+ return OpenGLRenderer::hasTextShadow(mPaint);
+ }
+
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) {
- SkPaint* paint = getPaint(renderer);
+ const SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
- fontRenderer.precache(paint, mText, mCount, mat4::identity());
+ fontRenderer.precache(paint, mText, mCount, SkMatrix::I());
- deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
}
@@ -1385,7 +1300,7 @@ protected:
class DrawTextOnPathOp : public DrawSomeTextOp {
public:
DrawTextOnPathOp(const char* text, int bytesCount, int count,
- SkPath* path, float hOffset, float vOffset, SkPaint* paint)
+ const SkPath* path, float hOffset, float vOffset, const SkPaint* paint)
: DrawSomeTextOp(text, bytesCount, count, paint),
mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
/* TODO: inherit from DrawBounded and init mLocalBounds */
@@ -1399,7 +1314,7 @@ public:
virtual const char* name() { return "DrawTextOnPath"; }
private:
- SkPath* mPath;
+ const SkPath* mPath;
float mHOffset;
float mVOffset;
};
@@ -1407,7 +1322,7 @@ private:
class DrawPosTextOp : public DrawSomeTextOp {
public:
DrawPosTextOp(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint)
+ const float* positions, const SkPaint* paint)
: DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
@@ -1425,37 +1340,40 @@ private:
class DrawTextOp : public DrawBoundedOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds)
+ const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds)
: DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
- memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
+ mPrecacheTransform = SkMatrix::InvalidMatrix();
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) {
- SkPaint* paint = getPaint(renderer);
+ const SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
- const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
+ SkMatrix transform;
+ renderer.findBestFontTransform(state.mMatrix, &transform);
if (mPrecacheTransform != transform) {
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
- deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
// don't merge decorated text - the decorations won't draw in order
- bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
- SkPaint::kStrikeThruText_Flag));
- deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ bool hasDecorations = mPaint->getFlags()
+ & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag);
+
+ deferInfo.mergeable = state.mMatrix.isPureTranslate()
+ && !hasDecorations
+ && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
Rect bounds;
- getLocalBounds(renderer.getDrawModifiers(), bounds);
+ getLocalBounds(bounds);
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
mPositions, getPaint(renderer), mTotalAdvance, bounds);
}
@@ -1492,7 +1410,7 @@ private:
float mY;
const float* mPositions;
float mTotalAdvance;
- mat4 mPrecacheTransform;
+ SkMatrix mPrecacheTransform;
};
///////////////////////////////////////////////////////////////////////////////
@@ -1502,7 +1420,7 @@ private:
class DrawFunctorOp : public DrawOp {
public:
DrawFunctorOp(Functor* functor)
- : DrawOp(0), mFunctor(functor) {}
+ : DrawOp(NULL), mFunctor(functor) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
renderer.startMark("GL functor");
@@ -1521,48 +1439,118 @@ private:
Functor* mFunctor;
};
-class DrawDisplayListOp : public DrawBoundedOp {
+class DrawRenderNodeOp : public DrawBoundedOp {
+ friend class RenderNode; // grant RenderNode access to info of child
public:
- DrawDisplayListOp(DisplayList* displayList, int flags)
- : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
- mDisplayList(displayList), mFlags(flags) {}
+ DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
+ : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0),
+ mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
- if (mDisplayList && mDisplayList->isRenderable()) {
- mDisplayList->defer(deferStruct, level + 1);
+ if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+ mRenderNode->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) {
- if (mDisplayList && mDisplayList->isRenderable()) {
- mDisplayList->replay(replayStruct, level + 1);
+ if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+ mRenderNode->replay(replayStruct, level + 1);
}
}
- // NOT USED since replay() is overridden
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return DrawGlInfo::kStatusDone;
+ LOG_ALWAYS_FATAL("should not be called, because replay() is overridden");
+ return 0;
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
- if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
- mDisplayList->output(level + 1);
+ OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags);
+ if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
+ mRenderNode->output(level + 1);
}
}
- virtual const char* name() { return "DrawDisplayList"; }
+ virtual const char* name() { return "DrawRenderNode"; }
+
+ RenderNode* renderNode() { return mRenderNode; }
private:
- DisplayList* mDisplayList;
- int mFlags;
+ RenderNode* mRenderNode;
+ const int mFlags;
+
+ ///////////////////////////
+ // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
+ ///////////////////////////
+ /**
+ * Records transform vs parent, used for computing total transform without rerunning DL contents
+ */
+ const mat4 mTransformFromParent;
+
+ /**
+ * Holds the transformation between the projection surface ViewGroup and this RenderNode
+ * drawing instance. Represents any translations / transformations done within the drawing of
+ * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
+ * DisplayList draw instance.
+ *
+ * Note: doesn't include transformation within the RenderNode, or its properties.
+ */
+ mat4 mTransformFromCompositingAncestor;
+ bool mSkipInOrderDraw;
+};
+
+/**
+ * Not a canvas operation, used only by 3d / z ordering logic in RenderNode::iterate()
+ */
+class DrawShadowOp : public DrawOp {
+public:
+ DrawShadowOp(const mat4& transformXY, const mat4& transformZ,
+ float casterAlpha, const SkPath* casterOutline)
+ : DrawOp(NULL)
+ , mTransformXY(transformXY)
+ , mTransformZ(transformZ)
+ , mCasterAlpha(casterAlpha)
+ , mCasterOutline(casterOutline) {
+ }
+
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
+ renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ TessellationCache::vertexBuffer_pair_t buffers;
+ Matrix4 drawTransform;
+ renderer.getMatrix(&drawTransform);
+ renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
+ renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
+ buffers);
+
+ return renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
+ }
+
+ virtual void output(int level, uint32_t logFlags) const {
+ OP_LOGS("DrawShadow");
+ }
+
+ virtual const char* name() { return "DrawShadow"; }
+
+private:
+ bool isCasterOpaque() { return mCasterAlpha >= 1.0f; }
+
+ const mat4 mTransformXY;
+ const mat4 mTransformZ;
+ const float mCasterAlpha;
+ const SkPath* mCasterOutline;
};
class DrawLayerOp : public DrawOp {
public:
DrawLayerOp(Layer* layer, float x, float y)
- : DrawOp(0), mLayer(layer), mX(x), mY(y) {}
+ : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLayer(mLayer, mX, mY);
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 8866029..94162fc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,120 +21,54 @@
#include <private/hwui/DrawGlInfo.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
#include "DisplayListLogBuffer.h"
#include "DisplayListOp.h"
#include "DisplayListRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
-DisplayListRenderer::DisplayListRenderer():
- mCaches(Caches::getInstance()), mDisplayListData(new DisplayListData),
- mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false),
- mHasDrawOps(false), mFunctorCount(0) {
+DisplayListRenderer::DisplayListRenderer()
+ : mCaches(Caches::getInstance())
+ , mDisplayListData(0)
+ , mTranslateX(0.0f)
+ , mTranslateY(0.0f)
+ , mHasTranslate(false)
+ , mHighContrastText(false)
+ , mRestoreSaveCount(-1) {
}
DisplayListRenderer::~DisplayListRenderer() {
- reset();
-}
-
-void DisplayListRenderer::reset() {
- mDisplayListData = new DisplayListData();
- mCaches.resourceCache.lock();
-
- for (size_t i = 0; i < mBitmapResources.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mOwnedBitmapResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mFilterResources.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mPatchResources.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
- }
-
- for (size_t i = 0; i < mShaders.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
- }
-
- for (size_t i = 0; i < mSourcePaths.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
- }
-
- for (size_t i = 0; i < mLayers.size(); i++) {
- mCaches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
- }
-
- mCaches.resourceCache.unlock();
-
- mBitmapResources.clear();
- mOwnedBitmapResources.clear();
- mFilterResources.clear();
- mPatchResources.clear();
- mSourcePaths.clear();
-
- mShaders.clear();
- mShaderMap.clear();
-
- mPaints.clear();
- mPaintMap.clear();
-
- mRegions.clear();
- mRegionMap.clear();
-
- mPaths.clear();
- mPathMap.clear();
-
- mMatrices.clear();
-
- mLayers.clear();
-
- mHasDrawOps = false;
- mFunctorCount = 0;
+ LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ "Destroyed a DisplayListRenderer during a record!");
}
///////////////////////////////////////////////////////////////////////////////
// Operations
///////////////////////////////////////////////////////////////////////////////
-DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) {
- if (!displayList) {
- displayList = new DisplayList(*this);
- } else {
- displayList->initFromDisplayListRenderer(*this, true);
- }
- displayList->setRenderable(mHasDrawOps);
- return displayList;
-}
-
-bool DisplayListRenderer::isDeferred() {
- return true;
-}
-
-void DisplayListRenderer::setViewport(int width, int height) {
- mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
-
- mWidth = width;
- mHeight = height;
+DisplayListData* DisplayListRenderer::finishRecording() {
+ mPaintMap.clear();
+ mRegionMap.clear();
+ mPathMap.clear();
+ DisplayListData* data = mDisplayListData;
+ mDisplayListData = 0;
+ return data;
}
status_t DisplayListRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
- mSnapshot = new Snapshot(mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- mSaveCount = 1;
- mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
- mDirtyClip = opaque;
+ LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ "prepareDirty called a second time during a recording!");
+ mDisplayListData = new DisplayListData();
+
+ initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3());
+ mDirtyClip = opaque;
mRestoreSaveCount = -1;
return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
@@ -154,13 +88,13 @@ void DisplayListRenderer::resume() {
status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
// Ignore dirty during recording, it matters only when we replay
addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mFunctorCount++;
+ mDisplayListData->functors.add(functor);
return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
}
int DisplayListRenderer::save(int flags) {
addStateOp(new (alloc()) SaveOp(flags));
- return OpenGLRenderer::save(flags);
+ return StatefulBaseRenderer::save(flags);
}
void DisplayListRenderer::restore() {
@@ -171,84 +105,86 @@ void DisplayListRenderer::restore() {
mRestoreSaveCount--;
insertTranslate();
- OpenGLRenderer::restore();
+ StatefulBaseRenderer::restore();
}
void DisplayListRenderer::restoreToCount(int saveCount) {
mRestoreSaveCount = saveCount;
insertTranslate();
- OpenGLRenderer::restoreToCount(saveCount);
+ StatefulBaseRenderer::restoreToCount(saveCount);
}
int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags) {
- addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, alpha, mode, flags));
- return OpenGLRenderer::save(flags);
+ const SkPaint* paint, int flags) {
+ paint = refPaint(paint);
+ addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, flags));
+ return StatefulBaseRenderer::save(flags);
}
-void DisplayListRenderer::translate(float dx, float dy) {
+void DisplayListRenderer::translate(float dx, float dy, float dz) {
+ // ignore dz, not used at defer time
mHasTranslate = true;
mTranslateX += dx;
mTranslateY += dy;
insertRestoreToCount();
- OpenGLRenderer::translate(dx, dy);
+ StatefulBaseRenderer::translate(dx, dy, dz);
}
void DisplayListRenderer::rotate(float degrees) {
addStateOp(new (alloc()) RotateOp(degrees));
- OpenGLRenderer::rotate(degrees);
+ StatefulBaseRenderer::rotate(degrees);
}
void DisplayListRenderer::scale(float sx, float sy) {
addStateOp(new (alloc()) ScaleOp(sx, sy));
- OpenGLRenderer::scale(sx, sy);
+ StatefulBaseRenderer::scale(sx, sy);
}
void DisplayListRenderer::skew(float sx, float sy) {
addStateOp(new (alloc()) SkewOp(sx, sy));
- OpenGLRenderer::skew(sx, sy);
+ StatefulBaseRenderer::skew(sx, sy);
}
-void DisplayListRenderer::setMatrix(SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::setMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) SetMatrixOp(matrix));
- OpenGLRenderer::setMatrix(matrix);
+ StatefulBaseRenderer::setMatrix(matrix);
}
-void DisplayListRenderer::concatMatrix(SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) ConcatMatrixOp(matrix));
- OpenGLRenderer::concatMatrix(matrix);
+ StatefulBaseRenderer::concatMatrix(matrix);
}
bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
SkRegion::Op op) {
addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
- return OpenGLRenderer::clipRect(left, top, right, bottom, op);
+ return StatefulBaseRenderer::clipRect(left, top, right, bottom, op);
}
-bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+bool DisplayListRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
path = refPath(path);
addStateOp(new (alloc()) ClipPathOp(path, op));
- return OpenGLRenderer::clipPath(path, op);
+ return StatefulBaseRenderer::clipPath(path, op);
}
-bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
region = refRegion(region);
addStateOp(new (alloc()) ClipRegionOp(region, op));
- return OpenGLRenderer::clipRegion(region, op);
+ return StatefulBaseRenderer::clipRegion(region, op);
}
-status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
- Rect& dirty, int32_t flags) {
+status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
+ DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
+ int opIndex = addDrawOp(op);
+ mDisplayListData->addChild(op);
- // TODO: To be safe, the display list should be ref-counted in the
- // resources cache, but we rely on the caller (UI toolkit) to
- // do the right thing for now
+ if (renderNode->stagingProperties().isProjectionReceiver()) {
+ // use staging property, since recording on UI thread
+ mDisplayListData->projectionReceiveIndex = opIndex;
+ }
- addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags));
return DrawGlInfo::kStatusDone;
}
@@ -258,68 +194,60 @@ status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) {
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
- bitmap = refBitmap(bitmap);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmap(bitmap);
- matrix = refMatrix(matrix);
paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint));
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, SkPaint* paint) {
- bitmap = refBitmap(bitmap);
- paint = refPaint(paint);
-
- if (srcLeft == 0 && srcTop == 0 &&
- srcRight == bitmap->width() && srcBottom == bitmap->height() &&
- (srcBottom - srcTop == dstBottom - dstTop) &&
- (srcRight - srcLeft == dstRight - dstLeft)) {
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ if (srcLeft == 0 && srcTop == 0
+ && srcRight == bitmap->width() && srcBottom == bitmap->height()
+ && (srcBottom - srcTop == dstBottom - dstTop)
+ && (srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
- return DrawGlInfo::kStatusDone;
- }
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(dstLeft, dstTop);
+ drawBitmap(bitmap, paint);
+ restore();
+ } else {
+ bitmap = refBitmap(bitmap);
+ paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
- srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, paint));
+ addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
+ srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint));
+ }
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top,
- SkPaint* paint) {
+status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmapData(bitmap);
paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, left, top, paint));
+ addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
- float* vertices, int* colors, SkPaint* paint) {
- int count = (meshWidth + 1) * (meshHeight + 1) * 2;
+status_t DisplayListRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+ int vertexCount = (meshWidth + 1) * (meshHeight + 1);
bitmap = refBitmap(bitmap);
- vertices = refBuffer<float>(vertices, count);
+ vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex
paint = refPaint(paint);
- colors = refBuffer<int>(colors, count);
+ colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex
addDrawOp(new (alloc()) DrawBitmapMeshOp(bitmap, meshWidth, meshHeight,
vertices, colors, paint));
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
- float left, float top, float right, float bottom, SkPaint* paint) {
+status_t DisplayListRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
bitmap = refBitmap(bitmap);
patch = refPatch(patch);
paint = refPaint(paint);
@@ -334,41 +262,56 @@ status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
}
status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
- SkPaint* paint) {
+ const SkPaint* paint) {
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint) {
+ float rx, float ry, const SkPaint* paint) {
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint));
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
+status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) {
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint));
return DrawGlInfo::kStatusDone;
}
+status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+ mDisplayListData->refProperty(x);
+ mDisplayListData->refProperty(y);
+ mDisplayListData->refProperty(radius);
+ mDisplayListData->refProperty(paint);
+ addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
+ &radius->value, &paint->value));
+ return DrawGlInfo::kStatusDone;
+}
+
status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
- SkPaint* paint) {
+ const SkPaint* paint) {
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
+ if (fabs(sweepAngle) >= 360.0f) {
+ return drawOval(left, top, right, bottom, paint);
+ }
+
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
startAngle, sweepAngle, useCenter, paint));
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
+status_t DisplayListRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
path = refPath(path);
paint = refPaint(paint);
@@ -376,7 +319,7 @@ status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
+status_t DisplayListRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
points = refBuffer<float>(points, count);
paint = refPaint(paint);
@@ -384,7 +327,7 @@ status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) {
+status_t DisplayListRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
points = refBuffer<float>(points, count);
paint = refPaint(paint);
@@ -393,7 +336,7 @@ status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* pain
}
status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
- SkPath* path, float hOffset, float vOffset, SkPaint* paint) {
+ const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
text = refText(text, bytesCount);
@@ -407,7 +350,7 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i
}
status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint) {
+ const float* positions, const SkPaint* paint) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
text = refText(text, bytesCount);
@@ -419,23 +362,56 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
return DrawGlInfo::kStatusDone;
}
+static void simplifyPaint(int color, SkPaint* paint) {
+ paint->setColor(color);
+ paint->setShader(NULL);
+ paint->setColorFilter(NULL);
+ paint->setLooper(NULL);
+ paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
+ paint->setStrokeJoin(SkPaint::kRound_Join);
+ paint->setLooper(NULL);
+}
+
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint,
+ float x, float y, const float* positions, const SkPaint* paint,
float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
text = refText(text, bytesCount);
positions = refBuffer<float>(positions, count * 2);
- paint = refPaint(paint);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, paint, totalAdvance, bounds);
- addDrawOp(op);
+ if (CC_UNLIKELY(mHighContrastText)) {
+ // high contrast draw path
+ int color = paint->getColor();
+ int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+ bool darken = channelSum < (128 * 3);
+
+ // outline
+ SkPaint* outlinePaint = copyPaint(paint);
+ simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint);
+ outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style);
+ addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds?
+
+ // inner
+ SkPaint* innerPaint = copyPaint(paint);
+ simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint);
+ innerPaint->setStyle(SkPaint::kFill_Style);
+ addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, innerPaint, totalAdvance, bounds));
+ } else {
+ // standard draw path
+ paint = refPaint(paint);
+
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, paint, totalAdvance, bounds);
+ addDrawOp(op);
+ }
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
+status_t DisplayListRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
if (count <= 0) return DrawGlInfo::kStatusDone;
rects = refBuffer<float>(rects, count);
@@ -444,34 +420,6 @@ status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint*
return DrawGlInfo::kStatusDone;
}
-void DisplayListRenderer::resetShader() {
- addStateOp(new (alloc()) ResetShaderOp());
-}
-
-void DisplayListRenderer::setupShader(SkiaShader* shader) {
- shader = refShader(shader);
- addStateOp(new (alloc()) SetupShaderOp(shader));
-}
-
-void DisplayListRenderer::resetColorFilter() {
- addStateOp(new (alloc()) ResetColorFilterOp());
-}
-
-void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) {
- filter = refColorFilter(filter);
- addStateOp(new (alloc()) SetupColorFilterOp(filter));
-}
-
-void DisplayListRenderer::resetShadow() {
- addStateOp(new (alloc()) ResetShadowOp());
- OpenGLRenderer::resetShadow();
-}
-
-void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
- addStateOp(new (alloc()) SetupShadowOp(radius, dx, dy, color));
- OpenGLRenderer::setupShadow(radius, dx, dy, color);
-}
-
void DisplayListRenderer::resetPaintFilter() {
addStateOp(new (alloc()) ResetPaintFilterOp());
}
@@ -499,20 +447,20 @@ void DisplayListRenderer::insertTranslate() {
}
}
-void DisplayListRenderer::addStateOp(StateOp* op) {
- addOpInternal(op);
+int DisplayListRenderer::addStateOp(StateOp* op) {
+ return addOpInternal(op);
}
-void DisplayListRenderer::addDrawOp(DrawOp* op) {
+int DisplayListRenderer::addDrawOp(DrawOp* op) {
Rect localBounds;
- if (op->getLocalBounds(mDrawModifiers, localBounds)) {
- bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top,
+ if (op->getLocalBounds(localBounds)) {
+ bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
localBounds.right, localBounds.bottom);
op->setQuickRejected(rejected);
}
- mHasDrawOps = true;
- addOpInternal(op);
+ mDisplayListData->hasDrawOps = true;
+ return addOpInternal(op);
}
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d233150..b5c0159 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -22,9 +22,8 @@
#include <SkPath.h>
#include <cutils/compiler.h>
-#include "DisplayList.h"
#include "DisplayListLogBuffer.h"
-#include "OpenGLRenderer.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
@@ -33,9 +32,6 @@ namespace uirenderer {
// Defines
///////////////////////////////////////////////////////////////////////////////
-#define MIN_WRITER_SIZE 4096
-#define OP_MAY_BE_SKIPPED_MASK 0xff000000
-
// Debug
#if DEBUG_DISPLAY_LIST
#define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__)
@@ -54,160 +50,128 @@ class DrawOp;
class StateOp;
/**
- * Records drawing commands in a display list for latter playback.
+ * Records drawing commands in a display list for later playback into an OpenGLRenderer.
*/
-class DisplayListRenderer: public OpenGLRenderer {
+class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
- ANDROID_API DisplayListRenderer();
+ DisplayListRenderer();
virtual ~DisplayListRenderer();
- ANDROID_API DisplayList* getDisplayList(DisplayList* displayList);
-
- virtual bool isDeferred();
+ DisplayListData* finishRecording();
- virtual void setViewport(int width, int height);
+// ----------------------------------------------------------------------------
+// Frame state operations
+// ----------------------------------------------------------------------------
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
-
- virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty);
-
virtual void interrupt();
virtual void resume();
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
virtual int save(int flags);
virtual void restore();
virtual void restoreToCount(int saveCount);
-
virtual int saveLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags);
+ const SkPaint* paint, int flags);
- virtual void translate(float dx, float dy);
+ // Matrix
+ virtual void translate(float dx, float dy, float dz = 0.0f);
virtual void rotate(float degrees);
virtual void scale(float sx, float sy);
virtual void skew(float sx, float sy);
- virtual void setMatrix(SkMatrix* matrix);
- virtual void concatMatrix(SkMatrix* matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
+ virtual void concatMatrix(const SkMatrix& matrix);
+ // Clip
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool clipPath(SkPath* path, SkRegion::Op op);
- virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
-
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags);
- virtual status_t drawLayer(Layer* layer, float x, float y);
- virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
- virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
- virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, SkPaint* paint);
- virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
- virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
- float* vertices, int* colors, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
- float left, float top, float right, float bottom, SkPaint* paint);
- virtual status_t drawColor(int color, SkXfermode::Mode mode);
- virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
- virtual status_t drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint);
- virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint);
- virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint);
- virtual status_t drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
- virtual status_t drawPath(SkPath* path, SkPaint* paint);
- virtual status_t drawLines(float* points, int count, SkPaint* paint);
- virtual status_t drawPoints(float* points, int count, SkPaint* paint);
- virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
- float hOffset, float vOffset, SkPaint* paint);
- virtual status_t drawPosText(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode);
-
- virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
-
- virtual void resetShader();
- virtual void setupShader(SkiaShader* shader);
-
- virtual void resetColorFilter();
- virtual void setupColorFilter(SkiaColorFilter* filter);
-
- virtual void resetShadow();
- virtual void setupShadow(float radius, float dx, float dy, int color);
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op);
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+ // Misc - should be implemented with SkPaint inspection
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
- ANDROID_API void reset();
-
- sp<DisplayListData> getDisplayListData() const {
- return mDisplayListData;
+ bool isCurrentTransformSimple() {
+ return currentTransform()->isSimple();
}
- const Vector<SkBitmap*>& getBitmapResources() const {
- return mBitmapResources;
- }
-
- const Vector<SkBitmap*>& getOwnedBitmapResources() const {
- return mOwnedBitmapResources;
- }
-
- const Vector<SkiaColorFilter*>& getFilterResources() const {
- return mFilterResources;
- }
-
- const Vector<Res_png_9patch*>& getPatchResources() const {
- return mPatchResources;
- }
-
- const Vector<SkiaShader*>& getShaders() const {
- return mShaders;
- }
-
- const Vector<SkPaint*>& getPaints() const {
- return mPaints;
- }
-
- const Vector<SkPath*>& getPaths() const {
- return mPaths;
- }
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual status_t drawColor(int color, SkXfermode::Mode mode);
- const SortedVector<SkPath*>& getSourcePaths() const {
- return mSourcePaths;
- }
+ // Bitmap-based
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint);
+ virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint);
+ virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint);
+ virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint);
+
+ // Shapes
+ virtual status_t drawRect(float left, float top, float right, float bottom,
+ const SkPaint* paint);
+ virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
+ virtual status_t drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint* paint);
+ virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
+ virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
+ virtual status_t drawOval(float left, float top, float right, float bottom,
+ const SkPaint* paint);
+ virtual status_t drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint);
+ virtual status_t drawPath(const SkPath* path, const SkPaint* paint);
+ virtual status_t drawLines(const float* points, int count, const SkPaint* paint);
+ virtual status_t drawPoints(const float* points, int count, const SkPaint* paint);
- const Vector<SkRegion*>& getRegions() const {
- return mRegions;
- }
+ // Text
+ virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode = kDrawOpMode_Immediate);
+ virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ float hOffset, float vOffset, const SkPaint* paint);
+ virtual status_t drawPosText(const char* text, int bytesCount, int count,
+ const float* positions, const SkPaint* paint);
- const Vector<Layer*>& getLayers() const {
- return mLayers;
- }
+// ----------------------------------------------------------------------------
+// Canvas draw operations - special
+// ----------------------------------------------------------------------------
+ virtual status_t drawLayer(Layer* layer, float x, float y);
+ virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
- const Vector<SkMatrix*>& getMatrices() const {
- return mMatrices;
- }
+ // TODO: rename for consistency
+ virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
- uint32_t getFunctorCount() const {
- return mFunctorCount;
+ void setHighContrastText(bool highContrastText) {
+ mHighContrastText = highContrastText;
}
-
private:
void insertRestoreToCount();
void insertTranslate();
LinearAllocator& alloc() { return mDisplayListData->allocator; }
- void addStateOp(StateOp* op);
- void addDrawOp(DrawOp* op);
- void addOpInternal(DisplayListOp* op) {
+
+ // Each method returns final index of op
+ int addStateOp(StateOp* op);
+ int addDrawOp(DrawOp* op);
+ int addOpInternal(DisplayListOp* op) {
insertRestoreToCount();
insertTranslate();
- mDisplayListData->displayListOps.add(op);
+ return mDisplayListData->displayListOps.add(op);
}
template<class T>
- inline T* refBuffer(const T* srcBuffer, int32_t count) {
- if (srcBuffer == NULL) return NULL;
+ inline const T* refBuffer(const T* srcBuffer, int32_t count) {
+ if (!srcBuffer) return NULL;
+
T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
@@ -217,153 +181,108 @@ private:
return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
}
- inline SkPath* refPath(SkPath* path) {
+ inline const SkPath* refPath(const SkPath* path) {
if (!path) return NULL;
- SkPath* pathCopy = mPathMap.valueFor(path);
+ const SkPath* pathCopy = mPathMap.valueFor(path);
if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
- pathCopy = new SkPath(*path);
- pathCopy->setSourcePath(path);
+ SkPath* newPathCopy = new SkPath(*path);
+ newPathCopy->setSourcePath(path);
+
+ pathCopy = newPathCopy;
// replaceValueFor() performs an add if the entry doesn't exist
mPathMap.replaceValueFor(path, pathCopy);
- mPaths.add(pathCopy);
+ mDisplayListData->paths.add(pathCopy);
}
- if (mSourcePaths.indexOf(path) < 0) {
+ if (mDisplayListData->sourcePaths.indexOf(path) < 0) {
mCaches.resourceCache.incrementRefcount(path);
- mSourcePaths.add(path);
+ mDisplayListData->sourcePaths.add(path);
}
return pathCopy;
}
- inline SkPaint* refPaint(SkPaint* paint) {
- if (!paint) {
- return paint;
- }
+ inline const SkPaint* refPaint(const SkPaint* paint) {
+ if (!paint) return NULL;
- SkPaint* paintCopy = mPaintMap.valueFor(paint);
+ const SkPaint* paintCopy = mPaintMap.valueFor(paint);
if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) {
paintCopy = new SkPaint(*paint);
// replaceValueFor() performs an add if the entry doesn't exist
mPaintMap.replaceValueFor(paint, paintCopy);
- mPaints.add(paintCopy);
+ mDisplayListData->paints.add(paintCopy);
}
return paintCopy;
}
- inline SkRegion* refRegion(SkRegion* region) {
+ inline SkPaint* copyPaint(const SkPaint* paint) {
+ if (!paint) return NULL;
+ SkPaint* paintCopy = new SkPaint(*paint);
+ mDisplayListData->paints.add(paintCopy);
+
+ return paintCopy;
+ }
+
+ inline const SkRegion* refRegion(const SkRegion* region) {
if (!region) {
return region;
}
- SkRegion* regionCopy = mRegionMap.valueFor(region);
+ const SkRegion* regionCopy = mRegionMap.valueFor(region);
// TODO: Add generation ID to SkRegion
if (regionCopy == NULL) {
regionCopy = new SkRegion(*region);
// replaceValueFor() performs an add if the entry doesn't exist
mRegionMap.replaceValueFor(region, regionCopy);
- mRegions.add(regionCopy);
+ mDisplayListData->regions.add(regionCopy);
}
return regionCopy;
}
- inline SkMatrix* refMatrix(SkMatrix* matrix) {
- if (matrix) {
- // Copying the matrix is cheap and prevents against the user changing
- // the original matrix before the operation that uses it
- SkMatrix* copy = new SkMatrix(*matrix);
- mMatrices.add(copy);
- return copy;
- }
- return matrix;
- }
-
inline Layer* refLayer(Layer* layer) {
- mLayers.add(layer);
+ mDisplayListData->layers.add(layer);
mCaches.resourceCache.incrementRefcount(layer);
return layer;
}
- inline SkBitmap* refBitmap(SkBitmap* bitmap) {
+ inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- mBitmapResources.add(bitmap);
+ mDisplayListData->bitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
- inline SkBitmap* refBitmapData(SkBitmap* bitmap) {
- mOwnedBitmapResources.add(bitmap);
+ inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
+ mDisplayListData->ownedBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
- inline SkiaShader* refShader(SkiaShader* shader) {
- if (!shader) return NULL;
-
- SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
- // TODO: We also need to handle generation ID changes in compose shaders
- if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
- shaderCopy = shader->copy();
- // replaceValueFor() performs an add if the entry doesn't exist
- mShaderMap.replaceValueFor(shader, shaderCopy);
- mShaders.add(shaderCopy);
- mCaches.resourceCache.incrementRefcount(shaderCopy);
- }
- return shaderCopy;
- }
-
- inline SkiaColorFilter* refColorFilter(SkiaColorFilter* colorFilter) {
- mFilterResources.add(colorFilter);
- mCaches.resourceCache.incrementRefcount(colorFilter);
- return colorFilter;
- }
-
- inline Res_png_9patch* refPatch(Res_png_9patch* patch) {
- mPatchResources.add(patch);
+ inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
+ mDisplayListData->patchResources.add(patch);
mCaches.resourceCache.incrementRefcount(patch);
return patch;
}
- Vector<SkBitmap*> mBitmapResources;
- Vector<SkBitmap*> mOwnedBitmapResources;
- Vector<SkiaColorFilter*> mFilterResources;
- Vector<Res_png_9patch*> mPatchResources;
-
- Vector<SkPaint*> mPaints;
- DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap;
-
- Vector<SkPath*> mPaths;
- DefaultKeyedVector<SkPath*, SkPath*> mPathMap;
-
- SortedVector<SkPath*> mSourcePaths;
-
- Vector<SkRegion*> mRegions;
- DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap;
-
- Vector<SkiaShader*> mShaders;
- DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
-
- Vector<SkMatrix*> mMatrices;
-
- Vector<Layer*> mLayers;
-
- int mRestoreSaveCount;
+ DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap;
+ DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
+ DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
Caches& mCaches;
- sp<DisplayListData> mDisplayListData;
+ DisplayListData* mDisplayListData;
float mTranslateX;
float mTranslateY;
bool mHasTranslate;
- bool mHasDrawOps;
+ bool mHighContrastText;
- uint32_t mFunctorCount;
+ int mRestoreSaveCount;
- friend class DisplayList;
+ friend class RenderNode;
}; // class DisplayListRenderer
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
new file mode 100644
index 0000000..2409554
--- /dev/null
+++ b/libs/hwui/DrawProfiler.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "DrawProfiler.h"
+
+#include <cutils/compiler.h>
+
+#include "OpenGLRenderer.h"
+#include "Properties.h"
+
+#define DEFAULT_MAX_FRAMES 128
+
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return
+
+#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
+
+#define PROFILE_DRAW_WIDTH 3
+#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
+#define PROFILE_DRAW_DP_PER_MS 7
+
+// Number of floats we want to display from FrameTimingData
+// If this is changed make sure to update the indexes below
+#define NUM_ELEMENTS 4
+
+#define RECORD_INDEX 0
+#define PREPARE_INDEX 1
+#define PLAYBACK_INDEX 2
+#define SWAPBUFFERS_INDEX 3
+
+// Must be NUM_ELEMENTS in size
+static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
+static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
+static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
+
+// We could get this from TimeLord and use the actual frame interval, but
+// this is good enough
+#define FRAME_THRESHOLD 16
+
+namespace android {
+namespace uirenderer {
+
+static int dpToPx(int dp, float density) {
+ return (int) (dp * density + 0.5f);
+}
+
+DrawProfiler::DrawProfiler()
+ : mType(kNone)
+ , mDensity(0)
+ , mData(NULL)
+ , mDataSize(0)
+ , mCurrentFrame(-1)
+ , mPreviousTime(0)
+ , mVerticalUnit(0)
+ , mHorizontalUnit(0)
+ , mThresholdStroke(0) {
+ setDensity(1);
+}
+
+DrawProfiler::~DrawProfiler() {
+ destroyData();
+}
+
+void DrawProfiler::setDensity(float density) {
+ if (CC_UNLIKELY(mDensity != density)) {
+ mDensity = density;
+ mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+ mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+ mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+ }
+}
+
+void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
+ RETURN_IF_DISABLED();
+ mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
+ mPreviousTime = systemTime(CLOCK_MONOTONIC);
+}
+
+void DrawProfiler::markPlaybackStart() {
+ RETURN_IF_DISABLED();
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+ mPreviousTime = now;
+}
+
+void DrawProfiler::markPlaybackEnd() {
+ RETURN_IF_DISABLED();
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+ mPreviousTime = now;
+}
+
+void DrawProfiler::finishFrame() {
+ RETURN_IF_DISABLED();
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+ mPreviousTime = now;
+ mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
+}
+
+void DrawProfiler::unionDirty(SkRect* dirty) {
+ RETURN_IF_DISABLED();
+ // Not worth worrying about minimizing the dirty region for debugging, so just
+ // dirty the entire viewport.
+ if (dirty) {
+ dirty->setEmpty();
+ }
+}
+
+void DrawProfiler::draw(OpenGLRenderer* canvas) {
+ if (CC_LIKELY(mType != kBars)) {
+ return;
+ }
+
+ prepareShapes(canvas->getViewportHeight());
+ drawGraph(canvas);
+ drawCurrentFrame(canvas);
+ drawThreshold(canvas);
+}
+
+void DrawProfiler::createData() {
+ if (mData) return;
+
+ mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
+ if (mDataSize <= 0) mDataSize = 1;
+ if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
+ mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
+ mRects = new float*[NUM_ELEMENTS];
+ for (int i = 0; i < NUM_ELEMENTS; i++) {
+ // 4 floats per rect
+ mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
+ }
+ mCurrentFrame = 0;
+}
+
+void DrawProfiler::destroyData() {
+ delete mData;
+ mData = NULL;
+}
+
+void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
+ r.top = r.bottom - (data * mVerticalUnit);
+ shapeOutput[0] = r.left;
+ shapeOutput[1] = r.top;
+ shapeOutput[2] = r.right;
+ shapeOutput[3] = r.bottom;
+ r.bottom = r.top;
+}
+
+void DrawProfiler::prepareShapes(const int baseline) {
+ Rect r;
+ r.right = mHorizontalUnit;
+ for (int i = 0; i < mDataSize; i++) {
+ const int shapeIndex = i * 4;
+ r.bottom = baseline;
+ addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
+ addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
+ addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
+ addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
+ r.translate(mHorizontalUnit, 0);
+ }
+}
+
+void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
+ SkPaint paint;
+ for (int i = 0; i < NUM_ELEMENTS; i++) {
+ paint.setColor(ELEMENT_COLORS[i]);
+ canvas->drawRects(mRects[i], mDataSize * 4, &paint);
+ }
+}
+
+void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
+ // This draws a solid rect over the entirety of the current frame's shape
+ // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
+ // which will therefore fully overlap the previously drawn rects
+ SkPaint paint;
+ paint.setColor(CURRENT_FRAME_COLOR);
+ const int i = mCurrentFrame * 4;
+ canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
+ mRects[0][i+3], &paint);
+}
+
+void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
+ SkPaint paint;
+ paint.setColor(THRESHOLD_COLOR);
+ paint.setStrokeWidth(mThresholdStroke);
+
+ float pts[4];
+ pts[0] = 0.0f;
+ pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+ pts[2] = canvas->getViewportWidth();
+ canvas->drawLines(pts, 4, &paint);
+}
+
+DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
+ ProfileType type = kNone;
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+ if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+ if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
+ type = kBars;
+ } else if (!strcmp(buf, "true")) {
+ type = kConsole;
+ }
+ }
+ return type;
+}
+
+bool DrawProfiler::loadSystemProperties() {
+ ProfileType newType = loadRequestedProfileType();
+ if (newType != mType) {
+ mType = newType;
+ if (mType == kNone) {
+ destroyData();
+ } else {
+ createData();
+ }
+ return true;
+ }
+ return false;
+}
+
+void DrawProfiler::dumpData(int fd) {
+ RETURN_IF_DISABLED();
+
+ // This method logs the last N frames (where N is <= mDataSize) since the
+ // last call to dumpData(). In other words if there's a dumpData(), draw frame,
+ // dumpData(), the last dumpData() should only log 1 frame.
+
+ const FrameTimingData emptyData = {0, 0, 0, 0};
+
+ FILE *file = fdopen(fd, "a");
+ fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+
+ for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
+ int i = (mCurrentFrame + frameOffset) % mDataSize;
+ if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
+ continue;
+ }
+ fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+ mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
+ }
+ // reset the buffer
+ memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
+ mCurrentFrame = 0;
+
+ fflush(file);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
new file mode 100644
index 0000000..7c06e5d
--- /dev/null
+++ b/libs/hwui/DrawProfiler.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef DRAWPROFILER_H
+#define DRAWPROFILER_H
+
+#include <utils/Timers.h>
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class OpenGLRenderer;
+
+class DrawProfiler {
+public:
+ DrawProfiler();
+ ~DrawProfiler();
+
+ bool loadSystemProperties();
+ void setDensity(float density);
+
+ void startFrame(nsecs_t recordDurationNanos = 0);
+ void markPlaybackStart();
+ void markPlaybackEnd();
+ void finishFrame();
+
+ void unionDirty(SkRect* dirty);
+ void draw(OpenGLRenderer* canvas);
+
+ void dumpData(int fd);
+
+private:
+ enum ProfileType {
+ kNone,
+ kConsole,
+ kBars,
+ };
+
+ typedef struct {
+ float record;
+ float prepare;
+ float playback;
+ float swapBuffers;
+ } FrameTimingData;
+
+ void createData();
+ void destroyData();
+
+ void addRect(Rect& r, float data, float* shapeOutput);
+ void prepareShapes(const int baseline);
+ void drawGraph(OpenGLRenderer* canvas);
+ void drawCurrentFrame(OpenGLRenderer* canvas);
+ void drawThreshold(OpenGLRenderer* canvas);
+
+ ProfileType loadRequestedProfileType();
+
+ ProfileType mType;
+ float mDensity;
+
+ FrameTimingData* mData;
+ int mDataSize;
+
+ int mCurrentFrame;
+ nsecs_t mPreviousTime;
+
+ int mVerticalUnit;
+ int mHorizontalUnit;
+ int mThresholdStroke;
+
+ /*
+ * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
+ * groups such that each group is drawn with the same paint.
+ * For example mRects[0] is the array of rect floats suitable for
+ * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
+ * information.
+ */
+ float** mRects;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWPROFILER_H */
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8d19ca2..3910381 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -72,19 +72,20 @@ status_t TextSetupFunctor::operator ()(int what, void* data) {
break;
}
}
- renderer->setupDrawColorFilter();
- renderer->setupDrawShader();
- renderer->setupDrawBlending(true, mode);
+ renderer->setupDrawColorFilter(paint->getColorFilter());
+ renderer->setupDrawShader(paint->getShader());
+ renderer->setupDrawBlending(paint);
renderer->setupDrawProgram();
- renderer->setupDrawModelView(x, y, x, y, pureTranslate, true);
+ renderer->setupDrawModelView(kModelViewMode_Translate, false,
+ 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate);
// Calling setupDrawTexture with the name 0 will enable the
// uv attributes and increase the texture unit count
// texture binding will be performed by the font renderer as
// needed
renderer->setupDrawTexture(0);
renderer->setupDrawPureColorUniforms();
- renderer->setupDrawColorFilterUniforms();
- renderer->setupDrawShaderUniforms(pureTranslate);
+ renderer->setupDrawColorFilterUniforms(paint->getColorFilter());
+ renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate);
renderer->setupDrawTextGammaUniforms();
return NO_ERROR;
@@ -497,7 +498,7 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
}
checkTextureUpdate();
- caches.bindIndicesBuffer();
+ caches.bindQuadIndicesBuffer();
if (!mDrawn) {
// If returns true, a VBO was bound and we must
@@ -514,8 +515,8 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
- caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
- caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
+ caches.bindPositionVertexPointer(force, &mesh[0].x);
+ caches.bindTexCoordsVertexPointer(force, &mesh[0].u);
force = false;
glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
@@ -586,12 +587,12 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
}
}
-void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
+void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
mCurrentFont = Font::create(this, paint, matrix);
}
-FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
+FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -612,8 +613,9 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
- uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
- uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
@@ -634,8 +636,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
memset(dataBuffer, 0, size);
- int penX = radius - bounds.left;
- int penY = radius - bounds.bottom;
+ int penX = intRadius - bounds.left;
+ int penY = intRadius - bounds.bottom;
if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
// text has non-whitespace, so draw and blur to create the shadow
@@ -675,7 +677,8 @@ void FontRenderer::finishRender() {
issueDrawCommand();
}
-void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
+void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs,
+ const SkMatrix& matrix) {
Font* font = Font::create(this, paint, matrix);
font->precache(paint, text, numGlyphs);
}
@@ -684,7 +687,7 @@ void FontRenderer::endPrecaching() {
checkTextureUpdate();
}
-bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
if (!mCurrentFont) {
@@ -702,8 +705,8 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t
return mDrawn;
}
-bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
+bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
float hOffset, float vOffset, Rect* bounds, Functor* functor) {
if (!mCurrentFont) {
ALOGE("No font set");
@@ -725,9 +728,10 @@ void FontRenderer::removeFont(const Font* font) {
}
}
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
- if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
@@ -766,12 +770,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int
}
#endif
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+ float *gaussian = new float[2 * intRadius + 1];
+ Blur::generateGaussianWeights(gaussian, intRadius);
uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
+ Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
delete[] gaussian;
delete[] scratch;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index aa7e776..5c96c6b 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -44,8 +44,6 @@ namespace RSC {
}
#endif
-class Functor;
-
namespace android {
namespace uirenderer {
@@ -64,7 +62,7 @@ public:
};
TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
+ int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(),
renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
alpha(alpha), mode(mode), paint(paint) {
}
@@ -78,7 +76,7 @@ public:
bool pureTranslate;
int alpha;
SkXfermode::Mode mode;
- SkPaint* paint;
+ const SkPaint* paint;
};
///////////////////////////////////////////////////////////////////////////////
@@ -97,20 +95,20 @@ public:
mGammaTable = gammaTable;
}
- void setFont(SkPaint* paint, const mat4& matrix);
+ void setFont(const SkPaint* paint, const SkMatrix& matrix);
- void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix);
+ void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
void endPrecaching();
// bounds is an out parameter
- bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds,
- Functor* functor, bool forceFinish = true);
+ bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
+ Rect* bounds, Functor* functor, bool forceFinish = true);
// bounds is an out parameter
- bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds,
- Functor* functor);
+ bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
+ float hOffset, float vOffset, Rect* bounds, Functor* functor);
struct DropShadow {
DropShadow() { };
@@ -130,8 +128,8 @@ public:
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
- DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
+ DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
+ uint32_t len, int numGlyphs, float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
@@ -220,7 +218,7 @@ private:
int32_t width, int32_t height);
// the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
+ void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 0916942..ffd1e8c 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -115,7 +115,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
+void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
if (texture) {
const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
@@ -185,7 +185,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
mCache.removeOldest();
}
- generateTexture(colors, positions, count, texture);
+ generateTexture(colors, positions, texture);
mSize += size;
mCache.put(gradient, texture);
@@ -238,8 +238,7 @@ void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float am
dst += 4 * sizeof(float);
}
-void GradientCache::generateTexture(uint32_t* colors, float* positions,
- int count, Texture* texture) {
+void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) {
const uint32_t width = texture->width;
const GLsizei rowBytes = width * bytesPerPixel();
uint8_t pixels[rowBytes * texture->height];
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 43934d9..6a783b1 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -151,7 +151,7 @@ private:
Texture* addLinearGradient(GradientCacheEntry& gradient,
uint32_t* colors, float* positions, int count);
- void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture);
+ void generateTexture(uint32_t* colors, float* positions, Texture* texture);
struct GradientInfo {
uint32_t width;
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
new file mode 100644
index 0000000..fc0e8a0
--- /dev/null
+++ b/libs/hwui/Interpolator.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 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 "Interpolator"
+
+#include "Interpolator.h"
+
+#include <cmath>
+#include <cutils/log.h>
+
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+Interpolator* Interpolator::createDefaultInterpolator() {
+ return new AccelerateDecelerateInterpolator();
+}
+
+float AccelerateDecelerateInterpolator::interpolate(float input) {
+ return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f;
+}
+
+float AccelerateInterpolator::interpolate(float input) {
+ if (mFactor == 1.0f) {
+ return input * input;
+ } else {
+ return pow(input, mDoubleFactor);
+ }
+}
+
+float AnticipateInterpolator::interpolate(float t) {
+ return t * t * ((mTension + 1) * t - mTension);
+}
+
+static float a(float t, float s) {
+ return t * t * ((s + 1) * t - s);
+}
+
+static float o(float t, float s) {
+ return t * t * ((s + 1) * t + s);
+}
+
+float AnticipateOvershootInterpolator::interpolate(float t) {
+ if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
+ else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
+}
+
+static float bounce(float t) {
+ return t * t * 8.0f;
+}
+
+float BounceInterpolator::interpolate(float t) {
+ t *= 1.1226f;
+ if (t < 0.3535f) return bounce(t);
+ else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
+ else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
+ else return bounce(t - 1.0435f) + 0.95f;
+}
+
+float CycleInterpolator::interpolate(float input) {
+ return sinf(2 * mCycles * M_PI * input);
+}
+
+float DecelerateInterpolator::interpolate(float input) {
+ float result;
+ if (mFactor == 1.0f) {
+ result = 1.0f - (1.0f - input) * (1.0f - input);
+ } else {
+ result = 1.0f - pow((1.0f - input), 2 * mFactor);
+ }
+ return result;
+}
+
+float OvershootInterpolator::interpolate(float t) {
+ t -= 1.0f;
+ return t * t * ((mTension + 1) * t + mTension) + 1.0f;
+}
+
+LUTInterpolator::LUTInterpolator(float* values, size_t size) {
+ mValues = values;
+ mSize = size;
+}
+
+LUTInterpolator::~LUTInterpolator() {
+ delete mValues;
+ mValues = 0;
+}
+
+float LUTInterpolator::interpolate(float input) {
+ float lutpos = input * mSize;
+ if (lutpos >= (mSize - 1)) {
+ return mValues[mSize - 1];
+ }
+
+ float ipart, weight;
+ weight = modff(lutpos, &ipart);
+
+ int i1 = (int) ipart;
+ int i2 = MathUtils::min(i1 + 1, mSize - 1);
+
+ LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+ " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+ i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
+ float v1 = mValues[i1];
+ float v2 = mValues[i2];
+
+ return MathUtils::lerp(v1, v2, weight);
+}
+
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
new file mode 100644
index 0000000..dfa0a85
--- /dev/null
+++ b/libs/hwui/Interpolator.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef INTERPOLATOR_H
+#define INTERPOLATOR_H
+
+#include <stddef.h>
+
+#include <cutils/compiler.h>
+
+namespace android {
+namespace uirenderer {
+
+class Interpolator {
+public:
+ virtual ~Interpolator() {}
+
+ virtual float interpolate(float input) = 0;
+
+ static Interpolator* createDefaultInterpolator();
+
+protected:
+ Interpolator() {}
+};
+
+class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator {
+public:
+ virtual float interpolate(float input);
+};
+
+class ANDROID_API AccelerateInterpolator : public Interpolator {
+public:
+ AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {}
+ virtual float interpolate(float input);
+private:
+ const float mFactor;
+ const float mDoubleFactor;
+};
+
+class ANDROID_API AnticipateInterpolator : public Interpolator {
+public:
+ AnticipateInterpolator(float tension) : mTension(tension) {}
+ virtual float interpolate(float input);
+private:
+ const float mTension;
+};
+
+class ANDROID_API AnticipateOvershootInterpolator : public Interpolator {
+public:
+ AnticipateOvershootInterpolator(float tension) : mTension(tension) {}
+ virtual float interpolate(float input);
+private:
+ const float mTension;
+};
+
+class ANDROID_API BounceInterpolator : public Interpolator {
+public:
+ virtual float interpolate(float input);
+};
+
+class ANDROID_API CycleInterpolator : public Interpolator {
+public:
+ CycleInterpolator(float cycles) : mCycles(cycles) {}
+ virtual float interpolate(float input);
+private:
+ const float mCycles;
+};
+
+class ANDROID_API DecelerateInterpolator : public Interpolator {
+public:
+ DecelerateInterpolator(float factor) : mFactor(factor) {}
+ virtual float interpolate(float input);
+private:
+ const float mFactor;
+};
+
+class ANDROID_API LinearInterpolator : public Interpolator {
+public:
+ virtual float interpolate(float input) { return input; }
+};
+
+class ANDROID_API OvershootInterpolator : public Interpolator {
+public:
+ OvershootInterpolator(float tension) : mTension(tension) {}
+ virtual float interpolate(float input);
+private:
+ const float mTension;
+};
+
+class ANDROID_API LUTInterpolator : public Interpolator {
+public:
+ LUTInterpolator(float* values, size_t size);
+ ~LUTInterpolator();
+
+ virtual float interpolate(float input);
+
+private:
+ float* mValues;
+ size_t mSize;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* INTERPOLATOR_H */
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 987bf03..8639ae1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,18 +18,21 @@
#include <utils/Log.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
+#include "RenderState.h"
#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
- caches(Caches::getInstance()), texture(caches) {
+Layer::Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight)
+ : caches(Caches::getInstance())
+ , renderState(renderState)
+ , texture(caches) {
mesh = NULL;
meshElementCount = 0;
cacheable = true;
@@ -41,22 +44,26 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
colorFilter = NULL;
deferredUpdateScheduled = false;
renderer = NULL;
- displayList = NULL;
+ renderNode = NULL;
fbo = 0;
stencil = NULL;
debugDrawUpdate = false;
hasDrawnSinceUpdate = false;
+ forceFilter = false;
deferredList = NULL;
+ convexMask = NULL;
caches.resourceCache.incrementRefcount(this);
+ rendererLightPosDirty = true;
}
Layer::~Layer() {
- if (colorFilter) caches.resourceCache.decrementRefcount(colorFilter);
+ SkSafeUnref(colorFilter);
removeFbo();
deleteTexture();
delete[] mesh;
delete deferredList;
+ delete renderer;
}
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -67,6 +74,24 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
}
+void Layer::requireRenderer() {
+ if (!renderer) {
+ renderer = new LayerRenderer(renderState, this);
+ renderer->initProperties();
+ }
+}
+
+void Layer::updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer) {
+ if (renderer && rendererLightPosDirty) {
+ // re-init renderer's light position, based upon last cached location in window
+ Vector3 lightPos = rootRenderer.getLightCenter();
+ cachedInvTransformInWindow.mapPoint3d(lightPos);
+ renderer->initLight(lightPos, rootRenderer.getLightRadius(),
+ rootRenderer.getAmbientShadowAlpha(), rootRenderer.getSpotShadowAlpha());
+ rendererLightPosDirty = false;
+ }
+}
+
bool Layer::resize(const uint32_t width, const uint32_t height) {
uint32_t desiredWidth = computeIdealWidth(width);
uint32_t desiredHeight = computeIdealWidth(height);
@@ -75,6 +100,8 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
return true;
}
+ ATRACE_NAME("resizeLayer");
+
const uint32_t maxTextureSize = caches.maxTextureSize;
if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
@@ -113,36 +140,38 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
void Layer::removeFbo(bool flush) {
if (stencil) {
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLuint previousFbo = renderState.getFramebuffer();
+ renderState.bindFramebuffer(fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
caches.renderBufferCache.put(stencil);
stencil = NULL;
}
if (fbo) {
- if (flush) LayerRenderer::flushLayer(this);
+ if (flush) LayerRenderer::flushLayer(renderState, this);
// If put fails the cache will delete the FBO
caches.fboCache.put(fbo);
fbo = 0;
}
}
-void Layer::setPaint(SkPaint* paint) {
+void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) {
+ requireRenderer();
+ this->renderNode = renderNode;
+ const Rect r(left, top, right, bottom);
+ dirtyRect.unionWith(r);
+ deferredUpdateScheduled = true;
+}
+
+void Layer::setPaint(const SkPaint* paint) {
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ setColorFilter((paint) ? paint->getColorFilter() : NULL);
}
-void Layer::setColorFilter(SkiaColorFilter* filter) {
- if (colorFilter) {
- caches.resourceCache.decrementRefcount(colorFilter);
- }
- colorFilter = filter;
- if (colorFilter) {
- caches.resourceCache.incrementRefcount(colorFilter);
- }
+void Layer::setColorFilter(SkColorFilter* filter) {
+ SkRefCnt_SafeAssign(colorFilter, filter);
}
void Layer::bindTexture() const {
@@ -186,7 +215,8 @@ void Layer::allocateTexture() {
}
}
-void Layer::defer() {
+void Layer::defer(const OpenGLRenderer& rootRenderer) {
+ updateLightPosFromRenderer(rootRenderer);
const float width = layer.getWidth();
const float height = layer.getHeight();
@@ -195,26 +225,24 @@ void Layer::defer() {
dirtyRect.set(0, 0, width, height);
}
- if (deferredList) {
- deferredList->reset(dirtyRect);
- } else {
- deferredList = new DeferredDisplayList(dirtyRect);
- }
+ delete deferredList;
+ deferredList = new DeferredDisplayList(dirtyRect);
+
DeferStateStruct deferredState(*deferredList, *renderer,
- DisplayList::kReplayFlag_ClipChildren);
+ RenderNode::kReplayFlag_ClipChildren);
- renderer->initViewport(width, height);
+ renderer->setViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
- displayList->defer(deferredState, 0);
+ renderNode->computeOrdering();
+ renderNode->defer(deferredState, 0);
deferredUpdateScheduled = false;
}
void Layer::cancelDefer() {
- renderer = NULL;
- displayList = NULL;
+ renderNode = NULL;
deferredUpdateScheduled = false;
if (deferredList) {
delete deferredList;
@@ -232,27 +260,26 @@ void Layer::flush() {
deferredList->flush(*renderer, dirtyRect);
renderer->finish();
- renderer = NULL;
dirtyRect.setEmpty();
- displayList = NULL;
+ renderNode = NULL;
}
}
-void Layer::render() {
+void Layer::render(const OpenGLRenderer& rootRenderer) {
+ updateLightPosFromRenderer(rootRenderer);
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
- renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren);
+ renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
renderer->finish();
- renderer = NULL;
dirtyRect.setEmpty();
deferredUpdateScheduled = false;
- displayList = NULL;
+ renderNode = NULL;
}
}; // namespace uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index b70042f..38c29c7 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_HWUI_LAYER_H
#define ANDROID_HWUI_LAYER_H
+#include <cutils/compiler.h>
#include <sys/types.h>
+#include <utils/StrongPointer.h>
#include <GLES2/gl2.h>
@@ -26,9 +28,9 @@
#include <SkPaint.h>
#include <SkXfermode.h>
+#include "Matrix.h"
#include "Rect.h"
#include "RenderBuffer.h"
-#include "SkiaColorFilter.h"
#include "Texture.h"
#include "Vertex.h"
@@ -41,16 +43,18 @@ namespace uirenderer {
// Forward declarations
class Caches;
+class RenderState;
class OpenGLRenderer;
-class DisplayList;
+class RenderNode;
class DeferredDisplayList;
class DeferStateStruct;
/**
* A layer has dimensions and is backed by an OpenGL texture or FBO.
*/
-struct Layer {
- Layer(const uint32_t layerWidth, const uint32_t layerHeight);
+class Layer {
+public:
+ Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight);
~Layer();
static uint32_t computeIdealWidth(uint32_t layerWidth);
@@ -82,15 +86,13 @@ struct Layer {
regionRect.translate(layer.left, layer.top);
}
- void updateDeferred(OpenGLRenderer* renderer, DisplayList* displayList,
- int left, int top, int right, int bottom) {
- this->renderer = renderer;
- this->displayList = displayList;
- const Rect r(left, top, right, bottom);
- dirtyRect.unionWith(r);
- deferredUpdateScheduled = true;
+ void setWindowTransform(Matrix4& windowTransform) {
+ cachedInvTransformInWindow.loadInverse(windowTransform);
+ rendererLightPosDirty = true;
}
+ void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
+
inline uint32_t getWidth() const {
return texture.width;
}
@@ -115,7 +117,7 @@ struct Layer {
texture.height = height;
}
- ANDROID_API void setPaint(SkPaint* paint);
+ ANDROID_API void setPaint(const SkPaint* paint);
inline void setBlend(bool blend) {
texture.blend = blend;
@@ -125,6 +127,14 @@ struct Layer {
return texture.blend;
}
+ inline void setForceFilter(bool forceFilter) {
+ this->forceFilter = forceFilter;
+ }
+
+ inline bool getForceFilter() const {
+ return forceFilter;
+ }
+
inline void setAlpha(int alpha) {
this->alpha = alpha;
}
@@ -216,11 +226,19 @@ struct Layer {
this->textureLayer = textureLayer;
}
- inline SkiaColorFilter* getColorFilter() const {
+ inline SkColorFilter* getColorFilter() const {
return colorFilter;
}
- ANDROID_API void setColorFilter(SkiaColorFilter* filter);
+ ANDROID_API void setColorFilter(SkColorFilter* filter);
+
+ inline void setConvexMask(const SkPath* convexMask) {
+ this->convexMask = convexMask;
+ }
+
+ inline const SkPath* getConvexMask() {
+ return convexMask;
+ }
void bindStencilRenderBuffer() const;
@@ -244,10 +262,10 @@ struct Layer {
return transform;
}
- void defer();
+ void defer(const OpenGLRenderer& rootRenderer);
void cancelDefer();
void flush();
- void render();
+ void render(const OpenGLRenderer& rootRenderer);
/**
* Bounds of the layer.
@@ -284,14 +302,19 @@ struct Layer {
*/
bool deferredUpdateScheduled;
OpenGLRenderer* renderer;
- DisplayList* displayList;
+ sp<RenderNode> renderNode;
Rect dirtyRect;
bool debugDrawUpdate;
bool hasDrawnSinceUpdate;
private:
+ void requireRenderer();
+ void updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer);
+
Caches& caches;
+ RenderState& renderState;
+
/**
* Name of the FBO used to render the layer. If the name is 0
* this layer is not backed by an FBO, but a simple texture.
@@ -338,12 +361,18 @@ private:
/**
* Color filter used to draw this layer. Optional.
*/
- SkiaColorFilter* colorFilter;
+ SkColorFilter* colorFilter;
+
+ /**
+ * Indicates raster data backing the layer is scaled, requiring filtration.
+ */
+ bool forceFilter;
/**
* Opacity of the layer.
*/
int alpha;
+
/**
* Blending mode of the layer.
*/
@@ -360,11 +389,24 @@ private:
mat4 transform;
/**
+ * Cached transform of layer in window, updated only on creation / resize
+ */
+ mat4 cachedInvTransformInWindow;
+ bool rendererLightPosDirty;
+
+ /**
* Used to defer display lists when the layer is updated with a
* display list.
*/
DeferredDisplayList* deferredList;
+ /**
+ * This convex path should be used to mask the layer's draw to the screen.
+ *
+ * Data not owned/managed by layer object.
+ */
+ const SkPath* convexMask;
+
}; // struct Layer
}; // namespace uirenderer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 6be0146..13869aa 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -91,7 +91,7 @@ void LayerCache::clear() {
mCache.clear();
}
-Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
+Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) {
Layer* layer = NULL;
LayerEntry entry(width, height);
@@ -108,7 +108,7 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
} else {
LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
- layer = new Layer(entry.mWidth, entry.mHeight);
+ layer = new Layer(renderState, entry.mWidth, entry.mHeight);
layer->setBlend(true);
layer->setEmpty(true);
layer->setFbo(0);
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 221bfe0..1b0fc2c 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -24,6 +24,8 @@
namespace android {
namespace uirenderer {
+class RenderState;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -55,7 +57,7 @@ public:
* @param width The desired width of the layer
* @param height The desired height of the layer
*/
- Layer* get(const uint32_t width, const uint32_t height);
+ Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height);
/**
* Adds the layer to the cache. The layer will not be added if there is
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 006d145..e3b0daf 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -15,11 +15,13 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
#include <ui/Rect.h>
#include <private/hwui/DrawGlInfo.h>
+#include "RenderState.h"
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Matrix.h"
@@ -33,21 +35,19 @@ namespace uirenderer {
// Rendering
///////////////////////////////////////////////////////////////////////////////
-LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) {
+LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
+ : OpenGLRenderer(renderState)
+ , mLayer(layer) {
}
LayerRenderer::~LayerRenderer() {
}
-void LayerRenderer::setViewport(int width, int height) {
- initViewport(width, height);
-}
-
status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
- glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo());
+ renderState().bindFramebuffer(mLayer->getFbo());
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
@@ -92,7 +92,7 @@ void LayerRenderer::finish() {
// who will invoke OpenGLRenderer::resume()
}
-GLint LayerRenderer::getTargetFbo() const {
+GLuint LayerRenderer::getTargetFbo() const {
return mLayer->getFbo();
}
@@ -117,7 +117,7 @@ void LayerRenderer::ensureStencilBuffer() {
///////////////////////////////////////////////////////////////////////////////
Region* LayerRenderer::getRegion() const {
- if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
+ if (currentSnapshot()->flags & Snapshot::kFlagFboTarget) {
return OpenGLRenderer::getRegion();
}
return &mLayer->region;
@@ -184,7 +184,8 @@ void LayerRenderer::generateMesh() {
// Layers management
///////////////////////////////////////////////////////////////////////////////
-Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
+Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+ ATRACE_CALL();
LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
Caches& caches = Caches::getInstance();
@@ -195,7 +196,7 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
}
caches.activeTexture(0);
- Layer* layer = caches.layerCache.get(width, height);
+ Layer* layer = caches.layerCache.get(renderState, width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
return NULL;
@@ -221,15 +222,13 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
width / float(layer->getWidth()), 0.0f);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
- layer->setBlend(!isOpaque);
layer->setColorFilter(NULL);
layer->setDirty(true);
layer->region.clear();
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+ GLuint previousFbo = renderState.getFramebuffer();
- glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+ renderState.bindFramebuffer(layer->getFbo());
layer->bindTexture();
// Initialize the texture if needed
@@ -240,7 +239,7 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
// This should only happen if we run out of memory
if (glGetError() != GL_NO_ERROR) {
ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
caches.resourceCache.decrementRefcount(layer);
return NULL;
}
@@ -249,7 +248,7 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
return layer;
}
@@ -270,13 +269,12 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
return true;
}
-Layer* LayerRenderer::createTextureLayer(bool isOpaque) {
+Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
LAYER_RENDERER_LOGD("Creating new texture layer");
- Layer* layer = new Layer(0, 0);
+ Layer* layer = new Layer(renderState, 0, 0);
layer->setCacheable(false);
layer->setTextureLayer(true);
- layer->setBlend(!isOpaque);
layer->setEmpty(true);
layer->setFbo(0);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
@@ -292,14 +290,15 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) {
}
void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
- bool isOpaque, GLenum renderTarget, float* transform) {
+ bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform) {
if (layer) {
layer->setBlend(!isOpaque);
+ layer->setForceFilter(forceFilter);
layer->setSize(width, height);
layer->layer.set(0.0f, 0.0f, width, height);
layer->region.set(width, height);
layer->regionRect.set(0.0f, 0.0f, width, height);
- layer->getTexTransform().load(transform);
+ layer->getTexTransform().load(textureTransform);
if (renderTarget != layer->getRenderTarget()) {
layer->setRenderTarget(renderTarget);
@@ -312,6 +311,7 @@ void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t he
void LayerRenderer::destroyLayer(Layer* layer) {
if (layer) {
+ ATRACE_CALL();
LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
layer->getWidth(), layer->getHeight(), layer->getFbo());
@@ -337,7 +337,7 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) {
}
}
-void LayerRenderer::flushLayer(Layer* layer) {
+void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
if (!layer) return;
@@ -346,20 +346,23 @@ void LayerRenderer::flushLayer(Layer* layer) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Extensions::getInstance().hasDiscardFramebuffer()) {
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLuint previousFbo = renderState.getFramebuffer();
+ if (fbo != previousFbo) {
+ renderState.bindFramebuffer(fbo);
+ }
const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ if (fbo != previousFbo) {
+ renderState.bindFramebuffer(previousFbo);
+ }
}
}
#endif
}
-bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
+bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
Caches& caches = Caches::getInstance();
if (layer && bitmap->width() <= caches.maxTextureSize &&
bitmap->height() <= caches.maxTextureSize) {
@@ -374,7 +377,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
GLuint texture;
GLuint previousFbo;
- GLuint previousViewport[4];
+ GLsizei previousViewportWidth;
+ GLsizei previousViewportHeight;
GLenum format;
GLenum type;
@@ -382,20 +386,20 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
GLenum error = GL_NO_ERROR;
bool status = false;
- switch (bitmap->config()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
format = GL_ALPHA;
type = GL_UNSIGNED_BYTE;
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
default:
format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
@@ -409,9 +413,9 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
layer->setAlpha(255, SkXfermode::kSrc_Mode);
layer->setFbo(fbo);
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ previousFbo = renderState.getFramebuffer();
+ renderState.getViewport(&previousViewportWidth, &previousViewportHeight);
+ renderState.bindFramebuffer(fbo);
glGenTextures(1, &texture);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
@@ -436,7 +440,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
if ((error = glGetError()) != GL_NO_ERROR) goto error;
{
- LayerRenderer renderer(layer);
+ LayerRenderer renderer(renderState, layer);
renderer.setViewport(bitmap->width(), bitmap->height());
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
@@ -476,13 +480,12 @@ error:
}
#endif
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
caches.deleteTexture(texture);
caches.fboCache.put(fbo);
- glViewport(previousViewport[0], previousViewport[1],
- previousViewport[2], previousViewport[3]);
+ renderState.setViewport(previousViewportWidth, previousViewportHeight);
return status;
}
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 5f86731..bf7828c 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -27,6 +27,8 @@
namespace android {
namespace uirenderer {
+class RenderState;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -44,30 +46,30 @@ namespace uirenderer {
class LayerRenderer: public OpenGLRenderer {
public:
- ANDROID_API LayerRenderer(Layer* layer);
+ LayerRenderer(RenderState& renderState, Layer* layer);
virtual ~LayerRenderer();
- virtual void setViewport(int width, int height);
+ virtual void onViewportInitialized() { /* do nothing */ }
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- ANDROID_API static Layer* createTextureLayer(bool isOpaque);
- ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
- ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
- ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
- bool isOpaque, GLenum renderTarget, float* transform);
- ANDROID_API static void destroyLayer(Layer* layer);
+ static Layer* createTextureLayer(RenderState& renderState);
+ static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height);
+ static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
+ static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
+ bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform);
+ static void destroyLayer(Layer* layer);
ANDROID_API static void destroyLayerDeferred(Layer* layer);
- ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap);
+ static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
- static void flushLayer(Layer* layer);
+ static void flushLayer(RenderState& renderState, Layer* layer);
protected:
virtual void ensureStencilBuffer();
virtual bool hasLayer() const;
virtual Region* getRegion() const;
- virtual GLint getTargetFbo() const;
+ virtual GLuint getTargetFbo() const;
virtual bool suppressErrorChecks() const;
private:
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index ba22071..9f2014f 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -89,8 +89,9 @@ uint8_t Matrix4::getType() const {
float m01 = data[kSkewX];
float m10 = data[kSkewY];
float m11 = data[kScaleY];
+ float m32 = data[kTranslateZ];
- if (m01 != 0.0f || m10 != 0.0f) {
+ if (m01 != 0.0f || m10 != 0.0f || m32 != 0.0f) {
mType |= kTypeAffine;
}
@@ -131,11 +132,13 @@ bool Matrix4::changesBounds() const {
}
bool Matrix4::isPureTranslate() const {
- return getGeometryType() <= kTypeTranslate;
+ // NOTE: temporary hack to workaround ignoreTransform behavior with Z values
+ // TODO: separate this into isPure2dTranslate vs isPure3dTranslate
+ return getGeometryType() <= kTypeTranslate && (data[kTranslateZ] == 0.0f);
}
bool Matrix4::isSimple() const {
- return getGeometryType() <= (kTypeScale | kTypeTranslate);
+ return getGeometryType() <= (kTypeScale | kTypeTranslate) && (data[kTranslateZ] == 0.0f);
}
bool Matrix4::isIdentity() const {
@@ -382,6 +385,19 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float
mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
}
+float Matrix4::mapZ(const Vector3& orig) const {
+ // duplicates logic for mapPoint3d's z coordinate
+ return orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
+}
+
+void Matrix4::mapPoint3d(Vector3& vec) const {
+ //TODO: optimize simple case
+ const Vector3 orig(vec);
+ vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX];
+ vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY];
+ vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
+}
+
#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
void Matrix4::mapPoint(float& x, float& y) const {
@@ -401,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const {
}
void Matrix4::mapRect(Rect& r) const {
+ if (isIdentity()) return;
+
if (isSimple()) {
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
@@ -466,8 +484,8 @@ void Matrix4::decomposeScale(float& sx, float& sy) const {
sy = copysignf(sqrtf(len), data[mat4::kScaleY]);
}
-void Matrix4::dump() const {
- ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType());
+void Matrix4::dump(const char* label) const {
+ ALOGD("%s[simple=%d, type=0x%x", label ? label : "Matrix4", isSimple(), getType());
ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index b861ba4..1c5c578 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -26,12 +26,20 @@
namespace android {
namespace uirenderer {
-#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
-#define MATRIX_ARGS(m) \
+#define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_ARGS(m) \
(m)->get(0), (m)->get(1), (m)->get(2), \
(m)->get(3), (m)->get(4), (m)->get(5), \
(m)->get(6), (m)->get(7), (m)->get(8)
+#define MATRIX_4_STRING "[%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" \
+ " [%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]"
+#define MATRIX_4_ARGS(m) \
+ (m)->data[0], (m)->data[4], (m)->data[8], (m)->data[12], \
+ (m)->data[1], (m)->data[5], (m)->data[9], (m)->data[13], \
+ (m)->data[2], (m)->data[6], (m)->data[10], (m)->data[14], \
+ (m)->data[3], (m)->data[7], (m)->data[11], (m)->data[15] \
+
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
@@ -134,17 +142,19 @@ public:
void multiply(float v);
- void translate(float x, float y) {
+ void translate(float x, float y, float z = 0) {
if ((getType() & sGeometryMask) <= kTypeTranslate) {
data[kTranslateX] += x;
data[kTranslateY] += y;
+ data[kTranslateZ] += z;
+ mType |= kTypeUnknown;
} else {
// Doing a translation will only affect the translate bit of the type
// Save the type
uint8_t type = mType;
Matrix4 u;
- u.loadTranslate(x, y, 0.0f);
+ u.loadTranslate(x, y, z);
multiply(u);
// Restore the type and fix the translate bit
@@ -190,15 +200,17 @@ public:
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
- void mapRect(Rect& r) const;
- void mapPoint(float& x, float& y) const;
+ float mapZ(const Vector3& orig) const;
+ void mapPoint3d(Vector3& vec) const;
+ void mapPoint(float& x, float& y) const; // 2d only
+ void mapRect(Rect& r) const; // 2d only
float getTranslateX() const;
float getTranslateY() const;
void decomposeScale(float& sx, float& sy) const;
- void dump() const;
+ void dump(const char* label = NULL) const;
static const Matrix4& identity();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 4d76bed..721ab3d 100644..100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,8 @@
#include <sys/types.h>
#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkShader.h>
#include <SkTypeface.h>
#include <utils/Log.h>
@@ -34,9 +36,20 @@
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
#include "Fence.h"
+#include "RenderState.h"
#include "PathTessellator.h"
#include "Properties.h"
+#include "ShadowTessellator.h"
+#include "SkiaShader.h"
+#include "utils/GLUtils.h"
#include "Vector.h"
+#include "VertexBuffer.h"
+
+#if DEBUG_DETAILED_EVENTS
+ #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
+#else
+ #define EVENT_LOGD(...)
+#endif
namespace android {
namespace uirenderer {
@@ -45,12 +58,12 @@ namespace uirenderer {
// Defines
///////////////////////////////////////////////////////////////////////////////
-#define RAD_TO_DEG (180.0f / 3.14159265f)
-#define MIN_ANGLE 0.001f
-
-#define ALPHA_THRESHOLD 0
-
-#define FILTER(paint) (!paint || paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST)
+static GLenum getFilter(const SkPaint* paint) {
+ if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
+ return GL_LINEAR;
+ }
+ return GL_NEAREST;
+}
///////////////////////////////////////////////////////////////////////////////
// Globals
@@ -119,19 +132,22 @@ static inline T min(T a, T b) {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-OpenGLRenderer::OpenGLRenderer():
- mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
+OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
+ : mFrameStarted(false)
+ , mCaches(Caches::getInstance())
+ , mExtensions(Extensions::getInstance())
+ , mRenderState(renderState)
+ , mScissorOptimizationDisabled(false)
+ , mCountOverdraw(false)
+ , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
+ , mLightRadius(FLT_MIN)
+ , mAmbientShadowAlpha(0)
+ , mSpotShadowAlpha(0) {
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
-
- mFirstSnapshot = new Snapshot;
- mFrameStarted = false;
- mCountOverdraw = false;
-
- mScissorOptimizationDisabled = false;
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -150,56 +166,30 @@ void OpenGLRenderer::initProperties() {
}
}
+void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ mLightCenter = lightCenter;
+ mLightRadius = lightRadius;
+ mAmbientShadowAlpha = ambientShadowAlpha;
+ mSpotShadowAlpha = spotShadowAlpha;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////////////////////////////
-void OpenGLRenderer::setName(const char* name) {
- if (name) {
- mName.setTo(name);
- } else {
- mName.clear();
- }
-}
-
-const char* OpenGLRenderer::getName() const {
- return mName.string();
-}
-
-bool OpenGLRenderer::isDeferred() {
- return false;
-}
-
-void OpenGLRenderer::setViewport(int width, int height) {
- initViewport(width, height);
-
+void OpenGLRenderer::onViewportInitialized() {
glDisable(GL_DITHER);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnableVertexAttribArray(Program::kBindingPosition);
}
-void OpenGLRenderer::initViewport(int width, int height) {
- mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
-
- mWidth = width;
- mHeight = height;
-
- mFirstSnapshot->height = height;
- mFirstSnapshot->viewport.set(0, 0, width, height);
-}
-
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
mCaches.clearGarbage();
-
+ initializeSaveStack(left, top, right, bottom, mLightCenter);
mOpaque = opaque;
- mSnapshot = new Snapshot(mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- mSnapshot->fbo = getTargetFbo();
- mSaveCount = 1;
-
- mSnapshot->setClip(left, top, right, bottom);
mTilingClip.set(left, top, right, bottom);
}
@@ -211,14 +201,14 @@ status_t OpenGLRenderer::startFrame() {
discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
- glViewport(0, 0, mWidth, mHeight);
+ mRenderState.setViewport(getWidth(), getHeight());
// Functors break the tiling extension in pretty spectacular ways
// This ensures we don't use tiling when a functor is going to be
// invoked during the frame
mSuppressTiling = mCaches.hasRegisteredFunctors();
- startTiling(mSnapshot, true);
+ startTilingCurrentClip(true);
debugOverdraw(true, true);
@@ -226,10 +216,6 @@ status_t OpenGLRenderer::startFrame() {
mTilingClip.right, mTilingClip.bottom, mOpaque);
}
-status_t OpenGLRenderer::prepare(bool opaque) {
- return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
-}
-
status_t OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
@@ -239,7 +225,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
// The framebuffer renderer will first defer the display list
// for each layer and wait until the first drawing command
// to start the frame
- if (mSnapshot->fbo == 0) {
+ if (currentSnapshot()->fbo == 0) {
syncState();
updateLayers();
} else {
@@ -254,7 +240,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
// perform a discard to let the driver know we don't need to preserve
// the back buffer for this frame.
if (mExtensions.hasDiscardFramebuffer() &&
- left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
+ left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) {
const bool isFbo = getTargetFbo() == 0;
const GLenum attachments[] = {
isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
@@ -266,7 +252,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
if (!opaque || mCountOverdraw) {
mCaches.enableScissor();
- mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
+ mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
return DrawGlInfo::kStatusDrew;
}
@@ -283,21 +269,36 @@ void OpenGLRenderer::syncState() {
}
}
-void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
+void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
if (!mSuppressTiling) {
- Rect* clip = &mTilingClip;
- if (s->flags & Snapshot::kFlagFboTarget) {
- clip = &(s->layer->clipRect);
+ const Snapshot* snapshot = currentSnapshot();
+
+ const Rect* clip = &mTilingClip;
+ if (snapshot->flags & Snapshot::kFlagFboTarget) {
+ clip = &(snapshot->layer->clipRect);
}
- startTiling(*clip, s->height, opaque);
+ startTiling(*clip, getViewportHeight(), opaque, expand);
}
}
-void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
+void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
if (!mSuppressTiling) {
- mCaches.startTiling(clip.left, windowHeight - clip.bottom,
+ if(expand) {
+ // Expand the startTiling region by 1
+ int leftNotZero = (clip.left > 0) ? 1 : 0;
+ int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
+
+ mCaches.startTiling(
+ clip.left - leftNotZero,
+ windowHeight - clip.bottom - topNotZero,
+ clip.right - clip.left + leftNotZero + 1,
+ clip.bottom - clip.top + topNotZero + 1,
+ opaque);
+ } else {
+ mCaches.startTiling(clip.left, windowHeight - clip.bottom,
clip.right - clip.left, clip.bottom - clip.top, opaque);
+ }
}
}
@@ -313,28 +314,12 @@ void OpenGLRenderer::finish() {
// of the current frame
if (getTargetFbo() == 0) {
mCaches.pathCache.trim();
+ mCaches.tessellationCache.trim();
}
if (!suppressErrorChecks()) {
#if DEBUG_OPENGL
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGD("GL error from OpenGLRenderer: 0x%x", status);
- switch (status) {
- case GL_INVALID_ENUM:
- ALOGE(" GL_INVALID_ENUM");
- break;
- case GL_INVALID_VALUE:
- ALOGE(" GL_INVALID_VALUE");
- break;
- case GL_INVALID_OPERATION:
- ALOGE(" GL_INVALID_OPERATION");
- break;
- case GL_OUT_OF_MEMORY:
- ALOGE(" Out of memory!");
- break;
- }
- }
+ GLUtils::dumpGLErrors();
#endif
#if DEBUG_MEMORY_USAGE
@@ -353,110 +338,23 @@ void OpenGLRenderer::finish() {
mFrameStarted = false;
}
-void OpenGLRenderer::interrupt() {
- if (mCaches.currentProgram) {
- if (mCaches.currentProgram->isInUse()) {
- mCaches.currentProgram->remove();
- mCaches.currentProgram = NULL;
- }
- }
- mCaches.resetActiveTexture();
- mCaches.unbindMeshBuffer();
- mCaches.unbindIndicesBuffer();
- mCaches.resetVertexPointers();
- mCaches.disableTexCoordsVertexArray();
- debugOverdraw(false, false);
-}
-
-void OpenGLRenderer::resume() {
- sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
- glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
- glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
- debugOverdraw(true, false);
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
- mCaches.enableScissor();
- mCaches.resetScissor();
- dirtyClip();
-
- mCaches.activeTexture(0);
- mCaches.resetBoundTextures();
-
- mCaches.blend = true;
- glEnable(GL_BLEND);
- glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
- glBlendEquation(GL_FUNC_ADD);
-}
-
void OpenGLRenderer::resumeAfterLayer() {
- sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
- glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
- glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
+ mRenderState.setViewport(getViewportWidth(), getViewportHeight());
+ mRenderState.bindFramebuffer(currentSnapshot()->fbo);
debugOverdraw(true, false);
mCaches.resetScissor();
dirtyClip();
}
-void OpenGLRenderer::detachFunctor(Functor* functor) {
- mFunctors.remove(functor);
-}
-
-void OpenGLRenderer::attachFunctor(Functor* functor) {
- mFunctors.add(functor);
-}
-
-status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
- status_t result = DrawGlInfo::kStatusDone;
- size_t count = mFunctors.size();
-
- if (count > 0) {
- interrupt();
- SortedVector<Functor*> functors(mFunctors);
- mFunctors.clear();
-
- DrawGlInfo info;
- info.clipLeft = 0;
- info.clipTop = 0;
- info.clipRight = 0;
- info.clipBottom = 0;
- info.isLayer = false;
- info.width = 0;
- info.height = 0;
- memset(info.transform, 0, sizeof(float) * 16);
-
- for (size_t i = 0; i < count; i++) {
- Functor* f = functors.itemAt(i);
- result |= (*f)(DrawGlInfo::kModeProcess, &info);
-
- if (result & DrawGlInfo::kStatusDraw) {
- Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
- dirty.unionWith(localDirty);
- }
-
- if (result & DrawGlInfo::kStatusInvoke) {
- mFunctors.add(f);
- }
- }
- resume();
- }
-
- return result;
-}
-
status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
-
- detachFunctor(functor);
+ if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
-
- Rect clip(*mSnapshot->clipRect);
+ Rect clip(*currentClipRect());
clip.snapToPixelBoundaries();
// Since we don't know what the functor will draw, let's dirty
- // tne entire clip region
+ // the entire clip region
if (hasLayer()) {
dirtyLayerUnchecked(clip, getRegion());
}
@@ -467,40 +365,45 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
info.clipRight = clip.right;
info.clipBottom = clip.bottom;
info.isLayer = hasLayer();
- info.width = getSnapshot()->viewport.getWidth();
- info.height = getSnapshot()->height;
- getSnapshot()->transform->copyTo(&info.transform[0]);
+ info.width = getViewportWidth();
+ info.height = getViewportHeight();
+ currentTransform()->copyTo(&info.transform[0]);
- bool dirtyClip = mDirtyClip;
+ bool prevDirtyClip = mDirtyClip;
// setup GL state for functor
if (mDirtyClip) {
setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
}
- if (mCaches.enableScissor() || dirtyClip) {
+ if (mCaches.enableScissor() || prevDirtyClip) {
setScissorFromClip();
}
- interrupt();
-
- // call functor immediately after GL state setup
- status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
-
- if (result != DrawGlInfo::kStatusDone) {
- Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
- dirty.unionWith(localDirty);
- if (result & DrawGlInfo::kStatusInvoke) {
- mFunctors.add(functor);
- }
- }
+ mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
+ // Scissor may have been modified, reset dirty clip
+ dirtyClip();
- resume();
- return result | DrawGlInfo::kStatusDrew;
+ return DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
// Debug
///////////////////////////////////////////////////////////////////////////////
+void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const {
+#if DEBUG_DETAILED_EVENTS
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ eventMark(buf);
+#endif
+}
+
+
void OpenGLRenderer::eventMark(const char* name) const {
mCaches.eventMark(0, name);
}
@@ -514,17 +417,7 @@ void OpenGLRenderer::endMark() const {
}
void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
- if (mCaches.debugOverdraw && getTargetFbo() == 0) {
- if (clear) {
- mCaches.disableScissor();
- mCaches.stencil.clear();
- }
- if (enable) {
- mCaches.stencil.enableDebugWrite();
- } else {
- mCaches.stencil.disable();
- }
- }
+ mRenderState.debugOverdraw(enable, clear);
}
void OpenGLRenderer::renderOverdraw() {
@@ -532,7 +425,7 @@ void OpenGLRenderer::renderOverdraw() {
const Rect* clip = &mTilingClip;
mCaches.enableScissor();
- mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
+ mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom,
clip->right - clip->left, clip->bottom - clip->top);
// 1x overdraw
@@ -556,9 +449,9 @@ void OpenGLRenderer::renderOverdraw() {
}
void OpenGLRenderer::countOverdraw() {
- size_t count = mWidth * mHeight;
+ size_t count = getWidth() * getHeight();
uint32_t* buffer = new uint32_t[count];
- glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
+ glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
size_t total = 0;
for (size_t i = 0; i < count; i++) {
@@ -575,8 +468,8 @@ void OpenGLRenderer::countOverdraw() {
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
- if (layer->deferredUpdateScheduled && layer->renderer &&
- layer->displayList && layer->displayList->isRenderable()) {
+ if (layer->deferredUpdateScheduled && layer->renderer
+ && layer->renderNode.get() && layer->renderNode->isRenderable()) {
ATRACE_CALL();
Rect& dirty = layer->dirtyRect;
@@ -587,14 +480,14 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
}
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
- layer->render();
+ layer->render(*this);
} else {
- layer->defer();
+ layer->defer(*this);
}
if (inFrame) {
resumeAfterLayer();
- startTiling(mSnapshot);
+ startTilingCurrentClip();
}
layer->debugDrawUpdate = mCaches.debugLayersUpdates;
@@ -629,7 +522,7 @@ void OpenGLRenderer::updateLayers() {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
mLayerUpdates.clear();
- glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ mRenderState.bindFramebuffer(getTargetFbo());
}
endMark();
}
@@ -657,7 +550,7 @@ void OpenGLRenderer::flushLayers() {
}
mLayerUpdates.clear();
- glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ mRenderState.bindFramebuffer(getTargetFbo());
endMark();
}
@@ -704,6 +597,7 @@ void OpenGLRenderer::clearLayerUpdates() {
}
void OpenGLRenderer::flushLayerUpdates() {
+ ATRACE_CALL();
syncState();
updateLayers();
flushLayers();
@@ -715,49 +609,14 @@ void OpenGLRenderer::flushLayerUpdates() {
// State management
///////////////////////////////////////////////////////////////////////////////
-int OpenGLRenderer::getSaveCount() const {
- return mSaveCount;
-}
+void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
+ bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer;
+ bool restoreClip = removed.flags & Snapshot::kFlagClipSet;
+ bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
-int OpenGLRenderer::save(int flags) {
- return saveSnapshot(flags);
-}
-
-void OpenGLRenderer::restore() {
- if (mSaveCount > 1) {
- restoreSnapshot();
+ if (restoreViewport) {
+ mRenderState.setViewport(getViewportWidth(), getViewportHeight());
}
-}
-
-void OpenGLRenderer::restoreToCount(int saveCount) {
- if (saveCount < 1) saveCount = 1;
-
- while (mSaveCount > saveCount) {
- restoreSnapshot();
- }
-}
-
-int OpenGLRenderer::saveSnapshot(int flags) {
- mSnapshot = new Snapshot(mSnapshot, flags);
- return mSaveCount++;
-}
-
-bool OpenGLRenderer::restoreSnapshot() {
- bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
- bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
- bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
-
- sp<Snapshot> current = mSnapshot;
- sp<Snapshot> previous = mSnapshot->previous;
-
- if (restoreOrtho) {
- Rect& r = previous->viewport;
- glViewport(r.left, r.top, r.right, r.bottom);
- mOrthoMatrix.load(current->orthoMatrix);
- }
-
- mSaveCount--;
- mSnapshot = previous;
if (restoreClip) {
dirtyClip();
@@ -766,11 +625,9 @@ bool OpenGLRenderer::restoreSnapshot() {
if (restoreLayer) {
endMark(); // Savelayer
startMark("ComposeLayer");
- composeLayer(current, previous);
+ composeLayer(removed, restored);
endMark();
}
-
- return restoreClip;
}
///////////////////////////////////////////////////////////////////////////////
@@ -778,12 +635,11 @@ bool OpenGLRenderer::restoreSnapshot() {
///////////////////////////////////////////////////////////////////////////////
int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags) {
- const GLuint previousFbo = mSnapshot->fbo;
+ const SkPaint* paint, int flags, const SkPath* convexMask) {
const int count = saveSnapshot(flags);
- if (!mSnapshot->isIgnored()) {
- createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo);
+ if (!currentSnapshot()->isIgnored()) {
+ createLayer(left, top, right, bottom, paint, flags, convexMask);
}
return count;
@@ -792,22 +648,24 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
const Rect untransformedBounds(bounds);
- currentTransform().mapRect(bounds);
+ currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*mSnapshot->clipRect)) {
+ if (bounds.intersect(*currentClipRect())) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
// When the layer is not an FBO, we may use glCopyTexImage so we
// need to make sure the layer does not extend outside the bounds
// of the framebuffer
- if (!bounds.intersect(mSnapshot->previous->viewport)) {
+ const Snapshot& previous = *(currentSnapshot()->previous);
+ Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
+ if (!bounds.intersect(previousViewport)) {
bounds.setEmpty();
} else if (fboLayer) {
clip.set(bounds);
mat4 inverse;
- inverse.loadInverse(currentTransform());
+ inverse.loadInverse(*currentTransform());
inverse.mapRect(clip);
clip.snapToPixelBoundaries();
if (clip.intersect(untransformedBounds)) {
@@ -829,16 +687,15 @@ void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect
(fboLayer && clip.isEmpty())) {
mSnapshot->empty = fboLayer;
} else {
- mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+ mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer);
}
}
int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags) {
- const GLuint previousFbo = mSnapshot->fbo;
+ const SkPaint* paint, int flags) {
const int count = saveSnapshot(flags);
- if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
// initialize the snapshot as though it almost represents an FBO layer so deferred draw
// operations will be able to store and restore the current clip and transform info, and
// quick rejection will be correct (for display lists)
@@ -846,19 +703,19 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
Rect bounds(left, top, right, bottom);
Rect clip;
calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, alpha);
+ updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
- if (!mSnapshot->isIgnored()) {
+ if (!currentSnapshot()->isIgnored()) {
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+ mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ mSnapshot->roundRectClipState = NULL;
}
}
return count;
}
-
/**
* Layers are viewed by Skia are slightly different than layers in image editing
* programs (for instance.) When a layer is created, previously created layers
@@ -911,7 +768,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
* something actually gets drawn are the layers regions cleared.
*/
bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) {
+ const SkPaint* paint, int flags, const SkPath* convexMask) {
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
@@ -921,26 +778,27 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
Rect clip;
Rect bounds(left, top, right, bottom);
calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha);
+ updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
// Bail out if we won't draw in this snapshot
- if (mSnapshot->isIgnored()) {
+ if (currentSnapshot()->isIgnored()) {
return false;
}
mCaches.activeTexture(0);
- Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
+ Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
if (!layer) {
return false;
}
- layer->setAlpha(alpha, mode);
+ layer->setPaint(paint);
layer->layer.set(bounds);
layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
bounds.getWidth() / float(layer->getWidth()), 0.0f);
- layer->setColorFilter(mDrawModifiers.mColorFilter);
+
layer->setBlend(true);
layer->setDirty(false);
+ layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
// Save the layer in the snapshot
mSnapshot->flags |= Snapshot::kFlagIsLayer;
@@ -948,7 +806,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
startMark("SaveLayer");
if (fboLayer) {
- return createFboLayer(layer, bounds, clip, previousFbo);
+ return createFboLayer(layer, bounds, clip);
} else {
// Copy the framebuffer into the layer
layer->bindTexture();
@@ -963,8 +821,9 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
layer->setEmpty(false);
}
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
- mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ bounds.left, getViewportHeight() - bounds.bottom,
+ bounds.getWidth(), bounds.getHeight());
// Enqueue the buffer coordinates to clear the corresponding region later
mLayers.push(new Rect(bounds));
@@ -974,24 +833,22 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
return true;
}
-bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
+bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
layer->clipRect.set(clip);
layer->setFbo(mCaches.fboCache.get());
mSnapshot->region = &mSnapshot->layer->region;
- mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer |
- Snapshot::kFlagDirtyOrtho;
+ mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
mSnapshot->fbo = layer->getFbo();
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
- mSnapshot->height = bounds.getHeight();
- mSnapshot->orthoMatrix.load(mOrthoMatrix);
+ mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ mSnapshot->roundRectClipState = NULL;
endTiling();
debugOverdraw(false, false);
// Bind texture to FBO
- glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+ mRenderState.bindFramebuffer(layer->getFbo());
layer->bindTexture();
// Initialize the texture if needed
@@ -1003,7 +860,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
- startTiling(mSnapshot, true);
+ // Expand the startTiling region by 1
+ startTilingCurrentClip(true, true);
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
mCaches.enableScissor();
@@ -1014,27 +872,26 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui
dirtyClip();
// Change the ortho projection
- glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
- mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
-
+ mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
return true;
}
/**
* Read the documentation of createLayer() before doing anything in this method.
*/
-void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
- if (!current->layer) {
+void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) {
+ if (!removed.layer) {
ALOGE("Attempting to compose a layer that does not exist");
return;
}
- Layer* layer = current->layer;
+ Layer* layer = removed.layer;
const Rect& rect = layer->layer;
- const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
+ const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
bool clipRequired = false;
- quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected
+ calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
+ &clipRequired, NULL, false); // safely ignore return, should never be rejected
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
if (fboLayer) {
@@ -1046,15 +903,19 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
layer->removeFbo(false);
// Unbind current FBO and restore previous one
- glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+ mRenderState.bindFramebuffer(restored.fbo);
debugOverdraw(true, false);
- startTiling(previous);
+ startTilingCurrentClip();
}
if (!fboLayer && layer->getAlpha() < 255) {
- drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
+ SkPaint layerPaint;
+ layerPaint.setAlpha(layer->getAlpha());
+ layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+ layerPaint.setColorFilter(layer->getColorFilter());
+
+ drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true);
// Required below, composeLayerRect() will divide by 255
layer->setAlpha(255);
}
@@ -1066,14 +927,8 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
// When the layer is stored in an FBO, we can save a bit of fillrate by
// drawing only the dirty region
if (fboLayer) {
- dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
- if (layer->getColorFilter()) {
- setupColorFilter(layer->getColorFilter());
- }
+ dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform);
composeLayerRegion(layer, rect);
- if (layer->getColorFilter()) {
- resetColorFilter();
- }
} else if (!rect.isEmpty()) {
dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
@@ -1088,6 +943,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
dirtyClip();
// Failing to add the layer to the cache should happen only if the layer is too large
+ layer->setConvexMask(NULL);
if (!mCaches.layerCache.put(layer)) {
LAYER_LOGD("Deleting layer");
Caches::getInstance().resourceCache.decrementRefcount(layer);
@@ -1105,51 +961,60 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
}
setupDrawTextureTransform();
setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter();
- setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode());
+ setupDrawColorFilter(layer->getColorFilter());
+ setupDrawBlending(layer);
setupDrawProgram();
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
+ setupDrawColorFilterUniforms(layer->getColorFilter());
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
setupDrawTexture(layer->getTexture());
} else {
setupDrawExternalTexture(layer->getTexture());
}
- if (currentTransform().isPureTranslate() &&
+ if (currentTransform()->isPureTranslate() &&
+ !layer->getForceFilter() &&
layer->getWidth() == (uint32_t) rect.getWidth() &&
layer->getHeight() == (uint32_t) rect.getHeight()) {
- const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
+ const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
+ const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
layer->setFilter(GL_NEAREST);
- setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false,
+ x, y, x + rect.getWidth(), y + rect.getHeight(), true);
} else {
layer->setFilter(GL_LINEAR);
- setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false,
+ rect.left, rect.top, rect.right, rect.bottom);
}
setupDrawTextureTransformUniforms(layer->getTexTransform());
- setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
+ setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
- if (!layer->isTextureLayer()) {
+ if (layer->isTextureLayer()) {
+ EVENT_LOGD("composeTextureLayerRect");
+ resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
+ drawTextureLayer(layer, rect);
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ } else {
+ EVENT_LOGD("composeHardwareLayerRect");
const Rect& texCoords = layer->texCoords;
resetDrawTextureTexCoords(texCoords.left, texCoords.top,
texCoords.right, texCoords.bottom);
float x = rect.left;
float y = rect.top;
- bool simpleTransform = currentTransform().isPureTranslate() &&
+ bool simpleTransform = currentTransform()->isPureTranslate() &&
layer->getWidth() == (uint32_t) rect.getWidth() &&
layer->getHeight() == (uint32_t) rect.getHeight();
if (simpleTransform) {
// When we're swapping, the layer is already in screen coordinates
if (!swap) {
- x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
- y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
+ x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
+ y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
}
layer->setFilter(GL_NEAREST, true);
@@ -1157,18 +1022,18 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap)
layer->setFilter(GL_LINEAR, true);
}
- float alpha = getLayerAlpha(layer);
- bool blend = layer->isBlend() || alpha < 1.0f;
+ SkPaint layerPaint;
+ layerPaint.setAlpha(getLayerAlpha(layer) * 255);
+ layerPaint.setXfermodeMode(layer->getMode());
+ layerPaint.setColorFilter(layer->getColorFilter());
+
+ bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f;
drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
- layer->getTexture(), alpha, layer->getMode(), blend,
- &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ layer->getTexture(), &layerPaint, blend,
+ &mMeshVertices[0].x, &mMeshVertices[0].u,
GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
- } else {
- resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
- drawTextureLayer(layer, rect);
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
}
@@ -1189,7 +1054,76 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap)
#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
+// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
+// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
+class LayerShader : public SkShader {
+public:
+ LayerShader(Layer* layer, const SkMatrix* localMatrix)
+ : INHERITED(localMatrix)
+ , mLayer(layer) {
+ }
+
+ virtual bool asACustomShader(void** data) const {
+ if (data) {
+ *data = static_cast<void*>(mLayer);
+ }
+ return true;
+ }
+
+ virtual bool isOpaque() const {
+ return !mLayer->isBlend();
+ }
+
+protected:
+ virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
+ LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
+ }
+
+ virtual void flatten(SkWriteBuffer&) const {
+ LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
+ }
+
+ virtual Factory getFactory() const {
+ LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
+ return NULL;
+ }
+private:
+ // Unowned.
+ Layer* mLayer;
+ typedef SkShader INHERITED;
+};
+
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
+ if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
+
+ if (layer->getConvexMask()) {
+ save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // clip to the area of the layer the mask can be larger
+ clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
+
+ // create LayerShader to map SaveLayer content into subsequent draw
+ SkMatrix shaderMatrix;
+ shaderMatrix.setTranslate(rect.left, rect.bottom);
+ shaderMatrix.preScale(1, -1);
+ LayerShader layerShader(layer, &shaderMatrix);
+ paint.setShader(&layerShader);
+
+ // Since the drawing primitive is defined in local drawing space,
+ // we don't need to modify the draw matrix
+ const SkPath* maskPath = layer->getConvexMask();
+ DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
+
+ paint.setShader(NULL);
+ restore();
+
+ return;
+ }
+
if (layer->region.isRect()) {
layer->setRegionAsRect();
@@ -1199,90 +1133,92 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
return;
}
- if (CC_LIKELY(!layer->region.isEmpty())) {
- size_t count;
- const android::Rect* rects;
- Region safeRegion;
- if (CC_LIKELY(hasRectToRectTransform())) {
- rects = layer->region.getArray(&count);
- } else {
- safeRegion = Region::createTJunctionFreeRegion(layer->region);
- rects = safeRegion.getArray(&count);
- }
+ EVENT_LOGD("composeLayerRegion");
+ // standard Region based draw
+ size_t count;
+ const android::Rect* rects;
+ Region safeRegion;
+ if (CC_LIKELY(hasRectToRectTransform())) {
+ rects = layer->region.getArray(&count);
+ } else {
+ safeRegion = Region::createTJunctionFreeRegion(layer->region);
+ rects = safeRegion.getArray(&count);
+ }
- const float alpha = getLayerAlpha(layer);
- const float texX = 1.0f / float(layer->getWidth());
- const float texY = 1.0f / float(layer->getHeight());
- const float height = rect.getHeight();
+ const float alpha = getLayerAlpha(layer);
+ const float texX = 1.0f / float(layer->getWidth());
+ const float texY = 1.0f / float(layer->getHeight());
+ const float height = rect.getHeight();
- setupDraw();
+ setupDraw();
- // We must get (and therefore bind) the region mesh buffer
- // after we setup drawing in case we need to mess with the
- // stencil buffer in setupDraw()
- TextureVertex* mesh = mCaches.getRegionMesh();
- uint32_t numQuads = 0;
+ // We must get (and therefore bind) the region mesh buffer
+ // after we setup drawing in case we need to mess with the
+ // stencil buffer in setupDraw()
+ TextureVertex* mesh = mCaches.getRegionMesh();
+ uint32_t numQuads = 0;
- setupDrawWithTexture();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter();
- setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false);
- setupDrawProgram();
- setupDrawDirtyRegionsDisabled();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawTexture(layer->getTexture());
- if (currentTransform().isPureTranslate()) {
- const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
+ setupDrawWithTexture();
+ setupDrawColor(alpha, alpha, alpha, alpha);
+ setupDrawColorFilter(layer->getColorFilter());
+ setupDrawBlending(layer);
+ setupDrawProgram();
+ setupDrawDirtyRegionsDisabled();
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms(layer->getColorFilter());
+ setupDrawTexture(layer->getTexture());
+ if (currentTransform()->isPureTranslate()) {
+ const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
+ const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
- layer->setFilter(GL_NEAREST);
- setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
- }
- setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
+ layer->setFilter(GL_NEAREST);
+ setupDrawModelView(kModelViewMode_Translate, false,
+ x, y, x + rect.getWidth(), y + rect.getHeight(), true);
+ } else {
+ layer->setFilter(GL_LINEAR);
+ setupDrawModelView(kModelViewMode_Translate, false,
+ rect.left, rect.top, rect.right, rect.bottom);
+ }
+ setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (height - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (height - r->bottom) * texY;
-
- // TODO: Reject quads outside of the clip
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
-
- numQuads++;
-
- if (numQuads >= gMaxNumberOfQuads) {
- DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
- GL_UNSIGNED_SHORT, NULL));
- numQuads = 0;
- mesh = mCaches.getRegionMesh();
- }
- }
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (height - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (height - r->bottom) * texY;
+
+ // TODO: Reject quads outside of the clip
+ TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+ TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+ TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+ TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+
+ numQuads++;
- if (numQuads > 0) {
+ if (numQuads >= gMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
+ numQuads = 0;
+ mesh = mCaches.getRegionMesh();
}
+ }
+
+ if (numQuads > 0) {
+ DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
+ GL_UNSIGNED_SHORT, NULL));
+ }
#if DEBUG_LAYERS_AS_REGIONS
- drawRegionRects(layer->region);
+ drawRegionRectsDebug(layer->region);
#endif
- layer->region.clear();
- }
+ layer->region.clear();
}
-void OpenGLRenderer::drawRegionRects(const Region& region) {
#if DEBUG_LAYERS_AS_REGIONS
+void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
size_t count;
const android::Rect* rects = region.getArray(&count);
@@ -1300,15 +1236,15 @@ void OpenGLRenderer::drawRegionRects(const Region& region) {
top = rects[i].top;
}
+ SkPaint paint;
+ paint.setColor(colors[offset + (i & 0x1)]);
Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
- drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
- SkXfermode::kSrcOver_Mode);
+ drawColorRect(r.left, r.top, r.right, r.bottom, paint);
}
-#endif
}
+#endif
-void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
- SkXfermode::Mode mode, bool dirty) {
+void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) {
Vector<float> rects;
SkRegion::Iterator it(region);
@@ -1321,7 +1257,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
it.next();
}
- drawColorRects(rects.array(), rects.size(), color, mode, true, dirty, false);
+ drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false);
}
void OpenGLRenderer::dirtyLayer(const float left, const float top,
@@ -1342,7 +1278,7 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top,
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (bounds.intersect(*mSnapshot->clipRect)) {
+ if (bounds.intersect(*currentClipRect())) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (!dirty.isEmpty()) {
@@ -1351,12 +1287,12 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
}
}
-void OpenGLRenderer::drawIndexedQuads(Vertex* mesh, GLsizei quadsCount) {
+void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
GLsizei elementsCount = quadsCount * 6;
while (elementsCount > 0) {
GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
- setupDrawIndexedVertices(&mesh[0].position[0]);
+ setupDrawIndexedVertices(&mesh[0].x);
glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
elementsCount -= drawCount;
@@ -1370,7 +1306,8 @@ void OpenGLRenderer::clearLayerRegions() {
const size_t count = mLayers.size();
if (count == 0) return;
- if (!mSnapshot->isIgnored()) {
+ if (!currentSnapshot()->isIgnored()) {
+ EVENT_LOGD("clearLayerRegions");
// Doing several glScissor/glClear here can negatively impact
// GPUs with a tiler architecture, instead we draw quads with
// the Clear blending mode
@@ -1398,14 +1335,18 @@ void OpenGLRenderer::clearLayerRegions() {
// the same thing again
mLayers.clear();
+ SkPaint clearPaint;
+ clearPaint.setXfermodeMode(SkXfermode::kClear_Mode);
+
setupDraw(false);
setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
- setupDrawBlending(true, SkXfermode::kClear_Mode);
+ setupDrawBlending(&clearPaint, true);
setupDrawProgram();
setupDrawPureColorUniforms();
- setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
+ setupDrawModelView(kModelViewMode_Translate, false,
+ 0.0f, 0.0f, 0.0f, 0.0f, true);
- drawIndexedQuads(&mesh[0], count);
+ issueIndexedQuadDraw(&mesh[0], count);
if (scissorChanged) mCaches.enableScissor();
} else {
@@ -1421,58 +1362,62 @@ void OpenGLRenderer::clearLayerRegions() {
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect& currentClip = *(mSnapshot->clipRect);
- const mat4& currentMatrix = *(mSnapshot->transform);
+ const Rect* currentClip = currentClipRect();
+ const mat4* currentMatrix = currentTransform();
if (stateDeferFlags & kStateDeferFlag_Draw) {
// state has bounds initialized in local coordinates
if (!state.mBounds.isEmpty()) {
- currentMatrix.mapRect(state.mBounds);
+ currentMatrix->mapRect(state.mBounds);
Rect clippedBounds(state.mBounds);
// NOTE: if we ever want to use this clipping info to drive whether the scissor
// is used, it should more closely duplicate the quickReject logic (in how it uses
// snapToPixelBoundaries)
- if(!clippedBounds.intersect(currentClip)) {
+ if(!clippedBounds.intersect(*currentClip)) {
// quick rejected
return true;
}
state.mClipSideFlags = kClipSide_None;
- if (!currentClip.contains(state.mBounds)) {
+ if (!currentClip->contains(state.mBounds)) {
int& flags = state.mClipSideFlags;
// op partially clipped, so record which sides are clipped for clip-aware merging
- if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
- if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
- if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
- if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
+ if (currentClip->left > state.mBounds.left) flags |= kClipSide_Left;
+ if (currentClip->top > state.mBounds.top) flags |= kClipSide_Top;
+ if (currentClip->right < state.mBounds.right) flags |= kClipSide_Right;
+ if (currentClip->bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
}
state.mBounds.set(clippedBounds);
} else {
// Empty bounds implies size unknown. Label op as conservatively clipped to disable
// overdraw avoidance (since we don't know what it overlaps)
state.mClipSideFlags = kClipSide_ConservativeFull;
- state.mBounds.set(currentClip);
+ state.mBounds.set(*currentClip);
}
}
state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
if (state.mClipValid) {
- state.mClip.set(currentClip);
+ state.mClip.set(*currentClip);
}
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
// (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
- state.mMatrix.load(currentMatrix);
+ state.mMatrix.load(*currentMatrix);
state.mDrawModifiers = mDrawModifiers;
- state.mAlpha = mSnapshot->alpha;
+ state.mAlpha = currentSnapshot()->alpha;
+
+ // always store/restore, since it's just a pointer
+ state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
return false;
}
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
- currentTransform().load(state.mMatrix);
- mDrawModifiers = state.mDrawModifiers;
+ setMatrix(state.mMatrix);
mSnapshot->alpha = state.mAlpha;
+ mDrawModifiers = state.mDrawModifiers;
+ mSnapshot->roundRectClipState = state.mRoundRectClipState;
if (state.mClipValid && !skipClipRestore) {
mSnapshot->setClip(state.mClip.left, state.mClip.top,
@@ -1492,64 +1437,21 @@ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
if (clipRect != NULL) {
mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
} else {
- mSnapshot->setClip(0, 0, mWidth, mHeight);
+ mSnapshot->setClip(0, 0, getWidth(), getHeight());
}
dirtyClip();
mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
}
///////////////////////////////////////////////////////////////////////////////
-// Transforms
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::translate(float dx, float dy) {
- currentTransform().translate(dx, dy);
-}
-
-void OpenGLRenderer::rotate(float degrees) {
- currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f);
-}
-
-void OpenGLRenderer::scale(float sx, float sy) {
- currentTransform().scale(sx, sy, 1.0f);
-}
-
-void OpenGLRenderer::skew(float sx, float sy) {
- currentTransform().skew(sx, sy);
-}
-
-void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
- if (matrix) {
- currentTransform().load(*matrix);
- } else {
- currentTransform().loadIdentity();
- }
-}
-
-bool OpenGLRenderer::hasRectToRectTransform() {
- return CC_LIKELY(currentTransform().rectToRect());
-}
-
-void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
- currentTransform().copyTo(*matrix);
-}
-
-void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
- SkMatrix transform;
- currentTransform().copyTo(transform);
- transform.preConcat(*matrix);
- currentTransform().load(transform);
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Clipping
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setScissorFromClip() {
- Rect clip(*mSnapshot->clipRect);
+ Rect clip(*currentClipRect());
clip.snapToPixelBoundaries();
- if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
+ if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom,
clip.getWidth(), clip.getHeight())) {
mDirtyClip = false;
}
@@ -1560,7 +1462,7 @@ void OpenGLRenderer::ensureStencilBuffer() {
// cannot attach a stencil buffer to fbo0 dynamically. Let's
// just hope we have one when hasLayer() returns false.
if (hasLayer()) {
- attachStencilBufferToLayer(mSnapshot->layer);
+ attachStencilBufferToLayer(currentSnapshot()->layer);
}
}
@@ -1582,7 +1484,9 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
void OpenGLRenderer::setStencilFromClip() {
if (!mCaches.debugOverdraw) {
- if (!mSnapshot->clipRegion->isEmpty()) {
+ if (!currentSnapshot()->clipRegion->isEmpty()) {
+ EVENT_LOGD("setStencilFromClip - enabling");
+
// NOTE: The order here is important, we must set dirtyClip to false
// before any draw call to avoid calling back into this method
mDirtyClip = false;
@@ -1591,7 +1495,7 @@ void OpenGLRenderer::setStencilFromClip() {
mCaches.stencil.enableWrite();
- // Clear the stencil but first make sure we restrict drawing
+ // Clear and update the stencil, but first make sure we restrict drawing
// to the region's bounds
bool resetScissor = mCaches.enableScissor();
if (resetScissor) {
@@ -1599,7 +1503,14 @@ void OpenGLRenderer::setStencilFromClip() {
setScissorFromClip();
}
mCaches.stencil.clear();
- if (resetScissor) mCaches.disableScissor();
+
+ // stash and disable the outline clip state, since stencil doesn't account for outline
+ bool storedSkipOutlineClip = mSkipOutlineClip;
+ mSkipOutlineClip = true;
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
// NOTE: We could use the region contour path to generate a smaller mesh
// Since we are using the stencil we could use the red book path
@@ -1607,149 +1518,90 @@ void OpenGLRenderer::setStencilFromClip() {
// The last parameter is important: we are not drawing in the color buffer
// so we don't want to dirty the current layer, if any
- drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false);
+ drawRegionRects(*(currentSnapshot()->clipRegion), paint, false);
+ if (resetScissor) mCaches.disableScissor();
+ mSkipOutlineClip = storedSkipOutlineClip;
mCaches.stencil.enableTest();
// Draw the region used to generate the stencil if the appropriate debug
// mode is enabled
if (mCaches.debugStencilClip == Caches::kStencilShowRegion) {
- drawRegionRects(*mSnapshot->clipRegion, 0x7f0000ff, SkXfermode::kSrcOver_Mode);
+ paint.setColor(0x7f0000ff);
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ drawRegionRects(*(currentSnapshot()->clipRegion), paint);
}
} else {
+ EVENT_LOGD("setStencilFromClip - disabling");
mCaches.stencil.disable();
}
}
}
-const Rect& OpenGLRenderer::getClipBounds() {
- return mSnapshot->getLocalClip();
-}
-
-bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
- bool snapOut, bool* clipRequired) {
- if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
- return true;
- }
-
- Rect r(left, top, right, bottom);
- currentTransform().mapRect(r);
- r.snapGeometryToPixelBoundaries(snapOut);
-
- Rect clipRect(*mSnapshot->clipRect);
- clipRect.snapToPixelBoundaries();
-
- if (!clipRect.intersects(r)) return true;
-
- if (clipRequired) *clipRequired = !clipRect.contains(r);
- return false;
-}
-
-bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
- SkPaint* paint) {
- // AA geometry will likely have a ramp around it (not accounted for in local bounds). Snap out
- // the final mapped rect to ensure correct clipping behavior for the ramp.
- bool snapOut = paint->isAntiAlias();
+/**
+ * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out.
+ *
+ * @param paint if not null, the bounds will be expanded to account for stroke depending on paint
+ * style, and tessellated AA ramp
+ */
+bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
+ const SkPaint* paint) {
+ bool snapOut = paint && paint->isAntiAlias();
- if (paint->getStyle() != SkPaint::kFill_Style) {
+ if (paint && paint->getStyle() != SkPaint::kFill_Style) {
float outset = paint->getStrokeWidth() * 0.5f;
- return quickReject(left - outset, top - outset, right + outset, bottom + outset, snapOut);
- } else {
- return quickReject(left, top, right, bottom, snapOut);
+ left -= outset;
+ top -= outset;
+ right += outset;
+ bottom += outset;
}
-}
-bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom, bool snapOut) {
bool clipRequired = false;
- if (quickRejectNoScissor(left, top, right, bottom, snapOut, &clipRequired)) {
+ bool roundRectClipRequired = false;
+ if (calculateQuickRejectForScissor(left, top, right, bottom,
+ &clipRequired, &roundRectClipRequired, snapOut)) {
return true;
}
- if (!isDeferred()) {
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
- }
+ // not quick rejected, so enable the scissor if clipRequired
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mSkipOutlineClip = !roundRectClipRequired;
return false;
}
void OpenGLRenderer::debugClip() {
#if DEBUG_CLIP_REGIONS
- if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) {
- drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode);
- }
-#endif
-}
-
-bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
- if (CC_LIKELY(currentTransform().rectToRect())) {
- bool clipped = mSnapshot->clip(left, top, right, bottom, op);
- if (clipped) {
- dirtyClip();
- }
- return !mSnapshot->clipRect->isEmpty();
- }
-
- SkPath path;
- path.addRect(left, top, right, bottom);
-
- return OpenGLRenderer::clipPath(&path, op);
-}
-
-bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
- SkMatrix transform;
- currentTransform().copyTo(transform);
-
- SkPath transformed;
- path->transform(transform, &transformed);
-
- SkRegion clip;
- if (!mSnapshot->previous->clipRegion->isEmpty()) {
- clip.setRegion(*mSnapshot->previous->clipRegion);
- } else {
- if (mSnapshot->previous == mFirstSnapshot) {
- clip.setRect(0, 0, mWidth, mHeight);
- } else {
- Rect* bounds = mSnapshot->previous->clipRect;
- clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
- }
- }
-
- SkRegion region;
- region.setPath(transformed, clip);
-
- bool clipped = mSnapshot->clipRegionTransformed(region, op);
- if (clipped) {
- dirtyClip();
- }
- return !mSnapshot->clipRect->isEmpty();
-}
+ if (!currentSnapshot()->clipRegion->isEmpty()) {
+ SkPaint paint;
+ paint.setColor(0x7f00ff00);
+ drawRegionRects(*(currentSnapshot()->clipRegion, paint);
-bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
- bool clipped = mSnapshot->clipRegionTransformed(*region, op);
- if (clipped) {
- dirtyClip();
}
- return !mSnapshot->clipRect->isEmpty();
-}
-
-Rect* OpenGLRenderer::getClipRect() {
- return mSnapshot->clipRect;
+#endif
}
///////////////////////////////////////////////////////////////////////////////
// Drawing commands
///////////////////////////////////////////////////////////////////////////////
-void OpenGLRenderer::setupDraw(bool clear) {
- // TODO: It would be best if we could do this before quickReject()
+void OpenGLRenderer::setupDraw(bool clearLayer) {
+ // TODO: It would be best if we could do this before quickRejectSetupScissor()
// changes the scissor test state
- if (clear) clearLayerRegions();
+ if (clearLayer) clearLayerRegions();
// Make sure setScissor & setStencil happen at the beginning of
// this method
if (mDirtyClip) {
if (mCaches.scissorEnabled) {
setScissorFromClip();
}
- setStencilFromClip();
+
+ if (clearLayer) {
+ setStencilFromClip();
+ } else {
+ // While clearing layer, force disable stencil buffer, since
+ // it's invalid to stencil-clip *during* the layer clear
+ mCaches.stencil.disable();
+ }
}
mDescription.reset();
@@ -1788,8 +1640,9 @@ void OpenGLRenderer::setupDrawNoTexture() {
mCaches.disableTexCoordsVertexArray();
}
-void OpenGLRenderer::setupDrawAA() {
- mDescription.isAA = true;
+void OpenGLRenderer::setupDrawVertexAlpha(bool useShadowAlphaInterp) {
+ mDescription.hasVertexAlpha = true;
+ mDescription.useShadowAlphaInterp = useShadowAlphaInterp;
}
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
@@ -1798,7 +1651,7 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
mColorSet = true;
- mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA);
+ mSetShaderColor = mDescription.setColorModulate(mColorA);
}
void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
@@ -1807,7 +1660,7 @@ void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
mColorSet = true;
- mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
+ mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA);
}
void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
@@ -1820,18 +1673,26 @@ void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
mColorG = g;
mColorB = b;
mColorSet = true;
- mSetShaderColor = mDescription.setColor(r, g, b, a);
+ mSetShaderColor = mDescription.setColorModulate(a);
}
-void OpenGLRenderer::setupDrawShader() {
- if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->describe(mDescription, mExtensions);
+void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
+ if (shader != NULL) {
+ SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
}
}
-void OpenGLRenderer::setupDrawColorFilter() {
- if (mDrawModifiers.mColorFilter) {
- mDrawModifiers.mColorFilter->describe(mDescription, mExtensions);
+void OpenGLRenderer::setupDrawColorFilter(const SkColorFilter* filter) {
+ if (filter == NULL) {
+ return;
+ }
+
+ SkXfermode::Mode mode;
+ if (filter->asColorMode(NULL, &mode)) {
+ mDescription.colorOp = ProgramDescription::kColorBlend;
+ mDescription.colorMode = mode;
+ } else if (filter->asColorMatrix(NULL)) {
+ mDescription.colorOp = ProgramDescription::kColorMatrix;
}
}
@@ -1843,71 +1704,76 @@ void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
}
}
-void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) {
+static bool isBlendedColorFilter(const SkColorFilter* filter) {
+ if (filter == NULL) {
+ return false;
+ }
+ return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+}
+
+void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
+ SkXfermode::Mode mode = layer->getMode();
// When the blending mode is kClear_Mode, we need to use a modulate color
// argb=1,0,0,0
accountForClear(mode);
- bool blend = (mColorSet && mColorA < 1.0f) ||
- (mDrawModifiers.mShader && mDrawModifiers.mShader->blend());
+ // TODO: check shader blending, once we have shader drawing support for layers.
+ bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f ||
+ (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter());
chooseBlending(blend, mode, mDescription, swapSrcDst);
}
-void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
+void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool swapSrcDst) {
+ SkXfermode::Mode mode = getXfermodeDirect(paint);
// When the blending mode is kClear_Mode, we need to use a modulate color
// argb=1,0,0,0
accountForClear(mode);
blend |= (mColorSet && mColorA < 1.0f) ||
- (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
- (mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend());
+ (getShader(paint) && !getShader(paint)->isOpaque()) ||
+ isBlendedColorFilter(getColorFilter(paint));
chooseBlending(blend, mode, mDescription, swapSrcDst);
}
void OpenGLRenderer::setupDrawProgram() {
useProgram(mCaches.programCache.get(mDescription));
+ if (mDescription.hasRoundRectClip) {
+ // TODO: avoid doing this repeatedly, stashing state pointer in program
+ const RoundRectClipState* state = mSnapshot->roundRectClipState;
+ const Rect& innerRect = state->innerRect;
+ glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"),
+ innerRect.left, innerRect.top,
+ innerRect.right, innerRect.bottom);
+ glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"),
+ state->radius);
+ glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"),
+ 1, GL_FALSE, &state->matrix.data[0]);
+ }
}
void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
mTrackDirtyRegions = false;
}
-void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom,
- bool ignoreTransform) {
- mModelView.loadTranslate(left, top, 0.0f);
- if (!ignoreTransform) {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
- if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform());
- } else {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
- if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
+void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset,
+ float left, float top, float right, float bottom, bool ignoreTransform) {
+ mModelViewMatrix.loadTranslate(left, top, 0.0f);
+ if (mode == kModelViewMode_TranslateAndScale) {
+ mModelViewMatrix.scale(right - left, bottom - top, 1.0f);
}
-}
-void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
- mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), currentTransform(), offset);
-}
-
-void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
- bool ignoreTransform, bool ignoreModelView) {
- if (!ignoreModelView) {
- mModelView.loadTranslate(left, top, 0.0f);
- mModelView.scale(right - left, bottom - top, 1.0f);
- } else {
- mModelView.loadIdentity();
- }
bool dirty = right - left > 0.0f && bottom - top > 0.0f;
- if (!ignoreTransform) {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
- if (mTrackDirtyRegions && dirty) {
- dirtyLayer(left, top, right, bottom, currentTransform());
+ const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform();
+ mCaches.currentProgram->set(mSnapshot->getOrthoMatrix(), mModelViewMatrix, transformMatrix, offset);
+ if (dirty && mTrackDirtyRegions) {
+ if (!ignoreTransform) {
+ dirtyLayer(left, top, right, bottom, *currentTransform());
+ } else {
+ dirtyLayer(left, top, right, bottom);
}
- } else {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
- if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom);
}
}
-void OpenGLRenderer::setupDrawColorUniforms() {
- if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
+void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
+ if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
}
}
@@ -1918,27 +1784,65 @@ void OpenGLRenderer::setupDrawPureColorUniforms() {
}
}
-void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
- if (mDrawModifiers.mShader) {
- if (ignoreTransform) {
- mModelView.loadInverse(currentTransform());
- }
- mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
- mModelView, *mSnapshot, &mTextureUnit);
+void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) {
+ if (shader == NULL) {
+ return;
}
-}
-void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
- if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
- mat4::identity(), *mSnapshot, &mTextureUnit);
+ if (ignoreTransform) {
+ // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
+ // because it was built into modelView / the geometry, and the description needs to
+ // compensate.
+ mat4 modelViewWithoutTransform;
+ modelViewWithoutTransform.loadInverse(*currentTransform());
+ modelViewWithoutTransform.multiply(mModelViewMatrix);
+ mModelViewMatrix.load(modelViewWithoutTransform);
}
+
+ SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
}
-void OpenGLRenderer::setupDrawColorFilterUniforms() {
- if (mDrawModifiers.mColorFilter) {
- mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram);
+void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
+ if (NULL == filter) {
+ return;
+ }
+
+ SkColor color;
+ SkXfermode::Mode mode;
+ if (filter->asColorMode(&color, &mode)) {
+ const int alpha = SkColorGetA(color);
+ const GLfloat a = alpha / 255.0f;
+ const GLfloat r = a * SkColorGetR(color) / 255.0f;
+ const GLfloat g = a * SkColorGetG(color) / 255.0f;
+ const GLfloat b = a * SkColorGetB(color) / 255.0f;
+ glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a);
+ return;
+ }
+
+ SkScalar srcColorMatrix[20];
+ if (filter->asColorMatrix(srcColorMatrix)) {
+
+ float colorMatrix[16];
+ memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
+ memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
+ memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
+ memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
+
+ // Skia uses the range [0..255] for the addition vector, but we need
+ // the [0..1] range to apply the vector in GLSL
+ float colorVector[4];
+ colorVector[0] = srcColorMatrix[4] / 255.0f;
+ colorVector[1] = srcColorMatrix[9] / 255.0f;
+ colorVector[2] = srcColorMatrix[14] / 255.0f;
+ colorVector[3] = srcColorMatrix[19] / 255.0f;
+
+ glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1,
+ GL_FALSE, colorMatrix);
+ glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector);
+ return;
}
+
+ // it is an error if we ever get here
}
void OpenGLRenderer::setupDrawTextGammaUniforms() {
@@ -1972,7 +1876,8 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
GL_FALSE, &transform.data[0]);
}
-void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
+ const GLvoid* texCoords, GLuint vbo) {
bool force = false;
if (!vertices || vbo) {
force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
@@ -1988,7 +1893,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v
mCaches.unbindIndicesBuffer();
}
-void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors) {
+void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
+ const GLvoid* texCoords, const GLvoid* colors) {
bool force = mCaches.unbindMeshBuffer();
GLsizei stride = sizeof(ColorTextureVertex);
@@ -2005,7 +1911,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid*
mCaches.unbindIndicesBuffer();
}
-void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices,
+ const GLvoid* texCoords, GLuint vbo) {
bool force = false;
// If vbo is != 0 we want to treat the vertices parameter as an offset inside
// a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
@@ -2015,7 +1922,7 @@ void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, G
} else {
force = mCaches.unbindMeshBuffer();
}
- mCaches.bindIndicesBuffer();
+ mCaches.bindQuadIndicesBuffer();
mCaches.bindPositionVertexPointer(force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
@@ -2025,7 +1932,7 @@ void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, G
void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
bool force = mCaches.unbindMeshBuffer();
- mCaches.bindIndicesBuffer();
+ mCaches.bindQuadIndicesBuffer();
mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
}
@@ -2033,44 +1940,37 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
// Drawing
///////////////////////////////////////////////////////////////////////////////
-status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
- int32_t replayFlags) {
+status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
- if (displayList && displayList->isRenderable()) {
+ if (renderNode && renderNode->isRenderable()) {
+ // compute 3d ordering
+ renderNode->computeOrdering();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
- displayList->replay(replayStruct, 0);
+ renderNode->replay(replayStruct, 0);
return status | replayStruct.mDrawGlStatus;
}
bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
- DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
+ DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
- displayList->defer(deferStruct, 0);
+ renderNode->defer(deferStruct, 0);
flushLayers();
status = startFrame();
- return status | deferredList.flush(*this, dirty);
+ return deferredList.flush(*this, dirty) | status;
}
- return DrawGlInfo::kStatusDone;
-}
-
-void OpenGLRenderer::outputDisplayList(DisplayList* displayList) {
- if (displayList) {
- displayList->output(1);
- }
+ // Even if there is no drawing command(Ex: invisible),
+ // it still needs startFrame to clear buffer and start tiling.
+ return startFrame();
}
-void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) {
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
+void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint) {
int color = paint != NULL ? paint->getColor() : 0;
float x = left;
@@ -2079,20 +1979,20 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
texture->setWrap(GL_CLAMP_TO_EDGE, true);
bool ignoreTransform = false;
- if (currentTransform().isPureTranslate()) {
- x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
- y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
+ if (currentTransform()->isPureTranslate()) {
+ x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
+ y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
ignoreTransform = true;
texture->setFilter(GL_NEAREST, true);
} else {
- texture->setFilter(FILTER(paint), true);
+ texture->setFilter(getFilter(paint), true);
}
// No need to check for a UV mapper on the texture object, only ARGB_8888
// bitmaps get packed in the atlas
drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
+ paint, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
@@ -2101,44 +2001,37 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
* will not set the scissor enable or dirty the current layer, if any.
* The caller is responsible for properly dirtying the current layer.
*/
-status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
- TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) {
+status_t OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ int bitmapCount, TextureVertex* vertices, bool pureTranslate,
+ const Rect& bounds, const SkPaint* paint) {
mCaches.activeTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(pureTranslate ? GL_NEAREST : FILTER(paint), true);
+ texture->setFilter(pureTranslate ? GL_NEAREST : getFilter(paint), true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
- if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
- int color = paint != NULL ? paint->getColor() : 0;
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
- texture->id, paint != NULL, color, alpha, mode,
- &vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, true, true, false);
+ texture->id, paint, &vertices[0].x, &vertices[0].u,
+ GL_TRIANGLES, bitmapCount * 6, true,
+ kModelViewMode_Translate, false);
} else {
drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
- texture->id, alpha / 255.0f, mode, texture->blend,
- &vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false);
+ texture->id, paint, texture->blend, &vertices[0].x, &vertices[0].u,
+ GL_TRIANGLES, bitmapCount * 6, false, true, 0,
+ kModelViewMode_Translate, false);
}
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
- const float right = left + bitmap->width();
- const float bottom = top + bitmap->height();
-
- if (quickReject(left, top, right, bottom)) {
+status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+ if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
return DrawGlInfo::kStatusDone;
}
@@ -2147,48 +2040,17 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
- if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
- drawAlphaBitmap(texture, left, top, paint);
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
+ drawAlphaBitmap(texture, 0, 0, paint);
} else {
- drawTextureRect(left, top, right, bottom, texture, paint);
+ drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint);
}
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
- Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
- transform.mapRect(r);
-
- if (quickReject(r.left, r.top, r.right, r.bottom)) {
- return DrawGlInfo::kStatusDone;
- }
-
- mCaches.activeTexture(0);
- Texture* texture = getTexture(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
- const AutoTexture autoCleanup(texture);
-
- // This could be done in a cheaper way, all we need is pass the matrix
- // to the vertex shader. The save/restore is a bit overkill.
- save(SkCanvas::kMatrix_SaveFlag);
- concatMatrix(matrix);
- if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
- drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
- } else {
- drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
- }
- restore();
-
- return DrawGlInfo::kStatusDrew;
-}
-
-status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
- const float right = left + bitmap->width();
- const float bottom = top + bitmap->height();
-
- if (quickReject(left, top, right, bottom)) {
+status_t OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
+ if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
return DrawGlInfo::kStatusDone;
}
@@ -2196,18 +2058,18 @@ status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top,
Texture* texture = mCaches.textureCache.getTransient(bitmap);
const AutoTexture autoCleanup(texture);
- if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
- drawAlphaBitmap(texture, left, top, paint);
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
+ drawAlphaBitmap(texture, 0, 0, paint);
} else {
- drawTextureRect(left, top, right, bottom, texture, paint);
+ drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint);
}
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
- float* vertices, int* colors, SkPaint* paint) {
- if (!vertices || mSnapshot->isIgnored()) {
+status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+ if (!vertices || currentSnapshot()->isIgnored()) {
return DrawGlInfo::kStatusDone;
}
@@ -2221,14 +2083,16 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
const uint32_t count = meshWidth * meshHeight * 6;
- ColorTextureVertex mesh[count];
- ColorTextureVertex* vertex = mesh;
+ Vector<ColorTextureVertex> mesh; // TODO: use C++11 unique_ptr
+ mesh.setCapacity(count);
+ ColorTextureVertex* vertex = mesh.editArray();
bool cleanupColors = false;
if (!colors) {
uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
- colors = new int[colorsCount];
- memset(colors, 0xff, colorsCount * sizeof(int));
+ int* newColors = new int[colorsCount];
+ memset(newColors, 0xff, colorsCount * sizeof(int));
+ colors = newColors;
cleanupColors = true;
}
@@ -2271,7 +2135,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
}
}
- if (quickReject(left, top, right, bottom)) {
+ if (quickRejectSetupScissor(left, top, right, bottom)) {
if (cleanupColors) delete[] colors;
return DrawGlInfo::kStatusDone;
}
@@ -2286,7 +2150,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
const AutoTexture autoCleanup(texture);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(FILTER(paint), true);
+ texture->setFilter(getFilter(paint), true);
int alpha;
SkXfermode::Mode mode;
@@ -2295,21 +2159,21 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
float a = alpha / 255.0f;
if (hasLayer()) {
- dirtyLayer(left, top, right, bottom, currentTransform());
+ dirtyLayer(left, top, right, bottom, *currentTransform());
}
setupDraw();
setupDrawWithTextureAndColor();
setupDrawColor(a, a, a, a);
- setupDrawColorFilter();
- setupDrawBlending(true, mode, false);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawBlending(paint, true);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
- setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, false);
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f);
setupDrawTexture(texture->id);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0], &mesh[0].color[0]);
+ setupDrawColorFilterUniforms(getColorFilter(paint));
+ setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r);
glDrawArrays(GL_TRIANGLES, 0, count);
@@ -2323,11 +2187,11 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
+status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom,
- SkPaint* paint) {
- if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+ const SkPaint* paint) {
+ if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) {
return DrawGlInfo::kStatusDone;
}
@@ -2349,10 +2213,6 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
mCaches.unbindMeshBuffer();
resetDrawTextureTexCoords(u1, v1, u2, v2);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
texture->setWrap(GL_CLAMP_TO_EDGE, true);
float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
@@ -2362,12 +2222,12 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
// Apply a scale transform on the canvas only when a shader is in use
// Skia handles the ratio between the dst and src rects as a scale factor
// when a shader is set
- bool useScaleTransform = mDrawModifiers.mShader && scaled;
+ bool useScaleTransform = getShader(paint) && scaled;
bool ignoreTransform = false;
- if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) {
- float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f);
- float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f);
+ if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) {
+ float x = (int) floorf(dstLeft + currentTransform()->getTranslateX() + 0.5f);
+ float y = (int) floorf(dstTop + currentTransform()->getTranslateY() + 0.5f);
dstRight = x + (dstRight - dstLeft);
dstBottom = y + (dstBottom - dstTop);
@@ -2375,10 +2235,10 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
dstLeft = x;
dstTop = y;
- texture->setFilter(scaled ? FILTER(paint) : GL_NEAREST, true);
+ texture->setFilter(scaled ? getFilter(paint) : GL_NEAREST, true);
ignoreTransform = true;
} else {
- texture->setFilter(FILTER(paint), true);
+ texture->setFilter(getFilter(paint), true);
}
if (CC_UNLIKELY(useScaleTransform)) {
@@ -2393,16 +2253,15 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
dstBottom = srcBottom - srcTop;
}
- if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
- int color = paint ? paint->getColor() : 0;
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
- texture->id, paint != NULL, color, alpha, mode,
- &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ texture->id, paint,
+ &mMeshVertices[0].x, &mMeshVertices[0].u,
GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
} else {
drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
- texture->id, alpha / 255.0f, mode, texture->blend,
- &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ texture->id, paint, texture->blend,
+ &mMeshVertices[0].x, &mMeshVertices[0].u,
GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
}
@@ -2415,9 +2274,9 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
- float left, float top, float right, float bottom, SkPaint* paint) {
- if (quickReject(left, top, right, bottom)) {
+status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
+ if (quickRejectSetupScissor(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
@@ -2428,9 +2287,10 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
}
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
- float left, float top, float right, float bottom, SkPaint* paint) {
- if (quickReject(left, top, right, bottom)) {
+status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
+ AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
+ const SkPaint* paint) {
+ if (quickRejectSetupScissor(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
@@ -2443,15 +2303,11 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- const bool pureTranslate = currentTransform().isPureTranslate();
+ const bool pureTranslate = currentTransform()->isPureTranslate();
// Mark the current layer dirty where we are going to draw the patch
if (hasLayer() && mesh->hasEmptyQuads) {
- const float offsetX = left + currentTransform().getTranslateX();
- const float offsetY = top + currentTransform().getTranslateY();
+ const float offsetX = left + currentTransform()->getTranslateX();
+ const float offsetY = top + currentTransform()->getTranslateY();
const size_t count = mesh->quads.size();
for (size_t i = 0; i < count; i++) {
const Rect& bounds = mesh->quads.itemAt(i);
@@ -2461,27 +2317,26 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl
dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
} else {
dirtyLayer(left + bounds.left, top + bounds.top,
- left + bounds.right, top + bounds.bottom, currentTransform());
+ left + bounds.right, top + bounds.bottom, *currentTransform());
}
}
}
+ bool ignoreTransform = false;
if (CC_LIKELY(pureTranslate)) {
- const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
- const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
+ const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
+ const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
right = x + right - left;
bottom = y + bottom - top;
- drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
- GL_TRIANGLES, mesh->indexCount, false, true,
- mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
- } else {
- drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
- GL_TRIANGLES, mesh->indexCount, false, false,
- mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
+ left = x;
+ top = y;
+ ignoreTransform = true;
}
+ drawIndexedTextureMesh(left, top, right, bottom, texture->id, paint,
+ texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
+ GL_TRIANGLES, mesh->indexCount, false, ignoreTransform,
+ mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads);
}
return DrawGlInfo::kStatusDrew;
@@ -2492,8 +2347,8 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl
* will not set the scissor enable or dirty the current layer, if any.
* The caller is responsible for properly dirtying the current layer.
*/
-status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
- TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) {
+status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) {
mCaches.activeTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
@@ -2502,58 +2357,67 @@ status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
- mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, indexCount, false, true, 0, true, false);
+ drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, paint,
+ texture->blend, &vertices[0].x, &vertices[0].u,
+ GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false);
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
- bool useOffset) {
+status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
+ const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) {
+ // not missing call to quickReject/dirtyLayer, always done at a higher level
if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
return DrawGlInfo::kStatusDone;
}
+ Rect bounds(vertexBuffer.getBounds());
+ bounds.translate(translateX, translateY);
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+
int color = paint->getColor();
- SkXfermode::Mode mode = getXfermode(paint->getXfermode());
bool isAA = paint->isAntiAlias();
setupDraw();
setupDrawNoTexture();
- if (isAA) setupDrawAA();
+ if (isAA) setupDrawVertexAlpha((displayFlags & kVertexBuffer_ShadowInterp));
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(isAA, mode);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawShader(getShader(paint));
+ setupDrawBlending(paint, isAA);
setupDrawProgram();
- setupDrawModelViewIdentity(useOffset);
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderIdentityUniforms();
+ setupDrawModelView(kModelViewMode_Translate, (displayFlags & kVertexBuffer_Offset),
+ translateX, translateY, 0, 0);
+ setupDrawColorUniforms(getShader(paint));
+ setupDrawColorFilterUniforms(getColorFilter(paint));
+ setupDrawShaderUniforms(getShader(paint));
- void* vertices = vertexBuffer.getBuffer();
+ const void* vertices = vertexBuffer.getBuffer();
bool force = mCaches.unbindMeshBuffer();
mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
mCaches.resetTexCoordsVertexPointer();
- mCaches.unbindIndicesBuffer();
int alphaSlot = -1;
if (isAA) {
void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
-
// TODO: avoid enable/disable in back to back uses of the alpha attribute
glEnableVertexAttribArray(alphaSlot);
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
+ const VertexBuffer::Mode mode = vertexBuffer.getMode();
+ if (mode == VertexBuffer::kStandard) {
+ mCaches.unbindIndicesBuffer();
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
+ } else if (mode == VertexBuffer::kOnePolyRingShadow) {
+ mCaches.bindShadowIndicesBuffer();
+ glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+ } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
+ mCaches.bindShadowIndicesBuffer();
+ glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+ }
if (isAA) {
glDisableVertexAttribArray(alphaSlot);
@@ -2571,17 +2435,10 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa
*
* Doesn't yet support joins, caps, or path effects.
*/
-status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
+status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
VertexBuffer vertexBuffer;
// TODO: try clipping large paths to viewport
- PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
-
- if (hasLayer()) {
- SkRect bounds = path.getBounds();
- PathTessellator::expandBoundsForStroke(bounds, paint, false);
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
- }
-
+ PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
return drawVertexBuffer(vertexBuffer, paint);
}
@@ -2596,58 +2453,58 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
* TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
* memory transfer by removing need for degenerate vertices.
*/
-status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
+status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
+ if (currentSnapshot()->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
count &= ~0x3; // round down to nearest four
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer);
+ PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
+ const Rect& bounds = buffer.getBounds();
- if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
-
- bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(buffer, paint, useOffset);
+ int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
+ return drawVertexBuffer(buffer, paint, displayFlags);
}
-status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
+status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
+ if (currentSnapshot()->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
count &= ~0x1; // round down to nearest two
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
+ PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
- if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ const Rect& bounds = buffer.getBounds();
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
-
- bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(buffer, paint, useOffset);
+ int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
+ return drawVertexBuffer(buffer, paint, displayFlags);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
// No need to check against the clip, we fill the clip region
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
- Rect& clip(*mSnapshot->clipRect);
+ Rect clip(*currentClipRect());
clip.snapToPixelBoundaries();
- drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setXfermodeMode(mode);
+
+ drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true);
return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture,
- SkPaint* paint) {
+ const SkPaint* paint) {
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2660,8 +2517,8 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex
}
status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* p) {
- if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
+ float rx, float ry, const SkPaint* p) {
+ if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
return DrawGlInfo::kStatusDone;
}
@@ -2673,20 +2530,13 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float
return drawShape(left, top, texture, p);
}
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- float outset = p->getStrokeWidth() / 2;
- rect.outset(outset, outset);
- rx += outset;
- ry += outset;
- }
- path.addRoundRect(rect, rx, ry);
- return drawConvexPath(path, p);
+ const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
+ *currentTransform(), *p, right - left, bottom - top, rx, ry);
+ return drawVertexBuffer(left, top, *vertexBuffer, p);
}
-status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
- if (mSnapshot->isIgnored() || quickRejectPreStroke(x - radius, y - radius,
+status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
+ if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(x - radius, y - radius,
x + radius, y + radius, p) ||
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
return DrawGlInfo::kStatusDone;
@@ -2707,8 +2557,8 @@ status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p)
}
status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
- SkPaint* p) {
- if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
+ const SkPaint* p) {
+ if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
return DrawGlInfo::kStatusDone;
}
@@ -2729,16 +2579,12 @@ status_t OpenGLRenderer::drawOval(float left, float top, float right, float bott
}
status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* p) {
- if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) {
+ if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
return DrawGlInfo::kStatusDone;
}
- if (fabs(sweepAngle) >= 360.0f) {
- return drawOval(left, top, right, bottom, p);
- }
-
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
mCaches.activeTexture(0);
@@ -2766,8 +2612,9 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
// See SkPaintDefaults.h
#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
-status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
- if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) ||
+status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
+ const SkPaint* p) {
+ if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
(p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
return DrawGlInfo::kStatusDone;
}
@@ -2791,70 +2638,75 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott
return drawConvexPath(path, p);
}
- if (p->isAntiAlias() && !currentTransform().isSimple()) {
+ if (p->isAntiAlias() && !currentTransform()->isSimple()) {
SkPath path;
path.addRect(left, top, right, bottom);
return drawConvexPath(path, p);
} else {
- drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
+ drawColorRect(left, top, right, bottom, p);
return DrawGlInfo::kStatusDrew;
}
}
-void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
- const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
- float x, float y) {
+void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
+ int bytesCount, int count, const float* positions,
+ FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.activeTexture(0);
+ TextShadow textShadow;
+ if (!getTextShadow(paint, &textShadow)) {
+ LOG_ALWAYS_FATAL("failed to query shadow attributes");
+ }
+
// NOTE: The drop shadow will not perform gamma correction
// if shader-based correction is enabled
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
const ShadowTexture* shadow = mCaches.dropShadowCache.get(
- paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
+ paint, text, bytesCount, count, textShadow.radius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!shadow) return;
const AutoTexture autoCleanup(shadow);
- const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
- const float sy = y - shadow->top + mDrawModifiers.mShadowDy;
+ const float sx = x - shadow->left + textShadow.dx;
+ const float sy = y - shadow->top + textShadow.dy;
- const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
- int shadowColor = mDrawModifiers.mShadowColor;
- if (mDrawModifiers.mShader) {
- shadowColor = 0xffffffff;
+ const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha;
+ if (getShader(paint)) {
+ textShadow.color = SK_ColorWHITE;
}
setupDraw();
setupDrawWithTexture(true);
- setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
+ setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawShader(getShader(paint));
+ setupDrawBlending(paint, true);
setupDrawProgram();
- setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false,
+ sx, sy, sx + shadow->width, sy + shadow->height);
setupDrawTexture(shadow->id);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms();
+ setupDrawColorFilterUniforms(getColorFilter(paint));
+ setupDrawShaderUniforms(getShader(paint));
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (mDrawModifiers.mHasShadow ? 1.0f : paint->getAlpha()) * mSnapshot->alpha;
+ float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * mSnapshot->alpha;
return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint) {
- if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
+ const float* positions, const SkPaint* paint) {
+ if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) {
return DrawGlInfo::kStatusDone;
}
// NOTE: Skia does not support perspective transform on drawPosText yet
- if (!currentTransform().isSimple()) {
+ if (!currentTransform()->isSimple()) {
return DrawGlInfo::kStatusDone;
}
@@ -2862,26 +2714,26 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
float x = 0.0f;
float y = 0.0f;
- const bool pureTranslate = currentTransform().isPureTranslate();
+ const bool pureTranslate = currentTransform()->isPureTranslate();
if (pureTranslate) {
- x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
- y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
+ x = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f);
+ y = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f);
}
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- fontRenderer.setFont(paint, mat4::identity());
+ fontRenderer.setFont(paint, SkMatrix::I());
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
+ if (CC_UNLIKELY(hasTextShadow(paint))) {
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
- alpha, mode, 0.0f, 0.0f);
+ alpha, 0.0f, 0.0f);
}
// Pick the appropriate texture filtering
- bool linearFilter = currentTransform().changesBounds();
+ bool linearFilter = currentTransform()->changesBounds();
if (pureTranslate && !linearFilter) {
linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
}
@@ -2897,7 +2749,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
positions, hasActiveLayer ? &bounds : NULL, &functor)) {
if (hasActiveLayer) {
if (!pureTranslate) {
- currentTransform().mapRect(bounds);
+ currentTransform()->mapRect(bounds);
}
dirtyLayerUnchecked(bounds, getRegion());
}
@@ -2906,31 +2758,36 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
return DrawGlInfo::kStatusDrew;
}
-mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
- mat4 fontTransform;
+bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
if (CC_LIKELY(transform.isPureTranslate())) {
- fontTransform = mat4::identity();
- } else {
- if (CC_UNLIKELY(transform.isPerspective())) {
- fontTransform = mat4::identity();
- } else {
- float sx, sy;
- currentTransform().decomposeScale(sx, sy);
- fontTransform.loadScale(sx, sy, 1.0f);
- }
+ outMatrix->setIdentity();
+ return false;
+ } else if (CC_UNLIKELY(transform.isPerspective())) {
+ outMatrix->setIdentity();
+ return true;
}
- return fontTransform;
+
+ /**
+ * Input is a non-perspective, scaling transform. Generate a scale-only transform,
+ * with values rounded to the nearest int.
+ */
+ float sx, sy;
+ transform.decomposeScale(sx, sy);
+ outMatrix->setScale(
+ roundf(fmaxf(1.0f, sx)),
+ roundf(fmaxf(1.0f, sy)));
+ return true;
}
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == kDrawOpMode_Immediate) {
// The checks for corner-case ignorable text and quick rejection is only done for immediate
// drawing as ops from DeferredDisplayList are already filtered for these
- if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
- quickReject(bounds)) {
+ if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint) ||
+ quickRejectSetupScissor(bounds)) {
return DrawGlInfo::kStatusDone;
}
}
@@ -2938,7 +2795,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
const float oldX = x;
const float oldY = y;
- const mat4& transform = currentTransform();
+ const mat4& transform = *currentTransform();
const bool pureTranslate = transform.isPureTranslate();
if (CC_LIKELY(pureTranslate)) {
@@ -2952,10 +2809,10 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
- fontRenderer.setFont(paint, mat4::identity());
+ if (CC_UNLIKELY(hasTextShadow(paint))) {
+ fontRenderer.setFont(paint, SkMatrix::I());
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
- alpha, mode, oldX, oldY);
+ alpha, oldX, oldY);
}
const bool hasActiveLayer = hasLayer();
@@ -2971,15 +2828,15 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
// Applying the full matrix in the shader is the easiest way to handle
// rotation and perspective and allows us to always generated quads in the
// font renderer which greatly simplifies the code, clipping in particular.
- mat4 fontTransform = findBestFontTransform(transform);
+ SkMatrix fontTransform;
+ bool linearFilter = findBestFontTransform(transform, &fontTransform)
+ || fabs(y - (int) y) > 0.0f
+ || fabs(x - (int) x) > 0.0f;
fontRenderer.setFont(paint, fontTransform);
-
- // Pick the appropriate texture filtering
- bool linearFilter = !pureTranslate || fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
fontRenderer.setTextureFiltering(linearFilter);
// TODO: Implement better clipping for scaled/rotated text
- const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
+ const Rect* clip = !pureTranslate ? NULL : currentClipRect();
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
@@ -3004,14 +2861,14 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
+ drawTextDecorations(totalAdvance, oldX, oldY, paint);
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
- float hOffset, float vOffset, SkPaint* paint) {
- if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
+status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+ const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
+ if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) {
return DrawGlInfo::kStatusDone;
}
@@ -3019,7 +2876,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
mCaches.enableScissor();
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- fontRenderer.setFont(paint, mat4::identity());
+ fontRenderer.setFont(paint, SkMatrix::I());
fontRenderer.setTextureFiltering(true);
int alpha;
@@ -3035,7 +2892,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) {
if (hasActiveLayer) {
- currentTransform().mapRect(bounds);
+ currentTransform()->mapRect(bounds);
dirtyLayerUnchecked(bounds, getRegion());
}
}
@@ -3043,8 +2900,8 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+status_t OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
+ if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
mCaches.activeTexture(0);
@@ -3069,14 +2926,14 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
if (layer->isTextureLayer()) {
transform = &layer->getTransform();
if (!transform->isIdentity()) {
- save(0);
- currentTransform().multiply(*transform);
+ save(SkCanvas::kMatrix_SaveFlag);
+ concatMatrix(*transform);
}
}
bool clipRequired = false;
- const bool rejected = quickRejectNoScissor(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), false, &clipRequired);
+ const bool rejected = calculateQuickRejectForScissor(x, y,
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false);
if (rejected) {
if (transform && !transform->isIdentity()) {
@@ -3085,39 +2942,40 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
return DrawGlInfo::kStatusDone;
}
+ EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y,
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), clipRequired);
+
updateLayer(layer, true);
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
mCaches.activeTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
- SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter;
- mDrawModifiers.mColorFilter = layer->getColorFilter();
-
if (layer->region.isRect()) {
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
composeLayerRect(layer, layer->regionRect));
} else if (layer->mesh) {
+
const float a = getLayerAlpha(layer);
setupDraw();
setupDrawWithTexture();
setupDrawColor(a, a, a, a);
- setupDrawColorFilter();
- setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false);
+ setupDrawColorFilter(layer->getColorFilter());
+ setupDrawBlending(layer);
setupDrawProgram();
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
+ setupDrawColorFilterUniforms(layer->getColorFilter());
setupDrawTexture(layer->getTexture());
- if (CC_LIKELY(currentTransform().isPureTranslate())) {
- int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
- int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
+ if (CC_LIKELY(currentTransform()->isPureTranslate())) {
+ int tx = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f);
+ int ty = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f);
layer->setFilter(GL_NEAREST);
- setupDrawModelViewTranslate(tx, ty,
+ setupDrawModelView(kModelViewMode_Translate, false, tx, ty,
tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
} else {
layer->setFilter(GL_LINEAR);
- setupDrawModelViewTranslate(x, y,
+ setupDrawModelView(kModelViewMode_Translate, false, x, y,
x + layer->layer.getWidth(), y + layer->layer.getHeight());
}
@@ -3127,7 +2985,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
while (elementsCount > 0) {
GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
- setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
+ setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL));
@@ -3138,16 +2996,16 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
}
#if DEBUG_LAYERS_AS_REGIONS
- drawRegionRects(layer->region);
+ drawRegionRectsDebug(layer->region);
#endif
}
- mDrawModifiers.mColorFilter = oldFilter;
-
if (layer->debugDrawUpdate) {
layer->debugDrawUpdate = false;
- drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
- 0x7f00ff00, SkXfermode::kSrcOver_Mode);
+
+ SkPaint paint;
+ paint.setColor(0x7f00ff00);
+ drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint);
}
}
layer->hasDrawnSinceUpdate = true;
@@ -3160,49 +3018,6 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
}
///////////////////////////////////////////////////////////////////////////////
-// Shaders
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetShader() {
- mDrawModifiers.mShader = NULL;
-}
-
-void OpenGLRenderer::setupShader(SkiaShader* shader) {
- mDrawModifiers.mShader = shader;
- if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->setCaches(mCaches);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Color filters
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetColorFilter() {
- mDrawModifiers.mColorFilter = NULL;
-}
-
-void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
- mDrawModifiers.mColorFilter = filter;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Drop shadow
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetShadow() {
- mDrawModifiers.mHasShadow = false;
-}
-
-void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
- mDrawModifiers.mHasShadow = true;
- mDrawModifiers.mShadowRadius = radius;
- mDrawModifiers.mShadowDx = dx;
- mDrawModifiers.mShadowDy = dy;
- mDrawModifiers.mShadowColor = color;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Draw filters
///////////////////////////////////////////////////////////////////////////////
@@ -3220,7 +3035,7 @@ void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
}
-SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
+const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) {
if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) {
return paint;
}
@@ -3238,7 +3053,7 @@ SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
-Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
+Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) {
Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
if (!texture) {
return mCaches.textureCache.get(bitmap);
@@ -3247,8 +3062,8 @@ Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
}
void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
- float x, float y, SkPaint* paint) {
- if (quickReject(x, y, x + texture->width, y + texture->height)) {
+ float x, float y, const SkPaint* paint) {
+ if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
return;
}
@@ -3259,15 +3074,16 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
setupDraw();
setupDrawWithTexture(true);
setupDrawAlpha8Color(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawShader(getShader(paint));
+ setupDrawBlending(paint, true);
setupDrawProgram();
- setupDrawModelView(x, y, x + texture->width, y + texture->height);
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false,
+ x, y, x + texture->width, y + texture->height);
setupDrawTexture(texture->id);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms();
+ setupDrawColorFilterUniforms(getColorFilter(paint));
+ setupDrawShaderUniforms(getShader(paint));
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3278,8 +3094,8 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
- float x, float y, SkPaint* paint) {
+void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
+ const SkPaint* paint) {
// Handle underline and strike-through
uint32_t flags = paint->getFlags();
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
@@ -3323,27 +3139,66 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
}
}
-status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored()) {
+status_t OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
+ if (currentSnapshot()->isIgnored()) {
return DrawGlInfo::kStatusDone;
}
- int color = paint->getColor();
- // If a shader is set, preserve only the alpha
- if (mDrawModifiers.mShader) {
- color |= 0x00ffffff;
+ return drawColorRects(rects, count, paint, false, true, true);
+}
+
+static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) {
+ // map z coordinate with true 3d matrix
+ point.z = transformZ.mapZ(point);
+
+ // map x,y coordinates with draw/Skia matrix
+ transformXY.mapPoint(point.x, point.y);
+}
+
+status_t OpenGLRenderer::drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
+ if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
+
+ // TODO: use quickRejectWithScissor. For now, always force enable scissor.
+ mCaches.enableScissor();
+
+ SkPaint paint;
+ paint.setAntiAlias(true); // want to use AlphaVertex
+
+ // The caller has made sure casterAlpha > 0.
+ float ambientShadowAlpha = mAmbientShadowAlpha;
+ if (CC_UNLIKELY(mCaches.propertyAmbientShadowStrength >= 0)) {
+ ambientShadowAlpha = mCaches.propertyAmbientShadowStrength;
+ }
+ if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0);
+ drawVertexBuffer(*ambientShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
+ }
+
+ float spotShadowAlpha = mSpotShadowAlpha;
+ if (CC_UNLIKELY(mCaches.propertySpotShadowStrength >= 0)) {
+ spotShadowAlpha = mCaches.propertySpotShadowStrength;
+ }
+ if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0);
+ drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
}
- SkXfermode::Mode mode = getXfermode(paint->getXfermode());
- return drawColorRects(rects, count, color, mode);
+ return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
- SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
+status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
+ bool ignoreTransform, bool dirty, bool clip) {
if (count == 0) {
return DrawGlInfo::kStatusDone;
}
+ int color = paint->getColor();
+ // If a shader is set, preserve only the alpha
+ if (getShader(paint)) {
+ color |= 0x00ffffff;
+ }
+
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
@@ -3369,69 +3224,68 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
bottom = fmaxf(bottom, b);
}
- if (clip && quickReject(left, top, right, bottom)) {
+ if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
setupDraw();
setupDrawNoTexture();
- setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
- setupDrawShader();
- setupDrawColorFilter();
- setupDrawBlending(mode);
+ setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
+ setupDrawShader(getShader(paint));
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawBlending(paint);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
- setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true);
- setupDrawColorUniforms();
- setupDrawShaderUniforms();
- setupDrawColorFilterUniforms();
+ setupDrawModelView(kModelViewMode_Translate, false,
+ 0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
+ setupDrawColorUniforms(getShader(paint));
+ setupDrawShaderUniforms(getShader(paint));
+ setupDrawColorFilterUniforms(getColorFilter(paint));
if (dirty && hasLayer()) {
- dirtyLayer(left, top, right, bottom, currentTransform());
+ dirtyLayer(left, top, right, bottom, *currentTransform());
}
- drawIndexedQuads(&mesh[0], count / 4);
+ issueIndexedQuadDraw(&mesh[0], count / 4);
return DrawGlInfo::kStatusDrew;
}
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform) {
+ const SkPaint* paint, bool ignoreTransform) {
+ int color = paint->getColor();
// If a shader is set, preserve only the alpha
- if (mDrawModifiers.mShader) {
+ if (getShader(paint)) {
color |= 0x00ffffff;
}
setupDraw();
setupDrawNoTexture();
- setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
- setupDrawShader();
- setupDrawColorFilter();
- setupDrawBlending(mode);
+ setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
+ setupDrawShader(getShader(paint));
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawBlending(paint);
setupDrawProgram();
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
- setupDrawColorUniforms();
- setupDrawShaderUniforms(ignoreTransform);
- setupDrawColorFilterUniforms();
+ setupDrawModelView(kModelViewMode_TranslateAndScale, false,
+ left, top, right, bottom, ignoreTransform);
+ setupDrawColorUniforms(getShader(paint));
+ setupDrawShaderUniforms(getShader(paint), ignoreTransform);
+ setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawSimpleMesh();
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
- Texture* texture, SkPaint* paint) {
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
+ Texture* texture, const SkPaint* paint) {
texture->setWrap(GL_CLAMP_TO_EDGE, true);
GLvoid* vertices = (GLvoid*) NULL;
GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
if (texture->uvMapper) {
- vertices = &mMeshVertices[0].position[0];
- texCoords = &mMeshVertices[0].texture[0];
+ vertices = &mMeshVertices[0].x;
+ texCoords = &mMeshVertices[0].u;
Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
texture->uvMapper->map(uvs);
@@ -3439,17 +3293,17 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b
resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
}
- if (CC_LIKELY(currentTransform().isPureTranslate())) {
- const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
- const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
+ if (CC_LIKELY(currentTransform()->isPureTranslate())) {
+ const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
+ const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
texture->setFilter(GL_NEAREST, true);
drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- alpha / 255.0f, mode, texture->blend, vertices, texCoords,
+ paint, texture->blend, vertices, texCoords,
GL_TRIANGLE_STRIP, gMeshCount, false, true);
} else {
- texture->setFilter(FILTER(paint), true);
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+ texture->setFilter(getFilter(paint), true);
+ drawTextureMesh(left, top, right, bottom, texture->id, paint,
texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
}
@@ -3458,86 +3312,85 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b
}
}
-void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
- GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
- drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
- (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount);
-}
-
void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
- GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLuint texture, const SkPaint* paint, bool blend,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
+ bool swapSrcDst, bool ignoreTransform, GLuint vbo,
+ ModelViewMode modelViewMode, bool dirty) {
+
+ int a;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &a, &mode);
+ const float alpha = a / 255.0f;
setupDraw();
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter();
- setupDrawBlending(blend, mode, swapSrcDst);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawBlending(paint, blend, swapSrcDst);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
- if (!ignoreScale) {
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
- } else {
- setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
- }
+ setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
setupDrawTexture(texture);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
+ setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawMesh(vertices, texCoords, vbo);
glDrawArrays(drawMode, 0, elementsCount);
}
void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
- GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLuint texture, const SkPaint* paint, bool blend,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
+ bool swapSrcDst, bool ignoreTransform, GLuint vbo,
+ ModelViewMode modelViewMode, bool dirty) {
+
+ int a;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &a, &mode);
+ const float alpha = a / 255.0f;
setupDraw();
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter();
- setupDrawBlending(blend, mode, swapSrcDst);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawBlending(paint, blend, swapSrcDst);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
- if (!ignoreScale) {
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
- } else {
- setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
- }
+ setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
setupDrawTexture(texture);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
+ setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawMeshIndices(vertices, texCoords, vbo);
glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
}
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
- GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
+ GLuint texture, const SkPaint* paint,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool ignoreScale, bool dirty) {
+ bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) {
+
+ int color = paint != NULL ? paint->getColor() : 0;
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
setupDraw();
setupDrawWithTexture(true);
- if (hasColor) {
+ if (paint != NULL) {
setupDrawAlpha8Color(color, alpha);
}
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
+ setupDrawColorFilter(getColorFilter(paint));
+ setupDrawShader(getShader(paint));
+ setupDrawBlending(paint, true);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
- if (!ignoreScale) {
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
- } else {
- setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
- }
+ setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
setupDrawTexture(texture);
setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms();
+ setupDrawColorFilterUniforms(getColorFilter(paint));
+ setupDrawShaderUniforms(getShader(paint), ignoreTransform);
setupDrawMesh(vertices, texCoords);
glDrawArrays(drawMode, 0, elementsCount);
@@ -3545,6 +3398,13 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
ProgramDescription& description, bool swapSrcDst) {
+
+ if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) {
+ blend = true;
+ mDescription.hasRoundRectClip = true;
+ }
+ mSkipOutlineClip = true;
+
if (mCountOverdraw) {
if (!mCaches.blend) glEnable(GL_BLEND);
if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
@@ -3618,23 +3478,23 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo
TextureVertex::setUV(v++, u2, v2);
}
-void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
+void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
getAlphaAndModeDirect(paint, alpha, mode);
if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
// if drawing a layer, ignore the paint's alpha
*alpha = mDrawModifiers.mOverrideLayerAlpha * 255;
}
- *alpha *= mSnapshot->alpha;
+ *alpha *= currentSnapshot()->alpha;
}
-float OpenGLRenderer::getLayerAlpha(Layer* layer) const {
+float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
float alpha;
if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
alpha = mDrawModifiers.mOverrideLayerAlpha;
} else {
alpha = layer->getAlpha() / 255.0f;
}
- return alpha * mSnapshot->alpha;
+ return alpha * currentSnapshot()->alpha;
}
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 9afb7ad..e9ca5d9 100644..100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -21,12 +21,14 @@
#include <GLES2/gl2ext.h>
#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkRegion.h>
-#include <SkShader.h>
#include <SkXfermode.h>
+#include <utils/Blur.h>
#include <utils/Functor.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
@@ -41,16 +43,25 @@
#include "Matrix.h"
#include "Program.h"
#include "Rect.h"
+#include "Renderer.h"
#include "Snapshot.h"
-#include "Vertex.h"
-#include "SkiaShader.h"
-#include "SkiaColorFilter.h"
+#include "StatefulBaseRenderer.h"
#include "UvMapper.h"
+#include "Vertex.h"
#include "Caches.h"
+#include "CanvasProperty.h"
+
+class SkShader;
namespace android {
namespace uirenderer {
+class DeferredDisplayState;
+class RenderState;
+class RenderNode;
+class TextSetupFunctor;
+class VertexBuffer;
+
struct DrawModifiers {
DrawModifiers() {
reset();
@@ -60,17 +71,8 @@ struct DrawModifiers {
memset(this, 0, sizeof(DrawModifiers));
}
- SkiaShader* mShader;
- SkiaColorFilter* mColorFilter;
float mOverrideLayerAlpha;
- // Drop shadow
- bool mHasShadow;
- float mShadowRadius;
- float mShadowDx;
- float mShadowDy;
- int mShadowColor;
-
// Draw filters
bool mHasDrawFilter;
int mPaintFilterClearBits;
@@ -82,12 +84,6 @@ enum StateDeferFlags {
kStateDeferFlag_Clip = 0x2
};
-enum DrawOpMode {
- kDrawOpMode_Immediate,
- kDrawOpMode_Defer,
- kDrawOpMode_Flush
-};
-
enum ClipSideFlags {
kClipSide_None = 0x0,
kClipSide_Left = 0x1,
@@ -98,235 +94,117 @@ enum ClipSideFlags {
kClipSide_ConservativeFull = 0x1F
};
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-
-class DeferredDisplayState;
-class DisplayList;
-class TextSetupFunctor;
-class VertexBuffer;
+enum VertexBufferDisplayFlags {
+ kVertexBuffer_Offset = 0x1,
+ kVertexBuffer_ShadowInterp = 0x2,
+};
/**
- * OpenGL renderer used to draw accelerated 2D graphics. The API is a
- * simplified version of Skia's Canvas API.
+ * Defines additional transformation that should be applied by the model view matrix, beyond that of
+ * the currentTransform()
*/
-class OpenGLRenderer {
-public:
- ANDROID_API OpenGLRenderer();
- virtual ~OpenGLRenderer();
-
- /**
- * Sets the name of this renderer. The name is optional and
- * empty by default. If the pointer is null the name is set
- * to the empty string.
- */
- ANDROID_API void setName(const char* name);
-
+enum ModelViewMode {
/**
- * Returns the name of this renderer as UTF8 string.
- * The returned pointer is never null.
+ * Used when the model view should simply translate geometry passed to the shader. The resulting
+ * matrix will be a simple translation.
*/
- ANDROID_API const char* getName() const;
+ kModelViewMode_Translate = 0,
/**
- * Read externally defined properties to control the behavior
- * of the renderer.
+ * Used when the model view should translate and scale geometry. The resulting matrix will be a
+ * translation + scale. This is frequently used together with VBO 0, the (0,0,1,1) rect.
*/
- ANDROID_API void initProperties();
+ kModelViewMode_TranslateAndScale = 1,
+};
- /**
- * Indicates whether this renderer executes drawing commands immediately.
- * If this method returns true, the drawing commands will be executed
- * later.
- */
- virtual bool isDeferred();
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * OpenGL Renderer implementation.
+ */
+class OpenGLRenderer : public StatefulBaseRenderer {
+public:
+ OpenGLRenderer(RenderState& renderState);
+ virtual ~OpenGLRenderer();
- /**
- * Sets the dimension of the underlying drawing surface. This method must
- * be called at least once every time the drawing surface changes size.
- *
- * @param width The width in pixels of the underlysing surface
- * @param height The height in pixels of the underlysing surface
- */
- virtual void setViewport(int width, int height);
+ void initProperties();
+ void initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. When this method is invoked, the
- * entire drawing surface is assumed to be redrawn.
- *
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared
- */
- ANDROID_API status_t prepare(bool opaque);
-
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. Only the specified rectangle of the
- * frame is assumed to be dirty. A clip will automatically be set to
- * the specified rectangle.
- *
- * @param left The left coordinate of the dirty rectangle
- * @param top The top coordinate of the dirty rectangle
- * @param right The right coordinate of the dirty rectangle
- * @param bottom The bottom coordinate of the dirty rectangle
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared in the specified dirty rectangle
- */
+ virtual void onViewportInitialized();
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
-
- /**
- * Indicates the end of a frame. This method must be invoked whenever
- * the caller is done rendering a frame.
- */
virtual void finish();
- /**
- * This method must be invoked before handing control over to a draw functor.
- * See callDrawGLFunction() for instance.
- *
- * This command must not be recorded inside display lists.
- */
- virtual void interrupt();
-
- /**
- * This method must be invoked after getting control back from a draw functor.
- *
- * This command must not be recorded inside display lists.
- */
- virtual void resume();
-
- ANDROID_API void setCountOverdrawEnabled(bool enabled) {
+ void setCountOverdrawEnabled(bool enabled) {
mCountOverdraw = enabled;
}
- ANDROID_API float getOverdraw() {
+ float getOverdraw() {
return mCountOverdraw ? mOverdraw : 0.0f;
}
- ANDROID_API status_t invokeFunctors(Rect& dirty);
- ANDROID_API void detachFunctor(Functor* functor);
- ANDROID_API void attachFunctor(Functor* functor);
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
- ANDROID_API void pushLayerUpdate(Layer* layer);
- ANDROID_API void cancelLayerUpdate(Layer* layer);
- ANDROID_API void clearLayerUpdates();
- ANDROID_API void flushLayerUpdates();
-
- ANDROID_API int getSaveCount() const;
- virtual int save(int flags);
- virtual void restore();
- virtual void restoreToCount(int saveCount);
-
- ANDROID_API int saveLayer(float left, float top, float right, float bottom,
- SkPaint* paint, int flags) {
- SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
- if (paint) mode = getXfermode(paint->getXfermode());
- return saveLayer(left, top, right, bottom, paint ? paint->getAlpha() : 255, mode, flags);
- }
- ANDROID_API int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, int flags) {
- return saveLayer(left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags);
- }
- virtual int saveLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags);
-
- int saveLayerDeferred(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags);
-
- virtual void translate(float dx, float dy);
- virtual void rotate(float degrees);
- virtual void scale(float sx, float sy);
- virtual void skew(float sx, float sy);
-
- bool hasRectToRectTransform();
- ANDROID_API void getMatrix(SkMatrix* matrix);
- virtual void setMatrix(SkMatrix* matrix);
- virtual void concatMatrix(SkMatrix* matrix);
-
- ANDROID_API const Rect& getClipBounds();
-
- /**
- * Performs a quick reject but adjust the bounds to account for stroke width if necessary,
- * and handling snapOut for AA geometry.
- */
- bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
+ void pushLayerUpdate(Layer* layer);
+ void cancelLayerUpdate(Layer* layer);
+ void clearLayerUpdates();
+ void flushLayerUpdates();
- /**
- * Returns false and sets scissor based upon bounds if drawing won't be clipped out
- */
- bool quickReject(float left, float top, float right, float bottom, bool snapOut = false);
- bool quickReject(const Rect& bounds) {
- return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, int flags) {
+ return saveLayer(left, top, right, bottom, paint, flags, NULL);
}
- /**
- * Same as quickReject, without the scissor, instead returning clipRequired through pointer.
- * clipRequired will be only set if not rejected
- */
- ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom,
- bool snapOut = false, bool* clipRequired = NULL);
- bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) {
- return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom,
- clipRequired);
- }
+ // Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if
+ // created, which will in turn clip to that mask when drawn back/restored.
+ int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, int flags, const SkPath* convexMask);
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool clipPath(SkPath* path, SkRegion::Op op);
- virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
- virtual Rect* getClipRect();
+ int saveLayerDeferred(float left, float top, float right, float bottom,
+ const SkPaint* paint, int flags);
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags);
- virtual void outputDisplayList(DisplayList* displayList);
+ virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
virtual status_t drawLayer(Layer* layer, float x, float y);
- virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
- status_t drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
- TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint);
- virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
- virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
+ virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, SkPaint* paint);
- virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
- virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
- float* vertices, int* colors, SkPaint* paint);
- status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
- TextureVertex* vertices, uint32_t indexCount, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
- float left, float top, float right, float bottom, SkPaint* paint);
- status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
- float left, float top, float right, float bottom, SkPaint* paint);
+ float dstRight, float dstBottom, const SkPaint* paint);
+ virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint);
+ virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint);
+ status_t drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint);
+ virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint);
+ status_t drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
+ float left, float top, float right, float bottom, const SkPaint* paint);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
- virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+ virtual status_t drawRect(float left, float top, float right, float bottom,
+ const SkPaint* paint);
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint);
- virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint);
- virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint);
+ float rx, float ry, const SkPaint* paint);
+ virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
+ virtual status_t drawOval(float left, float top, float right, float bottom,
+ const SkPaint* paint);
virtual status_t drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
- virtual status_t drawPath(SkPath* path, SkPaint* paint);
- virtual status_t drawLines(float* points, int count, SkPaint* paint);
- virtual status_t drawPoints(float* points, int count, SkPaint* paint);
- virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
- float hOffset, float vOffset, SkPaint* paint);
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint);
+ virtual status_t drawPath(const SkPath* path, const SkPaint* paint);
+ virtual status_t drawLines(const float* points, int count, const SkPaint* paint);
+ virtual status_t drawPoints(const float* points, int count, const SkPaint* paint);
+ virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ float hOffset, float vOffset, const SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint);
+ const float* positions, const SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
- virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
-
- virtual void resetShader();
- virtual void setupShader(SkiaShader* shader);
-
- virtual void resetColorFilter();
- virtual void setupColorFilter(SkiaColorFilter* filter);
+ virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
- virtual void resetShadow();
- virtual void setupShadow(float radius, float dx, float dy, int color);
+ status_t drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer);
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
@@ -334,7 +212,7 @@ public:
// If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer)
void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; }
- SkPaint* filterPaint(SkPaint* paint);
+ const SkPaint* filterPaint(const SkPaint* paint);
/**
* Store the current display state (most importantly, the current clip and transform), and
@@ -349,8 +227,8 @@ public:
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
- ANDROID_API bool isCurrentTransformSimple() {
- return mSnapshot->transform->isSimple();
+ bool isCurrentTransformSimple() {
+ return currentTransform()->isSimple();
}
Caches& getCaches() {
@@ -362,8 +240,8 @@ public:
return mSnapshot->clipRegion->isEmpty();
}
- int getViewportWidth() { return getSnapshot()->viewport.getWidth(); }
- int getViewportHeight() { return getSnapshot()->viewport.getHeight(); }
+ int getViewportWidth() { return currentSnapshot()->getViewportWidth(); }
+ int getViewportHeight() { return currentSnapshot()->getViewportHeight(); }
/**
* Scales the alpha on the current snapshot. This alpha value will be modulated
@@ -379,6 +257,11 @@ public:
void eventMark(const char* name) const;
/**
+ * Inserts a formatted event marker in the stream of GL commands.
+ */
+ void eventMarkDEBUG(const char *fmt, ...) const;
+
+ /**
* Inserts a named group marker in the stream of GL commands. This marker
* can be used by tools to group commands into logical groups. A call to
* this method must always be followed later on by a call to endMark().
@@ -400,12 +283,12 @@ public:
* @param alpha Where to store the resulting alpha
* @param mode Where to store the resulting xfermode
*/
- static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+ static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
*mode = getXfermodeDirect(paint);
*alpha = getAlphaDirect(paint);
}
- static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) {
+ static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
if (!paint) return SkXfermode::kSrcOver_Mode;
return getXfermode(paint->getXfermode());
}
@@ -415,11 +298,38 @@ public:
return paint->getAlpha();
}
+ struct TextShadow {
+ SkScalar radius;
+ float dx;
+ float dy;
+ SkColor color;
+ };
+
+ static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+ SkDrawLooper::BlurShadowRec blur;
+ if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+ if (textShadow) {
+ textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+ textShadow->dx = blur.fOffset.fX;
+ textShadow->dy = blur.fOffset.fY;
+ textShadow->color = blur.fColor;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static inline bool hasTextShadow(const SkPaint* paint) {
+ return getTextShadow(paint, NULL);
+ }
+
/**
- * Return the best transform to use to rasterize text given a full
- * transform matrix.
+ * Build the best transform to use to rasterize text given a full
+ * transform matrix, and whether filteration is needed.
+ *
+ * Returns whether filtration is needed
*/
- mat4 findBestFontTransform(const mat4& transform) const;
+ bool findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const;
#if DEBUG_MERGE_BEHAVIOR
void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) {
@@ -435,13 +345,12 @@ public:
}
#endif
-protected:
- /**
- * Computes the projection matrix, initialize the first snapshot
- * and stores the dimensions of the render target.
- */
- void initViewport(int width, int height);
+ const Vector3& getLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
+ float getLightRadius() const { return mLightRadius; }
+ uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; }
+ uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; }
+protected:
/**
* Perform the setup specific to a frame. This method does not
* issue any OpenGL commands.
@@ -477,6 +386,13 @@ protected:
*/
void attachStencilBufferToLayer(Layer* layer);
+ bool quickRejectSetupScissor(float left, float top, float right, float bottom,
+ const SkPaint* paint = NULL);
+ bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = NULL) {
+ return quickRejectSetupScissor(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, paint);
+ }
+
/**
* Compose the layer defined in the current snapshot with the layer
* defined by the previous snapshot.
@@ -486,7 +402,7 @@ protected:
* @param curent The current snapshot containing the layer to compose
* @param previous The previous snapshot to compose the current layer with
*/
- virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
+ virtual void composeLayer(const Snapshot& current, const Snapshot& previous);
/**
* Marks the specified region as dirty at the specified bounds.
@@ -494,13 +410,6 @@ protected:
void dirtyLayerUnchecked(Rect& bounds, Region* region);
/**
- * Returns the current snapshot.
- */
- sp<Snapshot> getSnapshot() const {
- return mSnapshot;
- }
-
- /**
* Returns the region of the current layer.
*/
virtual Region* getRegion() const {
@@ -517,7 +426,7 @@ protected:
/**
* Returns the name of the FBO this renderer is rendering into.
*/
- virtual GLint getTargetFbo() const {
+ virtual GLuint getTargetFbo() const {
return 0;
}
@@ -538,25 +447,29 @@ protected:
* @param alpha Where to store the resulting alpha
* @param mode Where to store the resulting xfermode
*/
- inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
+ inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
/**
* Gets the alpha from a layer, accounting for snapshot alpha and overrideLayerAlpha
*
* @param layer The layer from which the alpha is extracted
*/
- inline float getLayerAlpha(Layer* layer) const;
+ inline float getLayerAlpha(const Layer* layer) const;
/**
- * Safely retrieves the mode from the specified xfermode. If the specified
- * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
+ * Safely retrieves the ColorFilter from the given Paint. If the paint is
+ * null then null is returned.
*/
- static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
- SkXfermode::Mode resultMode;
- if (!SkXfermode::AsMode(mode, &resultMode)) {
- resultMode = SkXfermode::kSrcOver_Mode;
- }
- return resultMode;
+ static inline SkColorFilter* getColorFilter(const SkPaint* paint) {
+ return paint ? paint->getColorFilter() : NULL;
+ }
+
+ /**
+ * Safely retrieves the Shader from the given Paint. If the paint is
+ * null then null is returned.
+ */
+ static inline const SkShader* getShader(const SkPaint* paint) {
+ return paint ? paint->getShader() : NULL;
}
/**
@@ -566,6 +479,8 @@ protected:
return false;
}
+ inline RenderState& renderState() { return mRenderState; }
+
private:
/**
* Discards the content of the framebuffer if supported by the driver.
@@ -582,19 +497,18 @@ private:
/**
* Tells the GPU what part of the screen is about to be redrawn.
- * This method will use the clip rect that we started drawing the
- * frame with.
+ * This method will use the current layer space clip rect.
* This method needs to be invoked every time getTargetFbo() is
* bound again.
*/
- void startTiling(const sp<Snapshot>& snapshot, bool opaque = false);
+ void startTilingCurrentClip(bool opaque = false, bool expand = false);
/**
* Tells the GPU what part of the screen is about to be redrawn.
* This method needs to be invoked every time getTargetFbo() is
* bound again.
*/
- void startTiling(const Rect& clip, int windowHeight, bool opaque = false);
+ void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false);
/**
* Tells the GPU that we are done drawing the frame or that we
@@ -602,23 +516,7 @@ private:
*/
void endTiling();
- /**
- * Saves the current state of the renderer as a new snapshot.
- * The new snapshot is saved in mSnapshot and the previous snapshot
- * is linked from mSnapshot->previous.
- *
- * @param flags The save flags; see SkCanvas for more information
- *
- * @return The new save count. This value can be passed to #restoreToCount()
- */
- int saveSnapshot(int flags);
-
- /**
- * Restores the current snapshot; mSnapshot becomes mSnapshot->previous.
- *
- * @return True if the clip was modified.
- */
- bool restoreSnapshot();
+ void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored);
/**
* Sets the clipping rectangle using glScissor. The clip is defined by
@@ -654,12 +552,12 @@ private:
* @param alpha The translucency of the layer
* @param mode The blending mode of the layer
* @param flags The layer save flags
- * @param previousFbo The name of the current framebuffer
+ * @param mask A mask to use when drawing the layer back, may be empty
*
* @return True if the layer was successfully created, false otherwise
*/
bool createLayer(float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo);
+ const SkPaint* paint, int flags, const SkPath* convexMask);
/**
* Creates a new layer stored in the specified snapshot as an FBO.
@@ -667,9 +565,8 @@ private:
* @param layer The layer to store as an FBO
* @param snapshot The snapshot associated with the new layer
* @param bounds The bounds of the layer
- * @param previousFbo The name of the current framebuffer
*/
- bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo);
+ bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip);
/**
* Compose the specified layer as a region.
@@ -716,12 +613,11 @@ private:
* @param top The top coordinate of the rectangle
* @param right The right coordinate of the rectangle
* @param bottom The bottom coordinate of the rectangle
- * @param color The rectangle's ARGB color, defined as a packed 32 bits word
- * @param mode The Skia xfermode to use
+ * @param paint The paint containing the color, blending mode, etc.
* @param ignoreTransform True if the current transform should be ignored
*/
void drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform = false);
+ const SkPaint* paint, bool ignoreTransform = false);
/**
* Draws a series of colored rectangles with the specified color. The specified
@@ -730,15 +626,13 @@ private:
*
* @param rects A list of rectangles, 4 floats (left, top, right, bottom)
* per rectangle
- * @param color The rectangles' ARGB color, defined as a packed 32 bits word
- * @param mode The Skia xfermode to use
+ * @param paint The paint containing the color, blending mode, etc.
* @param ignoreTransform True if the current transform should be ignored
* @param dirty True if calling this method should dirty the current layer
* @param clip True if the rects should be clipped, false otherwise
*/
- status_t drawColorRects(const float* rects, int count, int color,
- SkXfermode::Mode mode, bool ignoreTransform = false,
- bool dirty = true, bool clip = true);
+ status_t drawColorRects(const float* rects, int count, const SkPaint* paint,
+ bool ignoreTransform = false, bool dirty = true, bool clip = true);
/**
* Draws the shape represented by the specified path texture.
@@ -751,7 +645,7 @@ private:
* @param texture The texture reprsenting the shape
* @param paint The paint to draw the shape with
*/
- status_t drawShape(float left, float top, const PathTexture* texture, SkPaint* paint);
+ status_t drawShape(float left, float top, const PathTexture* texture, const SkPaint* paint);
/**
* Draws the specified texture as an alpha bitmap. Alpha bitmaps obey
@@ -762,41 +656,33 @@ private:
* @param top The y coordinate of the bitmap
* @param paint The paint to render with
*/
- void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
+ void drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint);
/**
* Renders a strip of polygons with the specified paint, used for tessellated geometry.
*
* @param vertexBuffer The VertexBuffer to be drawn
* @param paint The paint to render with
- * @param useOffset Offset the vertexBuffer (used in drawing non-AA lines)
+ * @param flags flags with which to draw
*/
- status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
- bool useOffset = false);
+ status_t drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
+ const SkPaint* paint, int flags = 0);
/**
- * Renders the convex hull defined by the specified path as a strip of polygons.
- *
- * @param path The hull of the path to draw
- * @param paint The paint to render with
+ * Convenience for translating method
*/
- status_t drawConvexPath(const SkPath& path, SkPaint* paint);
+ status_t drawVertexBuffer(const VertexBuffer& vertexBuffer,
+ const SkPaint* paint, int flags = 0) {
+ return drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags);
+ }
/**
- * Draws a textured rectangle with the specified texture. The specified coordinates
- * are transformed by the current snapshot's transform matrix.
+ * Renders the convex hull defined by the specified path as a strip of polygons.
*
- * @param left The left coordinate of the rectangle
- * @param top The top coordinate of the rectangle
- * @param right The right coordinate of the rectangle
- * @param bottom The bottom coordinate of the rectangle
- * @param texture The texture name to map onto the rectangle
- * @param alpha An additional translucency parameter, between 0.0f and 1.0f
- * @param mode The blending mode
- * @param blend True if the texture contains an alpha channel
+ * @param path The hull of the path to draw
+ * @param paint The paint to render with
*/
- void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
- float alpha, SkXfermode::Mode mode, bool blend);
+ status_t drawConvexPath(const SkPath& path, const SkPaint* paint);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
@@ -810,7 +696,7 @@ private:
* @param paint The paint containing the alpha, blending mode, etc.
*/
void drawTextureRect(float left, float top, float right, float bottom,
- Texture* texture, SkPaint* paint);
+ Texture* texture, const SkPaint* paint);
/**
* Draws a textured mesh with the specified texture. If the indices are omitted,
@@ -822,8 +708,7 @@ private:
* @param right The right coordinate of the rectangle
* @param bottom The bottom coordinate of the rectangle
* @param texture The texture name to map onto the rectangle
- * @param alpha An additional translucency parameter, between 0.0f and 1.0f
- * @param mode The blending mode
+ * @param paint The paint containing the alpha, blending mode, colorFilter, etc.
* @param blend True if the texture contains an alpha channel
* @param vertices The vertices that define the mesh
* @param texCoords The texture coordinates of each vertex
@@ -831,32 +716,33 @@ private:
* @param swapSrcDst Whether or not the src and dst blending operations should be swapped
* @param ignoreTransform True if the current transform should be ignored
* @param vbo The VBO used to draw the mesh
- * @param ignoreScale True if the model view matrix should not be scaled
+ * @param modelViewMode Defines whether the model view matrix should be scaled
* @param dirty True if calling this method should dirty the current layer
*/
void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
- float alpha, SkXfermode::Mode mode, bool blend,
+ const SkPaint* paint, bool blend,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
- bool ignoreScale = false, bool dirty = true);
+ ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true);
void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture,
- float alpha, SkXfermode::Mode mode, bool blend,
+ const SkPaint* paint, bool blend,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
- bool ignoreScale = false, bool dirty = true);
+ ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true);
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
- GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
+ GLuint texture, const SkPaint* paint,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
+ bool ignoreTransform, ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale,
+ bool dirty = true);
/**
* Draws the specified list of vertices as quads using indexed GL_TRIANGLES.
* If the number of vertices to draw exceeds the number of indices we have
* pre-allocated, this method will generate several glDrawElements() calls.
*/
- void drawIndexedQuads(Vertex* mesh, GLsizei quadsCount);
+ void issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount);
/**
* Draws text underline and strike-through if needed.
@@ -868,8 +754,7 @@ private:
* @param y The y coordinate where the text will be drawn
* @param paint The paint to draw the text with
*/
- void drawTextDecorations(const char* text, int bytesCount, float totalAdvance,
- float x, float y, SkPaint* paint);
+ void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint);
/**
* Draws shadow layer on text (with optional positions).
@@ -881,12 +766,11 @@ private:
* @param positions The x, y positions of individual glyphs (or NULL)
* @param fontRenderer The font renderer object
* @param alpha The alpha value for drawing the shadow
- * @param mode The xfermode for drawing the shadow
* @param x The x coordinate where the shadow will be drawn
* @param y The y coordinate where the shadow will be drawn
*/
- void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
- const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+ void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count,
+ const float* positions, FontRenderer& fontRenderer, int alpha,
float x, float y);
/**
@@ -898,7 +782,7 @@ private:
* @param y The y coordinate where the texture will be drawn
* @param paint The paint to draw the texture with
*/
- void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
+ void drawPathTexture(const PathTexture* texture, float x, float y, const SkPaint* paint);
/**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
@@ -965,38 +849,56 @@ private:
void setupDrawWithTextureAndColor(bool isAlpha8 = false);
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
- void setupDrawAA();
+ void setupDrawVertexAlpha(bool useShadowAlphaInterp);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
void setupDrawTextGamma(const SkPaint* paint);
- void setupDrawShader();
- void setupDrawColorFilter();
- void setupDrawBlending(SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode,
- bool swapSrcDst = false);
- void setupDrawBlending(bool blend = true, SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode,
- bool swapSrcDst = false);
+ void setupDrawShader(const SkShader* shader);
+ void setupDrawColorFilter(const SkColorFilter* filter);
+ void setupDrawBlending(const Layer* layer, bool swapSrcDst = false);
+ void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false);
void setupDrawProgram();
void setupDrawDirtyRegionsDisabled();
- void setupDrawModelViewIdentity(bool offset = false);
- void setupDrawModelView(float left, float top, float right, float bottom,
- bool ignoreTransform = false, bool ignoreModelView = false);
- void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
- bool ignoreTransform = false);
- void setupDrawColorUniforms();
+
+ /**
+ * Setup the current program matrices based upon the nature of the geometry.
+ *
+ * @param mode If kModelViewMode_Translate, the geometry must be translated by the left and top
+ * parameters. If kModelViewMode_TranslateAndScale, the geometry that exists in the (0,0, 1,1)
+ * space must be scaled up and translated to fill the quad provided in (l,t,r,b). These
+ * transformations are stored in the modelView matrix and uploaded to the shader.
+ *
+ * @param offset Set to true if the the matrix should be fudged (translated) slightly to disambiguate
+ * geometry pixel positioning. See Vertex::GeometryFudgeFactor().
+ *
+ * @param ignoreTransform Set to true if l,t,r,b coordinates already in layer space,
+ * currentTransform() will be ignored. (e.g. when drawing clip in layer coordinates to stencil,
+ * or when simple translation has been extracted)
+ */
+ void setupDrawModelView(ModelViewMode mode, bool offset,
+ float left, float top, float right, float bottom, bool ignoreTransform = false);
+ void setupDrawColorUniforms(bool hasShader);
void setupDrawPureColorUniforms();
- void setupDrawShaderIdentityUniforms();
- void setupDrawShaderUniforms(bool ignoreTransform = false);
- void setupDrawColorFilterUniforms();
+
+ /**
+ * Setup uniforms for the current shader.
+ *
+ * @param shader SkShader on the current paint.
+ *
+ * @param ignoreTransform Set to true to ignore the transform in shader.
+ */
+ void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false);
+ void setupDrawColorFilterUniforms(const SkColorFilter* paint);
void setupDrawSimpleMesh();
void setupDrawTexture(GLuint texture);
void setupDrawExternalTexture(GLuint texture);
void setupDrawTextureTransform();
void setupDrawTextureTransformUniforms(mat4& transform);
void setupDrawTextGammaUniforms();
- void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
- void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors);
- void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
+ void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords = NULL, GLuint vbo = 0);
+ void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords, const GLvoid* colors);
+ void setupDrawMeshIndices(const GLvoid* vertices, const GLvoid* texCoords, GLuint vbo = 0);
void setupDrawIndexedVertices(GLvoid* vertices);
void accountForClear(SkXfermode::Mode mode);
@@ -1004,18 +906,19 @@ private:
void updateLayers();
void flushLayers();
+#if DEBUG_LAYERS_AS_REGIONS
/**
* Renders the specified region as a series of rectangles. This method
* is used for debugging only.
*/
- void drawRegionRects(const Region& region);
+ void drawRegionRectsDebug(const Region& region);
+#endif
/**
* Renders the specified region as a series of rectangles. The region
* must be in screen-space coordinates.
*/
- void drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode,
- bool dirty = false);
+ void drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty = false);
/**
* Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS
@@ -1034,10 +937,6 @@ private:
mDirtyClip = true;
}
- inline mat4& currentTransform() const {
- return *mSnapshot->transform;
- }
-
inline const UvMapper& getMapper(const Texture* texture) {
return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper;
}
@@ -1047,23 +946,24 @@ private:
* come from the texture cache or an atlas. If this method returns
* NULL, the texture could not be found and/or allocated.
*/
- Texture* getTexture(SkBitmap* bitmap);
+ Texture* getTexture(const SkBitmap* bitmap);
- // Dimensions of the drawing surface
- int mWidth, mHeight;
-
- // Matrix used for ortho projection in shaders
- mat4 mOrthoMatrix;
-
- // Model-view matrix used to position/size objects
- mat4 mModelView;
+ /**
+ * Model-view matrix used to position/size objects
+ *
+ * Stores operation-local modifications to the draw matrix that aren't incorporated into the
+ * currentTransform().
+ *
+ * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset,
+ * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale,
+ * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale
+ * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height
+ * of a bitmap.
+ *
+ * Used as input to SkiaShader transformation.
+ */
+ mat4 mModelViewMatrix;
- // Number of saved states
- int mSaveCount;
- // Base state
- sp<Snapshot> mFirstSnapshot;
- // Current state
- sp<Snapshot> mSnapshot;
// State used to define the clipping region
Rect mTilingClip;
// Is the target render surface opaque
@@ -1084,17 +984,13 @@ private:
// Various caches
Caches& mCaches;
Extensions& mExtensions;
+ RenderState& mRenderState;
// List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;
- // List of functors to invoke after a frame is drawn
- SortedVector<Functor*> mFunctors;
// List of layers to update at the beginning of a frame
Vector<Layer*> mLayerUpdates;
- // Indicates whether the clip must be restored
- bool mDirtyClip;
-
// The following fields are used to setup drawing
// Used to describe the shaders to generate
ProgramDescription mDescription;
@@ -1122,10 +1018,14 @@ private:
bool mCountOverdraw;
float mOverdraw;
- // Optional name of the renderer
- String8 mName;
+ bool mSkipOutlineClip;
+
+ // Lighting + shadows
+ Vector3 mLightCenter;
+ float mLightRadius;
+ uint8_t mAmbientShadowAlpha;
+ uint8_t mSpotShadowAlpha;
- friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
friend class DrawBitmapOp;
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
new file mode 100644
index 0000000..6dacd5e
--- /dev/null
+++ b/libs/hwui/Outline.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <SkPath.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class Outline {
+public:
+ Outline()
+ : mShouldClip(false)
+ , mType(kOutlineType_None)
+ , mRadius(0)
+ , mAlpha(0.0f) {}
+
+ void setRoundRect(int left, int top, int right, int bottom, float radius, float alpha) {
+ mType = kOutlineType_RoundRect;
+ mBounds.set(left, top, right, bottom);
+ mRadius = radius;
+ mPath.reset();
+ mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom),
+ radius, radius);
+ mAlpha = alpha;
+ }
+
+ void setConvexPath(const SkPath* outline, float alpha) {
+ if (!outline) {
+ setEmpty();
+ return;
+ }
+ mType = kOutlineType_ConvexPath;
+ mPath = *outline;
+ mBounds.set(outline->getBounds());
+ mAlpha = alpha;
+ }
+
+ void setEmpty() {
+ mType = kOutlineType_Empty;
+ mPath.reset();
+ mAlpha = 0.0f;
+ }
+
+ void setNone() {
+ mType = kOutlineType_None;
+ mPath.reset();
+ mAlpha = 0.0f;
+ }
+
+ bool isEmpty() const {
+ return mType == kOutlineType_Empty;
+ }
+
+ float getAlpha() const {
+ return mAlpha;
+ }
+
+ void setShouldClip(bool clip) {
+ mShouldClip = clip;
+ }
+
+ bool getShouldClip() const {
+ return mShouldClip;
+ }
+
+ bool willClip() const {
+ // only round rect outlines can be used for clipping
+ return mShouldClip && (mType == kOutlineType_RoundRect);
+ }
+
+ bool getAsRoundRect(Rect* outRect, float* outRadius) const {
+ if (mType == kOutlineType_RoundRect) {
+ outRect->set(mBounds);
+ *outRadius = mRadius;
+ return true;
+ }
+ return false;
+ }
+
+ const SkPath* getPath() const {
+ if (mType == kOutlineType_None || mType == kOutlineType_Empty) return NULL;
+
+ return &mPath;
+ }
+
+private:
+ enum OutlineType {
+ kOutlineType_None = 0,
+ kOutlineType_Empty = 1,
+ kOutlineType_ConvexPath = 2,
+ kOutlineType_RoundRect = 3
+ };
+
+ bool mShouldClip;
+ OutlineType mType;
+ Rect mBounds;
+ float mRadius;
+ float mAlpha;
+ SkPath mPath;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* OUTLINE_H */
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index b5e8838..1ba045d 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -36,7 +36,8 @@ namespace uirenderer {
// 9-patch structures
///////////////////////////////////////////////////////////////////////////////
-struct Patch {
+class Patch {
+public:
Patch();
~Patch();
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 0794aec..9ba8854 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
+#include <SkColor.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkRect.h>
@@ -51,7 +52,7 @@ PathDescription::PathDescription():
memset(&shape, 0, sizeof(Shape));
}
-PathDescription::PathDescription(ShapeType type, SkPaint* paint):
+PathDescription::PathDescription(ShapeType type, const SkPaint* paint):
type(type),
join(paint->getStrokeJoin()),
cap(paint->getStrokeCap()),
@@ -74,15 +75,11 @@ hash_t PathDescription::hash() const {
return JenkinsHashWhiten(hash);
}
-int PathDescription::compare(const PathDescription& rhs) const {
- return memcmp(this, &rhs, sizeof(PathDescription));
-}
-
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
-bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) {
+bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) {
// NOTE: This should only be used after PathTessellator handles joins properly
return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity;
}
@@ -108,15 +105,14 @@ void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
}
static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.setConfig(SkBitmap::kA8_Config, width, height);
- bitmap.allocPixels();
+ bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
bitmap.eraseColor(0);
}
static void initPaint(SkPaint& paint) {
// Make sure the paint is opaque, color, alpha, filter, etc.
// will be applied later when compositing the alpha8 texture
- paint.setColor(0xff000000);
+ paint.setColor(SK_ColorBLACK);
paint.setAlpha(255);
paint.setColorFilter(NULL);
paint.setMaskFilter(NULL);
@@ -163,14 +159,7 @@ PathCache::PathCache():
} else {
INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
}
- init();
-}
-PathCache::~PathCache() {
- mCache.clear();
-}
-
-void PathCache::init() {
mCache.setOnEntryRemovedListener(this);
GLint maxTextureSize;
@@ -180,6 +169,10 @@ void PathCache::init() {
mDebugEnabled = readDebugLevel() & kDebugCaches;
}
+PathCache::~PathCache() {
+ mCache.clear();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Size management
///////////////////////////////////////////////////////////////////////////////
@@ -341,12 +334,12 @@ PathCache::PathProcessor::PathProcessor(Caches& caches):
}
void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
- sp<PathTask> t = static_cast<PathTask* >(task.get());
+ PathTask* t = static_cast<PathTask*>(task.get());
ATRACE_NAME("pathPrecache");
float left, top, offset;
uint32_t width, height;
- PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height);
+ PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height);
PathTexture* texture = t->texture;
texture->left = left;
@@ -357,7 +350,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
SkBitmap* bitmap = new SkBitmap();
- drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height);
+ drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
t->setResult(bitmap);
} else {
texture->width = 0;
@@ -415,7 +408,7 @@ void PathCache::clearGarbage() {
* in the cache. The source path is also used to reclaim garbage when a
* Dalvik Path object is collected.
*/
-static SkPath* getSourcePath(SkPath* path) {
+static const SkPath* getSourcePath(const SkPath* path) {
const SkPath* sourcePath = path->getSourcePath();
if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
return const_cast<SkPath*>(sourcePath);
@@ -423,7 +416,7 @@ static SkPath* getSourcePath(SkPath* path) {
return path;
}
-PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
path = getSourcePath(path);
PathDescription entry(kShapePath, paint);
@@ -461,7 +454,7 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
return texture;
}
-void PathCache::precache(SkPath* path, SkPaint* paint) {
+void PathCache::precache(const SkPath* path, const SkPaint* paint) {
if (!Caches::getInstance().tasks.canRunTasks()) {
return;
}
@@ -509,7 +502,7 @@ void PathCache::precache(SkPath* path, SkPaint* paint) {
///////////////////////////////////////////////////////////////////////////////
PathTexture* PathCache::getRoundRect(float width, float height,
- float rx, float ry, SkPaint* paint) {
+ float rx, float ry, const SkPaint* paint) {
PathDescription entry(kShapeRoundRect, paint);
entry.shape.roundRect.mWidth = width;
entry.shape.roundRect.mHeight = height;
@@ -534,7 +527,7 @@ PathTexture* PathCache::getRoundRect(float width, float height,
// Circles
///////////////////////////////////////////////////////////////////////////////
-PathTexture* PathCache::getCircle(float radius, SkPaint* paint) {
+PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
PathDescription entry(kShapeCircle, paint);
entry.shape.circle.mRadius = radius;
@@ -554,7 +547,7 @@ PathTexture* PathCache::getCircle(float radius, SkPaint* paint) {
// Ovals
///////////////////////////////////////////////////////////////////////////////
-PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) {
+PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
PathDescription entry(kShapeOval, paint);
entry.shape.oval.mWidth = width;
entry.shape.oval.mHeight = height;
@@ -577,7 +570,7 @@ PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) {
// Rects
///////////////////////////////////////////////////////////////////////////////
-PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) {
+PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
PathDescription entry(kShapeRect, paint);
entry.shape.rect.mWidth = width;
entry.shape.rect.mHeight = height;
@@ -601,7 +594,7 @@ PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) {
///////////////////////////////////////////////////////////////////////////////
PathTexture* PathCache::getArc(float width, float height,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
PathDescription entry(kShapeArc, paint);
entry.shape.arc.mWidth = width;
entry.shape.arc.mHeight = height;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 24f88f1..bc34188 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -26,13 +26,14 @@
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
+#include "utils/Macros.h"
#include "utils/Pair.h"
class SkBitmap;
class SkCanvas;
class SkPaint;
class SkPath;
-class SkRect;
+struct SkRect;
namespace android {
namespace uirenderer {
@@ -107,6 +108,7 @@ enum ShapeType {
};
struct PathDescription {
+ DESCRIPTION_TYPE(PathDescription);
ShapeType type;
SkPaint::Join join;
SkPaint::Cap cap;
@@ -116,7 +118,7 @@ struct PathDescription {
SkPathEffect* pathEffect;
union Shape {
struct Path {
- SkPath* mPath;
+ const SkPath* mPath;
} path;
struct RoundRect {
float mWidth;
@@ -145,32 +147,9 @@ struct PathDescription {
} shape;
PathDescription();
- PathDescription(ShapeType shapeType, SkPaint* paint);
+ PathDescription(ShapeType shapeType, const SkPaint* paint);
hash_t hash() const;
-
- int compare(const PathDescription& rhs) const;
-
- bool operator==(const PathDescription& other) const {
- return compare(other) == 0;
- }
-
- bool operator!=(const PathDescription& other) const {
- return compare(other) != 0;
- }
-
- friend inline int strictly_order_type(
- const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs) < 0;
- }
-
- friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs);
- }
-
- friend inline hash_t hash_type(const PathDescription& entry) {
- return entry.hash();
- }
};
/**
@@ -207,13 +186,13 @@ public:
*/
uint32_t getSize();
- PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
- PathTexture* getCircle(float radius, SkPaint* paint);
- PathTexture* getOval(float width, float height, SkPaint* paint);
- PathTexture* getRect(float width, float height, SkPaint* paint);
+ PathTexture* getRoundRect(float width, float height, float rx, float ry, const SkPaint* paint);
+ PathTexture* getCircle(float radius, const SkPaint* paint);
+ PathTexture* getOval(float width, float height, const SkPaint* paint);
+ PathTexture* getRect(float width, float height, const SkPaint* paint);
PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
- bool useCenter, SkPaint* paint);
- PathTexture* get(SkPath* path, SkPaint* paint);
+ bool useCenter, const SkPaint* paint);
+ PathTexture* get(const SkPath* path, const SkPaint* paint);
/**
* Removes the specified path. This is meant to be called from threads
@@ -239,9 +218,9 @@ public:
/**
* Precaches the specified path using background threads.
*/
- void precache(SkPath* path, SkPaint* paint);
+ void precache(const SkPath* path, const SkPaint* paint);
- static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
+ static bool canDrawAsConvexPath(SkPath* path, const SkPaint* paint);
static void computePathBounds(const SkPath* path, const SkPaint* paint,
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
static void computeBounds(const SkRect& bounds, const SkPaint* paint,
@@ -292,17 +271,20 @@ private:
class PathTask: public Task<SkBitmap*> {
public:
- PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
- path(path), paint(*paint), texture(texture) {
+ PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
+ path(*path), paint(*paint), texture(texture) {
}
~PathTask() {
delete future()->get();
}
- SkPath* path;
- //copied, since input paint may not be immutable
- SkPaint paint;
+ // copied, since input path not refcounted / guaranteed to survive for duration of task
+ // TODO: avoid deep copy with refcounting
+ const SkPath path;
+
+ // copied, since input paint may not be immutable
+ const SkPaint paint;
PathTexture* texture;
};
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 3970913..e30ac19 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "PathTessellator"
+#define LOG_TAG "OpenGLRenderer"
#define LOG_NDEBUG 1
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define ATRACE_TAG ATRACE_TAG_VIEW
#define VERTEX_DEBUG 0
@@ -24,11 +24,11 @@
#define DEBUG_DUMP_ALPHA_BUFFER() \
for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
ALOGD("point %d at %f %f, alpha %f", \
- i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \
+ i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
}
#define DEBUG_DUMP_BUFFER() \
for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
- ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \
+ ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
}
#else
#define DEBUG_DUMP_ALPHA_BUFFER()
@@ -49,31 +49,34 @@
#include "Matrix.h"
#include "Vector.h"
#include "Vertex.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
-#define THRESHOLD 0.5f
+#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
+#define MAX_DEPTH 15
-void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint,
- bool forceExpand) {
- if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) {
- float outset = paint->getStrokeWidth() * 0.5f;
- if (outset == 0) outset = 0.5f; // account for hairline
- bounds.outset(outset, outset);
+/**
+ * Extracts the x and y scale from the transform as positive values, and clamps them
+ */
+void PathTessellator::extractTessellationScales(const Matrix4& transform,
+ float* scaleX, float* scaleY) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ } else {
+ float m00 = transform.data[Matrix4::kScaleX];
+ float m01 = transform.data[Matrix4::kSkewY];
+ float m10 = transform.data[Matrix4::kSkewX];
+ float m11 = transform.data[Matrix4::kScaleY];
+ *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
+ *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
}
}
-inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
- Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
-}
-
-inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
- AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
-}
-
/**
* Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
* from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
@@ -84,7 +87,7 @@ inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcP
*
* NOTE: assumes angles between normals 90 degrees or less
*/
-inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
}
@@ -93,24 +96,23 @@ inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& norma
*/
struct PaintInfo {
public:
- PaintInfo(const SkPaint* paint, const mat4 *transform) :
+ PaintInfo(const SkPaint* paint, const mat4& transform) :
style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
- inverseScaleX(1.0f), inverseScaleY(1.0f),
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
- if (CC_UNLIKELY(!transform->isPureTranslate())) {
- float m00 = transform->data[Matrix4::kScaleX];
- float m01 = transform->data[Matrix4::kSkewY];
- float m10 = transform->data[Matrix4::kSkewX];
- float m11 = transform->data[Matrix4::kScaleY];
- float scaleX = sqrt(m00 * m00 + m01 * m01);
- float scaleY = sqrt(m10 * m10 + m11 * m11);
- inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
- inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ } else {
+ float scaleX, scaleY;
+ PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
+ inverseScaleX = 1.0f / scaleX;
+ inverseScaleY = 1.0f / scaleY;
}
if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
2 * halfStrokeWidth < inverseScaleX) {
+ // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
halfStrokeWidth = 0.0f;
}
@@ -124,7 +126,7 @@ public:
float halfStrokeWidth;
float maxAlpha;
- inline void scaleOffsetForStrokeWidth(vec2& offset) const {
+ inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
if (halfStrokeWidth == 0.0f) {
// hairline - compensate for scale
offset.x *= 0.5f * inverseScaleX;
@@ -138,9 +140,8 @@ public:
* NOTE: the input will not always be a normal, especially for sharp edges - it should be the
* result of totalOffsetFromNormals (see documentation there)
*/
- inline vec2 deriveAAOffset(const vec2& offset) const {
- return vec2(offset.x * 0.5f * inverseScaleX,
- offset.y * 0.5f * inverseScaleY);
+ inline Vector2 deriveAAOffset(const Vector2& offset) const {
+ return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
}
/**
@@ -159,6 +160,17 @@ public:
}
return 0;
}
+
+ /**
+ * Outset the bounds of point data (for line endpoints or points) to account for AA stroke
+ * geometry.
+ */
+ void expandBoundsForStroke(Rect* bounds) const {
+ float outset = halfStrokeWidth;
+ if (outset == 0) outset = 0.5f;
+ bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
+ outset * inverseScaleY + Vertex::GeometryFudgeFactor());
+ }
};
void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
@@ -170,9 +182,9 @@ void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer&
int srcAindex = 0;
int srcBindex = perimeter.size() - 1;
while (srcAindex <= srcBindex) {
- copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
+ buffer[currentIndex++] = perimeter[srcAindex];
if (srcAindex == srcBindex) break;
- copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
+ buffer[currentIndex++] = perimeter[srcBindex];
srcAindex++;
srcBindex--;
}
@@ -192,25 +204,23 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver
int currentIndex = 0;
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->position[1] - last->position[1],
- last->position[0] - current->position[0]);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->position[1] - current->position[1],
- current->position[0] - next->position[0]);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
paintInfo.scaleOffsetForStrokeWidth(totalOffset);
Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
+ current->x + totalOffset.x,
+ current->y + totalOffset.y);
Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ current->x - totalOffset.x,
+ current->y - totalOffset.y);
last = current;
current = next;
@@ -218,20 +228,21 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver
}
// wrap around to beginning
- copyVertex(&buffer[currentIndex++], &buffer[0]);
- copyVertex(&buffer[currentIndex++], &buffer[1]);
+ buffer[currentIndex++] = buffer[0];
+ buffer[currentIndex++] = buffer[1];
DEBUG_DUMP_BUFFER();
}
static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
- const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
- vec2 strokeOffset = normal;
+ const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+ Vector2 strokeOffset = normal;
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 referencePoint(center.position[0], center.position[1]);
+ Vector2 referencePoint = {center.x, center.y};
if (paintInfo.cap == SkPaint::kSquare_Cap) {
- referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+ Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
+ referencePoint += rotated * (begin ? -1 : 1);
}
Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
@@ -255,11 +266,11 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
if (extra > 0) {
// tessellate both round caps
float beginTheta = atan2(
- - (vertices[0].position[0] - vertices[1].position[0]),
- vertices[0].position[1] - vertices[1].position[1]);
+ - (vertices[0].x - vertices[1].x),
+ vertices[0].y - vertices[1].y);
float endTheta = atan2(
- - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
- vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
+ - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
+ vertices[lastIndex].y - vertices[lastIndex - 1].y);
const float dTheta = PI / (extra + 1);
const float radialScale = 2.0f / (1 + cos(dTheta));
@@ -272,40 +283,38 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
}
beginTheta += dTheta;
- vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta));
+ Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
Vertex::set(&buffer[capOffset],
- vertices[0].position[0] + beginRadialOffset.x,
- vertices[0].position[1] + beginRadialOffset.y);
+ vertices[0].x + beginRadialOffset.x,
+ vertices[0].y + beginRadialOffset.y);
endTheta += dTheta;
- vec2 endRadialOffset(cos(endTheta), sin(endTheta));
+ Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
- vertices[lastIndex].position[0] + endRadialOffset.x,
- vertices[lastIndex].position[1] + endRadialOffset.y);
+ vertices[lastIndex].x + endRadialOffset.x,
+ vertices[lastIndex].y + endRadialOffset.y);
}
}
int currentIndex = extra;
const Vertex* last = &(vertices[0]);
const Vertex* current = &(vertices[1]);
- vec2 lastNormal(current->position[1] - last->position[1],
- last->position[0] - current->position[0]);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
- vec2 nextNormal(next->position[1] - current->position[1],
- current->position[0] - next->position[0]);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 center(current->position[0], current->position[1]);
+ Vector2 center = {current->x, current->y};
Vertex::set(&buffer[currentIndex++], center + strokeOffset);
Vertex::set(&buffer[currentIndex++], center - strokeOffset);
@@ -329,7 +338,7 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
* 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
*/
void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
+ VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
// generate alpha points - fill Alpha vertex gaps in between each point with
@@ -337,27 +346,25 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
int currentIndex = 0;
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->position[1] - last->position[1],
- last->position[0] - current->position[0]);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->position[1] - current->position[1],
- current->position[0] - next->position[0]);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
// AA point offset from original point is that point's normal, such that each side is offset
// by .5 pixels
- vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
+ Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
AlphaVertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y,
+ current->x + totalOffset.x,
+ current->y + totalOffset.y,
0.0f);
AlphaVertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y,
- 1.0f);
+ current->x - totalOffset.x,
+ current->y - totalOffset.y,
+ maxAlpha);
last = current;
current = next;
@@ -365,8 +372,8 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
}
// wrap around to beginning
- copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
- copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
+ buffer[currentIndex++] = buffer[0];
+ buffer[currentIndex++] = buffer[1];
// zig zag between all previous points on the inside of the hull to create a
// triangle strip that fills the hull, repeating the first inner point to
@@ -374,9 +381,9 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
int srcAindex = 0;
int srcBindex = perimeter.size() - 1;
while (srcAindex <= srcBindex) {
- copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
+ buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
if (srcAindex == srcBindex) break;
- copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
+ buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
srcAindex++;
srcBindex--;
}
@@ -391,7 +398,7 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
- AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
+ AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
const int extra = paintInfo.capExtraDivisions();
const int extraOffset = (extra + 1) / 2;
const int capIndex = isFirst
@@ -400,27 +407,28 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
if (isFirst) normal *= -1;
// TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
- vec2 AAOffset = paintInfo.deriveAAOffset(normal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
- vec2 strokeOffset = normal;
+ Vector2 strokeOffset = normal;
paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- vec2 outerOffset = strokeOffset + AAOffset;
- vec2 innerOffset = strokeOffset - AAOffset;
+ Vector2 outerOffset = strokeOffset + AAOffset;
+ Vector2 innerOffset = strokeOffset - AAOffset;
- vec2 capAAOffset;
+ Vector2 capAAOffset = {0, 0};
if (paintInfo.cap != SkPaint::kRound_Cap) {
// if the cap is square or butt, the inside primary cap vertices will be inset in two
// directions - both normal to the stroke, and parallel to it.
- capAAOffset = vec2(-AAOffset.y, AAOffset.x);
+ capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
}
// determine referencePoint, the center point for the 4 primary cap vertices
const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
- vec2 referencePoint(point->position[0], point->position[1]);
+ Vector2 referencePoint = {point->x, point->y};
if (paintInfo.cap == SkPaint::kSquare_Cap) {
// To account for square cap, move the primary cap vertices (that create the AA edge) by the
// stroke offset vector (rotated to be parallel to the stroke)
- referencePoint += vec2(-strokeOffset.y, strokeOffset.x);
+ Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
+ referencePoint += rotated;
}
AlphaVertex::set(&buffer[capIndex + 0],
@@ -453,7 +461,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
for (int i = 0; i < extra; i++) {
theta += dTheta;
- vec2 radialOffset(cos(theta), sin(theta));
+ Vector2 radialOffset = {cos(theta), sin(theta)};
// scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
radialOffset *= radialScale;
@@ -471,8 +479,8 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
if (isFirst && i == extra - extraOffset) {
//copy most recent two points to first two points
- copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]);
- copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]);
+ buffer[0] = buffer[capPerimIndex - 2];
+ buffer[1] = buffer[capPerimIndex - 1];
capPerimIndex = 2; // start writing the rest of the round cap at index 2
}
@@ -482,28 +490,28 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
int capFillIndex = startCapFillIndex;
for (int i = 0; i < extra + 2; i += 2) {
- copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]);
+ buffer[capFillIndex++] = buffer[1 + i];
// TODO: to support odd numbers of divisions, break here on the last iteration
- copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]);
+ buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
}
} else {
int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
for (int i = 0; i < extra + 2; i += 2) {
- copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]);
+ buffer[capFillIndex++] = buffer[capIndex + 1 + i];
// TODO: to support odd numbers of divisions, break here on the last iteration
- copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]);
+ buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
}
}
return;
}
if (isFirst) {
- copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]);
- copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]);
- copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!)
- copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]);
+ buffer[0] = buffer[postCapIndex + 2];
+ buffer[1] = buffer[postCapIndex + 3];
+ buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
+ buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
} else {
- copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]);
- copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]);
+ buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
+ buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
}
}
@@ -576,8 +584,7 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
const Vertex* last = &(vertices[0]);
const Vertex* current = &(vertices[1]);
- vec2 lastNormal(current->position[1] - last->position[1],
- last->position[0] - current->position[0]);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
// TODO: use normal from bezier traversal for cap, instead of from vertices
@@ -585,43 +592,42 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
- vec2 nextNormal(next->position[1] - current->position[1],
- current->position[0] - next->position[0]);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
- vec2 innerOffset = totalOffset;
+ Vector2 innerOffset = totalOffset;
paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- vec2 outerOffset = innerOffset + AAOffset;
+ Vector2 outerOffset = innerOffset + AAOffset;
innerOffset -= AAOffset;
AlphaVertex::set(&buffer[currentAAOuterIndex++],
- current->position[0] + outerOffset.x,
- current->position[1] + outerOffset.y,
+ current->x + outerOffset.x,
+ current->y + outerOffset.y,
0.0f);
AlphaVertex::set(&buffer[currentAAOuterIndex++],
- current->position[0] + innerOffset.x,
- current->position[1] + innerOffset.y,
+ current->x + innerOffset.x,
+ current->y + innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentStrokeIndex++],
- current->position[0] + innerOffset.x,
- current->position[1] + innerOffset.y,
+ current->x + innerOffset.x,
+ current->y + innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentStrokeIndex++],
- current->position[0] - innerOffset.x,
- current->position[1] - innerOffset.y,
+ current->x - innerOffset.x,
+ current->y - innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentAAInnerIndex--],
- current->position[0] - innerOffset.x,
- current->position[1] - innerOffset.y,
+ current->x - innerOffset.x,
+ current->y - innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentAAInnerIndex--],
- current->position[0] - outerOffset.x,
- current->position[1] - outerOffset.y,
+ current->x - outerOffset.x,
+ current->y - outerOffset.y,
0.0f);
current = next;
@@ -646,48 +652,46 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V
const Vertex* last = &(perimeter[perimeter.size() - 1]);
const Vertex* current = &(perimeter[0]);
- vec2 lastNormal(current->position[1] - last->position[1],
- last->position[0] - current->position[0]);
+ Vector2 lastNormal = {current->y - last->y, last->x - current->x};
lastNormal.normalize();
for (unsigned int i = 0; i < perimeter.size(); i++) {
const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- vec2 nextNormal(next->position[1] - current->position[1],
- current->position[0] - next->position[0]);
+ Vector2 nextNormal = {next->y - current->y, current->x - next->x};
nextNormal.normalize();
- vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+ Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
- vec2 innerOffset = totalOffset;
+ Vector2 innerOffset = totalOffset;
paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- vec2 outerOffset = innerOffset + AAOffset;
+ Vector2 outerOffset = innerOffset + AAOffset;
innerOffset -= AAOffset;
AlphaVertex::set(&buffer[currentAAOuterIndex++],
- current->position[0] + outerOffset.x,
- current->position[1] + outerOffset.y,
+ current->x + outerOffset.x,
+ current->y + outerOffset.y,
0.0f);
AlphaVertex::set(&buffer[currentAAOuterIndex++],
- current->position[0] + innerOffset.x,
- current->position[1] + innerOffset.y,
+ current->x + innerOffset.x,
+ current->y + innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentStrokeIndex++],
- current->position[0] + innerOffset.x,
- current->position[1] + innerOffset.y,
+ current->x + innerOffset.x,
+ current->y + innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentStrokeIndex++],
- current->position[0] - innerOffset.x,
- current->position[1] - innerOffset.y,
+ current->x - innerOffset.x,
+ current->y - innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentAAInnerIndex++],
- current->position[0] - innerOffset.x,
- current->position[1] - innerOffset.y,
+ current->x - innerOffset.x,
+ current->y - innerOffset.y,
paintInfo.maxAlpha);
AlphaVertex::set(&buffer[currentAAInnerIndex++],
- current->position[0] - outerOffset.x,
- current->position[1] - outerOffset.y,
+ current->x - outerOffset.x,
+ current->y - outerOffset.y,
0.0f);
last = current;
@@ -696,23 +700,23 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V
}
// wrap each strip around to beginning, creating degenerate tris to bridge strips
- copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
- copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
- copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
+ buffer[currentAAOuterIndex++] = buffer[0];
+ buffer[currentAAOuterIndex++] = buffer[1];
+ buffer[currentAAOuterIndex++] = buffer[1];
- copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
- copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
- copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
+ buffer[currentStrokeIndex++] = buffer[offset];
+ buffer[currentStrokeIndex++] = buffer[offset + 1];
+ buffer[currentStrokeIndex++] = buffer[offset + 1];
- copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
- copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
+ buffer[currentAAInnerIndex++] = buffer[2 * offset];
+ buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
// don't need to create last degenerate tri
DEBUG_DUMP_ALPHA_BUFFER();
}
void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
- const mat4 *transform, VertexBuffer& vertexBuffer) {
+ const mat4& transform, VertexBuffer& vertexBuffer) {
ATRACE_CALL();
const PaintInfo paintInfo(paint, transform);
@@ -733,7 +737,8 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
// force close if we're filling the path, since fill path expects closed perimeter.
bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
bool wasClosed = approximatePathOutlineVertices(path, forceClose,
- threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices);
+ threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
+ OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
if (!tempVertices.size()) {
// path was empty, return without allocating vertex buffer
@@ -743,7 +748,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
#if VERTEX_DEBUG
for (unsigned int i = 0; i < tempVertices.size(); i++) {
ALOGD("orig path: point at %f %f",
- tempVertices[i].position[0], tempVertices[i].position[1]);
+ tempVertices[i].x, tempVertices[i].y);
}
#endif
@@ -771,21 +776,15 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
}
}
-}
-static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
- rect.fLeft = fminf(rect.fLeft, x);
- rect.fTop = fminf(rect.fTop, y);
- rect.fRight = fmaxf(rect.fRight, x);
- rect.fBottom = fmaxf(rect.fBottom, y);
-}
-static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
- expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
+ Rect bounds(path.getBounds());
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
template <class TYPE>
static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
- const float* points, int count, SkRect& bounds) {
+ const float* points, int count, Rect& bounds) {
bounds.set(points[0], points[1], points[0], points[1]);
int numPoints = count / 2;
@@ -793,14 +792,14 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
for (int i = 0; i < count; i += 2) {
- expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+ bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
}
dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
}
-void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
- const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
+ const mat4& transform, VertexBuffer& vertexBuffer) {
const PaintInfo paintInfo(paint, transform);
// determine point shape
@@ -818,10 +817,12 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint*
Vector<Vertex> outlineVertices;
approximatePathOutlineVertices(path, true,
paintInfo.inverseScaleX * paintInfo.inverseScaleX,
- paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+ paintInfo.inverseScaleY * paintInfo.inverseScaleY,
+ OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
if (!outlineVertices.size()) return;
+ Rect bounds;
// tessellate, then duplicate outline across points
int numPoints = count / 2;
VertexBuffer tempBuffer;
@@ -829,15 +830,18 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint*
getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
} else {
- getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
+ // note: pass maxAlpha directly, since we want fill to be alpha modulated
+ getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
}
- expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
+ // expand bounds from vertex coords to pixel data
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
-void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
- const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
+ const mat4& transform, VertexBuffer& vertexBuffer) {
ATRACE_CALL();
const PaintInfo paintInfo(paint, transform);
@@ -857,6 +861,7 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p
tempVertices.push();
tempVertices.push();
Vertex* tempVerticesData = tempVertices.editArray();
+ Rect bounds;
bounds.set(points[0], points[1], points[0], points[1]);
for (int i = 0; i < count; i += 4) {
Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
@@ -869,24 +874,31 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p
}
// calculate bounds
- expandRectToCoverVertex(bounds, tempVerticesData[0]);
- expandRectToCoverVertex(bounds, tempVerticesData[1]);
+ bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
+ bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
}
- expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
-
// since multiple objects tessellated into buffer, separate them with degen tris
if (paintInfo.isAA) {
vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
} else {
vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
}
+
+ // expand bounds from vertex coords to pixel data
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
///////////////////////////////////////////////////////////////////////////////
// Simple path line approximation
///////////////////////////////////////////////////////////////////////////////
+bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
+ Vector<Vertex>& outputVertices) {
+ return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
+}
+
void pushToVector(Vector<Vertex>& vertices, float x, float y) {
// TODO: make this not yuck
vertices.push();
@@ -895,7 +907,8 @@ void pushToVector(Vector<Vertex>& vertices, float x, float y) {
}
bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
- float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex>& outputVertices) {
ATRACE_CALL();
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
@@ -922,7 +935,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
pts[0].x(), pts[0].y(),
pts[2].x(), pts[2].y(),
pts[1].x(), pts[1].y(),
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
break;
case SkPath::kCubic_Verb:
ALOGV("kCubic_Verb");
@@ -931,7 +944,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
pts[1].x(), pts[1].y(),
pts[3].x(), pts[3].y(),
pts[2].x(), pts[2].y(),
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
break;
default:
break;
@@ -939,8 +952,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
}
int size = outputVertices.size();
- if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
- outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
+ if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
+ outputVertices[0].y == outputVertices[size - 1].y) {
outputVertices.pop();
return true;
}
@@ -954,7 +967,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
- float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex>& outputVertices, int depth) {
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -962,8 +976,8 @@ void PathTessellator::recursiveCubicBezierVertices(
float d = d1 + d2;
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-
- if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (depth >= MAX_DEPTH
+ || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -987,11 +1001,11 @@ void PathTessellator::recursiveCubicBezierVertices(
recursiveCubicBezierVertices(
p1x, p1y, p1c1x, p1c1y,
mx, my, p1c1c2x, p1c1c2y,
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
recursiveCubicBezierVertices(
mx, my, p2c1c2x, p2c1c2y,
p2x, p2y, p2c2x, p2c2y,
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
}
}
@@ -999,12 +1013,15 @@ void PathTessellator::recursiveQuadraticBezierVertices(
float ax, float ay,
float bx, float by,
float cx, float cy,
- float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex>& outputVertices, int depth) {
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
+ if (depth >= MAX_DEPTH
+ || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
@@ -1018,9 +1035,9 @@ void PathTessellator::recursiveQuadraticBezierVertices(
float my = (acy + bcy) * 0.5f;
recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
- sqrInvScaleX, sqrInvScaleY, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
}
}
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 85797fc..8ac9a3b 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -22,100 +22,79 @@
#include "Matrix.h"
#include "Rect.h"
#include "Vertex.h"
+#include "VertexBuffer.h"
namespace android {
namespace uirenderer {
-class VertexBuffer {
+class PathTessellator {
public:
- VertexBuffer():
- mBuffer(0),
- mVertexCount(0),
- mCleanupMethod(NULL)
- {}
-
- ~VertexBuffer() {
- if (mCleanupMethod) mCleanupMethod(mBuffer);
- }
-
/**
- This should be the only method used by the PathTessellator. Subsequent calls to alloc will
- allocate space within the first allocation (useful if you want to eventually allocate
- multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
+ * Populates scaleX and scaleY with the 'tessellation scale' of the transform - the effective X
+ * and Y scales that tessellation will take into account when generating the 1.0 pixel thick
+ * ramp.
+ *
+ * Two instances of the same shape (size, paint, etc.) will only generate the same vertices if
+ * their tessellation scales are equal.
*/
- template <class TYPE>
- TYPE* alloc(int vertexCount) {
- if (mVertexCount) {
- TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
- // already have allocated the buffer, re-allocate space within
- if (mReallocBuffer != mBuffer) {
- // not first re-allocation, leave space for degenerate triangles to separate strips
- reallocBuffer += 2;
- }
- mReallocBuffer = reallocBuffer + vertexCount;
- return reallocBuffer;
- }
- mVertexCount = vertexCount;
- mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
- mCleanupMethod = &(cleanup<TYPE>);
-
- return (TYPE*)mBuffer;
- }
-
- template <class TYPE>
- void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
- int verticesToCopy = srcBuffer.getVertexCount();
-
- TYPE* dst = alloc<TYPE>(verticesToCopy);
- TYPE* src = (TYPE*)srcBuffer.getBuffer();
-
- for (int i = 0; i < verticesToCopy; i++) {
- TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
- }
- }
-
- void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr?
- unsigned int getVertexCount() const { return mVertexCount; }
-
- template <class TYPE>
- void createDegenerateSeparators(int allocSize) {
- TYPE* end = (TYPE*)mBuffer + mVertexCount;
- for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
- memcpy(degen, degen - 1, sizeof(TYPE));
- memcpy(degen + 1, degen + 2, sizeof(TYPE));
- }
- }
-
-private:
- template <class TYPE>
- static void cleanup(void* buffer) {
- delete[] (TYPE*)buffer;
- }
-
- void* mBuffer;
- unsigned int mVertexCount;
-
- void* mReallocBuffer; // used for multi-allocation
-
- void (*mCleanupMethod)(void*);
-};
-
-class PathTessellator {
-public:
- static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, bool forceExpand);
+ static void extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY);
+ /**
+ * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single
+ * triangle strip. Note: joins are not currently supported.
+ *
+ * @param path The path to be approximated
+ * @param paint The paint the path will be drawn with, indicating AA, painting style
+ * (stroke vs fill), stroke width, stroke cap & join style, etc.
+ * @param transform The transform the path is to be drawn with, used to drive stretch-aware path
+ * vertex approximation, and correct AA ramp offsetting.
+ * @param vertexBuffer The output buffer
+ */
static void tessellatePath(const SkPath& path, const SkPaint* paint,
- const mat4 *transform, VertexBuffer& vertexBuffer);
+ const mat4& transform, VertexBuffer& vertexBuffer);
- static void tessellatePoints(const float* points, int count, SkPaint* paint,
- const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ /**
+ * Populates a VertexBuffer with a tessellated approximation of points as a single triangle
+ * strip (with degenerate tris separating), respecting the shape defined by the paint cap.
+ *
+ * @param points The center vertices of the points to be drawn
+ * @param count The number of floats making up the point vertices
+ * @param paint The paint the points will be drawn with indicating AA, stroke width & cap
+ * @param transform The transform the points will be drawn with, used to drive stretch-aware path
+ * vertex approximation, and correct AA ramp offsetting
+ * @param vertexBuffer The output buffer
+ */
+ static void tessellatePoints(const float* points, int count, const SkPaint* paint,
+ const mat4& transform, VertexBuffer& vertexBuffer);
- static void tessellateLines(const float* points, int count, SkPaint* paint,
- const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ /**
+ * Populates a VertexBuffer with a tessellated approximation of lines as a single triangle
+ * strip (with degenerate tris separating).
+ *
+ * @param points Pairs of endpoints defining the lines to be drawn
+ * @param count The number of floats making up the line vertices
+ * @param paint The paint the lines will be drawn with indicating AA, stroke width & cap
+ * @param transform The transform the points will be drawn with, used to drive stretch-aware path
+ * vertex approximation, and correct AA ramp offsetting
+ * @param vertexBuffer The output buffer
+ */
+ static void tessellateLines(const float* points, int count, const SkPaint* paint,
+ const mat4& transform, VertexBuffer& vertexBuffer);
+
+ /**
+ * Approximates a convex, CW outline into a Vector of 2d vertices.
+ *
+ * @param path The outline to be approximated
+ * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating
+ * @param outputVertices An empty Vector which will be populated with the output
+ */
+ static bool approximatePathOutlineVertices(const SkPath &path, float thresholdSquared,
+ Vector<Vertex> &outputVertices);
private:
static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose,
- float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex> &outputVertices);
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex> &outputVertices);
/*
endpoints a & b,
@@ -125,8 +104,8 @@ private:
float ax, float ay,
float bx, float by,
float cx, float cy,
- float sqrInvScaleX, float sqrInvScaleY,
- Vector<Vertex> &outputVertices);
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex> &outputVertices, int depth = 0);
/*
endpoints p1, p2
@@ -137,8 +116,8 @@ private:
float c1x, float c1y,
float p2x, float p2y,
float c2x, float c2y,
- float sqrInvScaleX, float sqrInvScaleY,
- Vector<Vertex> &outputVertices);
+ float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+ Vector<Vertex> &outputVertices, int depth = 0);
};
}; // namespace uirenderer
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 58f5325..cc72ae0 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -60,7 +60,6 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
GLint status;
glGetProgramiv(mProgramId, GL_LINK_STATUS, &status);
if (status != GL_TRUE) {
- ALOGE("Error while linking shaders:");
GLint infoLen = 0;
glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
@@ -68,14 +67,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
glGetProgramInfoLog(mProgramId, infoLen, 0, &log[0]);
ALOGE("%s", log);
}
-
- glDetachShader(mProgramId, mVertexShader);
- glDetachShader(mProgramId, mFragmentShader);
-
- glDeleteShader(mVertexShader);
- glDeleteShader(mFragmentShader);
-
- glDeleteProgram(mProgramId);
+ LOG_ALWAYS_FATAL("Error while linking shaders");
} else {
mInitialized = true;
}
@@ -153,8 +145,7 @@ GLuint Program::buildShader(const char* source, GLenum type) {
// use a fixed size instead
GLchar log[512];
glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
- ALOGE("Error while compiling shader: %s", log);
- glDeleteShader(shader);
+ LOG_ALWAYS_FATAL("Error while compiling shader: %s", log);
return 0;
}
@@ -173,7 +164,7 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
// up and to the left.
// This offset value is based on an assumption that some hardware may use as
// little as 12.4 precision, so we offset by slightly more than 1/16.
- p.translate(Vertex::gGeometryFudgeFactor, Vertex::gGeometryFudgeFactor);
+ p.translate(Vertex::GeometryFudgeFactor(), Vertex::GeometryFudgeFactor());
glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
}
mProjection = projectionMatrix;
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index f6ac8ec..56773f4 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -45,18 +45,18 @@ namespace uirenderer {
#define COLOR_COMPONENT_THRESHOLD 1.0f
#define COLOR_COMPONENT_INV_THRESHOLD 0.0f
-#define PROGRAM_KEY_TEXTURE 0x1
-#define PROGRAM_KEY_A8_TEXTURE 0x2
-#define PROGRAM_KEY_BITMAP 0x4
-#define PROGRAM_KEY_GRADIENT 0x8
-#define PROGRAM_KEY_BITMAP_FIRST 0x10
-#define PROGRAM_KEY_COLOR_MATRIX 0x20
-#define PROGRAM_KEY_COLOR_LIGHTING 0x40
-#define PROGRAM_KEY_COLOR_BLEND 0x80
-#define PROGRAM_KEY_BITMAP_NPOT 0x100
-#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
-
-#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_TEXTURE 0x01
+#define PROGRAM_KEY_A8_TEXTURE 0x02
+#define PROGRAM_KEY_BITMAP 0x04
+#define PROGRAM_KEY_GRADIENT 0x08
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_BLEND 0x40
+#define PROGRAM_KEY_BITMAP_NPOT 0x80
+
+#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
// Encode the xfermodes on 6 bits
@@ -71,19 +71,21 @@ namespace uirenderer {
#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
#define PROGRAM_MODULATE_SHIFT 35
-#define PROGRAM_HAS_AA_SHIFT 36
+#define PROGRAM_HAS_VERTEX_ALPHA_SHIFT 36
+#define PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT 37
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 37
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 38
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 41
-#define PROGRAM_HAS_COLORS 41
+#define PROGRAM_HAS_COLORS 42
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
-#define PROGRAM_EMULATE_STENCIL 43
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
+#define PROGRAM_EMULATE_STENCIL 44
+#define PROGRAM_HAS_ROUND_RECT_CLIP 45
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -104,7 +106,6 @@ struct ProgramDescription {
enum ColorModifier {
kColorNone = 0,
kColorMatrix,
- kColorLighting,
kColorBlend
};
@@ -134,7 +135,8 @@ struct ProgramDescription {
bool hasBitmap;
bool isBitmapNpot;
- bool isAA; // drawing with a per-vertex alpha
+ bool hasVertexAlpha;
+ bool useShadowAlphaInterp;
bool hasGradient;
Gradient gradientType;
@@ -160,6 +162,7 @@ struct ProgramDescription {
bool hasDebugHighlight;
bool emulateStencil;
+ bool hasRoundRectClip;
/**
* Resets this description. All fields are reset back to the default
@@ -173,7 +176,8 @@ struct ProgramDescription {
hasColors = false;
- isAA = false;
+ hasVertexAlpha = false;
+ useShadowAlphaInterp = false;
modulate = false;
@@ -200,6 +204,8 @@ struct ProgramDescription {
gamma = 2.2f;
hasDebugHighlight = false;
+ emulateStencil = false;
+ hasRoundRectClip = false;
}
/**
@@ -207,7 +213,7 @@ struct ProgramDescription {
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
- bool setColor(const float r, const float g, const float b, const float a) {
+ bool setColorModulate(const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD;
return modulate;
}
@@ -217,7 +223,7 @@ struct ProgramDescription {
* the fragment shader. When this method returns true, the program should
* be provided with a modulation color.
*/
- bool setAlpha8Color(const float r, const float g, const float b, const float a) {
+ bool setAlpha8ColorModulate(const float r, const float g, const float b, const float a) {
modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
return modulate;
@@ -248,9 +254,6 @@ struct ProgramDescription {
case kColorMatrix:
key |= PROGRAM_KEY_COLOR_MATRIX;
break;
- case kColorLighting:
- key |= PROGRAM_KEY_COLOR_LIGHTING;
- break;
case kColorBlend:
key |= PROGRAM_KEY_COLOR_BLEND;
key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
@@ -261,7 +264,8 @@ struct ProgramDescription {
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
- if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
+ if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
+ if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
@@ -269,6 +273,7 @@ struct ProgramDescription {
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL;
+ if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index a5ce6f6..c802b18 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -46,7 +46,7 @@ const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
const char* gVS_Header_Attributes_Colors =
"attribute vec4 colors;\n";
-const char* gVS_Header_Attributes_AAVertexShapeParameters =
+const char* gVS_Header_Attributes_VertexAlphaParameters =
"attribute float vtxAlpha;\n";
const char* gVS_Header_Uniforms_TextureTransform =
"uniform mat4 mainTextureTransform;\n";
@@ -58,11 +58,13 @@ const char* gVS_Header_Uniforms_HasGradient =
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
+const char* gVS_Header_Uniforms_HasRoundRectClip =
+ "uniform mat4 roundRectInvTransform;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
const char* gVS_Header_Varyings_HasColors =
"varying vec4 outColors;\n";
-const char* gVS_Header_Varyings_IsAAVertexShape =
+const char* gVS_Header_Varyings_HasVertexAlpha =
"varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
@@ -85,6 +87,8 @@ const char* gVS_Header_Varyings_HasGradient[6] = {
"varying highp vec2 sweep;\n"
"varying vec2 ditherTexCoords;\n",
};
+const char* gVS_Header_Varyings_HasRoundRectClip =
+ "varying vec2 roundRectPos;\n";
const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
@@ -115,9 +119,14 @@ const char* gVS_Main_OutGradient[6] = {
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
- " gl_Position = projection * transform * position;\n";
-const char* gVS_Main_AAVertexShape =
+ " vec4 transformedPosition = projection * transform * position;\n"
+ " gl_Position = transformedPosition;\n";
+
+const char* gVS_Main_VertexAlpha =
" alpha = vtxAlpha;\n";
+
+const char* gVS_Main_HasRoundRectClip =
+ " roundRectPos = (roundRectInvTransform * transformedPosition).xy;\n";
const char* gVS_Footer =
"}\n\n";
@@ -148,21 +157,22 @@ const char* gFS_Uniforms_GradientSampler[2] = {
};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
-const char* gFS_Uniforms_ColorOp[4] = {
+const char* gFS_Uniforms_ColorOp[3] = {
// None
"",
// Matrix
"uniform mat4 colorMatrix;\n"
"uniform vec4 colorMatrixVector;\n",
- // Lighting
- "uniform vec4 lightingMul;\n"
- "uniform vec4 lightingAdd;\n",
// PorterDuff
"uniform vec4 colorBlend;\n"
};
const char* gFS_Uniforms_Gamma =
"uniform float gamma;\n";
+const char* gFS_Uniforms_HasRoundRectClip =
+ "uniform vec4 roundRectInnerRectLTRB;\n"
+ "uniform float roundRectRadius;\n";
+
const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
@@ -227,8 +237,10 @@ const char* gFS_Main_FetchColor =
" fragColor = color;\n";
const char* gFS_Main_ModulateColor =
" fragColor *= color.a;\n";
-const char* gFS_Main_AccountForAAVertexShape =
+const char* gFS_Main_ApplyVertexAlphaLinearInterp =
" fragColor *= alpha;\n";
+const char* gFS_Main_ApplyVertexAlphaShadowInterp =
+ " fragColor *= (1.0 - cos(alpha)) / 2.0;\n";
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
@@ -311,20 +323,27 @@ const char* gFS_Main_FragColor_Blend =
" gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n";
const char* gFS_Main_FragColor_Blend_Swap =
" gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n";
-const char* gFS_Main_ApplyColorOp[4] = {
+const char* gFS_Main_ApplyColorOp[3] = {
// None
"",
// Matrix
" fragColor *= colorMatrix;\n"
" fragColor += colorMatrixVector;\n"
" fragColor.rgb *= fragColor.a;\n",
- // Lighting
- " float lightingAlpha = fragColor.a;\n"
- " fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n"
- " fragColor.a = lightingAlpha;\n",
// PorterDuff
" fragColor = blendColors(colorBlend, fragColor);\n"
};
+
+// Note: LTRB -> xyzw
+const char* gFS_Main_FragColor_HasRoundRectClip =
+ " mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n"
+ " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n"
+
+ // divide + multiply by 128 to avoid falling out of range in length() function
+ " mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0)) / 128.0;\n"
+ " mediump float linearDist = roundRectRadius - (length(dist) * 128.0);\n"
+ " gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n";
+
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
const char* gFS_Main_EmulateStencil =
@@ -452,8 +471,8 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
- if (description.isAA) {
- shader.append(gVS_Header_Attributes_AAVertexShapeParameters);
+ if (description.hasVertexAlpha) {
+ shader.append(gVS_Header_Attributes_VertexAlphaParameters);
}
if (description.hasColors) {
shader.append(gVS_Header_Attributes_Colors);
@@ -469,12 +488,15 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Uniforms_HasRoundRectClip);
+ }
// Varyings
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ if (description.hasVertexAlpha) {
+ shader.append(gVS_Header_Varyings_HasVertexAlpha);
}
if (description.hasColors) {
shader.append(gVS_Header_Varyings_HasColors);
@@ -485,6 +507,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Varyings_HasRoundRectClip);
+ }
// Begin the shader
shader.append(gVS_Main); {
@@ -493,8 +518,8 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
} else if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Main_OutTexCoords);
}
- if (description.isAA) {
- shader.append(gVS_Main_AAVertexShape);
+ if (description.hasVertexAlpha) {
+ shader.append(gVS_Main_VertexAlpha);
}
if (description.hasColors) {
shader.append(gVS_Main_OutColors);
@@ -507,6 +532,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasGradient) {
shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Main_HasRoundRectClip);
+ }
}
// End the shader
shader.append(gVS_Footer);
@@ -541,8 +569,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ if (description.hasVertexAlpha) {
+ shader.append(gVS_Header_Varyings_HasVertexAlpha);
}
if (description.hasColors) {
shader.append(gVS_Header_Varyings_HasColors);
@@ -553,6 +581,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Varyings_HasRoundRectClip);
+ }
// Uniforms
int modulateOp = MODULATE_OP_NO_MODULATE;
@@ -575,11 +606,18 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasGammaCorrection) {
shader.append(gFS_Uniforms_Gamma);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gFS_Uniforms_HasRoundRectClip);
+ }
// Optimization for common cases
- if (!description.isAA && !blendFramebuffer && !description.hasColors &&
- description.colorOp == ProgramDescription::kColorNone &&
- !description.hasDebugHighlight && !description.emulateStencil) {
+ if (!description.hasVertexAlpha
+ && !blendFramebuffer
+ && !description.hasColors
+ && description.colorOp == ProgramDescription::kColorNone
+ && !description.hasDebugHighlight
+ && !description.emulateStencil
+ && !description.hasRoundRectClip) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -715,8 +753,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Apply the color op if needed
shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
- if (description.isAA) {
- shader.append(gFS_Main_AccountForAAVertexShape);
+ if (description.hasVertexAlpha) {
+ if (description.useShadowAlphaInterp) {
+ shader.append(gFS_Main_ApplyVertexAlphaShadowInterp);
+ } else {
+ shader.append(gFS_Main_ApplyVertexAlphaLinearInterp);
+ }
}
// Output the fragment
@@ -729,6 +771,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasColors) {
shader.append(gFS_Main_FragColor_HasColors);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gFS_Main_FragColor_HasRoundRectClip);
+ }
if (description.hasDebugHighlight) {
shader.append(gFS_Main_DebugHighlight);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 20b8f2f..feaee8e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -89,6 +89,36 @@ enum DebugLevel {
#define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
/**
+ * System property used to enable or disable hardware rendering profiling.
+ * The default value of this property is assumed to be false.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "true", to enable profiling
+ * "visual_bars", to enable profiling and visualize the results on screen
+ * "false", to disable profiling
+ */
+#define PROPERTY_PROFILE "debug.hwui.profile"
+#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
+
+/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes"
+
+/**
* Used to enable/disable non-rectangular clipping debugging.
*
* The accepted values are:
@@ -146,6 +176,7 @@ enum DebugLevel {
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_VERTEX_CACHE_SIZE "ro.hwui.vertex_cache_size"
#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -192,6 +223,7 @@ enum DebugLevel {
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 10.0f
+#define DEFAULT_VERTEX_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index dabd8d4..13265a9 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_RECT_H
#include <cmath>
+#include <SkRect.h>
#include <utils/Log.h>
@@ -26,9 +27,11 @@
namespace android {
namespace uirenderer {
-#define RECT_STRING "%7.2f %7.2f %7.2f %7.2f"
+#define RECT_STRING "%5.2f %5.2f %5.2f %5.2f"
#define RECT_ARGS(r) \
(r).left, (r).top, (r).right, (r).bottom
+#define SK_RECT_ARGS(r) \
+ (r).left(), (r).top(), (r).right(), (r).bottom()
///////////////////////////////////////////////////////////////////////////////
// Structs
@@ -68,6 +71,13 @@ public:
bottom(height) {
}
+ inline Rect(const SkRect& rect):
+ left(rect.fLeft),
+ top(rect.fTop),
+ right(rect.fRight),
+ bottom(rect.fBottom) {
+ }
+
friend int operator==(const Rect& a, const Rect& b) {
return !memcmp(&a, &b, sizeof(a));
}
@@ -165,6 +175,10 @@ public:
bottom += dy;
}
+ void inset(float delta) {
+ outset(-delta);
+ }
+
void outset(float delta) {
left -= delta;
top -= delta;
@@ -172,6 +186,13 @@ public:
bottom += delta;
}
+ void outset(float xdelta, float ydelta) {
+ left -= xdelta;
+ top -= ydelta;
+ right += xdelta;
+ bottom += ydelta;
+ }
+
/**
* Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
* errors.
@@ -190,19 +211,19 @@ public:
* from this inset will only incur similarly small errors in output, due to transparency
* in extreme outside of the geometry.
*/
- left = floorf(left + Vertex::gGeometryFudgeFactor);
- top = floorf(top + Vertex::gGeometryFudgeFactor);
- right = ceilf(right - Vertex::gGeometryFudgeFactor);
- bottom = ceilf(bottom - Vertex::gGeometryFudgeFactor);
+ left = floorf(left + Vertex::GeometryFudgeFactor());
+ top = floorf(top + Vertex::GeometryFudgeFactor());
+ right = ceilf(right - Vertex::GeometryFudgeFactor());
+ bottom = ceilf(bottom - Vertex::GeometryFudgeFactor());
} else {
/* For other geometry, we do the regular rounding in order to snap, but also outset the
* bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect
* with top left at (0.5, 0.5)) will err on the side of a larger damage rect.
*/
- left = floorf(left + 0.5f - Vertex::gGeometryFudgeFactor);
- top = floorf(top + 0.5f - Vertex::gGeometryFudgeFactor);
- right = floorf(right + 0.5f + Vertex::gGeometryFudgeFactor);
- bottom = floorf(bottom + 0.5f + Vertex::gGeometryFudgeFactor);
+ left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor());
+ top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor());
+ right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor());
+ bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor());
}
}
@@ -213,8 +234,22 @@ public:
bottom = floorf(bottom + 0.5f);
}
- void dump() const {
- ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
+ void roundOut() {
+ left = floorf(left);
+ top = floorf(top);
+ right = ceilf(right);
+ bottom = ceilf(bottom);
+ }
+
+ void expandToCoverVertex(float x, float y) {
+ left = fminf(left, x);
+ top = fminf(top, y);
+ right = fmaxf(right, x);
+ bottom = fmaxf(bottom, y);
+ }
+
+ void dump(const char* label = NULL) const {
+ ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
private:
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
new file mode 100644
index 0000000..0db6198
--- /dev/null
+++ b/libs/hwui/RenderNode.cpp
@@ -0,0 +1,909 @@
+/*
+ * Copyright (C) 2014 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 ATRACE_TAG ATRACE_TAG_VIEW
+#define LOG_TAG "OpenGLRenderer"
+
+#include "RenderNode.h"
+
+#include <algorithm>
+#include <string>
+
+#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
+
+#include "DamageAccumulator.h"
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+void RenderNode::outputLogBuffer(int fd) {
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ if (logBuffer.isEmpty()) {
+ return;
+ }
+
+ FILE *file = fdopen(fd, "a");
+
+ fprintf(file, "\nRecent DisplayList operations\n");
+ logBuffer.outputCommands(file);
+
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ fprintf(file, "\nCaches:\n%s", cachesLog.string());
+ fprintf(file, "\n");
+
+ fflush(file);
+}
+
+RenderNode::RenderNode()
+ : mDirtyPropertyFields(0)
+ , mNeedsDisplayListDataSync(false)
+ , mDisplayListData(0)
+ , mStagingDisplayListData(0)
+ , mAnimatorManager(*this)
+ , mLayer(0)
+ , mParentCount(0) {
+}
+
+RenderNode::~RenderNode() {
+ deleteDisplayListData();
+ delete mStagingDisplayListData;
+ LayerRenderer::destroyLayerDeferred(mLayer);
+}
+
+void RenderNode::setStagingDisplayList(DisplayListData* data) {
+ mNeedsDisplayListDataSync = true;
+ delete mStagingDisplayListData;
+ mStagingDisplayListData = data;
+ if (mStagingDisplayListData) {
+ Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
+ }
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void RenderNode::output(uint32_t level) {
+ ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
+ getName(), isRenderable());
+ ALOGD("%*s%s %d", level * 2, "", "Save",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
+ properties().debugOutputProperties(level);
+ int flags = DisplayListOp::kOpLogFlag_Recurse;
+ if (mDisplayListData) {
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ mDisplayListData->displayListOps[i]->output(level, flags);
+ }
+ }
+
+ ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
+}
+
+int RenderNode::getDebugSize() {
+ int size = sizeof(RenderNode);
+ if (mStagingDisplayListData) {
+ size += mStagingDisplayListData->allocator.usedSize();
+ }
+ if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
+ size += mDisplayListData->allocator.usedSize();
+ }
+ return size;
+}
+
+void RenderNode::prepareTree(TreeInfo& info) {
+ ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
+
+ prepareTreeImpl(info);
+}
+
+void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
+ mAnimatorManager.addAnimator(animator);
+}
+
+void RenderNode::damageSelf(TreeInfo& info) {
+ if (isRenderable()) {
+ if (properties().getClipDamageToBounds()) {
+ info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
+ } else {
+ // Hope this is big enough?
+ // TODO: Get this from the display list ops or something
+ info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
+ }
+ }
+}
+
+void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
+ LayerType layerType = properties().layerProperties().type();
+ if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+ // Damage applied so far needs to affect our parent, but does not require
+ // the layer to be updated. So we pop/push here to clear out the current
+ // damage and get a clean state for display list or children updates to
+ // affect, which will require the layer to be updated
+ info.damageAccumulator->popTransform();
+ info.damageAccumulator->pushTransform(this);
+ if (dirtyMask & DISPLAY_LIST) {
+ damageSelf(info);
+ }
+ }
+}
+
+void RenderNode::pushLayerUpdate(TreeInfo& info) {
+ LayerType layerType = properties().layerProperties().type();
+ // If we are not a layer OR we cannot be rendered (eg, view was detached)
+ // we need to destroy any Layers we may have had previously
+ if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+ if (CC_UNLIKELY(mLayer)) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = NULL;
+ }
+ return;
+ }
+
+ bool transformUpdateNeeded = false;
+ if (!mLayer) {
+ mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
+ applyLayerPropertiesToLayer(info);
+ damageSelf(info);
+ transformUpdateNeeded = true;
+ } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
+ if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = 0;
+ }
+ damageSelf(info);
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ info.damageAccumulator->computeCurrentTransform(&windowTransform);
+ mLayer->setWindowTransform(windowTransform);
+ }
+
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
+
+ if (!mLayer) {
+ if (info.errorHandler) {
+ std::string msg = "Unable to create layer for ";
+ msg += getName();
+ info.errorHandler->onError(msg);
+ }
+ return;
+ }
+
+
+ if (dirty.intersect(0, 0, getWidth(), getHeight())) {
+ dirty.roundOut();
+ mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
+ }
+ // This is not inside the above if because we may have called
+ // updateDeferred on a previous prepare pass that didn't have a renderer
+ if (info.renderer && mLayer->deferredUpdateScheduled) {
+ info.renderer->pushLayerUpdate(mLayer);
+ }
+}
+
+void RenderNode::prepareTreeImpl(TreeInfo& info) {
+ info.damageAccumulator->pushTransform(this);
+
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingPropertiesChanges(info);
+ }
+ uint32_t animatorDirtyMask = mAnimatorManager.animate(info);
+ prepareLayer(info, animatorDirtyMask);
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingDisplayListChanges(info);
+ }
+ prepareSubTree(info, mDisplayListData);
+ pushLayerUpdate(info);
+
+ info.damageAccumulator->popTransform();
+}
+
+void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
+ // Push the animators first so that setupStartValueIfNecessary() is called
+ // before properties() is trampled by stagingProperties(), as they are
+ // required by some animators.
+ mAnimatorManager.pushStaging(info);
+ if (mDirtyPropertyFields) {
+ mDirtyPropertyFields = 0;
+ damageSelf(info);
+ info.damageAccumulator->popTransform();
+ mProperties = mStagingProperties;
+ applyLayerPropertiesToLayer(info);
+ // We could try to be clever and only re-damage if the matrix changed.
+ // However, we don't need to worry about that. The cost of over-damaging
+ // here is only going to be a single additional map rect of this node
+ // plus a rect join(). The parent's transform (and up) will only be
+ // performed once.
+ info.damageAccumulator->pushTransform(this);
+ damageSelf(info);
+ }
+}
+
+void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
+ if (CC_LIKELY(!mLayer)) return;
+
+ const LayerProperties& props = properties().layerProperties();
+ mLayer->setAlpha(props.alpha(), props.xferMode());
+ mLayer->setColorFilter(props.colorFilter());
+ mLayer->setBlend(props.needsBlending());
+}
+
+void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
+ if (mNeedsDisplayListDataSync) {
+ mNeedsDisplayListDataSync = false;
+ // Make sure we inc first so that we don't fluctuate between 0 and 1,
+ // which would thrash the layer cache
+ if (mStagingDisplayListData) {
+ for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) {
+ mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
+ }
+ }
+ deleteDisplayListData();
+ mDisplayListData = mStagingDisplayListData;
+ mStagingDisplayListData = NULL;
+ if (mDisplayListData) {
+ for (size_t i = 0; i < mDisplayListData->functors.size(); i++) {
+ (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, NULL);
+ }
+ }
+ damageSelf(info);
+ }
+}
+
+void RenderNode::deleteDisplayListData() {
+ if (mDisplayListData) {
+ for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+ mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
+ }
+ }
+ delete mDisplayListData;
+ mDisplayListData = NULL;
+}
+
+void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
+ if (subtree) {
+ TextureCache& cache = Caches::getInstance().textureCache;
+ info.out.hasFunctors |= subtree->functors.size();
+ // TODO: Fix ownedBitmapResources to not require disabling prepareTextures
+ // and thus falling out of async drawing path.
+ if (subtree->ownedBitmapResources.size()) {
+ info.prepareTextures = false;
+ }
+ for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
+ info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
+ }
+ for (size_t i = 0; i < subtree->children().size(); i++) {
+ DrawRenderNodeOp* op = subtree->children()[i];
+ RenderNode* childNode = op->mRenderNode;
+ info.damageAccumulator->pushTransform(&op->mTransformFromParent);
+ childNode->prepareTreeImpl(info);
+ info.damageAccumulator->popTransform();
+ }
+ }
+}
+
+void RenderNode::destroyHardwareResources() {
+ if (mLayer) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = NULL;
+ }
+ if (mDisplayListData) {
+ for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+ mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources();
+ }
+ if (mNeedsDisplayListDataSync) {
+ // Next prepare tree we are going to push a new display list, so we can
+ // drop our current one now
+ deleteDisplayListData();
+ }
+ }
+}
+
+void RenderNode::decParentRefCount() {
+ LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
+ mParentCount--;
+ if (!mParentCount) {
+ // If a child of ours is being attached to our parent then this will incorrectly
+ // destroy its hardware resources. However, this situation is highly unlikely
+ // and the failure is "just" that the layer is re-created, so this should
+ // be safe enough
+ destroyHardwareResources();
+ }
+}
+
+/*
+ * For property operations, we pass a savecount of 0, since the operations aren't part of the
+ * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
+ * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
+ */
+#define PROPERTY_SAVECOUNT 0
+
+template <class T>
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
+#if DEBUG_DISPLAY_LIST
+ properties().debugOutputProperties(handler.level() + 1);
+#endif
+ if (properties().getLeft() != 0 || properties().getTop() != 0) {
+ renderer.translate(properties().getLeft(), properties().getTop());
+ }
+ if (properties().getStaticMatrix()) {
+ renderer.concatMatrix(*properties().getStaticMatrix());
+ } else if (properties().getAnimationMatrix()) {
+ renderer.concatMatrix(*properties().getAnimationMatrix());
+ }
+ if (properties().hasTransformMatrix()) {
+ if (properties().isTransformTranslateOnly()) {
+ renderer.translate(properties().getTranslationX(), properties().getTranslationY());
+ } else {
+ renderer.concatMatrix(*properties().getTransformMatrix());
+ }
+ }
+ const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+ int clipFlags = properties().getClippingFlags();
+ if (properties().getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+
+ renderer.setOverrideLayerAlpha(properties().getAlpha());
+ } else if (!properties().getHasOverlappingRendering()) {
+ renderer.scaleAlpha(properties().getAlpha());
+ } else {
+ Rect layerBounds(0, 0, getWidth(), getHeight());
+ int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipFlags) {
+ saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ properties().getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by saveLayer
+ }
+
+ SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
+ layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom,
+ properties().getAlpha() * 255, saveFlags);
+ handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ properties().getClippingRectForFlags(clipFlags, &clipRect);
+ ClipRectOp* op = new (handler.allocator()) ClipRectOp(
+ clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ }
+
+ // TODO: support both reveal clip and outline clip simultaneously
+ if (mProperties.getRevealClip().willClip()) {
+ Rect bounds;
+ mProperties.getRevealClip().getBounds(&bounds);
+ renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius());
+ } else if (mProperties.getOutline().willClip()) {
+ renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline()));
+ }
+
+}
+
+/**
+ * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
+ */
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
+ if (properties().getLeft() != 0 || properties().getTop() != 0) {
+ matrix.translate(properties().getLeft(), properties().getTop());
+ }
+ if (properties().getStaticMatrix()) {
+ mat4 stat(*properties().getStaticMatrix());
+ matrix.multiply(stat);
+ } else if (properties().getAnimationMatrix()) {
+ mat4 anim(*properties().getAnimationMatrix());
+ matrix.multiply(anim);
+ }
+
+ bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
+ if (properties().hasTransformMatrix() || applyTranslationZ) {
+ if (properties().isTransformTranslateOnly()) {
+ matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
+ true3dTransform ? properties().getZ() : 0.0f);
+ } else {
+ if (!true3dTransform) {
+ matrix.multiply(*properties().getTransformMatrix());
+ } else {
+ mat4 true3dMat;
+ true3dMat.loadTranslate(
+ properties().getPivotX() + properties().getTranslationX(),
+ properties().getPivotY() + properties().getTranslationY(),
+ properties().getZ());
+ true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
+ true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
+ true3dMat.rotate(properties().getRotation(), 0, 0, 1);
+ true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
+ true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
+
+ matrix.multiply(true3dMat);
+ }
+ }
+ }
+}
+
+/**
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
+ *
+ * This should be called before a call to defer() or drawDisplayList()
+ *
+ * Each DisplayList that serves as a 3d root builds its list of composited children,
+ * which are flagged to not draw in the standard draw loop.
+ */
+void RenderNode::computeOrdering() {
+ ATRACE_CALL();
+ mProjectedNodes.clear();
+
+ // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
+ // transform properties are applied correctly to top level children
+ if (mDisplayListData == NULL) return;
+ for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ childOp->mRenderNode->computeOrderingImpl(childOp,
+ properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
+ }
+}
+
+void RenderNode::computeOrderingImpl(
+ DrawRenderNodeOp* opState,
+ const SkPath* outlineOfProjectionSurface,
+ Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface) {
+ mProjectedNodes.clear();
+ if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+
+ // TODO: should avoid this calculation in most cases
+ // TODO: just calculate single matrix, down to all leaf composited elements
+ Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
+ localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
+
+ if (properties().getProjectBackwards()) {
+ // composited projectee, flag for out of order draw, save matrix, and store in proj surface
+ opState->mSkipInOrderDraw = true;
+ opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+ compositedChildrenOfProjectionSurface->add(opState);
+ } else {
+ // standard in order draw
+ opState->mSkipInOrderDraw = false;
+ }
+
+ if (mDisplayListData->children().size() > 0) {
+ const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+ bool haveAppliedPropertiesToProjection = false;
+ for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ RenderNode* child = childOp->mRenderNode;
+
+ const SkPath* projectionOutline = NULL;
+ Vector<DrawRenderNodeOp*>* projectionChildren = NULL;
+ const mat4* projectionTransform = NULL;
+ if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
+ // if receiving projections, collect projecting descendent
+
+ // Note that if a direct descendent is projecting backwards, we pass it's
+ // grandparent projection collection, since it shouldn't project onto it's
+ // parent, where it will already be drawing.
+ projectionOutline = properties().getOutline().getPath();
+ projectionChildren = &mProjectedNodes;
+ projectionTransform = &mat4::identity();
+ } else {
+ if (!haveAppliedPropertiesToProjection) {
+ applyViewPropertyTransforms(localTransformFromProjectionSurface);
+ haveAppliedPropertiesToProjection = true;
+ }
+ projectionOutline = outlineOfProjectionSurface;
+ projectionChildren = compositedChildrenOfProjectionSurface;
+ projectionTransform = &localTransformFromProjectionSurface;
+ }
+ child->computeOrderingImpl(childOp,
+ projectionOutline, projectionChildren, projectionTransform);
+ }
+ }
+}
+
+class DeferOperationHandler {
+public:
+ DeferOperationHandler(DeferStateStruct& deferStruct, int level)
+ : mDeferStruct(deferStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+ operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
+ inline void startMark(const char* name) {} // do nothing
+ inline void endMark() {}
+ inline int level() { return mLevel; }
+ inline int replayFlags() { return mDeferStruct.mReplayFlags; }
+ inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
+
+private:
+ DeferStateStruct& mDeferStruct;
+ const int mLevel;
+};
+
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
+ DeferOperationHandler handler(deferStruct, level);
+ issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
+}
+
+class ReplayOperationHandler {
+public:
+ ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
+ : mReplayStruct(replayStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+ mReplayStruct.mRenderer.eventMark(operation->name());
+#endif
+ operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
+ inline void startMark(const char* name) {
+ mReplayStruct.mRenderer.startMark(name);
+ }
+ inline void endMark() {
+ mReplayStruct.mRenderer.endMark();
+ }
+ inline int level() { return mLevel; }
+ inline int replayFlags() { return mReplayStruct.mReplayFlags; }
+ inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
+
+private:
+ ReplayStateStruct& mReplayStruct;
+ const int mLevel;
+};
+
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
+ ReplayOperationHandler handler(replayStruct, level);
+ issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
+}
+
+void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
+ if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
+
+ for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ RenderNode* child = childOp->mRenderNode;
+ float childZ = child->properties().getZ();
+
+ if (!MathUtils::isZero(childZ)) {
+ zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
+ childOp->mSkipInOrderDraw = true;
+ } else if (!child->properties().getProjectBackwards()) {
+ // regular, in order drawing DisplayList
+ childOp->mSkipInOrderDraw = false;
+ }
+ }
+
+ // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+template <class T>
+void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
+ if (properties().getAlpha() <= 0.0f
+ || properties().getOutline().getAlpha() <= 0.0f
+ || !properties().getOutline().getPath()) {
+ // no shadow to draw
+ return;
+ }
+
+ mat4 shadowMatrixXY(transformFromParent);
+ applyViewPropertyTransforms(shadowMatrixXY);
+
+ // Z matrix needs actual 3d transformation, so mapped z values will be correct
+ mat4 shadowMatrixZ(transformFromParent);
+ applyViewPropertyTransforms(shadowMatrixZ, true);
+
+ const SkPath* casterOutlinePath = properties().getOutline().getPath();
+ const SkPath* revealClipPath = properties().getRevealClip().getPath();
+ if (revealClipPath && revealClipPath->isEmpty()) return;
+
+ float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
+
+ const SkPath* outlinePath = casterOutlinePath;
+ if (revealClipPath) {
+ // if we can't simply use the caster's path directly, create a temporary one
+ SkPath* frameAllocatedPath = handler.allocPathForFrame();
+
+ // intersect the outline with the convex reveal clip
+ Op(*casterOutlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
+ outlinePath = frameAllocatedPath;
+ }
+
+ DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
+ shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath);
+ handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+}
+
+template <class T>
+int RenderNode::issueOperationsOfNegZChildren(
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler) {
+ if (zTranslatedNodes.isEmpty()) return -1;
+
+ // create a save around the body of the ViewGroup's draw method, so that
+ // matrix/clip methods don't affect composited children
+ int shadowSaveCount = renderer.getSaveCount();
+ handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+ issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+ return shadowSaveCount;
+}
+
+template <class T>
+void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo,
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler) {
+ if (zTranslatedNodes.isEmpty()) return;
+
+ LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to");
+ handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ renderer.setOverrideLayerAlpha(1.0f);
+
+ issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+}
+
+#define SHADOW_DELTA 0.1f
+
+template <class T>
+void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+ || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == kNegativeZChildren) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+
+ DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
+ endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
+
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
+ RenderNode* caster = casterOp->mRenderNode;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+ caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ // only the actual child DL draw needs to be in save/restore,
+ // since it modifies the renderer's matrix
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+ DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+ RenderNode* child = childOp->mRenderNode;
+
+ renderer.concatMatrix(childOp->mTransformFromParent);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
+ childOp->mSkipInOrderDraw = true;
+
+ renderer.restoreToCount(restoreTo);
+ drawIndex++;
+ }
+}
+
+template <class T>
+void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
+ DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
+ const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
+ int restoreTo = renderer.getSaveCount();
+
+ LinearAllocator& alloc = handler.allocator();
+ handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+ // Transform renderer to match background we're projecting onto
+ // (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
+ const DisplayListOp* op =
+ (mDisplayListData->displayListOps[mDisplayListData->projectionReceiveIndex]);
+ const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
+ const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
+ renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
+
+ // If the projection reciever has an outline, we mask each of the projected rendernodes to it
+ // Either with clipRect, or special saveLayer masking
+ if (projectionReceiverOutline != NULL) {
+ const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
+ if (projectionReceiverOutline->isRect(NULL)) {
+ // mask to the rect outline simply with clipRect
+ ClipRectOp* clipOp = new (alloc) ClipRectOp(
+ outlineBounds.left(), outlineBounds.top(),
+ outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
+ handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ } else {
+ // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
+ SaveLayerOp* op = new (alloc) SaveLayerOp(
+ outlineBounds.left(), outlineBounds.top(),
+ outlineBounds.right(), outlineBounds.bottom(),
+ 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
+ op->setMask(projectionReceiverOutline);
+ handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+ /* TODO: add optimizations here to take advantage of placement/size of projected
+ * children (which may shrink saveLayer area significantly). This is dependent on
+ * passing actual drawing/dirtying bounds of projected content down to native.
+ */
+ }
+ }
+
+ // draw projected nodes
+ for (size_t i = 0; i < mProjectedNodes.size(); i++) {
+ DrawRenderNodeOp* childOp = mProjectedNodes[i];
+
+ // matrix save, concat, and restore can be done safely without allocating operations
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
+ childOp->mSkipInOrderDraw = true;
+ renderer.restoreToCount(restoreTo);
+ }
+
+ if (projectionReceiverOutline != NULL) {
+ handler(new (alloc) RestoreToCountOp(restoreTo),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ }
+}
+
+/**
+ * This function serves both defer and replay modes, and will organize the displayList's component
+ * operations for a single frame:
+ *
+ * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
+ * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
+ * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
+ * defer vs replay logic, per operation
+ */
+template <class T>
+void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+ const int level = handler.level();
+ if (mDisplayListData->isEmpty()) {
+ DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
+
+ const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
+ // If we are updating the contents of mLayer, we don't want to apply any of
+ // the RenderNode's properties to this issueOperations pass. Those will all
+ // be applied when the layer is drawn, aka when this is true.
+ const bool useViewProperties = (!mLayer || drawLayer);
+ if (useViewProperties) {
+ const Outline& outline = properties().getOutline();
+ if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
+ }
+
+ handler.startMark(getName());
+
+#if DEBUG_DISPLAY_LIST
+ const Rect& clipRect = renderer.getLocalClipBounds();
+ DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
+ level * 2, "", this, getName(),
+ clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+#endif
+
+ LinearAllocator& alloc = handler.allocator();
+ int restoreTo = renderer.getSaveCount();
+ handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+ DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+ if (useViewProperties) {
+ setViewProperties<T>(renderer, handler);
+ }
+
+ bool quickRejected = properties().getClipToBounds()
+ && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
+ if (!quickRejected) {
+ if (drawLayer) {
+ handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
+ renderer.getSaveCount() - 1, properties().getClipToBounds());
+ } else {
+ Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
+
+ // for 3d root, draw children with negative z values
+ int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler);
+
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ const int saveCountOffset = renderer.getSaveCount() - 1;
+ const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+ const int size = static_cast<int>(mDisplayListData->displayListOps.size());
+ for (int i = 0; i < size; i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
+
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+ logBuffer.writeCommand(level, op->name());
+ handler(op, saveCountOffset, properties().getClipToBounds());
+
+ if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+ issueOperationsOfProjectedChildren(renderer, handler);
+ }
+ }
+
+ // for 3d root, draw children with positive z values
+ issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
+ }
+ }
+
+ DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
+ handler(new (alloc) RestoreToCountOp(restoreTo),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ renderer.setOverrideLayerAlpha(1.0f);
+
+ DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
+ handler.endMark();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
new file mode 100644
index 0000000..afa17d5
--- /dev/null
+++ b/libs/hwui/RenderNode.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef RENDERNODE_H
+#define RENDERNODE_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <utils/LinearAllocator.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "AnimatorManager.h"
+#include "DamageAccumulator.h"
+#include "Debug.h"
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "DisplayList.h"
+#include "RenderProperties.h"
+#include "TreeInfo.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaShader;
+
+class ClipRectOp;
+class SaveLayerOp;
+class SaveOp;
+class RestoreToCountOp;
+class DrawRenderNodeOp;
+
+/**
+ * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
+ *
+ * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
+ * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
+ * a renderer).
+ *
+ * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
+ * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * attached.
+ */
+class RenderNode : public VirtualLightRefBase {
+public:
+ enum DirtyPropertyMask {
+ GENERIC = 1 << 1,
+ TRANSLATION_X = 1 << 2,
+ TRANSLATION_Y = 1 << 3,
+ TRANSLATION_Z = 1 << 4,
+ SCALE_X = 1 << 5,
+ SCALE_Y = 1 << 6,
+ ROTATION = 1 << 7,
+ ROTATION_X = 1 << 8,
+ ROTATION_Y = 1 << 9,
+ X = 1 << 10,
+ Y = 1 << 11,
+ Z = 1 << 12,
+ ALPHA = 1 << 13,
+ DISPLAY_LIST = 1 << 14,
+ };
+
+ ANDROID_API RenderNode();
+ ANDROID_API virtual ~RenderNode();
+
+ // See flags defined in DisplayList.java
+ enum ReplayFlag {
+ kReplayFlag_ClipChildren = 0x1
+ };
+
+ ANDROID_API static void outputLogBuffer(int fd);
+
+ ANDROID_API void setStagingDisplayList(DisplayListData* newData);
+
+ void computeOrdering();
+
+ void defer(DeferStateStruct& deferStruct, const int level);
+ void replay(ReplayStateStruct& replayStruct, const int level);
+
+ ANDROID_API void output(uint32_t level = 1);
+ ANDROID_API int getDebugSize();
+
+ bool isRenderable() const {
+ return mDisplayListData && mDisplayListData->hasDrawOps;
+ }
+
+ bool hasProjectionReceiver() const {
+ return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+ }
+
+ const char* getName() const {
+ return mName.string();
+ }
+
+ void setName(const char* name) {
+ if (name) {
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
+ }
+ }
+
+ bool isPropertyFieldDirty(DirtyPropertyMask field) const {
+ return mDirtyPropertyFields & field;
+ }
+
+ void setPropertyFieldsDirty(uint32_t fields) {
+ mDirtyPropertyFields |= fields;
+ }
+
+ const RenderProperties& properties() const {
+ return mProperties;
+ }
+
+ RenderProperties& animatorProperties() {
+ return mProperties;
+ }
+
+ const RenderProperties& stagingProperties() {
+ return mStagingProperties;
+ }
+
+ RenderProperties& mutateStagingProperties() {
+ return mStagingProperties;
+ }
+
+ int getWidth() {
+ return properties().getWidth();
+ }
+
+ int getHeight() {
+ return properties().getHeight();
+ }
+
+ ANDROID_API virtual void prepareTree(TreeInfo& info);
+ void destroyHardwareResources();
+
+ // UI thread only!
+ ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
+
+ void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
+
+private:
+ typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
+
+ static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) {
+ for (size_t i = 0; i < nodes.size(); i++) {
+ if (nodes[i].key >= 0.0f) return i;
+ }
+ return nodes.size();
+ }
+
+ enum ChildrenSelectMode {
+ kNegativeZChildren,
+ kPositiveZChildren
+ };
+
+ void computeOrderingImpl(DrawRenderNodeOp* opState,
+ const SkPath* outlineOfProjectionSurface,
+ Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface);
+
+ template <class T>
+ inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
+
+ void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
+
+ template<class T>
+ inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
+
+ template <class T>
+ inline int issueOperationsOfNegZChildren(
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler);
+ template <class T>
+ inline void issueOperationsOfPosZChildren(int shadowRestoreTo,
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler);
+ template <class T>
+ inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
+
+ template <class T>
+ inline void issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler);
+
+ /**
+ * Issue the RenderNode's operations into a handler, recursing for subtrees through
+ * DrawRenderNodeOp's defer() or replay() methods
+ */
+ template <class T>
+ inline void issueOperations(OpenGLRenderer& renderer, T& handler);
+
+ class TextContainer {
+ public:
+ size_t length() const {
+ return mByteLength;
+ }
+
+ const char* text() const {
+ return (const char*) mText;
+ }
+
+ size_t mByteLength;
+ const char* mText;
+ };
+
+ void prepareTreeImpl(TreeInfo& info);
+ void pushStagingPropertiesChanges(TreeInfo& info);
+ void pushStagingDisplayListChanges(TreeInfo& info);
+ void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+ void applyLayerPropertiesToLayer(TreeInfo& info);
+ void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
+ void pushLayerUpdate(TreeInfo& info);
+ void deleteDisplayListData();
+ void damageSelf(TreeInfo& info);
+
+ void incParentRefCount() { mParentCount++; }
+ void decParentRefCount();
+
+ String8 mName;
+
+ uint32_t mDirtyPropertyFields;
+ RenderProperties mProperties;
+ RenderProperties mStagingProperties;
+
+ bool mNeedsDisplayListDataSync;
+ // WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
+ DisplayListData* mDisplayListData;
+ DisplayListData* mStagingDisplayListData;
+
+ friend class AnimatorManager;
+ AnimatorManager mAnimatorManager;
+
+ // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
+ // being in ~RenderNode() which may happen on any thread.
+ Layer* mLayer;
+
+ /**
+ * Draw time state - these properties are only set and used during rendering
+ */
+
+ // for projection surfaces, contains a list of all children items
+ Vector<DrawRenderNodeOp*> mProjectedNodes;
+
+ // How many references our parent(s) have to us. Typically this should alternate
+ // between 2 and 1 (when a staging push happens we inc first then dec)
+ // When this hits 0 we are no longer in the tree, so any hardware resources
+ // (specifically Layers) should be released.
+ // This is *NOT* thread-safe, and should therefore only be tracking
+ // mDisplayListData, not mStagingDisplayListData.
+ uint32_t mParentCount;
+}; // class RenderNode
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
new file mode 100644
index 0000000..250cadc
--- /dev/null
+++ b/libs/hwui/RenderProperties.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you mPrimitiveFields.may not use this file except in compliance with the License.
+ * You mPrimitiveFields.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 "OpenGLRenderer"
+
+#include "RenderProperties.h"
+
+#include <utils/Trace.h>
+
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkMatrix.h>
+#include <SkPath.h>
+#include <SkPathOps.h>
+
+#include "Matrix.h"
+#include "OpenGLRenderer.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+LayerProperties::LayerProperties()
+ : mType(kLayerTypeNone)
+ , mColorFilter(NULL) {
+ reset();
+}
+
+LayerProperties::~LayerProperties() {
+ setType(kLayerTypeNone);
+}
+
+void LayerProperties::reset() {
+ mOpaque = false;
+ setFromPaint(NULL);
+}
+
+bool LayerProperties::setColorFilter(SkColorFilter* filter) {
+ if (mColorFilter == filter) return false;
+ SkRefCnt_SafeAssign(mColorFilter, filter);
+ return true;
+}
+
+bool LayerProperties::setFromPaint(const SkPaint* paint) {
+ bool changed = false;
+ SkXfermode::Mode mode;
+ int alpha;
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ changed |= setAlpha(static_cast<uint8_t>(alpha));
+ changed |= setXferMode(mode);
+ changed |= setColorFilter(paint ? paint->getColorFilter() : NULL);
+ return changed;
+}
+
+LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
+ setType(other.type());
+ setOpaque(other.opaque());
+ setAlpha(other.alpha());
+ setXferMode(other.xferMode());
+ setColorFilter(other.colorFilter());
+ return *this;
+}
+
+RenderProperties::PrimitiveFields::PrimitiveFields()
+ : mClippingFlags(CLIP_TO_BOUNDS)
+ , mProjectBackwards(false)
+ , mProjectionReceiver(false)
+ , mAlpha(1)
+ , mHasOverlappingRendering(true)
+ , mElevation(0)
+ , mTranslationX(0), mTranslationY(0), mTranslationZ(0)
+ , mRotation(0), mRotationX(0), mRotationY(0)
+ , mScaleX(1), mScaleY(1)
+ , mPivotX(0), mPivotY(0)
+ , mLeft(0), mTop(0), mRight(0), mBottom(0)
+ , mWidth(0), mHeight(0)
+ , mPivotExplicitlySet(false)
+ , mMatrixOrPivotDirty(false) {
+}
+
+RenderProperties::ComputedFields::ComputedFields()
+ : mTransformMatrix(NULL) {
+}
+
+RenderProperties::ComputedFields::~ComputedFields() {
+ delete mTransformMatrix;
+}
+
+RenderProperties::RenderProperties()
+ : mStaticMatrix(NULL)
+ , mAnimationMatrix(NULL) {
+}
+
+RenderProperties::~RenderProperties() {
+ delete mStaticMatrix;
+ delete mAnimationMatrix;
+}
+
+RenderProperties& RenderProperties::operator=(const RenderProperties& other) {
+ if (this != &other) {
+ mPrimitiveFields = other.mPrimitiveFields;
+ setStaticMatrix(other.getStaticMatrix());
+ setAnimationMatrix(other.getAnimationMatrix());
+ setCameraDistance(other.getCameraDistance());
+ mLayerProperties = other.layerProperties();
+
+ // Force recalculation of the matrix, since other's dirty bit may be clear
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ updateMatrix();
+ }
+ return *this;
+}
+
+void RenderProperties::debugOutputProperties(const int level) const {
+ if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) {
+ ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mPrimitiveFields.mLeft, mPrimitiveFields.mTop);
+ }
+ if (mStaticMatrix) {
+ ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
+ level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix));
+ }
+ if (mAnimationMatrix) {
+ ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
+ level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix));
+ }
+ if (hasTransformMatrix()) {
+ if (isTransformTranslateOnly()) {
+ ALOGD("%*sTranslate %.2f, %.2f, %.2f",
+ level * 2, "", getTranslationX(), getTranslationY(), getZ());
+ } else {
+ ALOGD("%*sConcatMatrix %p: " SK_MATRIX_STRING,
+ level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix));
+ }
+ }
+
+ const bool isLayer = layerProperties().type() != kLayerTypeNone;
+ int clipFlags = getClippingFlags();
+ if (mPrimitiveFields.mAlpha < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+
+ ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
+ } else if (!mPrimitiveFields.mHasOverlappingRendering) {
+ ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
+ } else {
+ Rect layerBounds(0, 0, getWidth(), getHeight());
+ int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipFlags) {
+ saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by saveLayer
+ }
+
+ ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "",
+ (int)layerBounds.left, (int)layerBounds.top, (int)layerBounds.right, (int)layerBounds.bottom,
+ (int)(mPrimitiveFields.mAlpha * 255), saveFlags);
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ getClippingRectForFlags(clipFlags, &clipRect);
+ ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "",
+ (int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom);
+ }
+}
+
+void RenderProperties::updateMatrix() {
+ if (mPrimitiveFields.mMatrixOrPivotDirty) {
+ if (!mComputedFields.mTransformMatrix) {
+ // only allocate a mPrimitiveFields.matrix if we have a complex transform
+ mComputedFields.mTransformMatrix = new SkMatrix();
+ }
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mPivotX = mPrimitiveFields.mWidth / 2.0f;
+ mPrimitiveFields.mPivotY = mPrimitiveFields.mHeight / 2.0f;
+ }
+ SkMatrix* transform = mComputedFields.mTransformMatrix;
+ transform->reset();
+ if (MathUtils::isZero(getRotationX()) && MathUtils::isZero(getRotationY())) {
+ transform->setTranslate(getTranslationX(), getTranslationY());
+ transform->preRotate(getRotation(), getPivotX(), getPivotY());
+ transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
+ } else {
+ SkMatrix transform3D;
+ mComputedFields.mTransformCamera.save();
+ transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
+ mComputedFields.mTransformCamera.rotateX(mPrimitiveFields.mRotationX);
+ mComputedFields.mTransformCamera.rotateY(mPrimitiveFields.mRotationY);
+ mComputedFields.mTransformCamera.rotateZ(-mPrimitiveFields.mRotation);
+ mComputedFields.mTransformCamera.getMatrix(&transform3D);
+ transform3D.preTranslate(-getPivotX(), -getPivotY());
+ transform3D.postTranslate(getPivotX() + getTranslationX(),
+ getPivotY() + getTranslationY());
+ transform->postConcat(transform3D);
+ mComputedFields.mTransformCamera.restore();
+ }
+ mPrimitiveFields.mMatrixOrPivotDirty = false;
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
new file mode 100644
index 0000000..0c8d07f
--- /dev/null
+++ b/libs/hwui/RenderProperties.h
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef RENDERNODEPROPERTIES_H
+#define RENDERNODEPROPERTIES_H
+
+#include <algorithm>
+#include <stddef.h>
+#include <vector>
+#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+#include <SkRegion.h>
+
+#include "Animator.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "Outline.h"
+#include "utils/MathUtils.h"
+
+class SkBitmap;
+class SkColorFilter;
+class SkPaint;
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+class RenderNode;
+class RenderProperties;
+
+// The __VA_ARGS__ will be executed if a & b are not equal
+#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false)
+#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
+
+// Keep in sync with View.java:LAYER_TYPE_*
+enum LayerType {
+ kLayerTypeNone = 0,
+ // Although we cannot build the software layer directly (must be done at
+ // record time), this information is used when applying alpha.
+ kLayerTypeSoftware = 1,
+ kLayerTypeRenderLayer = 2,
+ // TODO: LayerTypeSurfaceTexture? Maybe?
+};
+
+enum ClippingFlags {
+ CLIP_TO_BOUNDS = 0x1 << 0,
+ CLIP_TO_CLIP_BOUNDS = 0x1 << 1,
+};
+
+class ANDROID_API LayerProperties {
+public:
+ bool setType(LayerType type) {
+ if (RP_SET(mType, type)) {
+ reset();
+ return true;
+ }
+ return false;
+ }
+
+ LayerType type() const {
+ return mType;
+ }
+
+ bool setOpaque(bool opaque) {
+ return RP_SET(mOpaque, opaque);
+ }
+
+ bool opaque() const {
+ return mOpaque;
+ }
+
+ bool setAlpha(uint8_t alpha) {
+ return RP_SET(mAlpha, alpha);
+ }
+
+ uint8_t alpha() const {
+ return mAlpha;
+ }
+
+ bool setXferMode(SkXfermode::Mode mode) {
+ return RP_SET(mMode, mode);
+ }
+
+ SkXfermode::Mode xferMode() const {
+ return mMode;
+ }
+
+ bool setColorFilter(SkColorFilter* filter);
+
+ SkColorFilter* colorFilter() const {
+ return mColorFilter;
+ }
+
+ // Sets alpha, xfermode, and colorfilter from an SkPaint
+ // paint may be NULL, in which case defaults will be set
+ bool setFromPaint(const SkPaint* paint);
+
+ bool needsBlending() const {
+ return !opaque() || alpha() < 255;
+ }
+
+ LayerProperties& operator=(const LayerProperties& other);
+
+private:
+ LayerProperties();
+ ~LayerProperties();
+ void reset();
+
+ friend class RenderProperties;
+
+ LayerType mType;
+ // Whether or not that Layer's content is opaque, doesn't include alpha
+ bool mOpaque;
+ uint8_t mAlpha;
+ SkXfermode::Mode mMode;
+ SkColorFilter* mColorFilter;
+};
+
+/*
+ * Data structure that holds the properties for a RenderNode
+ */
+class ANDROID_API RenderProperties {
+public:
+ RenderProperties();
+ virtual ~RenderProperties();
+
+ static bool setFlag(int flag, bool newValue, int* outFlags) {
+ if (newValue) {
+ if (!(flag & *outFlags)) {
+ *outFlags |= flag;
+ return true;
+ }
+ return false;
+ } else {
+ if (flag & *outFlags) {
+ *outFlags &= ~flag;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ RenderProperties& operator=(const RenderProperties& other);
+
+ bool setClipToBounds(bool clipToBounds) {
+ return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags);
+ }
+
+ bool setClipBounds(const Rect& clipBounds) {
+ bool ret = setFlag(CLIP_TO_CLIP_BOUNDS, true, &mPrimitiveFields.mClippingFlags);
+ return RP_SET(mPrimitiveFields.mClipBounds, clipBounds) || ret;
+ }
+
+ bool setClipBoundsEmpty() {
+ return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags);
+ }
+
+ bool setProjectBackwards(bool shouldProject) {
+ return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
+ }
+
+ bool setProjectionReceiver(bool shouldRecieve) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
+ }
+
+ bool isProjectionReceiver() const {
+ return mPrimitiveFields.mProjectionReceiver;
+ }
+
+ bool setStaticMatrix(const SkMatrix* matrix) {
+ delete mStaticMatrix;
+ if (matrix) {
+ mStaticMatrix = new SkMatrix(*matrix);
+ } else {
+ mStaticMatrix = NULL;
+ }
+ return true;
+ }
+
+ // Can return NULL
+ const SkMatrix* getStaticMatrix() const {
+ return mStaticMatrix;
+ }
+
+ bool setAnimationMatrix(const SkMatrix* matrix) {
+ delete mAnimationMatrix;
+ if (matrix) {
+ mAnimationMatrix = new SkMatrix(*matrix);
+ } else {
+ mAnimationMatrix = NULL;
+ }
+ return true;
+ }
+
+ bool setAlpha(float alpha) {
+ alpha = MathUtils::clampAlpha(alpha);
+ return RP_SET(mPrimitiveFields.mAlpha, alpha);
+ }
+
+ float getAlpha() const {
+ return mPrimitiveFields.mAlpha;
+ }
+
+ bool setHasOverlappingRendering(bool hasOverlappingRendering) {
+ return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
+ }
+
+ bool hasOverlappingRendering() const {
+ return mPrimitiveFields.mHasOverlappingRendering;
+ }
+
+ bool setElevation(float elevation) {
+ return RP_SET(mPrimitiveFields.mElevation, elevation);
+ // Don't dirty matrix/pivot, since they don't respect Z
+ }
+
+ float getElevation() const {
+ return mPrimitiveFields.mElevation;
+ }
+
+ bool setTranslationX(float translationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
+ }
+
+ float getTranslationX() const {
+ return mPrimitiveFields.mTranslationX;
+ }
+
+ bool setTranslationY(float translationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
+ }
+
+ float getTranslationY() const {
+ return mPrimitiveFields.mTranslationY;
+ }
+
+ bool setTranslationZ(float translationZ) {
+ return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
+ }
+
+ float getTranslationZ() const {
+ return mPrimitiveFields.mTranslationZ;
+ }
+
+ // Animation helper
+ bool setX(float value) {
+ return setTranslationX(value - getLeft());
+ }
+
+ // Animation helper
+ float getX() const {
+ return getLeft() + getTranslationX();
+ }
+
+ // Animation helper
+ bool setY(float value) {
+ return setTranslationY(value - getTop());
+ }
+
+ // Animation helper
+ float getY() const {
+ return getTop() + getTranslationY();
+ }
+
+ // Animation helper
+ bool setZ(float value) {
+ return setTranslationZ(value - getElevation());
+ }
+
+ float getZ() const {
+ return getElevation() + getTranslationZ();
+ }
+
+ bool setRotation(float rotation) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
+ }
+
+ float getRotation() const {
+ return mPrimitiveFields.mRotation;
+ }
+
+ bool setRotationX(float rotationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
+ }
+
+ float getRotationX() const {
+ return mPrimitiveFields.mRotationX;
+ }
+
+ bool setRotationY(float rotationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
+ }
+
+ float getRotationY() const {
+ return mPrimitiveFields.mRotationY;
+ }
+
+ bool setScaleX(float scaleX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
+ }
+
+ float getScaleX() const {
+ return mPrimitiveFields.mScaleX;
+ }
+
+ bool setScaleY(float scaleY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
+ }
+
+ float getScaleY() const {
+ return mPrimitiveFields.mScaleY;
+ }
+
+ bool setPivotX(float pivotX) {
+ if (RP_SET(mPrimitiveFields.mPivotX, pivotX)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
+ }
+
+ /* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
+ * so the value returned may be stale if the RenderProperties has been
+ * modified since the last call to updateMatrix()
+ */
+ float getPivotX() const {
+ return mPrimitiveFields.mPivotX;
+ }
+
+ bool setPivotY(float pivotY) {
+ if (RP_SET(mPrimitiveFields.mPivotY, pivotY)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
+ }
+
+ float getPivotY() const {
+ return mPrimitiveFields.mPivotY;
+ }
+
+ bool isPivotExplicitlySet() const {
+ return mPrimitiveFields.mPivotExplicitlySet;
+ }
+
+ bool setCameraDistance(float distance) {
+ if (distance != getCameraDistance()) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
+ return true;
+ }
+ return false;
+ }
+
+ float getCameraDistance() const {
+ // TODO: update getCameraLocationZ() to be const
+ return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
+ }
+
+ bool setLeft(int left) {
+ if (RP_SET(mPrimitiveFields.mLeft, left)) {
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getLeft() const {
+ return mPrimitiveFields.mLeft;
+ }
+
+ bool setTop(int top) {
+ if (RP_SET(mPrimitiveFields.mTop, top)) {
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getTop() const {
+ return mPrimitiveFields.mTop;
+ }
+
+ bool setRight(int right) {
+ if (RP_SET(mPrimitiveFields.mRight, right)) {
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getRight() const {
+ return mPrimitiveFields.mRight;
+ }
+
+ bool setBottom(int bottom) {
+ if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getBottom() const {
+ return mPrimitiveFields.mBottom;
+ }
+
+ bool setLeftTop(int left, int top) {
+ bool leftResult = setLeft(left);
+ bool topResult = setTop(top);
+ return leftResult || topResult;
+ }
+
+ bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
+ || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
+ mPrimitiveFields.mLeft = left;
+ mPrimitiveFields.mTop = top;
+ mPrimitiveFields.mRight = right;
+ mPrimitiveFields.mBottom = bottom;
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool offsetLeftRight(int offset) {
+ if (offset != 0) {
+ mPrimitiveFields.mLeft += offset;
+ mPrimitiveFields.mRight += offset;
+ return true;
+ }
+ return false;
+ }
+
+ bool offsetTopBottom(int offset) {
+ if (offset != 0) {
+ mPrimitiveFields.mTop += offset;
+ mPrimitiveFields.mBottom += offset;
+ return true;
+ }
+ return false;
+ }
+
+ int getWidth() const {
+ return mPrimitiveFields.mWidth;
+ }
+
+ int getHeight() const {
+ return mPrimitiveFields.mHeight;
+ }
+
+ const SkMatrix* getAnimationMatrix() const {
+ return mAnimationMatrix;
+ }
+
+ bool hasTransformMatrix() const {
+ return getTransformMatrix() && !getTransformMatrix()->isIdentity();
+ }
+
+ // May only call this if hasTransformMatrix() is true
+ bool isTransformTranslateOnly() const {
+ return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask;
+ }
+
+ const SkMatrix* getTransformMatrix() const {
+ LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!");
+ return mComputedFields.mTransformMatrix;
+ }
+
+ int getClippingFlags() const {
+ return mPrimitiveFields.mClippingFlags;
+ }
+
+ bool getClipToBounds() const {
+ return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS;
+ }
+
+ void getClippingRectForFlags(uint32_t flags, Rect* outRect) const {
+ if (flags & CLIP_TO_BOUNDS) {
+ outRect->set(0, 0, getWidth(), getHeight());
+ if (flags & CLIP_TO_CLIP_BOUNDS) {
+ outRect->intersect(mPrimitiveFields.mClipBounds);
+ }
+ } else {
+ outRect->set(mPrimitiveFields.mClipBounds);
+ }
+ }
+
+ bool getHasOverlappingRendering() const {
+ return mPrimitiveFields.mHasOverlappingRendering;
+ }
+
+ const Outline& getOutline() const {
+ return mPrimitiveFields.mOutline;
+ }
+
+ const RevealClip& getRevealClip() const {
+ return mPrimitiveFields.mRevealClip;
+ }
+
+ bool getProjectBackwards() const {
+ return mPrimitiveFields.mProjectBackwards;
+ }
+
+ void debugOutputProperties(const int level) const;
+
+ void updateMatrix();
+
+ Outline& mutableOutline() {
+ return mPrimitiveFields.mOutline;
+ }
+
+ RevealClip& mutableRevealClip() {
+ return mPrimitiveFields.mRevealClip;
+ }
+
+ const LayerProperties& layerProperties() const {
+ return mLayerProperties;
+ }
+
+ LayerProperties& mutateLayerProperties() {
+ return mLayerProperties;
+ }
+
+ // Returns true if damage calculations should be clipped to bounds
+ // TODO: Figure out something better for getZ(), as children should still be
+ // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
+ // for this RP's getZ() anyway, this can be optimized when we have a
+ // Z damage estimate instead of INT_MAX
+ bool getClipDamageToBounds() const {
+ return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
+ }
+
+private:
+ // Rendering properties
+ struct PrimitiveFields {
+ PrimitiveFields();
+
+ Outline mOutline;
+ RevealClip mRevealClip;
+ int mClippingFlags;
+ bool mProjectBackwards;
+ bool mProjectionReceiver;
+ float mAlpha;
+ bool mHasOverlappingRendering;
+ float mElevation;
+ float mTranslationX, mTranslationY, mTranslationZ;
+ float mRotation, mRotationX, mRotationY;
+ float mScaleX, mScaleY;
+ float mPivotX, mPivotY;
+ int mLeft, mTop, mRight, mBottom;
+ int mWidth, mHeight;
+ bool mPivotExplicitlySet;
+ bool mMatrixOrPivotDirty;
+ Rect mClipBounds;
+ } mPrimitiveFields;
+
+ SkMatrix* mStaticMatrix;
+ SkMatrix* mAnimationMatrix;
+ LayerProperties mLayerProperties;
+
+ /**
+ * These fields are all generated from other properties and are not set directly.
+ */
+ struct ComputedFields {
+ ComputedFields();
+ ~ComputedFields();
+
+ /**
+ * Stores the total transformation of the DisplayList based upon its scalar
+ * translate/rotate/scale properties.
+ *
+ * In the common translation-only case, the matrix isn't necessarily allocated,
+ * and the mTranslation properties are used directly.
+ */
+ SkMatrix* mTransformMatrix;
+
+ Sk3DView mTransformCamera;
+ } mComputedFields;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODEPROPERTIES_H */
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
new file mode 100644
index 0000000..97f379d
--- /dev/null
+++ b/libs/hwui/RenderState.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "RenderState.h"
+
+namespace android {
+namespace uirenderer {
+
+RenderState::RenderState()
+ : mCaches(NULL)
+ , mViewportWidth(0)
+ , mViewportHeight(0)
+ , mFramebuffer(0) {
+}
+
+RenderState::~RenderState() {
+}
+
+void RenderState::onGLContextCreated() {
+ // This is delayed because the first access of Caches makes GL calls
+ mCaches = &Caches::getInstance();
+ mCaches->init();
+}
+
+void RenderState::setViewport(GLsizei width, GLsizei height) {
+ mViewportWidth = width;
+ mViewportHeight = height;
+ glViewport(0, 0, mViewportWidth, mViewportHeight);
+}
+
+
+void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
+ *outWidth = mViewportWidth;
+ *outHeight = mViewportHeight;
+}
+
+void RenderState::bindFramebuffer(GLuint fbo) {
+ if (mFramebuffer != fbo) {
+ mFramebuffer = fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+ }
+}
+
+void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
+ interruptForFunctorInvoke();
+ (*functor)(mode, info);
+ resumeFromFunctorInvoke();
+}
+
+void RenderState::interruptForFunctorInvoke() {
+ if (mCaches->currentProgram) {
+ if (mCaches->currentProgram->isInUse()) {
+ mCaches->currentProgram->remove();
+ mCaches->currentProgram = NULL;
+ }
+ }
+ mCaches->resetActiveTexture();
+ mCaches->unbindMeshBuffer();
+ mCaches->unbindIndicesBuffer();
+ mCaches->resetVertexPointers();
+ mCaches->disableTexCoordsVertexArray();
+ debugOverdraw(false, false);
+}
+
+void RenderState::resumeFromFunctorInvoke() {
+ glViewport(0, 0, mViewportWidth, mViewportHeight);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+ debugOverdraw(false, false);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
+ mCaches->enableScissor();
+ mCaches->resetScissor();
+
+ mCaches->activeTexture(0);
+ mCaches->resetBoundTextures();
+
+ mCaches->blend = true;
+ glEnable(GL_BLEND);
+ glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
+ glBlendEquation(GL_FUNC_ADD);
+}
+
+void RenderState::debugOverdraw(bool enable, bool clear) {
+ if (mCaches->debugOverdraw && mFramebuffer == 0) {
+ if (clear) {
+ mCaches->disableScissor();
+ mCaches->stencil.clear();
+ }
+ if (enable) {
+ mCaches->stencil.enableDebugWrite();
+ } else {
+ mCaches->stencil.disable();
+ }
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h
new file mode 100644
index 0000000..f7116e2
--- /dev/null
+++ b/libs/hwui/RenderState.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef RENDERSTATE_H
+#define RENDERSTATE_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include "Caches.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+class RenderThread;
+}
+
+// TODO: Replace Cache's GL state tracking with this. For now it's more a thin
+// wrapper of Caches for users to migrate to.
+class RenderState {
+ PREVENT_COPY_AND_ASSIGN(RenderState);
+public:
+ void onGLContextCreated();
+
+ void setViewport(GLsizei width, GLsizei height);
+ void getViewport(GLsizei* outWidth, GLsizei* outHeight);
+
+ void bindFramebuffer(GLuint fbo);
+ GLint getFramebuffer() { return mFramebuffer; }
+
+ void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
+
+ void debugOverdraw(bool enable, bool clear);
+
+private:
+ friend class renderthread::RenderThread;
+
+ void interruptForFunctorInvoke();
+ void resumeFromFunctorInvoke();
+
+ RenderState();
+ ~RenderState();
+
+ Caches* mCaches;
+
+ GLsizei mViewportWidth;
+ GLsizei mViewportHeight;
+ GLuint mFramebuffer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERSTATE_H */
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
new file mode 100644
index 0000000..f5cd266
--- /dev/null
+++ b/libs/hwui/Renderer.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_RENDERER_H
+#define ANDROID_HWUI_RENDERER_H
+
+#include <SkRegion.h>
+
+#include <utils/String8.h>
+
+#include "AssetAtlas.h"
+#include "SkPaint.h"
+
+namespace android {
+
+class Functor;
+struct Res_png_9patch;
+
+namespace uirenderer {
+
+class RenderNode;
+class Layer;
+class Matrix4;
+class SkiaColorFilter;
+class Patch;
+
+enum DrawOpMode {
+ kDrawOpMode_Immediate,
+ kDrawOpMode_Defer,
+ kDrawOpMode_Flush
+};
+
+/**
+ * Hwui's abstract version of Canvas.
+ *
+ * Provides methods for frame state operations, as well as the SkCanvas style transform/clip state,
+ * and varied drawing operations.
+ *
+ * Should at some point interact with native SkCanvas.
+ */
+class ANDROID_API Renderer {
+public:
+ virtual ~Renderer() {}
+
+ /**
+ * Safely retrieves the mode from the specified xfermode. If the specified
+ * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
+ */
+ static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
+ SkXfermode::Mode resultMode;
+ if (!SkXfermode::AsMode(mode, &resultMode)) {
+ resultMode = SkXfermode::kSrcOver_Mode;
+ }
+ return resultMode;
+ }
+
+// ----------------------------------------------------------------------------
+// Frame state operations
+// ----------------------------------------------------------------------------
+ /**
+ * Sets the dimension of the underlying drawing surface. This method must
+ * be called at least once every time the drawing surface changes size.
+ *
+ * @param width The width in pixels of the underlysing surface
+ * @param height The height in pixels of the underlysing surface
+ */
+ virtual void setViewport(int width, int height) = 0;
+
+ /**
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. When this method is invoked, the
+ * entire drawing surface is assumed to be redrawn.
+ *
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared
+ */
+ virtual status_t prepare(bool opaque) = 0;
+
+ /**
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. Only the specified rectangle of the
+ * frame is assumed to be dirty. A clip will automatically be set to
+ * the specified rectangle.
+ *
+ * @param left The left coordinate of the dirty rectangle
+ * @param top The top coordinate of the dirty rectangle
+ * @param right The right coordinate of the dirty rectangle
+ * @param bottom The bottom coordinate of the dirty rectangle
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared in the specified dirty rectangle
+ */
+ virtual status_t prepareDirty(float left, float top, float right, float bottom,
+ bool opaque) = 0;
+
+ /**
+ * Indicates the end of a frame. This method must be invoked whenever
+ * the caller is done rendering a frame.
+ */
+ virtual void finish() = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const = 0;
+ virtual int save(int flags) = 0;
+ virtual void restore() = 0;
+ virtual void restoreToCount(int saveCount) = 0;
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, int flags) = 0;
+
+ int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, int flags) {
+ SkPaint paint;
+ paint.setAlpha(alpha);
+ return saveLayer(left, top, right, bottom, &paint, flags);
+ }
+
+ // Matrix
+ virtual void getMatrix(SkMatrix* outMatrix) const = 0;
+ virtual void translate(float dx, float dy, float dz = 0.0f) = 0;
+ virtual void rotate(float degrees) = 0;
+ virtual void scale(float sx, float sy) = 0;
+ virtual void skew(float sx, float sy) = 0;
+
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+ virtual void concatMatrix(const SkMatrix& matrix) = 0;
+
+ // clip
+ virtual const Rect& getLocalClipBounds() const = 0;
+ virtual bool quickRejectConservative(float left, float top,
+ float right, float bottom) const = 0;
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+
+ // Misc - should be implemented with SkPaint inspection
+ virtual void resetPaintFilter() = 0;
+ virtual void setupPaintFilter(int clearBits, int setBits) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual status_t drawColor(int color, SkXfermode::Mode mode) = 0;
+
+ // Bitmap-based
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) = 0;
+ virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) = 0;
+ virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) = 0;
+ virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) = 0;
+ virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint) = 0;
+
+ // Shapes
+ virtual status_t drawRect(float left, float top, float right, float bottom,
+ const SkPaint* paint) = 0;
+ virtual status_t drawRects(const float* rects, int count, const SkPaint* paint) = 0;
+ virtual status_t drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint* paint) = 0;
+ virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint) = 0;
+ virtual status_t drawOval(float left, float top, float right, float bottom,
+ const SkPaint* paint) = 0;
+ virtual status_t drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) = 0;
+ virtual status_t drawPath(const SkPath* path, const SkPaint* paint) = 0;
+ virtual status_t drawLines(const float* points, int count, const SkPaint* paint) = 0;
+ virtual status_t drawPoints(const float* points, int count, const SkPaint* paint) = 0;
+
+ // Text
+ virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode = kDrawOpMode_Immediate) = 0;
+ virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ float hOffset, float vOffset, const SkPaint* paint) = 0;
+ virtual status_t drawPosText(const char* text, int bytesCount, int count,
+ const float* positions, const SkPaint* paint) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations - special
+// ----------------------------------------------------------------------------
+ virtual status_t drawLayer(Layer* layer, float x, float y) = 0;
+ virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty,
+ int32_t replayFlags) = 0;
+
+ // TODO: rename for consistency
+ virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
+}; // class Renderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RENDERER_H
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 77292bf..8b553d1 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -40,7 +40,7 @@ void ResourceCache::logCache() {
ResourceCache::ResourceCache() {
Mutex::Autolock _l(mLock);
- mCache = new KeyedVector<void*, ResourceReference*>();
+ mCache = new KeyedVector<const void*, ResourceReference*>();
}
ResourceCache::~ResourceCache() {
@@ -61,27 +61,17 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType)
incrementRefcountLocked(resource, resourceType);
}
-void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
+void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) {
bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcount((void*) bitmapResource, kBitmap);
}
-void ResourceCache::incrementRefcount(SkPath* pathResource) {
+void ResourceCache::incrementRefcount(const SkPath* pathResource) {
incrementRefcount((void*) pathResource, kPath);
}
-void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
- SkSafeRef(shaderResource->getSkShader());
- incrementRefcount((void*) shaderResource, kShader);
-}
-
-void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) {
- SkSafeRef(filterResource->getSkColorFilter());
- incrementRefcount((void*) filterResource, kColorFilter);
-}
-
-void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) {
+void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
incrementRefcount((void*) patchResource, kNinePatch);
}
@@ -99,27 +89,17 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc
ref->refCount++;
}
-void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) {
+void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) {
bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcountLocked((void*) bitmapResource, kBitmap);
}
-void ResourceCache::incrementRefcountLocked(SkPath* pathResource) {
+void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) {
incrementRefcountLocked((void*) pathResource, kPath);
}
-void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
- SkSafeRef(shaderResource->getSkShader());
- incrementRefcountLocked((void*) shaderResource, kShader);
-}
-
-void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) {
- SkSafeRef(filterResource->getSkColorFilter());
- incrementRefcountLocked((void*) filterResource, kColorFilter);
-}
-
-void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) {
+void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
incrementRefcountLocked((void*) patchResource, kNinePatch);
}
@@ -132,27 +112,17 @@ void ResourceCache::decrementRefcount(void* resource) {
decrementRefcountLocked(resource);
}
-void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
+void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) {
bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcount((void*) bitmapResource);
}
-void ResourceCache::decrementRefcount(SkPath* pathResource) {
+void ResourceCache::decrementRefcount(const SkPath* pathResource) {
decrementRefcount((void*) pathResource);
}
-void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
- SkSafeUnref(shaderResource->getSkShader());
- decrementRefcount((void*) shaderResource);
-}
-
-void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) {
- SkSafeUnref(filterResource->getSkColorFilter());
- decrementRefcount((void*) filterResource);
-}
-
-void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) {
+void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
decrementRefcount((void*) patchResource);
}
@@ -173,27 +143,17 @@ void ResourceCache::decrementRefcountLocked(void* resource) {
}
}
-void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) {
+void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcountLocked((void*) bitmapResource);
}
-void ResourceCache::decrementRefcountLocked(SkPath* pathResource) {
+void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) {
decrementRefcountLocked((void*) pathResource);
}
-void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
- SkSafeUnref(shaderResource->getSkShader());
- decrementRefcountLocked((void*) shaderResource);
-}
-
-void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) {
- SkSafeUnref(filterResource->getSkColorFilter());
- decrementRefcountLocked((void*) filterResource);
-}
-
-void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) {
+void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
decrementRefcountLocked((void*) patchResource);
}
@@ -224,12 +184,12 @@ void ResourceCache::destructorLocked(SkPath* resource) {
}
}
-void ResourceCache::destructor(SkBitmap* resource) {
+void ResourceCache::destructor(const SkBitmap* resource) {
Mutex::Autolock _l(mLock);
destructorLocked(resource);
}
-void ResourceCache::destructorLocked(SkBitmap* resource) {
+void ResourceCache::destructorLocked(const SkBitmap* resource) {
ssize_t index = mCache->indexOfKey(resource);
ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
@@ -247,44 +207,6 @@ void ResourceCache::destructorLocked(SkBitmap* resource) {
}
}
-void ResourceCache::destructor(SkiaShader* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkiaShader* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
- // If we're not tracking this resource, just delete it
- delete resource;
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-void ResourceCache::destructor(SkiaColorFilter* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkiaColorFilter* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
- // If we're not tracking this resource, just delete it
- delete resource;
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
void ResourceCache::destructor(Res_png_9patch* resource) {
Mutex::Autolock _l(mLock);
destructorLocked(resource);
@@ -348,7 +270,7 @@ bool ResourceCache::recycleLocked(SkBitmap* resource) {
* This method should only be called while the mLock mutex is held (that mutex is grabbed
* by the various destructor() and recycle() methods which call this method).
*/
-void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceReference* ref) {
+void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
if (ref->recycled && ref->resourceType == kBitmap) {
((SkBitmap*) resource)->setPixels(NULL, NULL);
}
@@ -372,16 +294,6 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere
}
}
break;
- case kShader: {
- SkiaShader* shader = (SkiaShader*) resource;
- delete shader;
- }
- break;
- case kColorFilter: {
- SkiaColorFilter* filter = (SkiaColorFilter*) resource;
- delete filter;
- }
- break;
case kNinePatch: {
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index ea0c1b5..3864d4b 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -20,8 +20,6 @@
#include <cutils/compiler.h>
#include <SkBitmap.h>
-#include <SkiaColorFilter.h>
-#include <SkiaShader.h>
#include <utils/KeyedVector.h>
@@ -37,8 +35,6 @@ namespace uirenderer {
*/
enum ResourceType {
kBitmap,
- kShader,
- kColorFilter,
kNinePatch,
kPath,
kLayer
@@ -70,51 +66,39 @@ public:
void lock();
void unlock();
- void incrementRefcount(SkPath* resource);
- void incrementRefcount(SkBitmap* resource);
- void incrementRefcount(SkiaShader* resource);
- void incrementRefcount(SkiaColorFilter* resource);
- void incrementRefcount(Res_png_9patch* resource);
+ void incrementRefcount(const SkPath* resource);
+ void incrementRefcount(const SkBitmap* resource);
+ void incrementRefcount(const Res_png_9patch* resource);
void incrementRefcount(Layer* resource);
- void incrementRefcountLocked(SkPath* resource);
- void incrementRefcountLocked(SkBitmap* resource);
- void incrementRefcountLocked(SkiaShader* resource);
- void incrementRefcountLocked(SkiaColorFilter* resource);
- void incrementRefcountLocked(Res_png_9patch* resource);
+ void incrementRefcountLocked(const SkPath* resource);
+ void incrementRefcountLocked(const SkBitmap* resource);
+ void incrementRefcountLocked(const Res_png_9patch* resource);
void incrementRefcountLocked(Layer* resource);
- void decrementRefcount(SkBitmap* resource);
- void decrementRefcount(SkPath* resource);
- void decrementRefcount(SkiaShader* resource);
- void decrementRefcount(SkiaColorFilter* resource);
- void decrementRefcount(Res_png_9patch* resource);
+ void decrementRefcount(const SkBitmap* resource);
+ void decrementRefcount(const SkPath* resource);
+ void decrementRefcount(const Res_png_9patch* resource);
void decrementRefcount(Layer* resource);
- void decrementRefcountLocked(SkBitmap* resource);
- void decrementRefcountLocked(SkPath* resource);
- void decrementRefcountLocked(SkiaShader* resource);
- void decrementRefcountLocked(SkiaColorFilter* resource);
- void decrementRefcountLocked(Res_png_9patch* resource);
+ void decrementRefcountLocked(const SkBitmap* resource);
+ void decrementRefcountLocked(const SkPath* resource);
+ void decrementRefcountLocked(const Res_png_9patch* resource);
void decrementRefcountLocked(Layer* resource);
void destructor(SkPath* resource);
- void destructor(SkBitmap* resource);
- void destructor(SkiaShader* resource);
- void destructor(SkiaColorFilter* resource);
+ void destructor(const SkBitmap* resource);
void destructor(Res_png_9patch* resource);
void destructorLocked(SkPath* resource);
- void destructorLocked(SkBitmap* resource);
- void destructorLocked(SkiaShader* resource);
- void destructorLocked(SkiaColorFilter* resource);
+ void destructorLocked(const SkBitmap* resource);
void destructorLocked(Res_png_9patch* resource);
bool recycle(SkBitmap* resource);
bool recycleLocked(SkBitmap* resource);
private:
- void deleteResourceReferenceLocked(void* resource, ResourceReference* ref);
+ void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
void incrementRefcount(void* resource, ResourceType resourceType);
void incrementRefcountLocked(void* resource, ResourceType resourceType);
@@ -131,7 +115,7 @@ private:
*/
mutable Mutex mLock;
- KeyedVector<void*, ResourceReference*>* mCache;
+ KeyedVector<const void*, ResourceReference*>* mCache;
};
}; // namespace uirenderer
diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h
new file mode 100644
index 0000000..a9600f1
--- /dev/null
+++ b/libs/hwui/RevealClip.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef REVEALCLIP_H
+#define REVEALCLIP_H
+
+#include <SkPath.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class RevealClip {
+public:
+ RevealClip()
+ : mShouldClip(false)
+ , mX(0)
+ , mY(0)
+ , mRadius(0) {}
+
+ void set(bool shouldClip, float x, float y, float radius) {
+ mShouldClip = shouldClip;
+ mX = x;
+ mY = y;
+ mRadius = radius;
+
+ mPath.rewind();
+ if (mShouldClip) {
+ mPath.addCircle(x, y, radius);
+ }
+ }
+
+ bool willClip() const {
+ return mShouldClip;
+ }
+
+ void getBounds(Rect* outBounds) const {
+ outBounds->set(mX - mRadius, mY - mRadius,
+ mX + mRadius, mY + mRadius);
+ }
+ float getRadius() const { return mRadius; }
+
+ const SkPath* getPath() const {
+ if (!mShouldClip) return NULL;
+
+ return &mPath;
+ }
+
+private:
+ bool mShouldClip;
+ float mX;
+ float mY;
+ float mRadius;
+ SkPath mPath;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* REVEALCLIP_H */
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
new file mode 100644
index 0000000..e71439d
--- /dev/null
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2013 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 "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <math.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "AmbientShadow.h"
+#include "Caches.h"
+#include "ShadowTessellator.h"
+#include "SpotShadow.h"
+
+namespace android {
+namespace uirenderer {
+
+template<typename T>
+static inline T max(T a, T b) {
+ return a > b ? a : b;
+}
+
+void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
+ const Vector3* casterPolygon, int casterVertexCount,
+ const Vector3& centroid3d, const Rect& casterBounds,
+ const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
+ ATRACE_CALL();
+
+ // A bunch of parameters to tweak the shadow.
+ // TODO: Allow some of these changable by debug settings or APIs.
+ float heightFactor = 1.0f / 128;
+ const float geomFactor = 64;
+
+ Caches& caches = Caches::getInstance();
+ if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) {
+ heightFactor *= caches.propertyAmbientRatio;
+ }
+
+ Rect ambientShadowBounds(casterBounds);
+ ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
+
+ if (!localClip.intersects(ambientShadowBounds)) {
+#if DEBUG_SHADOW
+ ALOGD("Ambient shadow is out of clip rect!");
+#endif
+ return;
+ }
+
+ AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
+ casterVertexCount, centroid3d, heightFactor, geomFactor,
+ shadowVertexBuffer);
+}
+
+void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
+ const Vector3* casterPolygon, int casterVertexCount,
+ const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
+ const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
+ ATRACE_CALL();
+
+ Caches& caches = Caches::getInstance();
+
+ Vector3 adjustedLightCenter(lightCenter);
+ if (CC_UNLIKELY(caches.propertyLightPosY > 0)) {
+ adjustedLightCenter.y = - caches.propertyLightPosY; // negated since this shifts up
+ }
+ if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) {
+ adjustedLightCenter.z = caches.propertyLightPosZ;
+ }
+
+#if DEBUG_SHADOW
+ ALOGD("light center %f %f %f",
+ adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+#endif
+
+ // light position (because it's in local space) needs to compensate for receiver transform
+ // TODO: should apply to light orientation, not just position
+ Matrix4 reverseReceiverTransform;
+ reverseReceiverTransform.loadInverse(receiverTransform);
+ reverseReceiverTransform.mapPoint3d(adjustedLightCenter);
+
+ const int lightVertexCount = 8;
+ if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) {
+ lightRadius = caches.propertyLightDiameter;
+ }
+
+ // Now light and caster are both in local space, we will check whether
+ // the shadow is within the clip area.
+ Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius,
+ adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius);
+ lightRect.unionWith(localClip);
+ if (!lightRect.intersects(casterBounds)) {
+#if DEBUG_SHADOW
+ ALOGD("Spot shadow is out of clip rect!");
+#endif
+ return;
+ }
+
+ SpotShadow::createSpotShadow(isCasterOpaque,
+ casterPolygon, casterVertexCount, adjustedLightCenter, lightRadius,
+ lightVertexCount, shadowVertexBuffer);
+#if DEBUG_SHADOW
+ if(shadowVertexBuffer.getVertexCount() <= 0) {
+ ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
+ }
+#endif
+}
+
+void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
+ int currentIndex = 0;
+ const int rays = SHADOW_RAY_COUNT;
+ // For the penumbra area.
+ for (int layer = 0; layer < 2; layer ++) {
+ int baseIndex = layer * rays;
+ for (int i = 0; i < rays; i++) {
+ shadowIndices[currentIndex++] = i + baseIndex;
+ shadowIndices[currentIndex++] = rays + i + baseIndex;
+ }
+ // To close the loop, back to the ray 0.
+ shadowIndices[currentIndex++] = 0 + baseIndex;
+ // Note this is the same as the first index of next layer loop.
+ shadowIndices[currentIndex++] = rays + baseIndex;
+ }
+
+#if DEBUG_SHADOW
+ if (currentIndex != MAX_SHADOW_INDEX_COUNT) {
+ ALOGW("vertex index count is wrong. current %d, expected %d",
+ currentIndex, MAX_SHADOW_INDEX_COUNT);
+ }
+ for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) {
+ ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]);
+ }
+#endif
+}
+
+/**
+ * Calculate the centroid of a 2d polygon.
+ *
+ * @param poly The polygon, which is represented in a Vector2 array.
+ * @param polyLength The length of the polygon in terms of number of vertices.
+ * @return the centroid of the polygon.
+ */
+Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) {
+ double sumx = 0;
+ double sumy = 0;
+ int p1 = polyLength - 1;
+ double area = 0;
+ for (int p2 = 0; p2 < polyLength; p2++) {
+ double x1 = poly[p1].x;
+ double y1 = poly[p1].y;
+ double x2 = poly[p2].x;
+ double y2 = poly[p2].y;
+ double a = (x1 * y2 - x2 * y1);
+ sumx += (x1 + x2) * a;
+ sumy += (y1 + y2) * a;
+ area += a;
+ p1 = p2;
+ }
+
+ Vector2 centroid = poly[0];
+ if (area != 0) {
+ centroid = (Vector2){static_cast<float>(sumx / (3 * area)),
+ static_cast<float>(sumy / (3 * area))};
+ } else {
+ ALOGW("Area is 0 while computing centroid!");
+ }
+ return centroid;
+}
+
+/**
+ * Test whether the polygon is order in clockwise.
+ *
+ * @param polygon the polygon as a Vector2 array
+ * @param len the number of points of the polygon
+ */
+bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
+ if (len < 2 || polygon == NULL) {
+ return true;
+ }
+ double sum = 0;
+ double p1x = polygon[len - 1].x;
+ double p1y = polygon[len - 1].y;
+ for (int i = 0; i < len; i++) {
+
+ double p2x = polygon[i].x;
+ double p2y = polygon[i].y;
+ sum += p1x * p2y - p2x * p1y;
+ p1x = p2x;
+ p1y = p2y;
+ }
+ return sum < 0;
+}
+
+bool ShadowTessellator::isClockwisePath(const SkPath& path) {
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+ SkPath::Verb v;
+
+ Vector<Vector2> arrayForDirection;
+ while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+ switch (v) {
+ case SkPath::kMove_Verb:
+ arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()});
+ break;
+ case SkPath::kLine_Verb:
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
+ break;
+ case SkPath::kQuad_Verb:
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
+ arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
+ break;
+ case SkPath::kCubic_Verb:
+ arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
+ arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
+ arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()});
+ break;
+ default:
+ break;
+ }
+ }
+
+ return isClockwise(arrayForDirection.array(), arrayForDirection.size());
+}
+
+void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) {
+ int n = len / 2;
+ for (int i = 0; i < n; i++) {
+ Vertex tmp = polygon[i];
+ int k = len - 1 - i;
+ polygon[i] = polygon[k];
+ polygon[k] = tmp;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
new file mode 100644
index 0000000..cb65df5
--- /dev/null
+++ b/libs/hwui/ShadowTessellator.h
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_SHADOW_TESSELLATOR_H
+#define ANDROID_HWUI_SHADOW_TESSELLATOR_H
+
+#include "Debug.h"
+#include "Matrix.h"
+#include "OpenGLRenderer.h"
+#include "VertexBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+// All SHADOW_* are used to define all the geometry property of shadows.
+// Use a simplified example to illustrate the geometry setup here.
+// Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which
+// are 0 to 5 and 6 to 11. The area between them will be the penumbra area, and
+// the area inside the 2nd hexagon is the umbra.
+// Ambient shadow is using only 1 layer for opaque caster, otherwise, spot
+// shadow and ambient shadow are using 2 layers.
+// Triange strip indices for penumbra area: (0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11, 0, 6)
+// 0
+//
+// 5 6 1
+// 11 7
+//
+// 10 8
+// 4 9 2
+//
+// 3
+
+// The total number of rays starting from the centroid of shadow area, in order
+// to generate the shadow geometry.
+#define SHADOW_RAY_COUNT 128
+
+// The total number of all the vertices representing the shadow.
+// For the case we only have 1 layer, then we will just fill only 2/3 of it.
+#define SHADOW_VERTEX_COUNT (3 * SHADOW_RAY_COUNT)
+
+// The total number of indices used for drawing the shadow geometry as triangle strips.
+// Depending on the mode we are drawing, we can have 1 layer or 2 layers.
+// Therefore, we only build the longer index buffer.
+#define TWO_POLY_RING_SHADOW_INDEX_COUNT (4 * (SHADOW_RAY_COUNT + 1))
+#define ONE_POLY_RING_SHADOW_INDEX_COUNT (2 * (SHADOW_RAY_COUNT + 1))
+
+#define MAX_SHADOW_INDEX_COUNT TWO_POLY_RING_SHADOW_INDEX_COUNT
+
+#define SHADOW_MIN_CASTER_Z 0.001f
+
+#define MINIMAL_DELTA_THETA (M_PI / 180 / 1000)
+
+class ShadowTessellator {
+public:
+ static void tessellateAmbientShadow(bool isCasterOpaque,
+ const Vector3* casterPolygon, int casterVertexCount,
+ const Vector3& centroid3d, const Rect& casterBounds,
+ const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
+
+ static void tessellateSpotShadow(bool isCasterOpaque,
+ const Vector3* casterPolygon, int casterVertexCount,
+ const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
+ const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer);
+
+ static void generateShadowIndices(uint16_t* shadowIndices);
+
+ static Vector2 centroid2d(const Vector2* poly, int polyLength);
+
+ static bool isClockwise(const Vector2* polygon, int len);
+
+ /**
+ * Determine whether the path is clockwise, using the control points.
+ *
+ * TODO: Given the skia is using inverted Y coordinate, shadow system needs
+ * to convert to the same coordinate to avoid the extra reverse.
+ *
+ * @param path The path to be examined.
+ */
+ static bool isClockwisePath(const SkPath &path);
+
+ /**
+ * Reverse the vertex array.
+ *
+ * @param polygon The vertex array to be reversed.
+ * @param len The length of the vertex array.
+ */
+ static void reverseVertexArray(Vertex* polygon, int len);
+
+}; // ShadowTessellator
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
deleted file mode 100644
index df918be..0000000
--- a/libs/hwui/SkiaColorFilter.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.
- */
-
-#include "SkiaColorFilter.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Base color filter
-///////////////////////////////////////////////////////////////////////////////
-
-SkiaColorFilter::SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend):
- mType(type), mBlend(blend), mSkFilter(skFilter) {
-}
-
-SkiaColorFilter::~SkiaColorFilter() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Color matrix filter
-///////////////////////////////////////////////////////////////////////////////
-
-SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter* skFilter, float* matrix, float* vector):
- SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) {
- // Skia uses the range [0..255] for the addition vector, but we need
- // the [0..1] range to apply the vector in GLSL
- for (int i = 0; i < 4; i++) {
- mVector[i] /= 255.0f;
- }
-
- // TODO: We should be smarter about this
- mBlend = true;
-}
-
-SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
- delete[] mMatrix;
- delete[] mVector;
-}
-
-void SkiaColorMatrixFilter::describe(ProgramDescription& description,
- const Extensions& extensions) {
- description.colorOp = ProgramDescription::kColorMatrix;
-}
-
-void SkiaColorMatrixFilter::setupProgram(Program* program) {
- glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]);
- glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Lighting color filter
-///////////////////////////////////////////////////////////////////////////////
-
-SkiaLightingFilter::SkiaLightingFilter(SkColorFilter* skFilter, int multiply, int add):
- SkiaColorFilter(skFilter, kLighting, true) {
- mMulR = ((multiply >> 16) & 0xFF) / 255.0f;
- mMulG = ((multiply >> 8) & 0xFF) / 255.0f;
- mMulB = ((multiply ) & 0xFF) / 255.0f;
-
- mAddR = ((add >> 16) & 0xFF) / 255.0f;
- mAddG = ((add >> 8) & 0xFF) / 255.0f;
- mAddB = ((add ) & 0xFF) / 255.0f;
-
- // A lighting filter always ignores alpha
- mBlend = false;
-}
-
-void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) {
- description.colorOp = ProgramDescription::kColorLighting;
-}
-
-void SkiaLightingFilter::setupProgram(Program* program) {
- glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f);
- glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Blend color filter
-///////////////////////////////////////////////////////////////////////////////
-
-SkiaBlendFilter::SkiaBlendFilter(SkColorFilter* skFilter, int color, SkXfermode::Mode mode):
- SkiaColorFilter(skFilter, kBlend, true), mMode(mode) {
- const int alpha = (color >> 24) & 0xFF;
- mA = alpha / 255.0f;
- mR = mA * ((color >> 16) & 0xFF) / 255.0f;
- mG = mA * ((color >> 8) & 0xFF) / 255.0f;
- mB = mA * ((color ) & 0xFF) / 255.0f;
-
- // TODO: We should do something smarter here
- mBlend = true;
-}
-
-void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) {
- description.colorOp = ProgramDescription::kColorBlend;
- description.colorMode = mMode;
-}
-
-void SkiaBlendFilter::setupProgram(Program* program) {
- glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h
deleted file mode 100644
index 2feb834..0000000
--- a/libs/hwui/SkiaColorFilter.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HWUI_SKIA_COLOR_FILTER_H
-#define ANDROID_HWUI_SKIA_COLOR_FILTER_H
-
-#include <GLES2/gl2.h>
-#include <SkColorFilter.h>
-
-#include <cutils/compiler.h>
-
-#include "ProgramCache.h"
-#include "Extensions.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Base color filter
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a Skia color filter. A color filter modifies a ProgramDescription
- * and sets uniforms on the resulting shaders.
- */
-struct SkiaColorFilter {
- /**
- * Type of Skia color filter in use.
- */
- enum Type {
- kNone,
- kColorMatrix,
- kLighting,
- kBlend,
- };
-
- ANDROID_API SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend);
- virtual ~SkiaColorFilter();
-
- virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0;
- virtual void setupProgram(Program* program) = 0;
-
- inline bool blend() const {
- return mBlend;
- }
-
- Type type() const {
- return mType;
- }
-
- SkColorFilter* getSkColorFilter() {
- return mSkFilter;
- }
-
-protected:
- Type mType;
- bool mBlend;
-
-private:
- SkColorFilter *mSkFilter;
-}; // struct SkiaColorFilter
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementations
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * A color filter that multiplies the source color with a matrix and adds a vector.
- */
-struct SkiaColorMatrixFilter: public SkiaColorFilter {
- ANDROID_API SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector);
- ~SkiaColorMatrixFilter();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program);
-
-private:
- float* mMatrix;
- float* mVector;
-}; // struct SkiaColorMatrixFilter
-
-/**
- * A color filters that multiplies the source color with a fixed value and adds
- * another fixed value. Ignores the alpha channel of both arguments.
- */
-struct SkiaLightingFilter: public SkiaColorFilter {
- ANDROID_API SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add);
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program);
-
-private:
- GLfloat mMulR, mMulG, mMulB;
- GLfloat mAddR, mAddG, mAddB;
-}; // struct SkiaLightingFilter
-
-/**
- * A color filters that blends the source color with a specified destination color
- * and PorterDuff blending mode.
- */
-struct SkiaBlendFilter: public SkiaColorFilter {
- ANDROID_API SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode);
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program);
-
-private:
- SkXfermode::Mode mMode;
- GLfloat mR, mG, mB, mA;
-}; // struct SkiaBlendFilter
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 797ed10..c672bc4 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,9 +21,10 @@
#include <SkMatrix.h>
#include "Caches.h"
+#include "Layer.h"
+#include "Matrix.h"
#include "SkiaShader.h"
#include "Texture.h"
-#include "Matrix.h"
namespace android {
namespace uirenderer {
@@ -54,117 +55,245 @@ static inline void bindUniformColor(int slot, uint32_t color) {
a);
}
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
-
-void SkiaShader::copyFrom(const SkiaShader& shader) {
- mType = shader.mType;
- mKey = shader.mKey;
- mTileX = shader.mTileX;
- mTileY = shader.mTileY;
- mBlend = shader.mBlend;
- mUnitMatrix = shader.mUnitMatrix;
- mShaderMatrix = shader.mShaderMatrix;
- mGenerationId = shader.mGenerationId;
+static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
+ caches->bindTexture(texture->id);
+ texture->setWrapST(wrapS, wrapT);
}
-SkiaShader::SkiaShader(): mCaches(NULL) {
-}
+/**
+ * Compute the matrix to transform to screen space.
+ * @param screenSpace Output param for the computed matrix.
+ * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
+ * or identity.
+ * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
+ * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
+ */
+static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
+ const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
+ mat4 shaderMatrix;
+ // uses implicit construction
+ shaderMatrix.loadInverse(localMatrix);
+ // again, uses implicit construction
+ screenSpace.loadMultiply(unitMatrix, shaderMatrix);
+ screenSpace.multiply(modelViewMatrix);
+}
+
+// Returns true if one is a bitmap and the other is a gradient
+static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
+ return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
+ || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
+}
+
+SkiaShaderType SkiaShader::getType(const SkShader& shader) {
+ // First check for a gradient shader.
+ switch (shader.asAGradient(NULL)) {
+ case SkShader::kNone_GradientType:
+ // Not a gradient shader. Fall through to check for other types.
+ break;
+ case SkShader::kLinear_GradientType:
+ case SkShader::kRadial_GradientType:
+ case SkShader::kSweep_GradientType:
+ return kGradient_SkiaShaderType;
+ default:
+ // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+ return kNone_SkiaShaderType;
+ }
-SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
- mCaches(NULL) {
- setMatrix(matrix);
- mGenerationId = 0;
-}
+ // The shader is not a gradient. Check for a bitmap shader.
+ if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
+ return kBitmap_SkiaShaderType;
+ }
-SkiaShader::~SkiaShader() {
-}
+ // Check for a ComposeShader.
+ SkShader::ComposeRec rec;
+ if (shader.asACompose(&rec)) {
+ const SkiaShaderType shaderAType = getType(*rec.fShaderA);
+ const SkiaShaderType shaderBType = getType(*rec.fShaderB);
+
+ // Compose is only supported if one is a bitmap and the other is a
+ // gradient. Otherwise, return None to skip.
+ if (!bitmapAndGradient(shaderAType, shaderBType)) {
+ return kNone_SkiaShaderType;
+ }
+ return kCompose_SkiaShaderType;
+ }
-void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
-}
+ if (shader.asACustomShader(NULL)) {
+ return kLayer_SkiaShaderType;
+ }
-void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit) {
+ return kNone_SkiaShaderType;
}
-void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
- mCaches->bindTexture(texture->id);
- texture->setWrapST(wrapS, wrapT);
+typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+
+describeProc gDescribeProc[] = {
+ InvalidSkiaShader::describe,
+ SkiaBitmapShader::describe,
+ SkiaGradientShader::describe,
+ SkiaComposeShader::describe,
+ SkiaLayerShader::describe,
+};
+
+typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+
+setupProgramProc gSetupProgramProc[] = {
+ InvalidSkiaShader::setupProgram,
+ SkiaBitmapShader::setupProgram,
+ SkiaGradientShader::setupProgram,
+ SkiaComposeShader::setupProgram,
+ SkiaLayerShader::setupProgram,
+};
+
+void SkiaShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ gDescribeProc[getType(shader)](caches, description, extensions, shader);
}
-void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
- screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
- screenSpace.multiply(modelView);
+void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+
+ gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
}
///////////////////////////////////////////////////////////////////////////////
-// Bitmap shader
+// Layer shader
///////////////////////////////////////////////////////////////////////////////
-SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
- updateLocalMatrix(matrix);
+void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
+ const Extensions&, const SkShader& shader) {
+ description.hasBitmap = true;
}
-SkiaShader* SkiaBitmapShader::copy() {
- SkiaBitmapShader* copy = new SkiaBitmapShader();
- copy->copyFrom(*this);
- copy->mBitmap = mBitmap;
- return copy;
+void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+ Layer* layer;
+ if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+ LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
+ }
+
+ GLuint textureSlot = (*textureUnit)++;
+ caches->activeTexture(textureSlot);
+
+ const float width = layer->getWidth();
+ const float height = layer->getHeight();
+
+ mat4 textureTransform;
+ computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
+
+
+ // Uniforms
+ layer->bindTexture();
+ layer->setWrap(GL_CLAMP_TO_EDGE);
+ layer->setFilter(GL_LINEAR);
+
+ Program* program = caches->currentProgram;
+ glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
+ glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+ GL_FALSE, &textureTransform.data[0]);
+ glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
}
-void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
- Texture* texture = mCaches->textureCache.get(mBitmap);
- if (!texture) return;
- mTexture = texture;
+///////////////////////////////////////////////////////////////////////////////
+// Bitmap shader
+///////////////////////////////////////////////////////////////////////////////
+
+struct BitmapShaderInfo {
+ float width;
+ float height;
+ GLenum wrapS;
+ GLenum wrapT;
+ Texture* texture;
+};
+
+static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
+ BitmapShaderInfo* shaderInfo,
+ const Extensions& extensions,
+ const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
+ Texture* texture = caches->textureCache.get(&bitmap);
+ if (!texture) return false;
const float width = texture->width;
const float height = texture->height;
+ GLenum wrapS, wrapT;
- description.hasBitmap = true;
+ if (description) {
+ description->hasBitmap = true;
+ }
// The driver does not support non-power of two mirrored/repeated
// textures, so do it ourselves
if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
- (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
- description.isBitmapNpot = true;
- description.bitmapWrapS = gTileModes[mTileX];
- description.bitmapWrapT = gTileModes[mTileY];
- mWrapS = GL_CLAMP_TO_EDGE;
- mWrapT = GL_CLAMP_TO_EDGE;
+ (tileModes[0] != SkShader::kClamp_TileMode ||
+ tileModes[1] != SkShader::kClamp_TileMode)) {
+ if (description) {
+ description->isBitmapNpot = true;
+ description->bitmapWrapS = gTileModes[tileModes[0]];
+ description->bitmapWrapT = gTileModes[tileModes[1]];
+ }
+ wrapS = GL_CLAMP_TO_EDGE;
+ wrapT = GL_CLAMP_TO_EDGE;
} else {
- mWrapS = gTileModes[mTileX];
- mWrapT = gTileModes[mTileY];
+ wrapS = gTileModes[tileModes[0]];
+ wrapT = gTileModes[tileModes[1]];
}
+
+ if (shaderInfo) {
+ shaderInfo->width = width;
+ shaderInfo->height = height;
+ shaderInfo->wrapS = wrapS;
+ shaderInfo->wrapT = wrapT;
+ shaderInfo->texture = texture;
+ }
+ return true;
+}
+
+void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+ LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
+ }
+ bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
}
-void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
+void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+ LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
+ }
+
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mTexture;
- mTexture = NULL;
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
+ BitmapShaderInfo shaderInfo;
+ if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
+ return;
+ }
- const float width = texture->width;
- const float height = texture->height;
+ Program* program = caches->currentProgram;
+ Texture* texture = shaderInfo.texture;
+
+ const AutoTexture autoCleanup(texture);
mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, modelView);
+ computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
// Uniforms
- bindTexture(texture, mWrapS, mWrapT);
+ bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
texture->setFilter(GL_LINEAR);
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
GL_FALSE, &textureTransform.data[0]);
- glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+ glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+ 1.0f / shaderInfo.height);
}
///////////////////////////////////////////////////////////////////////////////
@@ -182,74 +311,6 @@ static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
matrix->postScale(inv, inv);
}
-SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
- float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
- mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
- SkPoint points[2];
- points[0].set(bounds[0], bounds[1]);
- points[1].set(bounds[2], bounds[3]);
-
- SkMatrix unitMatrix;
- toUnitMatrix(points, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
-
- updateLocalMatrix(matrix);
-
- mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaLinearGradientShader::~SkiaLinearGradientShader() {
- delete[] mBounds;
- delete[] mColors;
- delete[] mPositions;
-}
-
-SkiaShader* SkiaLinearGradientShader::copy() {
- SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
- copy->copyFrom(*this);
- copy->mBounds = new float[4];
- memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
-
-void SkiaLinearGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
- description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientLinear;
- description.isSimpleGradient = mIsSimple;
-}
-
-void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
- if (CC_UNLIKELY(!mIsSimple)) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
-
- Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
-
- // Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
- glUniform1i(program->getUniform("gradientSampler"), textureSlot);
- } else {
- bindUniformColor(program->getUniform("startColor"), mColors[0]);
- bindUniformColor(program->getUniform("endColor"), mColors[1]);
- }
-
- Caches::getInstance().dither.setupProgram(program, textureUnit);
-
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Circular gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -261,37 +322,6 @@ static void toCircularUnitMatrix(const float x, const float y, const float radiu
matrix->postScale(inv, inv);
}
-SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
- uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
- tileMode, matrix, blend) {
- SkMatrix unitMatrix;
- toCircularUnitMatrix(x, y, radius, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
-
- updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaCircularGradientShader::copy() {
- SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
- copy->copyFrom(*this);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
-
-void SkiaCircularGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
- description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientCircular;
- description.isSimpleGradient = mIsSimple;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Sweep gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -300,73 +330,103 @@ static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
matrix->setTranslate(-x, -y);
}
-SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
- float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
- SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode, matrix, blend),
- mColors(colors), mPositions(positions), mCount(count) {
- SkMatrix unitMatrix;
- toSweepUnitMatrix(x, y, &unitMatrix);
- mUnitMatrix.load(unitMatrix);
-
- updateLocalMatrix(matrix);
-
- mIsSimple = count == 2;
-}
-
-SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
- float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend):
- SkiaShader(type, key, tileMode, tileMode, matrix, blend),
- mColors(colors), mPositions(positions), mCount(count) {
-
- mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaSweepGradientShader::~SkiaSweepGradientShader() {
- delete[] mColors;
- delete[] mPositions;
-}
-
-SkiaShader* SkiaSweepGradientShader::copy() {
- SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
- copy->copyFrom(*this);
- copy->mColors = new uint32_t[mCount];
- memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
- copy->mPositions = new float[mCount];
- memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
- copy->mCount = mCount;
- copy->mIsSimple = mIsSimple;
- return copy;
-}
+///////////////////////////////////////////////////////////////////////////////
+// Common gradient code
+///////////////////////////////////////////////////////////////////////////////
-void SkiaSweepGradientShader::describe(ProgramDescription& description,
- const Extensions& extensions) {
+static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
+ return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
+}
+
+void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkShader::GradientInfo gradInfo;
+ gradInfo.fColorCount = 0;
+ gradInfo.fColors = NULL;
+ gradInfo.fColorOffsets = NULL;
+
+ switch (shader.asAGradient(&gradInfo)) {
+ case SkShader::kLinear_GradientType:
+ description.gradientType = ProgramDescription::kGradientLinear;
+ break;
+ case SkShader::kRadial_GradientType:
+ description.gradientType = ProgramDescription::kGradientCircular;
+ break;
+ case SkShader::kSweep_GradientType:
+ description.gradientType = ProgramDescription::kGradientSweep;
+ break;
+ default:
+ // Do nothing. This shader is unsupported.
+ return;
+ }
description.hasGradient = true;
- description.gradientType = ProgramDescription::kGradientSweep;
- description.isSimpleGradient = mIsSimple;
-}
-
-void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
- if (CC_UNLIKELY(!mIsSimple)) {
+ description.isSimpleGradient = isSimpleGradient(gradInfo);
+}
+
+void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+ // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
+ // how much space has been allocated for fColors and fColorOffsets. 10 was chosen
+ // arbitrarily, but should be >= 2.
+ // As output, it tells the number of actual colors/offsets in the gradient.
+ const int COLOR_COUNT = 10;
+ SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
+ SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
+
+ SkShader::GradientInfo gradInfo;
+ gradInfo.fColorCount = COLOR_COUNT;
+ gradInfo.fColors = colorStorage.get();
+ gradInfo.fColorOffsets = positionStorage.get();
+
+ SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
+
+ Program* program = caches->currentProgram;
+ if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+ if (gradInfo.fColorCount > COLOR_COUNT) {
+ // There was not enough room in our arrays for all the colors and offsets. Try again,
+ // now that we know the true number of colors.
+ gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
+ gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
+
+ shader.asAGradient(&gradInfo);
+ }
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ caches->activeTexture(textureSlot);
- Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
+#ifndef SK_SCALAR_IS_FLOAT
+ #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+ Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
+ gradInfo.fColorCount);
// Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+ bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
} else {
- bindUniformColor(program->getUniform("startColor"), mColors[0]);
- bindUniformColor(program->getUniform("endColor"), mColors[1]);
+ bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
+ bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
}
- mCaches->dither.setupProgram(program, textureUnit);
+ caches->dither.setupProgram(program, textureUnit);
+
+ SkMatrix unitMatrix;
+ switch (gradType) {
+ case SkShader::kLinear_GradientType:
+ toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+ break;
+ case SkShader::kRadial_GradientType:
+ toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+ gradInfo.fRadius[0], &unitMatrix);
+ break;
+ case SkShader::kSweep_GradientType:
+ toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
+ }
mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
+ computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
@@ -374,49 +434,39 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
// Compose shader
///////////////////////////////////////////////////////////////////////////////
-SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
- SkXfermode::Mode mode, SkShader* key):
- SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
- NULL, first->blend() || second->blend()),
- mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
-}
-
-SkiaComposeShader::~SkiaComposeShader() {
- if (mCleanup) {
- delete mFirst;
- delete mSecond;
+void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ SkShader::ComposeRec rec;
+ if (!shader.asACompose(&rec)) {
+ LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
}
-}
-
-SkiaShader* SkiaComposeShader::copy() {
- SkiaComposeShader* copy = new SkiaComposeShader();
- copy->copyFrom(*this);
- copy->mFirst = mFirst->copy();
- copy->mSecond = mSecond->copy();
- copy->mMode = mMode;
- copy->cleanup();
- return copy;
-}
-
-void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
- mFirst->describe(description, extensions);
- mSecond->describe(description, extensions);
- if (mFirst->type() == kBitmap) {
+ SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
+ SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
+ if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
description.isBitmapFirst = true;
}
- description.shadersMode = mMode;
+ if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
+ // TODO: Support other modes.
+ description.shadersMode = SkXfermode::kSrcOver_Mode;
+ }
}
-void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
- const Snapshot& snapshot, GLuint* textureUnit) {
+void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ SkShader::ComposeRec rec;
+ if (!shader.asACompose(&rec)) {
+ LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
+ }
+
// Apply this compose shader's local transform and pass it down to
// the child shaders. They will in turn apply their local transform
// to this matrix.
mat4 transform;
- computeScreenSpaceMatrix(transform, modelView);
+ computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
- mFirst->setupProgram(program, transform, snapshot, textureUnit);
- mSecond->setupProgram(program, transform, snapshot, textureUnit);
+ SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
+ SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
}
}; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index cc56c50..034c3f6 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -28,230 +28,90 @@
#include "ProgramCache.h"
#include "TextureCache.h"
#include "GradientCache.h"
-#include "Snapshot.h"
namespace android {
namespace uirenderer {
class Caches;
-
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
+class Layer;
/**
- * Represents a Skia shader. A shader will modify the GL context and active
- * program to recreate the original effect.
+ * Type of Skia shader in use.
*/
-struct SkiaShader {
- /**
- * Type of Skia shader in use.
- */
- enum Type {
- kNone,
- kBitmap,
- kLinearGradient,
- kCircularGradient,
- kSweepGradient,
- kCompose
- };
-
- ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
- virtual ~SkiaShader();
-
- virtual SkiaShader* copy() = 0;
- void copyFrom(const SkiaShader& shader);
-
- virtual void describe(ProgramDescription& description, const Extensions& extensions);
- virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
- inline SkShader* getSkShader() {
- return mKey;
- }
-
- inline bool blend() const {
- return mBlend;
- }
-
- Type type() const {
- return mType;
- }
-
- virtual void setCaches(Caches& caches) {
- mCaches = &caches;
- }
-
- uint32_t getGenerationId() {
- return mGenerationId;
+enum SkiaShaderType {
+ kNone_SkiaShaderType,
+ kBitmap_SkiaShaderType,
+ kGradient_SkiaShaderType,
+ kCompose_SkiaShaderType,
+ kLayer_SkiaShaderType
+};
+
+class SkiaShader {
+public:
+ static SkiaShaderType getType(const SkShader& shader);
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
+
+class InvalidSkiaShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader) {
+ // This shader is unsupported. Skip it.
}
-
- void setMatrix(SkMatrix* matrix) {
- updateLocalMatrix(matrix);
- mGenerationId++;
- }
-
- void updateLocalMatrix(const SkMatrix* matrix) {
- if (matrix) {
- mat4 localMatrix(*matrix);
- mShaderMatrix.loadInverse(localMatrix);
- } else {
- mShaderMatrix.loadIdentity();
- }
- }
-
- void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
-
-protected:
- SkiaShader();
-
- /**
- * The appropriate texture unit must have been activated prior to invoking
- * this method.
- */
- inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT);
-
- Type mType;
- SkShader* mKey;
- SkShader::TileMode mTileX;
- SkShader::TileMode mTileY;
- bool mBlend;
-
- Caches* mCaches;
-
- mat4 mUnitMatrix;
- mat4 mShaderMatrix;
-
-private:
- uint32_t mGenerationId;
-}; // struct SkiaShader
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementations
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * A shader that draws a bitmap.
- */
-struct SkiaBitmapShader: public SkiaShader {
- ANDROID_API SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaBitmapShader() : mBitmap(NULL), mTexture(NULL) {
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+ // This shader is unsupported. Skip it.
}
- SkBitmap* mBitmap;
- Texture* mTexture;
- GLenum mWrapS;
- GLenum mWrapT;
-}; // struct SkiaBitmapShader
-
+};
/**
- * A shader that draws a linear gradient.
+ * A shader that draws a layer.
*/
-struct SkiaLinearGradientShader: public SkiaShader {
- ANDROID_API SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions,
- int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
- ~SkiaLinearGradientShader();
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaLinearGradientShader() {
- }
-
- bool mIsSimple;
- float* mBounds;
- uint32_t* mColors;
- float* mPositions;
- int mCount;
-}; // struct SkiaLinearGradientShader
+class SkiaLayerShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaLayerShader
/**
- * A shader that draws a sweep gradient.
+ * A shader that draws a bitmap.
*/
-struct SkiaSweepGradientShader: public SkiaShader {
- ANDROID_API SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions,
- int count, SkShader* key, SkMatrix* matrix, bool blend);
- ~SkiaSweepGradientShader();
- SkiaShader* copy();
-
- virtual void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
+class SkiaBitmapShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
-protected:
- SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions,
- int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
- SkiaSweepGradientShader() {
- }
- bool mIsSimple;
- uint32_t* mColors;
- float* mPositions;
- int mCount;
-}; // struct SkiaSweepGradientShader
+}; // class SkiaBitmapShader
/**
- * A shader that draws a circular gradient.
+ * A shader that draws one of three types of gradient, depending on shader param.
*/
-struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
- ANDROID_API SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors,
- float* positions, int count, SkShader* key,SkShader::TileMode tileMode,
- SkMatrix* matrix, bool blend);
- SkiaShader* copy();
-
- void describe(ProgramDescription& description, const Extensions& extensions);
-
-private:
- SkiaCircularGradientShader() {
- }
-}; // struct SkiaCircularGradientShader
+class SkiaGradientShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
/**
* A shader that draws two shaders, composited with an xfermode.
*/
-struct SkiaComposeShader: public SkiaShader {
- ANDROID_API SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode,
- SkShader* key);
- ~SkiaComposeShader();
- SkiaShader* copy();
-
- void setCaches(Caches& caches) {
- SkiaShader::setCaches(caches);
- mFirst->setCaches(caches);
- mSecond->setCaches(caches);
- }
-
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
- GLuint* textureUnit);
-
-private:
- SkiaComposeShader(): mCleanup(false) {
- }
-
- void cleanup() {
- mCleanup = true;
- }
-
- SkiaShader* mFirst;
- SkiaShader* mSecond;
- SkXfermode::Mode mMode;
-
- bool mCleanup;
-}; // struct SkiaComposeShader
+class SkiaComposeShader {
+public:
+ static void describe(Caches* caches, ProgramDescription& description,
+ const Extensions& extensions, const SkShader& shader);
+ static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+ GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaComposeShader
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index d26ee38..ecc47d2 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -27,9 +27,15 @@ namespace uirenderer {
// Constructors
///////////////////////////////////////////////////////////////////////////////
-Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
- invisible(false), empty(false), alpha(1.0f) {
-
+Snapshot::Snapshot()
+ : flags(0)
+ , previous(NULL)
+ , layer(NULL)
+ , fbo(0)
+ , invisible(false)
+ , empty(false)
+ , alpha(1.0f)
+ , roundRectClipState(NULL) {
transform = &mTransformRoot;
clipRect = &mClipRectRoot;
region = NULL;
@@ -40,11 +46,17 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
* Copies the specified snapshot/ The specified snapshot is stored as
* the previous snapshot.
*/
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
- flags(0), previous(s), layer(s->layer), fbo(s->fbo),
- invisible(s->invisible), empty(false),
- viewport(s->viewport), height(s->height), alpha(s->alpha) {
-
+Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+ : flags(0)
+ , previous(s)
+ , layer(s->layer)
+ , fbo(s->fbo)
+ , invisible(s->invisible)
+ , empty(false)
+ , alpha(s->alpha)
+ , roundRectClipState(s->roundRectClipState)
+ , mViewportData(s->mViewportData)
+ , mRelativeLightCenter(s->mRelativeLightCenter) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
mTransformRoot.load(*s->transform);
transform = &mTransformRoot;
@@ -189,11 +201,60 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) {
///////////////////////////////////////////////////////////////////////////////
void Snapshot::resetTransform(float x, float y, float z) {
+ // before resetting, map current light pos with inverse of current transform
+ Vector3 center = mRelativeLightCenter;
+ mat4 inverse;
+ inverse.loadInverse(*transform);
+ inverse.mapPoint3d(center);
+ mRelativeLightCenter = center;
+
transform = &mTransformRoot;
transform->loadTranslate(x, y, z);
}
///////////////////////////////////////////////////////////////////////////////
+// Clipping round rect
+///////////////////////////////////////////////////////////////////////////////
+
+void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius) {
+ if (bounds.isEmpty()) {
+ clipRect->setEmpty();
+ return;
+ }
+
+ RoundRectClipState* state = new (allocator) RoundRectClipState;
+
+ // store the inverse drawing matrix
+ Matrix4 roundRectDrawingMatrix;
+ roundRectDrawingMatrix.load(getOrthoMatrix());
+ roundRectDrawingMatrix.multiply(*transform);
+ state->matrix.loadInverse(roundRectDrawingMatrix);
+
+ // compute area under rounded corners - only draws overlapping these rects need to be clipped
+ for (int i = 0 ; i < 4; i++) {
+ state->dangerRects[i] = bounds;
+ }
+ state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
+ state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
+ state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
+ state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
+ for (int i = 0; i < 4; i++) {
+ transform->mapRect(state->dangerRects[i]);
+
+ // round danger rects out as though they are AA geometry (since they essentially are)
+ state->dangerRects[i].snapGeometryToPixelBoundaries(true);
+ }
+
+ // store RR area
+ state->innerRect = bounds;
+ state->innerRect.inset(radius);
+ state->radius = radius;
+
+ // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
+ roundRectClipState = state;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Queries
///////////////////////////////////////////////////////////////////////////////
@@ -203,7 +264,7 @@ bool Snapshot::isIgnored() const {
void Snapshot::dump() const {
ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous.get(), height, isIgnored(), clipRegion && !clipRegion->isEmpty());
+ this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty());
ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f",
clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
ALOGD(" Transform (at %p):", transform);
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 5bdb18a..ad4ee9d 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -20,6 +20,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <ui/Region.h>
@@ -27,12 +28,40 @@
#include "Layer.h"
#include "Matrix.h"
+#include "Outline.h"
#include "Rect.h"
+#include "utils/Macros.h"
namespace android {
namespace uirenderer {
/**
+ * Temporary structure holding information for a single outline clip.
+ *
+ * These structures are treated as immutable once created, and only exist for a single frame, which
+ * is why they may only be allocated with a LinearAllocator.
+ */
+class RoundRectClipState {
+public:
+ /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ bool areaRequiresRoundRectClip(const Rect& rect) const {
+ return rect.intersects(dangerRects[0])
+ || rect.intersects(dangerRects[1])
+ || rect.intersects(dangerRects[2])
+ || rect.intersects(dangerRects[3]);
+ }
+
+ Matrix4 matrix;
+ Rect dangerRects[4];
+ Rect innerRect;
+ float radius;
+};
+
+/**
* A snapshot holds information about the current state of the rendering
* surface. A snapshot is usually created whenever the user calls save()
* and discarded when the user calls restore(). Once a snapshot is created,
@@ -65,17 +94,16 @@ public:
* Indicates that this snapshot is a special type of layer
* backed by an FBO. This flag only makes sense when the
* flag kFlagIsLayer is also set.
+ *
+ * Viewport has been modified to fit the new Fbo, and must be
+ * restored when this snapshot is restored.
*/
kFlagIsFboLayer = 0x4,
/**
- * Indicates that this snapshot has changed the ortho matrix.
- */
- kFlagDirtyOrtho = 0x8,
- /**
* Indicates that this snapshot or an ancestor snapshot is
* an FBO layer.
*/
- kFlagFboTarget = 0x10
+ kFlagFboTarget = 0x8,
};
/**
@@ -111,6 +139,11 @@ public:
ANDROID_API const Rect& getLocalClip();
/**
+ * Returns the current clip in render target coordinates.
+ */
+ const Rect& getRenderTargetClip() { return *clipRect; }
+
+ /**
* Resets the clip to the specified rect.
*/
void resetClip(float left, float top, float right, float bottom);
@@ -120,6 +153,22 @@ public:
*/
void resetTransform(float x, float y, float z);
+ void initializeViewport(int width, int height) {
+ mViewportData.initialize(width, height);
+ }
+
+ int getViewportWidth() const { return mViewportData.mWidth; }
+ int getViewportHeight() const { return mViewportData.mHeight; }
+ const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
+
+ const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
+ void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
+
+ /**
+ * Sets (and replaces) the current clipping outline
+ */
+ void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius);
+
/**
* Indicates whether this snapshot should be ignored. A snapshot
* is typicalled ignored if its layer is invisible or empty.
@@ -168,21 +217,6 @@ public:
bool empty;
/**
- * Current viewport.
- */
- Rect viewport;
-
- /**
- * Height of the framebuffer the snapshot is rendering into.
- */
- int height;
-
- /**
- * Contains the previous ortho matrix.
- */
- mat4 orthoMatrix;
-
- /**
* Local transformation. Holds the current translation, scale and
* rotation values.
*
@@ -228,9 +262,38 @@ public:
*/
float alpha;
+ /**
+ * Current clipping round rect.
+ *
+ * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips,
+ * never modified.
+ */
+ const RoundRectClipState* roundRectClipState;
+
void dump() const;
private:
+ struct ViewportData {
+ ViewportData() : mWidth(0), mHeight(0) {}
+ void initialize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
+ }
+
+ /*
+ * Width and height of current viewport.
+ *
+ * The viewport is always defined to be (0, 0, width, height).
+ */
+ int mWidth;
+ int mHeight;
+ /**
+ * Contains the current orthographic, projection matrix.
+ */
+ mat4 mOrthoMatrix;
+ };
+
void ensureClipRegion();
void copyClipRectFromRegion();
@@ -238,9 +301,11 @@ private:
mat4 mTransformRoot;
Rect mClipRectRoot;
- Rect mLocalClip;
+ Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this
SkRegion mClipRegionRoot;
+ ViewportData mViewportData;
+ Vector3 mRelativeLightCenter;
}; // class Snapshot
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
new file mode 100644
index 0000000..d726538
--- /dev/null
+++ b/libs/hwui/SpotShadow.cpp
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2014 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 "OpenGLRenderer"
+
+#define SHADOW_SHRINK_SCALE 0.1f
+
+#include <math.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+
+#include "ShadowTessellator.h"
+#include "SpotShadow.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+static const double EPSILON = 1e-7;
+
+/**
+ * Calculate the angle between and x and a y coordinate.
+ * The atan2 range from -PI to PI.
+ */
+static float angle(const Vector2& point, const Vector2& center) {
+ return atan2(point.y - center.y, point.x - center.x);
+}
+
+/**
+ * Calculate the intersection of a ray with the line segment defined by two points.
+ *
+ * Returns a negative value in error conditions.
+
+ * @param rayOrigin The start of the ray
+ * @param dx The x vector of the ray
+ * @param dy The y vector of the ray
+ * @param p1 The first point defining the line segment
+ * @param p2 The second point defining the line segment
+ * @return The distance along the ray if it intersects with the line segment, negative if otherwise
+ */
+static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
+ const Vector2& p1, const Vector2& p2) {
+ // The math below is derived from solving this formula, basically the
+ // intersection point should stay on both the ray and the edge of (p1, p2).
+ // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]);
+
+ double divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x);
+ if (divisor == 0) return -1.0f; // error, invalid divisor
+
+#if DEBUG_SHADOW
+ double interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor;
+ if (interpVal < 0 || interpVal > 1) {
+ ALOGW("rayIntersectPoints is hitting outside the segment %f", interpVal);
+ }
+#endif
+
+ double distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) +
+ rayOrigin.x * (p2.y - p1.y)) / divisor;
+
+ return distance; // may be negative in error cases
+}
+
+/**
+ * Sort points by their X coordinates
+ *
+ * @param points the points as a Vector2 array.
+ * @param pointsLength the number of vertices of the polygon.
+ */
+void SpotShadow::xsort(Vector2* points, int pointsLength) {
+ quicksortX(points, 0, pointsLength - 1);
+}
+
+/**
+ * compute the convex hull of a collection of Points
+ *
+ * @param points the points as a Vector2 array.
+ * @param pointsLength the number of vertices of the polygon.
+ * @param retPoly pre allocated array of floats to put the vertices
+ * @return the number of points in the polygon 0 if no intersection
+ */
+int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) {
+ xsort(points, pointsLength);
+ int n = pointsLength;
+ Vector2 lUpper[n];
+ lUpper[0] = points[0];
+ lUpper[1] = points[1];
+
+ int lUpperSize = 2;
+
+ for (int i = 2; i < n; i++) {
+ lUpper[lUpperSize] = points[i];
+ lUpperSize++;
+
+ while (lUpperSize > 2 && !ccw(
+ lUpper[lUpperSize - 3].x, lUpper[lUpperSize - 3].y,
+ lUpper[lUpperSize - 2].x, lUpper[lUpperSize - 2].y,
+ lUpper[lUpperSize - 1].x, lUpper[lUpperSize - 1].y)) {
+ // Remove the middle point of the three last
+ lUpper[lUpperSize - 2].x = lUpper[lUpperSize - 1].x;
+ lUpper[lUpperSize - 2].y = lUpper[lUpperSize - 1].y;
+ lUpperSize--;
+ }
+ }
+
+ Vector2 lLower[n];
+ lLower[0] = points[n - 1];
+ lLower[1] = points[n - 2];
+
+ int lLowerSize = 2;
+
+ for (int i = n - 3; i >= 0; i--) {
+ lLower[lLowerSize] = points[i];
+ lLowerSize++;
+
+ while (lLowerSize > 2 && !ccw(
+ lLower[lLowerSize - 3].x, lLower[lLowerSize - 3].y,
+ lLower[lLowerSize - 2].x, lLower[lLowerSize - 2].y,
+ lLower[lLowerSize - 1].x, lLower[lLowerSize - 1].y)) {
+ // Remove the middle point of the three last
+ lLower[lLowerSize - 2] = lLower[lLowerSize - 1];
+ lLowerSize--;
+ }
+ }
+
+ // output points in CW ordering
+ const int total = lUpperSize + lLowerSize - 2;
+ int outIndex = total - 1;
+ for (int i = 0; i < lUpperSize; i++) {
+ retPoly[outIndex] = lUpper[i];
+ outIndex--;
+ }
+
+ for (int i = 1; i < lLowerSize - 1; i++) {
+ retPoly[outIndex] = lLower[i];
+ outIndex--;
+ }
+ // TODO: Add test harness which verify that all the points are inside the hull.
+ return total;
+}
+
+/**
+ * Test whether the 3 points form a counter clockwise turn.
+ *
+ * @return true if a right hand turn
+ */
+bool SpotShadow::ccw(double ax, double ay, double bx, double by,
+ double cx, double cy) {
+ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON;
+}
+
+/**
+ * Calculates the intersection of poly1 with poly2 and put in poly2.
+ * Note that both poly1 and poly2 must be in CW order already!
+ *
+ * @param poly1 The 1st polygon, as a Vector2 array.
+ * @param poly1Length The number of vertices of 1st polygon.
+ * @param poly2 The 2nd and output polygon, as a Vector2 array.
+ * @param poly2Length The number of vertices of 2nd polygon.
+ * @return number of vertices in output polygon as poly2.
+ */
+int SpotShadow::intersection(const Vector2* poly1, int poly1Length,
+ Vector2* poly2, int poly2Length) {
+#if DEBUG_SHADOW
+ if (!ShadowTessellator::isClockwise(poly1, poly1Length)) {
+ ALOGW("Poly1 is not clockwise! Intersection is wrong!");
+ }
+ if (!ShadowTessellator::isClockwise(poly2, poly2Length)) {
+ ALOGW("Poly2 is not clockwise! Intersection is wrong!");
+ }
+#endif
+ Vector2 poly[poly1Length * poly2Length + 2];
+ int count = 0;
+ int pcount = 0;
+
+ // If one vertex from one polygon sits inside another polygon, add it and
+ // count them.
+ for (int i = 0; i < poly1Length; i++) {
+ if (testPointInsidePolygon(poly1[i], poly2, poly2Length)) {
+ poly[count] = poly1[i];
+ count++;
+ pcount++;
+
+ }
+ }
+
+ int insidePoly2 = pcount;
+ for (int i = 0; i < poly2Length; i++) {
+ if (testPointInsidePolygon(poly2[i], poly1, poly1Length)) {
+ poly[count] = poly2[i];
+ count++;
+ }
+ }
+
+ int insidePoly1 = count - insidePoly2;
+ // If all vertices from poly1 are inside poly2, then just return poly1.
+ if (insidePoly2 == poly1Length) {
+ memcpy(poly2, poly1, poly1Length * sizeof(Vector2));
+ return poly1Length;
+ }
+
+ // If all vertices from poly2 are inside poly1, then just return poly2.
+ if (insidePoly1 == poly2Length) {
+ return poly2Length;
+ }
+
+ // Since neither polygon fully contain the other one, we need to add all the
+ // intersection points.
+ Vector2 intersection = {0, 0};
+ for (int i = 0; i < poly2Length; i++) {
+ for (int j = 0; j < poly1Length; j++) {
+ int poly2LineStart = i;
+ int poly2LineEnd = ((i + 1) % poly2Length);
+ int poly1LineStart = j;
+ int poly1LineEnd = ((j + 1) % poly1Length);
+ bool found = lineIntersection(
+ poly2[poly2LineStart].x, poly2[poly2LineStart].y,
+ poly2[poly2LineEnd].x, poly2[poly2LineEnd].y,
+ poly1[poly1LineStart].x, poly1[poly1LineStart].y,
+ poly1[poly1LineEnd].x, poly1[poly1LineEnd].y,
+ intersection);
+ if (found) {
+ poly[count].x = intersection.x;
+ poly[count].y = intersection.y;
+ count++;
+ } else {
+ Vector2 delta = poly2[i] - poly1[j];
+ if (delta.lengthSquared() < EPSILON) {
+ poly[count] = poly2[i];
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0) {
+ return 0;
+ }
+
+ // Sort the result polygon around the center.
+ Vector2 center = {0.0f, 0.0f};
+ for (int i = 0; i < count; i++) {
+ center += poly[i];
+ }
+ center /= count;
+ sort(poly, count, center);
+
+#if DEBUG_SHADOW
+ // Since poly2 is overwritten as the result, we need to save a copy to do
+ // our verification.
+ Vector2 oldPoly2[poly2Length];
+ int oldPoly2Length = poly2Length;
+ memcpy(oldPoly2, poly2, sizeof(Vector2) * poly2Length);
+#endif
+
+ // Filter the result out from poly and put it into poly2.
+ poly2[0] = poly[0];
+ int lastOutputIndex = 0;
+ for (int i = 1; i < count; i++) {
+ Vector2 delta = poly[i] - poly2[lastOutputIndex];
+ if (delta.lengthSquared() >= EPSILON) {
+ poly2[++lastOutputIndex] = poly[i];
+ } else {
+ // If the vertices are too close, pick the inner one, because the
+ // inner one is more likely to be an intersection point.
+ Vector2 delta1 = poly[i] - center;
+ Vector2 delta2 = poly2[lastOutputIndex] - center;
+ if (delta1.lengthSquared() < delta2.lengthSquared()) {
+ poly2[lastOutputIndex] = poly[i];
+ }
+ }
+ }
+ int resultLength = lastOutputIndex + 1;
+
+#if DEBUG_SHADOW
+ testConvex(poly2, resultLength, "intersection");
+ testConvex(poly1, poly1Length, "input poly1");
+ testConvex(oldPoly2, oldPoly2Length, "input poly2");
+
+ testIntersection(poly1, poly1Length, oldPoly2, oldPoly2Length, poly2, resultLength);
+#endif
+
+ return resultLength;
+}
+
+/**
+ * Sort points about a center point
+ *
+ * @param poly The in and out polyogon as a Vector2 array.
+ * @param polyLength The number of vertices of the polygon.
+ * @param center the center ctr[0] = x , ctr[1] = y to sort around.
+ */
+void SpotShadow::sort(Vector2* poly, int polyLength, const Vector2& center) {
+ quicksortCirc(poly, 0, polyLength - 1, center);
+}
+
+/**
+ * Swap points pointed to by i and j
+ */
+void SpotShadow::swap(Vector2* points, int i, int j) {
+ Vector2 temp = points[i];
+ points[i] = points[j];
+ points[j] = temp;
+}
+
+/**
+ * quick sort implementation about the center.
+ */
+void SpotShadow::quicksortCirc(Vector2* points, int low, int high,
+ const Vector2& center) {
+ int i = low, j = high;
+ int p = low + (high - low) / 2;
+ float pivot = angle(points[p], center);
+ while (i <= j) {
+ while (angle(points[i], center) > pivot) {
+ i++;
+ }
+ while (angle(points[j], center) < pivot) {
+ j--;
+ }
+
+ if (i <= j) {
+ swap(points, i, j);
+ i++;
+ j--;
+ }
+ }
+ if (low < j) quicksortCirc(points, low, j, center);
+ if (i < high) quicksortCirc(points, i, high, center);
+}
+
+/**
+ * Sort points by x axis
+ *
+ * @param points points to sort
+ * @param low start index
+ * @param high end index
+ */
+void SpotShadow::quicksortX(Vector2* points, int low, int high) {
+ int i = low, j = high;
+ int p = low + (high - low) / 2;
+ float pivot = points[p].x;
+ while (i <= j) {
+ while (points[i].x < pivot) {
+ i++;
+ }
+ while (points[j].x > pivot) {
+ j--;
+ }
+
+ if (i <= j) {
+ swap(points, i, j);
+ i++;
+ j--;
+ }
+ }
+ if (low < j) quicksortX(points, low, j);
+ if (i < high) quicksortX(points, i, high);
+}
+
+/**
+ * Test whether a point is inside the polygon.
+ *
+ * @param testPoint the point to test
+ * @param poly the polygon
+ * @return true if the testPoint is inside the poly.
+ */
+bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint,
+ const Vector2* poly, int len) {
+ bool c = false;
+ double testx = testPoint.x;
+ double testy = testPoint.y;
+ for (int i = 0, j = len - 1; i < len; j = i++) {
+ double startX = poly[j].x;
+ double startY = poly[j].y;
+ double endX = poly[i].x;
+ double endY = poly[i].y;
+
+ if (((endY > testy) != (startY > testy)) &&
+ (testx < (startX - endX) * (testy - endY)
+ / (startY - endY) + endX)) {
+ c = !c;
+ }
+ }
+ return c;
+}
+
+/**
+ * Make the polygon turn clockwise.
+ *
+ * @param polygon the polygon as a Vector2 array.
+ * @param len the number of points of the polygon
+ */
+void SpotShadow::makeClockwise(Vector2* polygon, int len) {
+ if (polygon == 0 || len == 0) {
+ return;
+ }
+ if (!ShadowTessellator::isClockwise(polygon, len)) {
+ reverse(polygon, len);
+ }
+}
+
+/**
+ * Reverse the polygon
+ *
+ * @param polygon the polygon as a Vector2 array
+ * @param len the number of points of the polygon
+ */
+void SpotShadow::reverse(Vector2* polygon, int len) {
+ int n = len / 2;
+ for (int i = 0; i < n; i++) {
+ Vector2 tmp = polygon[i];
+ int k = len - 1 - i;
+ polygon[i] = polygon[k];
+ polygon[k] = tmp;
+ }
+}
+
+/**
+ * Intersects two lines in parametric form. This function is called in a tight
+ * loop, and we need double precision to get things right.
+ *
+ * @param x1 the x coordinate point 1 of line 1
+ * @param y1 the y coordinate point 1 of line 1
+ * @param x2 the x coordinate point 2 of line 1
+ * @param y2 the y coordinate point 2 of line 1
+ * @param x3 the x coordinate point 1 of line 2
+ * @param y3 the y coordinate point 1 of line 2
+ * @param x4 the x coordinate point 2 of line 2
+ * @param y4 the y coordinate point 2 of line 2
+ * @param ret the x,y location of the intersection
+ * @return true if it found an intersection
+ */
+inline bool SpotShadow::lineIntersection(double x1, double y1, double x2, double y2,
+ double x3, double y3, double x4, double y4, Vector2& ret) {
+ double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+ if (d == 0.0) return false;
+
+ double dx = (x1 * y2 - y1 * x2);
+ double dy = (x3 * y4 - y3 * x4);
+ double x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
+ double y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
+
+ // The intersection should be in the middle of the point 1 and point 2,
+ // likewise point 3 and point 4.
+ if (((x - x1) * (x - x2) > EPSILON)
+ || ((x - x3) * (x - x4) > EPSILON)
+ || ((y - y1) * (y - y2) > EPSILON)
+ || ((y - y3) * (y - y4) > EPSILON)) {
+ // Not interesected
+ return false;
+ }
+ ret.x = x;
+ ret.y = y;
+ return true;
+
+}
+
+/**
+ * Compute a horizontal circular polygon about point (x , y , height) of radius
+ * (size)
+ *
+ * @param points number of the points of the output polygon.
+ * @param lightCenter the center of the light.
+ * @param size the light size.
+ * @param ret result polygon.
+ */
+void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter,
+ float size, Vector3* ret) {
+ // TODO: Caching all the sin / cos values and store them in a look up table.
+ for (int i = 0; i < points; i++) {
+ double angle = 2 * i * M_PI / points;
+ ret[i].x = cosf(angle) * size + lightCenter.x;
+ ret[i].y = sinf(angle) * size + lightCenter.y;
+ ret[i].z = lightCenter.z;
+ }
+}
+
+/**
+* Generate the shadow from a spot light.
+*
+* @param poly x,y,z vertexes of a convex polygon that occludes the light source
+* @param polyLength number of vertexes of the occluding polygon
+* @param lightCenter the center of the light
+* @param lightSize the radius of the light source
+* @param lightVertexCount the vertex counter for the light polygon
+* @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
+* empty strip if error.
+*
+*/
+void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+ int polyLength, const Vector3& lightCenter, float lightSize,
+ int lightVertexCount, VertexBuffer& retStrips) {
+ Vector3 light[lightVertexCount * 3];
+ computeLightPolygon(lightVertexCount, lightCenter, lightSize, light);
+ computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly,
+ polyLength, retStrips);
+}
+
+/**
+ * Generate the shadow spot light of shape lightPoly and a object poly
+ *
+ * @param lightPoly x,y,z vertex of a convex polygon that is the light source
+ * @param lightPolyLength number of vertexes of the light source polygon
+ * @param poly x,y,z vertexes of a convex polygon that occludes the light source
+ * @param polyLength number of vertexes of the occluding polygon
+ * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
+ * empty strip if error.
+ */
+void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly,
+ int lightPolyLength, const Vector3& lightCenter, const Vector3* poly,
+ int polyLength, VertexBuffer& shadowTriangleStrip) {
+ // Point clouds for all the shadowed vertices
+ Vector2 shadowRegion[lightPolyLength * polyLength];
+ // Shadow polygon from one point light.
+ Vector2 outline[polyLength];
+ Vector2 umbraMem[polyLength * lightPolyLength];
+ Vector2* umbra = umbraMem;
+
+ int umbraLength = 0;
+
+ // Validate input, receiver is always at z = 0 plane.
+ bool inputPolyPositionValid = true;
+ for (int i = 0; i < polyLength; i++) {
+ if (poly[i].z >= lightPoly[0].z) {
+ inputPolyPositionValid = false;
+ ALOGW("polygon above the light");
+ break;
+ }
+ }
+
+ // If the caster's position is invalid, don't draw anything.
+ if (!inputPolyPositionValid) {
+ return;
+ }
+
+ // Calculate the umbra polygon based on intersections of all outlines
+ int k = 0;
+ for (int j = 0; j < lightPolyLength; j++) {
+ int m = 0;
+ for (int i = 0; i < polyLength; i++) {
+ // After validating the input, deltaZ is guaranteed to be positive.
+ float deltaZ = lightPoly[j].z - poly[i].z;
+ float ratioZ = lightPoly[j].z / deltaZ;
+ float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
+ float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
+
+ Vector2 newPoint = {x, y};
+ shadowRegion[k] = newPoint;
+ outline[m] = newPoint;
+
+ k++;
+ m++;
+ }
+
+ // For the first light polygon's vertex, use the outline as the umbra.
+ // Later on, use the intersection of the outline and existing umbra.
+ if (umbraLength == 0) {
+ for (int i = 0; i < polyLength; i++) {
+ umbra[i] = outline[i];
+ }
+ umbraLength = polyLength;
+ } else {
+ int col = ((j * 255) / lightPolyLength);
+ umbraLength = intersection(outline, polyLength, umbra, umbraLength);
+ if (umbraLength == 0) {
+ break;
+ }
+ }
+ }
+
+ // Generate the penumbra area using the hull of all shadow regions.
+ int shadowRegionLength = k;
+ Vector2 penumbra[k];
+ int penumbraLength = hull(shadowRegion, shadowRegionLength, penumbra);
+
+ Vector2 fakeUmbra[polyLength];
+ if (umbraLength < 3) {
+ // If there is no real umbra, make a fake one.
+ for (int i = 0; i < polyLength; i++) {
+ float deltaZ = lightCenter.z - poly[i].z;
+ float ratioZ = lightCenter.z / deltaZ;
+ float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x);
+ float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y);
+
+ fakeUmbra[i].x = x;
+ fakeUmbra[i].y = y;
+ }
+
+ // Shrink the centroid's shadow by 10%.
+ // TODO: Study the magic number of 10%.
+ Vector2 shadowCentroid =
+ ShadowTessellator::centroid2d(fakeUmbra, polyLength);
+ for (int i = 0; i < polyLength; i++) {
+ fakeUmbra[i] = shadowCentroid * (1.0f - SHADOW_SHRINK_SCALE) +
+ fakeUmbra[i] * SHADOW_SHRINK_SCALE;
+ }
+#if DEBUG_SHADOW
+ ALOGD("No real umbra make a fake one, centroid2d = %f , %f",
+ shadowCentroid.x, shadowCentroid.y);
+#endif
+ // Set the fake umbra, whose size is the same as the original polygon.
+ umbra = fakeUmbra;
+ umbraLength = polyLength;
+ }
+
+ generateTriangleStrip(isCasterOpaque, penumbra, penumbraLength, umbra,
+ umbraLength, poly, polyLength, shadowTriangleStrip);
+}
+
+/**
+ * Converts a polygon specified with CW vertices into an array of distance-from-centroid values.
+ *
+ * Returns false in error conditions
+ *
+ * @param poly Array of vertices. Note that these *must* be CW.
+ * @param polyLength The number of vertices in the polygon.
+ * @param polyCentroid The centroid of the polygon, from which rays will be cast
+ * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size
+ */
+bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid,
+ float* rayDist) {
+ const int rays = SHADOW_RAY_COUNT;
+ const float step = M_PI * 2 / rays;
+
+ const Vector2* lastVertex = &(poly[polyLength - 1]);
+ float startAngle = angle(*lastVertex, polyCentroid);
+
+ // Start with the ray that's closest to and less than startAngle
+ int rayIndex = floor((startAngle - EPSILON) / step);
+ rayIndex = (rayIndex + rays) % rays; // ensure positive
+
+ for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) {
+ /*
+ * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that
+ * intersect these will be those that are between the two angles from the centroid that the
+ * vertices define.
+ *
+ * Because the polygon vertices are stored clockwise, the closest ray with an angle
+ * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does
+ * not intersect with poly[i-1], poly[i].
+ */
+ float currentAngle = angle(poly[polyIndex], polyCentroid);
+
+ // find first ray that will not intersect the line segment poly[i-1] & poly[i]
+ int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step);
+ firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive
+
+ // Iterate through all rays that intersect with poly[i-1], poly[i] line segment.
+ // This may be 0 rays.
+ while (rayIndex != firstRayIndexOnNextSegment) {
+ float distanceToIntersect = rayIntersectPoints(polyCentroid,
+ cos(rayIndex * step),
+ sin(rayIndex * step),
+ *lastVertex, poly[polyIndex]);
+ if (distanceToIntersect < 0) {
+#if DEBUG_SHADOW
+ ALOGW("ERROR: convertPolyToRayDist failed");
+#endif
+ return false; // error case, abort
+ }
+
+ rayDist[rayIndex] = distanceToIntersect;
+
+ rayIndex = (rayIndex - 1 + rays) % rays;
+ }
+ lastVertex = &poly[polyIndex];
+ }
+
+ return true;
+}
+
+int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
+ const Vector3* poly, int polyLength, Vector2* occludedUmbra) {
+ // Occluded umbra area is computed as the intersection of the projected 2D
+ // poly and umbra.
+ for (int i = 0; i < polyLength; i++) {
+ occludedUmbra[i].x = poly[i].x;
+ occludedUmbra[i].y = poly[i].y;
+ }
+
+ // Both umbra and incoming polygon are guaranteed to be CW, so we can call
+ // intersection() directly.
+ return intersection(umbra, umbraLength,
+ occludedUmbra, polyLength);
+}
+
+#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f
+/**
+ * Generate a triangle strip given two convex polygons
+ *
+ * @param penumbra The outer polygon x,y vertexes
+ * @param penumbraLength The number of vertexes in the outer polygon
+ * @param umbra The inner outer polygon x,y vertexes
+ * @param umbraLength The number of vertexes in the inner polygon
+ * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
+ * empty strip if error.
+**/
+void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra,
+ int penumbraLength, const Vector2* umbra, int umbraLength,
+ const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip) {
+ const int rays = SHADOW_RAY_COUNT;
+ const int size = 2 * rays;
+ const float step = M_PI * 2 / rays;
+ // Centroid of the umbra.
+ Vector2 centroid = ShadowTessellator::centroid2d(umbra, umbraLength);
+#if DEBUG_SHADOW
+ ALOGD("centroid2d = %f , %f", centroid.x, centroid.y);
+#endif
+ // Intersection to the penumbra.
+ float penumbraDistPerRay[rays];
+ // Intersection to the umbra.
+ float umbraDistPerRay[rays];
+ // Intersection to the occluded umbra area.
+ float occludedUmbraDistPerRay[rays];
+
+ // convert CW polygons to ray distance encoding, aborting on conversion failure
+ if (!convertPolyToRayDist(umbra, umbraLength, centroid, umbraDistPerRay)) return;
+ if (!convertPolyToRayDist(penumbra, penumbraLength, centroid, penumbraDistPerRay)) return;
+
+ bool hasOccludedUmbraArea = false;
+ if (isCasterOpaque) {
+ Vector2 occludedUmbra[polyLength + umbraLength];
+ int occludedUmbraLength = calculateOccludedUmbra(umbra, umbraLength, poly, polyLength,
+ occludedUmbra);
+ // Make sure the centroid is inside the umbra, otherwise, fall back to the
+ // approach as if there is no occluded umbra area.
+ if (testPointInsidePolygon(centroid, occludedUmbra, occludedUmbraLength)) {
+ hasOccludedUmbraArea = true;
+ // Shrink the occluded umbra area to avoid pixel level artifacts.
+ for (int i = 0; i < occludedUmbraLength; i ++) {
+ occludedUmbra[i] = centroid + (occludedUmbra[i] - centroid) *
+ OCLLUDED_UMBRA_SHRINK_FACTOR;
+ }
+ if (!convertPolyToRayDist(occludedUmbra, occludedUmbraLength, centroid,
+ occludedUmbraDistPerRay)) {
+ return;
+ }
+ }
+ }
+
+ AlphaVertex* shadowVertices =
+ shadowTriangleStrip.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+
+ // NOTE: Shadow alpha values are transformed when stored in alphavertices,
+ // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
+ float transformedMaxAlpha = M_PI;
+
+ // Calculate the vertices (x, y, alpha) in the shadow area.
+ AlphaVertex centroidXYA;
+ AlphaVertex::set(&centroidXYA, centroid.x, centroid.y, transformedMaxAlpha);
+ for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+ float dx = cosf(step * rayIndex);
+ float dy = sinf(step * rayIndex);
+
+ // penumbra ring
+ float penumbraDistance = penumbraDistPerRay[rayIndex];
+ AlphaVertex::set(&shadowVertices[rayIndex],
+ dx * penumbraDistance + centroid.x,
+ dy * penumbraDistance + centroid.y, 0.0f);
+
+ // umbra ring
+ float umbraDistance = umbraDistPerRay[rayIndex];
+ AlphaVertex::set(&shadowVertices[rays + rayIndex],
+ dx * umbraDistance + centroid.x,
+ dy * umbraDistance + centroid.y,
+ transformedMaxAlpha);
+
+ // occluded umbra ring
+ if (hasOccludedUmbraArea) {
+ float occludedUmbraDistance = occludedUmbraDistPerRay[rayIndex];
+ AlphaVertex::set(&shadowVertices[2 * rays + rayIndex],
+ dx * occludedUmbraDistance + centroid.x,
+ dy * occludedUmbraDistance + centroid.y, transformedMaxAlpha);
+ } else {
+ // Put all vertices of the occluded umbra ring at the centroid.
+ shadowVertices[2 * rays + rayIndex] = centroidXYA;
+ }
+ }
+
+ shadowTriangleStrip.setMode(VertexBuffer::kTwoPolyRingShadow);
+ shadowTriangleStrip.computeBounds<AlphaVertex>();
+}
+
+/**
+ * This is only for experimental purpose.
+ * After intersections are calculated, we could smooth the polygon if needed.
+ * So far, we don't think it is more appealing yet.
+ *
+ * @param level The level of smoothness.
+ * @param rays The total number of rays.
+ * @param rayDist (In and Out) The distance for each ray.
+ *
+ */
+void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) {
+ for (int k = 0; k < level; k++) {
+ for (int i = 0; i < rays; i++) {
+ float p1 = rayDist[(rays - 1 + i) % rays];
+ float p2 = rayDist[i];
+ float p3 = rayDist[(i + 1) % rays];
+ rayDist[i] = (p1 + p2 * 2 + p3) / 4;
+ }
+ }
+}
+
+#if DEBUG_SHADOW
+
+#define TEST_POINT_NUMBER 128
+
+/**
+ * Calculate the bounds for generating random test points.
+ */
+void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound,
+ Vector2& upperBound ) {
+ if (inVector.x < lowerBound.x) {
+ lowerBound.x = inVector.x;
+ }
+
+ if (inVector.y < lowerBound.y) {
+ lowerBound.y = inVector.y;
+ }
+
+ if (inVector.x > upperBound.x) {
+ upperBound.x = inVector.x;
+ }
+
+ if (inVector.y > upperBound.y) {
+ upperBound.y = inVector.y;
+ }
+}
+
+/**
+ * For debug purpose, when things go wrong, dump the whole polygon data.
+ */
+static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName) {
+ for (int i = 0; i < polyLength; i++) {
+ ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+ }
+}
+
+/**
+ * Test whether the polygon is convex.
+ */
+bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength,
+ const char* name) {
+ bool isConvex = true;
+ for (int i = 0; i < polygonLength; i++) {
+ Vector2 start = polygon[i];
+ Vector2 middle = polygon[(i + 1) % polygonLength];
+ Vector2 end = polygon[(i + 2) % polygonLength];
+
+ double delta = (double(middle.x) - start.x) * (double(end.y) - start.y) -
+ (double(middle.y) - start.y) * (double(end.x) - start.x);
+ bool isCCWOrCoLinear = (delta >= EPSILON);
+
+ if (isCCWOrCoLinear) {
+ ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f),"
+ "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!",
+ name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta);
+ isConvex = false;
+ break;
+ }
+ }
+ return isConvex;
+}
+
+/**
+ * Test whether or not the polygon (intersection) is within the 2 input polygons.
+ * Using Marte Carlo method, we generate a random point, and if it is inside the
+ * intersection, then it must be inside both source polygons.
+ */
+void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length,
+ const Vector2* poly2, int poly2Length,
+ const Vector2* intersection, int intersectionLength) {
+ // Find the min and max of x and y.
+ Vector2 lowerBound(FLT_MAX, FLT_MAX);
+ Vector2 upperBound(-FLT_MAX, -FLT_MAX);
+ for (int i = 0; i < poly1Length; i++) {
+ updateBound(poly1[i], lowerBound, upperBound);
+ }
+ for (int i = 0; i < poly2Length; i++) {
+ updateBound(poly2[i], lowerBound, upperBound);
+ }
+
+ bool dumpPoly = false;
+ for (int k = 0; k < TEST_POINT_NUMBER; k++) {
+ // Generate a random point between minX, minY and maxX, maxY.
+ double randomX = rand() / double(RAND_MAX);
+ double randomY = rand() / double(RAND_MAX);
+
+ Vector2 testPoint;
+ testPoint.x = lowerBound.x + randomX * (upperBound.x - lowerBound.x);
+ testPoint.y = lowerBound.y + randomY * (upperBound.y - lowerBound.y);
+
+ // If the random point is in both poly 1 and 2, then it must be intersection.
+ if (testPointInsidePolygon(testPoint, intersection, intersectionLength)) {
+ if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) {
+ dumpPoly = true;
+ ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
+ " not in the poly1",
+ testPoint.x, testPoint.y);
+ }
+
+ if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) {
+ dumpPoly = true;
+ ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
+ " not in the poly2",
+ testPoint.x, testPoint.y);
+ }
+ }
+ }
+
+ if (dumpPoly) {
+ dumpPolygon(intersection, intersectionLength, "intersection");
+ for (int i = 1; i < intersectionLength; i++) {
+ Vector2 delta = intersection[i] - intersection[i - 1];
+ ALOGD("Intersetion i, %d Vs i-1 is delta %f", i, delta.lengthSquared());
+ }
+
+ dumpPolygon(poly1, poly1Length, "poly 1");
+ dumpPolygon(poly2, poly2Length, "poly 2");
+ }
+}
+#endif
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
new file mode 100644
index 0000000..d65ea89
--- /dev/null
+++ b/libs/hwui/SpotShadow.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HWUI_SPOT_SHADOW_H
+#define ANDROID_HWUI_SPOT_SHADOW_H
+
+#include "Debug.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+class SpotShadow {
+public:
+ static void createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+ int polyLength, const Vector3& lightCenter, float lightSize,
+ int lightVertexCount, VertexBuffer& retStrips);
+
+private:
+ static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
+ const Vector3* poly, int polyLength, Vector2* occludedUmbra);
+ static void computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly,
+ int lightPolyLength, const Vector3& lightCenter, const Vector3* poly,
+ int polyLength, VertexBuffer& retstrips);
+
+ static void computeLightPolygon(int points, const Vector3& lightCenter,
+ float size, Vector3* ret);
+
+ static void smoothPolygon(int level, int rays, float* rayDist);
+ static float rayIntersectPoly(const Vector2* poly, int polyLength,
+ const Vector2& point, float dx, float dy);
+
+ static void xsort(Vector2* points, int pointsLength);
+ static int hull(Vector2* points, int pointsLength, Vector2* retPoly);
+ static bool ccw(double ax, double ay, double bx, double by, double cx, double cy);
+ static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length);
+ static void sort(Vector2* poly, int polyLength, const Vector2& center);
+
+ static void swap(Vector2* points, int i, int j);
+ static void quicksortCirc(Vector2* points, int low, int high, const Vector2& center);
+ static void quicksortX(Vector2* points, int low, int high);
+
+ static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
+ static void makeClockwise(Vector2* polygon, int len);
+ static void reverse(Vector2* polygon, int len);
+ static inline bool lineIntersection(double x1, double y1, double x2, double y2,
+ double x3, double y3, double x4, double y4, Vector2& ret);
+
+ static void generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra,
+ int penumbraLength, const Vector2* umbra, int umbraLength,
+ const Vector3* poly, int polyLength, VertexBuffer& retstrips);
+
+#if DEBUG_SHADOW
+ // Verification utility function.
+ static bool testConvex(const Vector2* polygon, int polygonLength,
+ const char* name);
+ static void testIntersection(const Vector2* poly1, int poly1Length,
+ const Vector2* poly2, int poly2Length,
+ const Vector2* intersection, int intersectionLength);
+ static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound );
+#endif
+
+}; // SpotShadow
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SPOT_SHADOW_H
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
new file mode 100644
index 0000000..06c5ab4
--- /dev/null
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2014 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 "OpenGLRenderer"
+
+#include <SkCanvas.h>
+
+#include "StatefulBaseRenderer.h"
+
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+StatefulBaseRenderer::StatefulBaseRenderer()
+ : mDirtyClip(false)
+ , mWidth(-1)
+ , mHeight(-1)
+ , mSaveCount(1)
+ , mFirstSnapshot(new Snapshot)
+ , mSnapshot(mFirstSnapshot) {
+}
+
+void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
+ float clipRight, float clipBottom, const Vector3& lightCenter) {
+ mSnapshot = new Snapshot(mFirstSnapshot,
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
+ mSnapshot->fbo = getTargetFbo();
+ mSnapshot->setRelativeLightCenter(lightCenter);
+ mSaveCount = 1;
+}
+
+void StatefulBaseRenderer::setViewport(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ mFirstSnapshot->initializeViewport(width, height);
+ onViewportInitialized();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Save (layer)
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Non-virtual implementation of save, guaranteed to save without side-effects
+ *
+ * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save
+ * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
+ */
+int StatefulBaseRenderer::saveSnapshot(int flags) {
+ mSnapshot = new Snapshot(mSnapshot, flags);
+ return mSaveCount++;
+}
+
+int StatefulBaseRenderer::save(int flags) {
+ return saveSnapshot(flags);
+}
+
+/**
+ * Non-virtual implementation of restore, guaranteed to restore without side-effects.
+ */
+void StatefulBaseRenderer::restoreSnapshot() {
+ sp<Snapshot> toRemove = mSnapshot;
+ sp<Snapshot> toRestore = mSnapshot->previous;
+
+ mSaveCount--;
+ mSnapshot = toRestore;
+
+ // subclass handles restore implementation
+ onSnapshotRestored(*toRemove, *toRestore);
+}
+
+void StatefulBaseRenderer::restore() {
+ if (mSaveCount > 1) {
+ restoreSnapshot();
+ }
+}
+
+void StatefulBaseRenderer::restoreToCount(int saveCount) {
+ if (saveCount < 1) saveCount = 1;
+
+ while (mSaveCount > saveCount) {
+ restoreSnapshot();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Matrix
+///////////////////////////////////////////////////////////////////////////////
+
+void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const {
+ matrix->load(*(mSnapshot->transform));
+}
+
+void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
+ mSnapshot->transform->copyTo(*matrix);
+}
+
+void StatefulBaseRenderer::translate(float dx, float dy, float dz) {
+ mSnapshot->transform->translate(dx, dy, dz);
+}
+
+void StatefulBaseRenderer::rotate(float degrees) {
+ mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
+}
+
+void StatefulBaseRenderer::scale(float sx, float sy) {
+ mSnapshot->transform->scale(sx, sy, 1.0f);
+}
+
+void StatefulBaseRenderer::skew(float sx, float sy) {
+ mSnapshot->transform->skew(sx, sy);
+}
+
+void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
+ mSnapshot->transform->load(matrix);
+}
+
+void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
+ mSnapshot->transform->load(matrix);
+}
+
+void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
+ mat4 transform(matrix);
+ mSnapshot->transform->multiply(transform);
+}
+
+void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) {
+ mSnapshot->transform->multiply(matrix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clip
+///////////////////////////////////////////////////////////////////////////////
+
+bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ if (CC_LIKELY(currentTransform()->rectToRect())) {
+ mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
+ return !mSnapshot->clipRect->isEmpty();
+ }
+
+ SkPath path;
+ path.addRect(left, top, right, bottom);
+
+ return StatefulBaseRenderer::clipPath(&path, op);
+}
+
+bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
+ SkMatrix transform;
+ currentTransform()->copyTo(transform);
+
+ SkPath transformed;
+ path->transform(transform, &transformed);
+
+ SkRegion clip;
+ if (!mSnapshot->previous->clipRegion->isEmpty()) {
+ clip.setRegion(*mSnapshot->previous->clipRegion);
+ } else {
+ if (mSnapshot->previous == firstSnapshot()) {
+ clip.setRect(0, 0, getWidth(), getHeight());
+ } else {
+ Rect* bounds = mSnapshot->previous->clipRect;
+ clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
+ }
+ }
+
+ SkRegion region;
+ region.setPath(transformed, clip);
+
+ // region is the transformed input path, masked by the previous clip
+ mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
+ return !mSnapshot->clipRect->isEmpty();
+}
+
+bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
+ return !mSnapshot->clipRect->isEmpty();
+}
+
+void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
+ Rect bounds;
+ float radius;
+ if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
+
+ if (!MathUtils::isPositive(radius)) {
+ // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
+ clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op);
+ return;
+ }
+ setClippingRoundRect(allocator, bounds, radius);
+}
+
+void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius) {
+ mSnapshot->setClippingRoundRect(allocator, rect, radius);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Quick Rejection
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
+ * the clipRect. Does not modify the scissor.
+ *
+ * @param clipRequired if not null, will be set to true if element intersects clip
+ * (and wasn't rejected)
+ *
+ * @param snapOut if set, the geometry will be treated as having an AA ramp.
+ * See Rect::snapGeometryToPixelBoundaries()
+ */
+bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
+ float right, float bottom,
+ bool* clipRequired, bool* roundRectClipRequired,
+ bool snapOut) const {
+ if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ return true;
+ }
+
+ Rect r(left, top, right, bottom);
+ currentTransform()->mapRect(r);
+ r.snapGeometryToPixelBoundaries(snapOut);
+
+ Rect clipRect(*currentClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ if (!clipRect.intersects(r)) return true;
+
+ // clip is required if geometry intersects clip rect
+ if (clipRequired) {
+ *clipRequired = !clipRect.contains(r);
+ }
+
+ // round rect clip is required if RR clip exists, and geometry intersects its corners
+ if (roundRectClipRequired) {
+ *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
+ && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
+ }
+ return false;
+}
+
+/**
+ * Returns false if drawing won't be clipped out.
+ *
+ * Makes the decision conservatively, by rounding out the mapped rect before comparing with the
+ * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but
+ * rejection is still desired.
+ *
+ * This function, unlike quickRejectSetupScissor, should be used where precise geometry information
+ * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass
+ * rejection where precise rejection isn't important, or precise information isn't available.
+ */
+bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
+ float right, float bottom) const {
+ if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ return true;
+ }
+
+ Rect r(left, top, right, bottom);
+ currentTransform()->mapRect(r);
+ r.roundOut(); // rounded out to be conservative
+
+ Rect clipRect(*currentClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ if (!clipRect.intersects(r)) return true;
+
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
new file mode 100644
index 0000000..3957d36
--- /dev/null
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
+#define ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
+
+#include <utils/RefBase.h>
+
+#include "Renderer.h"
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Abstract Renderer subclass, which implements Canvas state methods.
+ *
+ * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
+ * Renderer interface. Drawing and recording classes that extend StatefulBaseRenderer will have
+ * different use cases:
+ *
+ * Drawing subclasses (i.e. OpenGLRenderer) can query attributes (such as transform) or hook into
+ * changes (e.g. save/restore) with minimal surface area for manipulating the stack itself.
+ *
+ * Recording subclasses (i.e. DisplayListRenderer) can both record and pass through state operations
+ * to StatefulBaseRenderer, so that not only will querying operations work (getClip/Matrix), but so
+ * that quickRejection can also be used.
+ */
+class StatefulBaseRenderer : public Renderer {
+public:
+ StatefulBaseRenderer();
+
+ virtual status_t prepare(bool opaque) {
+ return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
+ }
+
+ /**
+ * Initialize the first snapshot, computing the projection matrix, and stores the dimensions of
+ * the render target.
+ */
+ virtual void setViewport(int width, int height);
+ void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+ const Vector3& lightCenter);
+
+ // getters
+ bool hasRectToRectTransform() const {
+ return CC_LIKELY(currentTransform()->rectToRect());
+ }
+
+ // Save (layer)
+ virtual int getSaveCount() const { return mSaveCount; }
+ virtual int save(int flags);
+ virtual void restore();
+ virtual void restoreToCount(int saveCount);
+ //virtual int saveLayer(float left, float top, float right, float bottom,
+ // int alpha, SkXfermode::Mode mode, int flags);
+
+ // Matrix
+ void getMatrix(Matrix4* outMatrix) const;
+ virtual void getMatrix(SkMatrix* outMatrix) const;
+ virtual void translate(float dx, float dy, float dz = 0.0f);
+ virtual void rotate(float degrees);
+ virtual void scale(float sx, float sy);
+ virtual void skew(float sx, float sy);
+
+ virtual void setMatrix(const SkMatrix& matrix);
+ void setMatrix(const Matrix4& matrix); // internal only convenience method
+ virtual void concatMatrix(const SkMatrix& matrix);
+ void concatMatrix(const Matrix4& matrix); // internal only convenience method
+
+ // Clip
+ virtual const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
+
+ virtual bool quickRejectConservative(float left, float top, float right, float bottom) const;
+
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op);
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+ /**
+ * Does not support different clipping Ops (that is, every call to setClippingOutline is
+ * effectively using SkRegion::kReplaceOp)
+ *
+ * The clipping outline is independent from the regular clip.
+ */
+ void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
+ void setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius);
+
+protected:
+ const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
+
+ int getWidth() { return mWidth; }
+ int getHeight() { return mHeight; }
+
+ // Save
+ int saveSnapshot(int flags);
+ void restoreSnapshot();
+
+ // allows subclasses to control what value is stored in snapshot's fbo field in
+ // initializeSaveStack
+ virtual GLuint getTargetFbo() const {
+ return -1;
+ }
+
+ // Clip
+ bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
+ bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const;
+
+ /**
+ * Called just after a restore has occurred. The 'removed' snapshot popped from the stack,
+ * 'restored' snapshot has become the top/current.
+ *
+ * Subclasses can override this method to handle layer restoration
+ */
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {};
+
+ virtual void onViewportInitialized() {};
+
+ inline const Rect* currentClipRect() const {
+ return mSnapshot->clipRect;
+ }
+
+ inline const mat4* currentTransform() const {
+ return mSnapshot->transform;
+ }
+
+ inline const Snapshot* currentSnapshot() const {
+ return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get();
+ }
+
+ inline const Snapshot* firstSnapshot() const {
+ return mFirstSnapshot.get();
+ }
+
+ // indicites that the clip has been changed since the last time it was consumed
+ bool mDirtyClip;
+
+private:
+ // Dimensions of the drawing surface
+ int mWidth, mHeight;
+
+ // Number of saved states
+ int mSaveCount;
+
+ // Base state
+ sp<Snapshot> mFirstSnapshot;
+
+protected:
+ // Current state
+ // TODO: should become private, once hooks needed by OpenGLRenderer are added
+ sp<Snapshot> mSnapshot;
+}; // class StatefulBaseRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
index 2764523..8ce57db 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/Stencil.cpp
@@ -35,7 +35,7 @@ namespace uirenderer {
Stencil::Stencil(): mState(kDisabled) {
}
-uint32_t Stencil::getStencilSize() {
+uint8_t Stencil::getStencilSize() {
return STENCIL_BUFFER_SIZE;
}
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
index 83ad668..c5e5186 100644
--- a/libs/hwui/Stencil.h
+++ b/libs/hwui/Stencil.h
@@ -40,7 +40,7 @@ public:
* Returns the desired size for the stencil buffer. If the returned value
* is 0, then no stencil buffer is required.
*/
- ANDROID_API static uint32_t getStencilSize();
+ ANDROID_API static uint8_t getStencilSize();
/**
* Returns the smallest stencil format accepted by render buffers.
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
new file mode 100644
index 0000000..0a9aeb8
--- /dev/null
+++ b/libs/hwui/TessellationCache.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
+
+#include "Caches.h"
+#include "OpenGLRenderer.h"
+#include "PathTessellator.h"
+#include "ShadowTessellator.h"
+#include "TessellationCache.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::Description::Description()
+ : type(kNone)
+ , scaleX(1.0f)
+ , scaleY(1.0f)
+ , aa(false)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , strokeWidth(1.0f) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
+ : type(type)
+ , aa(paint.isAntiAlias())
+ , cap(paint.getStrokeCap())
+ , style(paint.getStyle())
+ , strokeWidth(paint.getStrokeWidth()) {
+ PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
+ memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t TessellationCache::Description::hash() const {
+ uint32_t hash = JenkinsHashMix(0, type);
+ hash = JenkinsHashMix(hash, aa);
+ hash = JenkinsHashMix(hash, cap);
+ hash = JenkinsHashMix(hash, style);
+ hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+ hash = JenkinsHashMix(hash, android::hash_type(scaleX));
+ hash = JenkinsHashMix(hash, android::hash_type(scaleY));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+ return JenkinsHashWhiten(hash);
+}
+
+void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
+ matrix->loadScale(scaleX, scaleY, 1.0f);
+ paint->setAntiAlias(aa);
+ paint->setStrokeCap(cap);
+ paint->setStyle(style);
+ paint->setStrokeWidth(strokeWidth);
+}
+
+TessellationCache::ShadowDescription::ShadowDescription()
+ : nodeKey(NULL) {
+ memset(&matrixData, 0, 16 * sizeof(float));
+}
+
+TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
+ : nodeKey(nodeKey) {
+ memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
+}
+
+hash_t TessellationCache::ShadowDescription::hash() const {
+ uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
+ return JenkinsHashWhiten(hash);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// General purpose tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
+public:
+ TessellationTask(Tessellator tessellator, const Description& description)
+ : tessellator(tessellator)
+ , description(description) {
+ }
+
+ ~TessellationTask() {}
+
+ Tessellator tessellator;
+ Description description;
+};
+
+class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
+public:
+ TessellationProcessor(Caches& caches)
+ : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
+ ~TessellationProcessor() {}
+
+ virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
+ TessellationTask* t = static_cast<TessellationTask*>(task.get());
+ ATRACE_NAME("shape tessellation");
+ VertexBuffer* buffer = t->tessellator(t->description);
+ t->setResult(buffer);
+ }
+};
+
+class TessellationCache::Buffer {
+public:
+ Buffer(const sp<Task<VertexBuffer*> >& task)
+ : mTask(task)
+ , mBuffer(NULL) {
+ }
+
+ ~Buffer() {
+ mTask.clear();
+ delete mBuffer;
+ }
+
+ unsigned int getSize() {
+ blockOnPrecache();
+ return mBuffer->getSize();
+ }
+
+ const VertexBuffer* getVertexBuffer() {
+ blockOnPrecache();
+ return mBuffer;
+ }
+
+private:
+ void blockOnPrecache() {
+ if (mTask != NULL) {
+ mBuffer = mTask->getResult();
+ LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache");
+ mTask.clear();
+ }
+ }
+ sp<Task<VertexBuffer*> > mTask;
+ VertexBuffer* mBuffer;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadow tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
+ const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius)
+ : drawTransform(*drawTransform)
+ , localClip(localClip)
+ , opaque(opaque)
+ , casterPerimeter(*casterPerimeter)
+ , transformXY(*transformXY)
+ , transformZ(*transformZ)
+ , lightCenter(lightCenter)
+ , lightRadius(lightRadius) {
+ }
+
+ ~ShadowTask() {
+ TessellationCache::vertexBuffer_pair_t* bufferPair = getResult();
+ delete bufferPair->getFirst();
+ delete bufferPair->getSecond();
+ delete bufferPair;
+ }
+
+ /* Note - we deep copy all task parameters, because *even though* pointers into Allocator
+ * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame,
+ * certain Allocators are destroyed before trim() is called to flush incomplete tasks.
+ *
+ * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks
+ * before tearning down single-frame LinearAllocators.
+ */
+ const Matrix4 drawTransform;
+ const Rect localClip;
+ bool opaque;
+ const SkPath casterPerimeter;
+ const Matrix4 transformXY;
+ const Matrix4 transformZ;
+ const Vector3 lightCenter;
+ const float lightRadius;
+};
+
+static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
+ // map z coordinate with true 3d matrix
+ point.z = transformZ->mapZ(point);
+
+ // map x,y coordinates with draw/Skia matrix
+ transformXY->mapPoint(point.x, point.y);
+}
+
+static void tessellateShadows(
+ const Matrix4* drawTransform, const Rect* localClip,
+ bool isCasterOpaque, const SkPath* casterPerimeter,
+ const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
+ const Vector3& lightCenter, float lightRadius,
+ VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
+
+ // tessellate caster outline into a 2d polygon
+ Vector<Vertex> casterVertices2d;
+ const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
+ PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
+ casterRefinementThresholdSquared, casterVertices2d);
+ if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
+ ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
+ casterVertices2d.size());
+ }
+
+ if (casterVertices2d.size() == 0) return;
+
+ // map 2d caster poly into 3d
+ const int casterVertexCount = casterVertices2d.size();
+ Vector3 casterPolygon[casterVertexCount];
+ float minZ = FLT_MAX;
+ float maxZ = -FLT_MAX;
+ for (int i = 0; i < casterVertexCount; i++) {
+ const Vertex& point2d = casterVertices2d[i];
+ casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
+ mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
+ minZ = fmin(minZ, casterPolygon[i].z);
+ maxZ = fmax(maxZ, casterPolygon[i].z);
+ }
+
+ // map the centroid of the caster into 3d
+ Vector2 centroid = ShadowTessellator::centroid2d(
+ reinterpret_cast<const Vector2*>(casterVertices2d.array()),
+ casterVertexCount);
+ Vector3 centroid3d = {centroid.x, centroid.y, 0};
+ mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
+
+ // if the caster intersects the z=0 plane, lift it in Z so it doesn't
+ if (minZ < SHADOW_MIN_CASTER_Z) {
+ float casterLift = SHADOW_MIN_CASTER_Z - minZ;
+ for (int i = 0; i < casterVertexCount; i++) {
+ casterPolygon[i].z += casterLift;
+ }
+ centroid3d.z += casterLift;
+ }
+
+ // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
+ // We only have ortho projection, so we can just ignore the Z in caster for
+ // simple rejection calculation.
+ Rect casterBounds(casterPerimeter->getBounds());
+ casterTransformXY->mapRect(casterBounds);
+
+ // actual tessellation of both shadows
+ ShadowTessellator::tessellateAmbientShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
+ casterBounds, *localClip, maxZ, ambientBuffer);
+
+ ShadowTessellator::tessellateSpotShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount,
+ *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
+ spotBuffer);
+}
+
+class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowProcessor(Caches& caches)
+ : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
+ ~ShadowProcessor() {}
+
+ virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) {
+ ShadowTask* t = static_cast<ShadowTask*>(task.get());
+ ATRACE_NAME("shadow tessellation");
+
+ VertexBuffer* ambientBuffer = new VertexBuffer;
+ VertexBuffer* spotBuffer = new VertexBuffer;
+ tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
+ &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
+ *ambientBuffer, *spotBuffer);
+
+ t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::TessellationCache()
+ : mSize(0)
+ , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE))
+ , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
+ , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+ }
+
+ mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
+ mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
+ mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+TessellationCache::~TessellationCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TessellationCache::getSize() {
+ LruCache<Description, Buffer*>::Iterator iter(mCache);
+ uint32_t size = 0;
+ while (iter.next()) {
+ size += iter.value()->getSize();
+ }
+ return size;
+}
+
+uint32_t TessellationCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TessellationCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+
+void TessellationCache::trim() {
+ uint32_t size = getSize();
+ while (size > mMaxSize) {
+ size -= mCache.peekOldestValue()->getSize();
+ mCache.removeOldest();
+ }
+ mShadowCache.clear();
+}
+
+void TessellationCache::clear() {
+ mCache.clear();
+ mShadowCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::BufferRemovedListener::operator()(Description& description,
+ Buffer*& buffer) {
+ delete buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadows
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+
+ sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
+ casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
+ if (mShadowProcessor == NULL) {
+ mShadowProcessor = new ShadowProcessor(Caches::getInstance());
+ }
+ mShadowProcessor->add(task);
+
+ task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache
+ mShadowCache.put(key, task.get());
+}
+
+void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+ ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ if (!task) {
+ precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
+ transformXY, transformZ, lightCenter, lightRadius);
+ task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ }
+ LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached");
+ outBuffers = *(task->getResult());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Tessellation precaching
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
+ const Description& entry, Tessellator tessellator) {
+ Buffer* buffer = mCache.get(entry);
+ if (!buffer) {
+ // not cached, enqueue a task to fill the buffer
+ sp<TessellationTask> task = new TessellationTask(tessellator, entry);
+ buffer = new Buffer(task);
+
+ if (mProcessor == NULL) {
+ mProcessor = new TessellationProcessor(Caches::getInstance());
+ }
+ mProcessor->add(task);
+ mCache.put(entry, buffer);
+ }
+ return buffer;
+}
+
+static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
+ const SkPath& path) {
+ Matrix4 matrix;
+ SkPaint paint;
+ description.setupMatrixAndPaint(&matrix, &paint);
+ VertexBuffer* buffer = new VertexBuffer();
+ PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
+ return buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect
+///////////////////////////////////////////////////////////////////////////////
+
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
+ SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
+ description.shape.roundRect.height);
+ float rx = description.shape.roundRect.rx;
+ float ry = description.shape.roundRect.ry;
+ if (description.style == SkPaint::kStrokeAndFill_Style) {
+ float outset = description.strokeWidth / 2;
+ rect.outset(outset, outset);
+ rx += outset;
+ ry += outset;
+ }
+ SkPath path;
+ path.addRoundRect(rect, rx, ry);
+ return tessellatePath(description, path);
+}
+
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
+ const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ Description entry(Description::kRoundRect, transform, paint);
+ entry.shape.roundRect.width = width;
+ entry.shape.roundRect.height = height;
+ entry.shape.roundRect.rx = rx;
+ entry.shape.roundRect.ry = ry;
+ return getOrCreateBuffer(entry, &tessellateRoundRect);
+}
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
new file mode 100644
index 0000000..688a699
--- /dev/null
+++ b/libs/hwui/TessellationCache.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H
+#define ANDROID_HWUI_TESSELLATION_CACHE_H
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/Vector.h>
+
+#include "Debug.h"
+#include "utils/Macros.h"
+#include "utils/Pair.h"
+#include "VertexBuffer.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+class SkPath;
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache {
+public:
+ typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
+
+ struct Description {
+ DESCRIPTION_TYPE(Description);
+ enum Type {
+ kNone,
+ kRoundRect,
+ };
+
+ Type type;
+ float scaleX;
+ float scaleY;
+ bool aa;
+ SkPaint::Cap cap;
+ SkPaint::Style style;
+ float strokeWidth;
+ union Shape {
+ struct RoundRect {
+ float width;
+ float height;
+ float rx;
+ float ry;
+ } roundRect;
+ } shape;
+
+ Description();
+ Description(Type type, const Matrix4& transform, const SkPaint& paint);
+ hash_t hash() const;
+ void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
+ };
+
+ struct ShadowDescription {
+ DESCRIPTION_TYPE(ShadowDescription);
+ const void* nodeKey;
+ float matrixData[16];
+
+ ShadowDescription();
+ ShadowDescription(const void* nodeKey, const Matrix4* drawTransform);
+ hash_t hash() const;
+ };
+
+ TessellationCache();
+ ~TessellationCache();
+
+ /**
+ * Clears the cache. This causes all TessellationBuffers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+ /**
+ * Trims the contents of the cache, removing items until it's under its
+ * specified limit.
+ *
+ * Trimming is used for caches that support pre-caching from a worker
+ * thread. During pre-caching the maximum limit of the cache can be
+ * exceeded for the duration of the frame. It is therefore required to
+ * trim the cache at the end of the frame to keep the total amount of
+ * memory used under control.
+ *
+ * Also removes transient Shadow VertexBuffers, which aren't cached between frames.
+ */
+ void trim();
+
+ // TODO: precache/get for Oval, Lines, Points, etc.
+
+ void precacheRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ getRoundRectBuffer(transform, paint, width, height, rx, ry);
+ }
+ const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry);
+
+ void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius);
+
+ void getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius,
+ vertexBuffer_pair_t& outBuffers);
+
+private:
+ class Buffer;
+ class TessellationTask;
+ class TessellationProcessor;
+
+ typedef VertexBuffer* (*Tessellator)(const Description&);
+
+ Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint,
+ float width, float height);
+ Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry);
+
+ Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator);
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+
+ bool mDebugEnabled;
+
+ mutable Mutex mLock;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // General tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<VertexBuffer*> > mProcessor;
+ LruCache<Description, Buffer*> mCache;
+ class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> {
+ void operator()(Description& description, Buffer*& buffer);
+ };
+ BufferRemovedListener mBufferRemovedListener;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Shadow tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<vertexBuffer_pair_t*> > mShadowProcessor;
+
+ // holds a pointer, and implicit strong ref to each shadow task of the frame
+ LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache;
+ class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> {
+ void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) {
+ bufferPairTask->decStrong(NULL);
+ }
+ };
+ BufferPairRemovedListener mBufferPairRemovedListener;
+
+}; // class TessellationCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 0b2c130..4eec462 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -147,7 +147,7 @@ void TextDropShadowCache::setMaxSize(uint32_t maxSize) {
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) {
+void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) {
if (texture) {
mSize -= texture->bitmapSize;
@@ -168,7 +168,7 @@ void TextDropShadowCache::clear() {
mCache.clear();
}
-ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len,
int numGlyphs, float radius, const float* positions) {
ShadowText entry(paint, radius, len, text, positions);
ShadowTexture* texture = mCache.get(entry);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 04d7357..54b930b 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -38,7 +38,7 @@ struct ShadowText {
}
// len is the number of bytes in text
- ShadowText(SkPaint* paint, float radius, uint32_t len, const char* srcText,
+ ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText,
const float* positions):
len(len), radius(radius), positions(positions) {
// TODO: Propagate this through the API, we should not cast here
@@ -135,7 +135,7 @@ public:
*/
void operator()(ShadowText& text, ShadowTexture*& texture);
- ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
+ ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len,
int numGlyphs, float radius, const float* positions);
/**
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 7923ce7..e783905 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -25,14 +25,14 @@ namespace android {
namespace uirenderer {
Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
}
Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index d48ec59..d5601f8 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -94,6 +94,12 @@ public:
*/
const UvMapper* uvMapper;
+ /**
+ * Whether or not the Texture is marked in use and thus not evictable for
+ * the current frame. This is reset at the start of a new frame.
+ */
+ bool isInUse;
+
private:
/**
* Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2f102e0..3b8a9a4 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
#include <GLES2/gl2.h>
@@ -34,7 +35,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
TextureCache::TextureCache():
- mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
char property[PROPERTY_VALUE_MAX];
@@ -58,7 +59,7 @@ TextureCache::TextureCache():
}
TextureCache::TextureCache(uint32_t maxByteSize):
- mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
init();
}
@@ -103,7 +104,7 @@ void TextureCache::setFlushRate(float flushRate) {
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
+void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) {
// This will be called already locked
if (texture) {
mSize -= texture->bitmapSize;
@@ -121,47 +122,93 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
// Caching
///////////////////////////////////////////////////////////////////////////////
-Texture* TextureCache::get(SkBitmap* bitmap) {
- Texture* texture = mCache.get(bitmap);
+void TextureCache::resetMarkInUse() {
+ LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache);
+ while (iter.next()) {
+ iter.value()->isInUse = false;
+ }
+}
+
+bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) {
+ if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
+ ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
+ bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
+ return false;
+ }
+ return true;
+}
+
+// Returns a prepared Texture* that either is already in the cache or can fit
+// in the cache (and is thus added to the cache)
+Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
+ Texture* texture = mCache.get(bitmap->pixelRef());
if (!texture) {
- if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
- ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
- bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
+ if (!canMakeTextureFromBitmap(bitmap)) {
return NULL;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
+ bool canCache = size < mMaxSize;
// Don't even try to cache a bitmap that's bigger than the cache
- if (size < mMaxSize) {
- while (mSize + size > mMaxSize) {
+ while (canCache && mSize + size > mMaxSize) {
+ Texture* oldest = mCache.peekOldestValue();
+ if (oldest && !oldest->isInUse) {
mCache.removeOldest();
+ } else {
+ canCache = false;
}
}
- texture = new Texture();
- texture->bitmapSize = size;
- generateTexture(bitmap, texture, false);
+ if (canCache) {
+ texture = new Texture();
+ texture->bitmapSize = size;
+ generateTexture(bitmap, texture, false);
- if (size < mMaxSize) {
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
bitmap, texture->id, size, mSize);
if (mDebugEnabled) {
ALOGD("Texture created, size = %d", size);
}
- mCache.put(bitmap, texture);
- } else {
- texture->cleanup = true;
+ mCache.put(bitmap->pixelRef(), texture);
}
- } else if (bitmap->getGenerationID() != texture->generation) {
+ } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
+ // Texture was in the cache but is dirty, re-upload
+ // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
generateTexture(bitmap, texture, true);
}
return texture;
}
-Texture* TextureCache::getTransient(SkBitmap* bitmap) {
+bool TextureCache::prefetchAndMarkInUse(const SkBitmap* bitmap) {
+ Texture* texture = getCachedTexture(bitmap);
+ if (texture) {
+ texture->isInUse = true;
+ }
+ return texture;
+}
+
+Texture* TextureCache::get(const SkBitmap* bitmap) {
+ Texture* texture = getCachedTexture(bitmap);
+
+ if (!texture) {
+ if (!canMakeTextureFromBitmap(bitmap)) {
+ return NULL;
+ }
+
+ const uint32_t size = bitmap->rowBytes() * bitmap->height();
+ texture = new Texture();
+ texture->bitmapSize = size;
+ generateTexture(bitmap, texture, false);
+ texture->cleanup = true;
+ }
+
+ return texture;
+}
+
+Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
Texture* texture = new Texture();
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
texture->cleanup = true;
@@ -171,11 +218,11 @@ Texture* TextureCache::getTransient(SkBitmap* bitmap) {
return texture;
}
-void TextureCache::remove(SkBitmap* bitmap) {
- mCache.remove(bitmap);
+void TextureCache::remove(const SkBitmap* bitmap) {
+ mCache.remove(bitmap->pixelRef());
}
-void TextureCache::removeDeferred(SkBitmap* bitmap) {
+void TextureCache::removeDeferred(const SkBitmap* bitmap) {
Mutex::Autolock _l(mLock);
mGarbage.push(bitmap);
}
@@ -184,8 +231,8 @@ void TextureCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- SkBitmap* bitmap = mGarbage.itemAt(i);
- mCache.remove(bitmap);
+ const SkBitmap* bitmap = mGarbage.itemAt(i);
+ mCache.remove(bitmap->pixelRef());
delete bitmap;
}
mGarbage.clear();
@@ -211,7 +258,7 @@ void TextureCache::flush() {
}
}
-void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
+void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
SkAutoLockPixels alp(*bitmap);
if (!bitmap->readyToDraw()) {
@@ -219,6 +266,8 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
return;
}
+ ATRACE_CALL();
+
// We could also enable mipmapping if both bitmap dimensions are powers
// of 2 but we'd have to deal with size changes. Let's keep this simple
const bool canMipMap = Extensions::getInstance().hasNPot();
@@ -239,20 +288,20 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
Caches::getInstance().bindTexture(texture->id);
- switch (bitmap->getConfig()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
@@ -260,14 +309,14 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
// decoding happened
texture->blend = !bitmap->isOpaque();
break;
- case SkBitmap::kARGB_4444_Config:
- case SkBitmap::kIndex8_Config:
+ case kARGB_4444_SkColorType:
+ case kIndex_8_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
texture->blend = !bitmap->isOpaque();
break;
default:
- ALOGW("Unsupported bitmap config: %d", bitmap->getConfig());
+ ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
break;
}
@@ -284,11 +333,10 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
}
}
-void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
+void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
uint32_t width, uint32_t height) {
SkBitmap rgbaBitmap;
- rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
- rgbaBitmap.allocPixels();
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 909d762..61db5b0 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -49,7 +49,7 @@ namespace uirenderer {
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
-class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> {
+class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> {
public:
TextureCache();
TextureCache(uint32_t maxByteSize);
@@ -59,28 +59,40 @@ public:
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(SkBitmap*& bitmap, Texture*& texture);
+ void operator()(const SkPixelRef*& pixelRef, Texture*& texture);
+
+ /**
+ * Resets all Textures to not be marked as in use
+ */
+ void resetMarkInUse();
+
+ /**
+ * Attempts to precache the SkBitmap. Returns true if a Texture was successfully
+ * acquired for the bitmap, false otherwise. If a Texture was acquired it is
+ * marked as in use.
+ */
+ bool prefetchAndMarkInUse(const SkBitmap* bitmap);
/**
* Returns the texture associated with the specified bitmap. If the texture
* cannot be found in the cache, a new texture is generated.
*/
- Texture* get(SkBitmap* bitmap);
+ Texture* get(const SkBitmap* bitmap);
/**
* Returns the texture associated with the specified bitmap. The generated
* texture is not kept in the cache. The caller must destroy the texture.
*/
- Texture* getTransient(SkBitmap* bitmap);
+ Texture* getTransient(const SkBitmap* bitmap);
/**
* Removes the texture associated with the specified bitmap.
* Upon remove the texture is freed.
*/
- void remove(SkBitmap* bitmap);
+ void remove(const SkBitmap* bitmap);
/**
* Removes the texture associated with the specified bitmap. This is meant
* to be called from threads that are not the EGL context thread.
*/
- void removeDeferred(SkBitmap* bitmap);
+ void removeDeferred(const SkBitmap* bitmap);
/**
* Process deferred removals.
*/
@@ -116,21 +128,26 @@ public:
void setFlushRate(float flushRate);
private:
+
+ bool canMakeTextureFromBitmap(const SkBitmap* bitmap);
+
+ Texture* getCachedTexture(const SkBitmap* bitmap);
+
/**
* Generates the texture from a bitmap into the specified texture structure.
*
* @param regenerate If true, the bitmap data is reuploaded into the texture, but
* no new texture is generated.
*/
- void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
+ void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
- void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
+ void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
- LruCache<SkBitmap*, Texture*> mCache;
+ LruCache<const SkPixelRef*, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
@@ -140,7 +157,7 @@ private:
bool mDebugEnabled;
- Vector<SkBitmap*> mGarbage;
+ Vector<const SkBitmap*> mGarbage;
mutable Mutex mLock;
}; // class TextureCache
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
new file mode 100644
index 0000000..331f157
--- /dev/null
+++ b/libs/hwui/TreeInfo.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef TREEINFO_H
+#define TREEINFO_H
+
+#include <string>
+
+#include <utils/Timers.h>
+
+#include "DamageAccumulator.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class BaseRenderNodeAnimator;
+class AnimationListener;
+class OpenGLRenderer;
+class RenderState;
+
+class AnimationHook {
+public:
+ virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0;
+protected:
+ ~AnimationHook() {}
+};
+
+class ErrorHandler {
+public:
+ virtual void onError(const std::string& message) = 0;
+protected:
+ ~ErrorHandler() {}
+};
+
+// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
+class TreeInfo {
+ PREVENT_COPY_AND_ASSIGN(TreeInfo);
+public:
+ enum TraversalMode {
+ // The full monty - sync, push, run animators, etc... Used by DrawFrameTask
+ // May only be used if both the UI thread and RT thread are blocked on the
+ // prepare
+ MODE_FULL,
+ // Run only what can be done safely on RT thread. Currently this only means
+ // animators, but potentially things like SurfaceTexture updates
+ // could be handled by this as well if there are no listeners
+ MODE_RT_ONLY,
+ };
+
+ explicit TreeInfo(TraversalMode mode, RenderState& renderState)
+ : mode(mode)
+ , frameTimeMs(0)
+ , animationHook(NULL)
+ , prepareTextures(mode == MODE_FULL)
+ , damageAccumulator(NULL)
+ , renderState(renderState)
+ , renderer(NULL)
+ , errorHandler(NULL)
+ {}
+
+ explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
+ : mode(mode)
+ , frameTimeMs(clone.frameTimeMs)
+ , animationHook(clone.animationHook)
+ , prepareTextures(mode == MODE_FULL)
+ , damageAccumulator(clone.damageAccumulator)
+ , renderState(clone.renderState)
+ , renderer(clone.renderer)
+ , errorHandler(clone.errorHandler)
+ {}
+
+ const TraversalMode mode;
+ nsecs_t frameTimeMs;
+ AnimationHook* animationHook;
+ // TODO: Remove this? Currently this is used to signal to stop preparing
+ // textures if we run out of cache space.
+ bool prepareTextures;
+
+ // Must not be null during actual usage
+ DamageAccumulator* damageAccumulator;
+ RenderState& renderState;
+ // The renderer that will be drawing the next frame. Use this to push any
+ // layer updates or similar. May be NULL.
+ OpenGLRenderer* renderer;
+ ErrorHandler* errorHandler;
+
+ struct Out {
+ Out()
+ : hasFunctors(false)
+ , hasAnimations(false)
+ , requiresUiRedraw(false)
+ , canDrawThisFrame(true)
+ {}
+ bool hasFunctors;
+ // This is only updated if evaluateAnimations is true
+ bool hasAnimations;
+ // This is set to true if there is an animation that RenderThread cannot
+ // animate itself, such as if hasFunctors is true
+ // This is only set if hasAnimations is true
+ bool requiresUiRedraw;
+ // This is set to true if draw() can be called this frame
+ // false means that we must delay until the next vsync pulse as frame
+ // production is outrunning consumption
+ // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+ // *OR* will post itself for the next vsync automatically, use this
+ // only to avoid calling draw()
+ bool canDrawThisFrame;
+ } out;
+
+ // TODO: Damage calculations
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TREEINFO_H */
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 497924e..2a9f01c 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -24,16 +24,13 @@ namespace uirenderer {
// Classes
///////////////////////////////////////////////////////////////////////////////
+// MUST BE A POD - this means no ctor or dtor!
struct Vector2 {
float x;
float y;
- Vector2() :
- x(0.0f), y(0.0f) {
- }
-
- Vector2(float px, float py) :
- x(px), y(py) {
+ float lengthSquared() const {
+ return x * x + y * y;
}
float length() const {
@@ -71,19 +68,19 @@ struct Vector2 {
}
Vector2 operator+(const Vector2& v) const {
- return Vector2(x + v.x, y + v.y);
+ return (Vector2){x + v.x, y + v.y};
}
Vector2 operator-(const Vector2& v) const {
- return Vector2(x - v.x, y - v.y);
+ return (Vector2){x - v.x, y - v.y};
}
Vector2 operator/(float s) const {
- return Vector2(x / s, y / s);
+ return (Vector2){x / s, y / s};
}
Vector2 operator*(float s) const {
- return Vector2(x * s, y * s);
+ return (Vector2){x * s, y * s};
}
void normalize() {
@@ -93,7 +90,7 @@ struct Vector2 {
}
Vector2 copyNormalized() const {
- Vector2 v(x, y);
+ Vector2 v = {x, y};
v.normalize();
return v;
}
@@ -107,11 +104,17 @@ struct Vector2 {
}
}; // class Vector2
-///////////////////////////////////////////////////////////////////////////////
-// Types
-///////////////////////////////////////////////////////////////////////////////
+// MUST BE A POD - this means no ctor or dtor!
+class Vector3 {
+public:
+ float x;
+ float y;
+ float z;
-typedef Vector2 vec2;
+ void dump() {
+ ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+ }
+};
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 790d4fc..4ff0b18 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -33,21 +33,22 @@ struct Vertex {
* Program::set()), and used to make geometry damage rect calculation conservative (see
* Rect::snapGeometryToPixelBoundaries())
*/
- static const float gGeometryFudgeFactor = 0.0656f;
+ static float GeometryFudgeFactor() { return 0.0656f; }
- float position[2];
+
+ float x, y;
static inline void set(Vertex* vertex, float x, float y) {
- vertex[0].position[0] = x;
- vertex[0].position[1] = y;
+ vertex[0].x = x;
+ vertex[0].y = y;
}
- static inline void set(Vertex* vertex, vec2 val) {
+ static inline void set(Vertex* vertex, Vector2 val) {
set(vertex, val.x, val.y);
}
static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) {
- set(vertex, src.position[0] + x, src.position[1] + y);
+ set(vertex, src.x + x, src.y + y);
}
}; // struct Vertex
@@ -56,19 +57,19 @@ struct Vertex {
* Simple structure to describe a vertex with a position and texture UV.
*/
struct TextureVertex {
- float position[2];
- float texture[2];
+ float x, y;
+ float u, v;
static inline void set(TextureVertex* vertex, float x, float y, float u, float v) {
- vertex[0].position[0] = x;
- vertex[0].position[1] = y;
- vertex[0].texture[0] = u;
- vertex[0].texture[1] = v;
+ vertex[0].x = x;
+ vertex[0].y = y;
+ vertex[0].u = u;
+ vertex[0].v = v;
}
static inline void setUV(TextureVertex* vertex, float u, float v) {
- vertex[0].texture[0] = u;
- vertex[0].texture[1] = v;
+ vertex[0].u = u;
+ vertex[0].v = v;
}
}; // struct TextureVertex
@@ -76,17 +77,17 @@ struct TextureVertex {
* Simple structure to describe a vertex with a position, texture UV and ARGB color.
*/
struct ColorTextureVertex : TextureVertex {
- float color[4];
+ float r, g, b, a;
static inline void set(ColorTextureVertex* vertex, float x, float y,
float u, float v, int color) {
TextureVertex::set(vertex, x, y, u, v);
const float a = ((color >> 24) & 0xff) / 255.0f;
- vertex[0].color[0] = a * ((color >> 16) & 0xff) / 255.0f;
- vertex[0].color[1] = a * ((color >> 8) & 0xff) / 255.0f;
- vertex[0].color[2] = a * ((color ) & 0xff) / 255.0f;
- vertex[0].color[3] = a;
+ vertex[0].r = a * ((color >> 16) & 0xff) / 255.0f;
+ vertex[0].g = a * ((color >> 8) & 0xff) / 255.0f;
+ vertex[0].b = a * ((color ) & 0xff) / 255.0f;
+ vertex[0].a = a;
}
}; // struct ColorTextureVertex
@@ -103,7 +104,7 @@ struct AlphaVertex : Vertex {
static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
float x, float y) {
- Vertex::set(vertex, src.position[0] + x, src.position[1] + y);
+ Vertex::set(vertex, src.x + x, src.y + y);
vertex[0].alpha = src.alpha;
}
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
new file mode 100644
index 0000000..3837f88
--- /dev/null
+++ b/libs/hwui/VertexBuffer.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
+#define ANDROID_HWUI_VERTEX_BUFFER_H
+
+
+namespace android {
+namespace uirenderer {
+
+class VertexBuffer {
+public:
+ enum Mode {
+ kStandard = 0,
+ kOnePolyRingShadow = 1,
+ kTwoPolyRingShadow = 2
+ };
+
+ VertexBuffer()
+ : mBuffer(0)
+ , mVertexCount(0)
+ , mByteCount(0)
+ , mMode(kStandard)
+ , mCleanupMethod(NULL)
+ {}
+
+ ~VertexBuffer() {
+ if (mCleanupMethod) mCleanupMethod(mBuffer);
+ }
+
+ /**
+ This should be the only method used by the Tessellator. Subsequent calls to
+ alloc will allocate space within the first allocation (useful if you want to
+ eventually allocate multiple regions within a single VertexBuffer, such as
+ with PathTessellator::tessellateLines())
+ */
+ template <class TYPE>
+ TYPE* alloc(int vertexCount) {
+ if (mVertexCount) {
+ TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
+ // already have allocated the buffer, re-allocate space within
+ if (mReallocBuffer != mBuffer) {
+ // not first re-allocation, leave space for degenerate triangles to separate strips
+ reallocBuffer += 2;
+ }
+ mReallocBuffer = reallocBuffer + vertexCount;
+ return reallocBuffer;
+ }
+ mVertexCount = vertexCount;
+ mByteCount = mVertexCount * sizeof(TYPE);
+ mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
+
+ mCleanupMethod = &(cleanup<TYPE>);
+
+ return (TYPE*)mBuffer;
+ }
+
+ template <class TYPE>
+ void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
+ int verticesToCopy = srcBuffer.getVertexCount();
+
+ TYPE* dst = alloc<TYPE>(verticesToCopy);
+ TYPE* src = (TYPE*)srcBuffer.getBuffer();
+
+ for (int i = 0; i < verticesToCopy; i++) {
+ TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
+ }
+ }
+
+ /**
+ * Brute force bounds computation, used only if the producer of this
+ * vertex buffer can't determine bounds more simply/efficiently
+ */
+ template <class TYPE>
+ void computeBounds(int vertexCount = 0) {
+ if (!mVertexCount) {
+ mBounds.setEmpty();
+ return;
+ }
+
+ // default: compute over every vertex
+ if (vertexCount == 0) vertexCount = mVertexCount;
+
+ TYPE* current = (TYPE*)mBuffer;
+ TYPE* end = current + vertexCount;
+ mBounds.set(current->x, current->y, current->x, current->y);
+ for (; current < end; current++) {
+ mBounds.expandToCoverVertex(current->x, current->y);
+ }
+ }
+
+ const void* getBuffer() const { return mBuffer; }
+ const Rect& getBounds() const { return mBounds; }
+ unsigned int getVertexCount() const { return mVertexCount; }
+ unsigned int getSize() const { return mByteCount; }
+ Mode getMode() const { return mMode; }
+
+ void setBounds(Rect bounds) { mBounds = bounds; }
+ void setMode(Mode mode) { mMode = mode; }
+
+ template <class TYPE>
+ void createDegenerateSeparators(int allocSize) {
+ TYPE* end = (TYPE*)mBuffer + mVertexCount;
+ for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
+ memcpy(degen, degen - 1, sizeof(TYPE));
+ memcpy(degen + 1, degen + 2, sizeof(TYPE));
+ }
+ }
+
+private:
+ template <class TYPE>
+ static void cleanup(void* buffer) {
+ delete[] (TYPE*)buffer;
+ }
+
+ Rect mBounds;
+ void* mBuffer;
+ unsigned int mVertexCount;
+ unsigned int mByteCount;
+ Mode mMode;
+
+ void* mReallocBuffer; // used for multi-allocation
+
+ void (*mCleanupMethod)(void*);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_VERTEX_BUFFER_H
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index d5f38b5..24ffb80 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -114,7 +114,7 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint3
mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
mCaches(Caches::getInstance()) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE);
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
@@ -143,7 +143,7 @@ void CacheTexture::init() {
// reset, then create a new remainder space to start again
reset();
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE);
}
void CacheTexture::releaseMesh() {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 61b38f8..4cc4f22 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -54,7 +54,7 @@ struct CacheBlock {
CacheBlock* mNext;
CacheBlock* mPrev;
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height):
mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
}
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index e69b0cc..ba878ba 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -45,7 +45,8 @@ Font::Font(FontRenderer* state, const Font::FontDescription& desc) :
mDeviceProperties = SkDeviceProperties::Make(SkDeviceProperties::Geometry::MakeDefault(), 1.0f);
}
-Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix) {
+Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix)
+ : mLookupTransform(rasterMatrix) {
mFontId = SkTypeface::UniqueID(paint->getTypeface());
mFontSize = paint->getTextSize();
mFlags = 0;
@@ -58,9 +59,6 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix)
mStrokeWidth = paint->getStrokeWidth();
mAntiAliasing = paint->isAntiAlias();
mHinting = paint->getHinting();
- mLookupTransform.reset();
- mLookupTransform[SkMatrix::kMScaleX] = roundf(fmaxf(1.0f, matrix[mat4::kScaleX]));
- mLookupTransform[SkMatrix::kMScaleY] = roundf(fmaxf(1.0f, matrix[mat4::kScaleY]));
if (!mLookupTransform.invert(&mInverseLookupTransform)) {
ALOGW("Could not query the inverse lookup transform for this font");
}
@@ -142,12 +140,12 @@ void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
if (bounds->bottom > nPenY) {
bounds->bottom = nPenY;
}
@@ -164,12 +162,12 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- float nPenX = x + glyph->mBitmapLeft;
- float nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
float width = (float) glyph->mBitmapWidth;
float height = (float) glyph->mBitmapHeight;
+ float nPenX = x + glyph->mBitmapLeft;
+ float nPenY = y + glyph->mBitmapTop + height;
+
float u1 = glyph->mBitmapMinU;
float u2 = glyph->mBitmapMaxU;
float v1 = glyph->mBitmapMinV;
@@ -183,10 +181,13 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ float width = (float) glyph->mBitmapWidth;
+ float height = (float) glyph->mBitmapHeight;
+
SkPoint p[4];
- p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
- p[1].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
- p[2].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
+ p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + height);
+ p[1].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop + height);
+ p[2].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop);
p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop);
mDescription.mInverseLookupTransform.mapPoints(p, 4);
@@ -279,7 +280,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float
glyph->mCacheTexture);
}
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bool precaching) {
CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit);
if (cachedGlyph) {
// Is the glyph still in texture cache?
@@ -295,14 +296,14 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre
return cachedGlyph;
}
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, const float* positions) {
render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
0, 0, NULL, positions);
}
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, const SkPath* path, float hOffset, float vOffset) {
if (numGlyphs == 0 || text == NULL || len == 0) {
return;
}
@@ -351,7 +352,7 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len
}
}
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds, const float* positions) {
if (bounds == NULL) {
ALOGE("No return rectangle provided to measure text");
@@ -361,7 +362,7 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le
render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
}
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
ATRACE_NAME("precacheText");
if (numGlyphs == 0 || text == NULL) {
@@ -382,7 +383,7 @@ void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
}
}
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
if (numGlyphs == 0 || text == NULL || len == 0) {
@@ -428,8 +429,8 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
}
}
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache,
- CachedGlyphInfo* glyph, bool precaching) {
+void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
+ SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) {
glyph->mAdvanceX = skiaGlyph.fAdvanceX;
glyph->mAdvanceY = skiaGlyph.fAdvanceY;
glyph->mBitmapLeft = skiaGlyph.fLeft;
@@ -472,7 +473,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCac
}
}
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching) {
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
@@ -486,7 +487,7 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
return newGlyph;
}
-Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix) {
+Font* Font::create(FontRenderer* state, const SkPaint* paint, const SkMatrix& matrix) {
FontDescription description(paint, matrix);
Font* font = state->mActiveFonts.get(description);
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 1e0e0c8..0f10464 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_FONT_H
#define ANDROID_HWUI_FONT_H
+#include <vector>
+
#include <utils/KeyedVector.h>
#include <SkScalar.h>
@@ -50,7 +52,7 @@ public:
};
struct FontDescription {
- FontDescription(const SkPaint* paint, const mat4& matrix);
+ FontDescription(const SkPaint* paint, const SkMatrix& matrix);
static int compare(const FontDescription& lhs, const FontDescription& rhs);
@@ -79,11 +81,11 @@ public:
~Font();
- void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, const float* positions);
- void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
+ void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, const SkPath* path, float hOffset, float vOffset);
const Font::FontDescription& getDescription() const {
return mDescription;
@@ -92,7 +94,7 @@ public:
/**
* Creates a new font associated with the specified font state.
*/
- static Font* create(FontRenderer* state, const SkPaint* paint, const mat4& matrix);
+ static Font* create(FontRenderer* state, const SkPaint* paint, const SkMatrix& matrix);
private:
friend class FontRenderer;
@@ -108,20 +110,20 @@ private:
MEASURE,
};
- void precache(SkPaint* paint, const char* text, int numGlyphs);
+ void precache(const SkPaint* paint, const char* text, int numGlyphs);
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds, const float* positions);
void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache,
- CachedGlyphInfo* glyph, bool precaching);
+ CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
+ SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching);
void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
@@ -138,7 +140,8 @@ private:
void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+ CachedGlyphInfo* getCachedGlyph(const SkPaint* paint, glyph_t textUnit,
+ bool precaching = false);
FontRenderer* mState;
FontDescription mDescription;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
new file mode 100644
index 0000000..e673b0d
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "CanvasContext.h"
+
+#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
+
+#include "EglManager.h"
+#include "RenderThread.h"
+#include "../Caches.h"
+#include "../DeferredLayerUpdater.h"
+#include "../RenderState.h"
+#include "../LayerRenderer.h"
+#include "../OpenGLRenderer.h"
+#include "../Stencil.h"
+
+#define TRIM_MEMORY_COMPLETE 80
+#define TRIM_MEMORY_UI_HIDDEN 20
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode)
+ : mRenderThread(thread)
+ , mEglManager(thread.eglManager())
+ , mEglSurface(EGL_NO_SURFACE)
+ , mDirtyRegionsEnabled(false)
+ , mOpaque(!translucent)
+ , mCanvas(NULL)
+ , mHaveNewSurface(false)
+ , mRootRenderNode(rootRenderNode) {
+}
+
+CanvasContext::~CanvasContext() {
+ destroyCanvasAndSurface();
+ mRenderThread.removeFrameCallback(this);
+}
+
+void CanvasContext::destroyCanvasAndSurface() {
+ if (mCanvas) {
+ delete mCanvas;
+ mCanvas = 0;
+ }
+ setSurface(NULL);
+}
+
+void CanvasContext::setSurface(ANativeWindow* window) {
+ mNativeWindow = window;
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (window) {
+ mEglSurface = mEglManager.createSurface(window);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
+ mHaveNewSurface = true;
+ makeCurrent();
+ } else {
+ mRenderThread.removeFrameCallback(this);
+ }
+}
+
+void CanvasContext::swapBuffers() {
+ mEglManager.swapBuffers(mEglSurface);
+ mHaveNewSurface = false;
+}
+
+void CanvasContext::requireSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "requireSurface() called but no surface set!");
+ makeCurrent();
+}
+
+bool CanvasContext::initialize(ANativeWindow* window) {
+ if (mCanvas) return false;
+ setSurface(window);
+ mCanvas = new OpenGLRenderer(mRenderThread.renderState());
+ mCanvas->initProperties();
+ return true;
+}
+
+void CanvasContext::updateSurface(ANativeWindow* window) {
+ setSurface(window);
+}
+
+void CanvasContext::pauseSurface(ANativeWindow* window) {
+ // TODO: For now we just need a fence, in the future suspend any animations
+ // and such to prevent from trying to render into this surface
+}
+
+void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ if (!mCanvas) return;
+ mCanvas->setViewport(width, height);
+ mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha);
+}
+
+void CanvasContext::setOpaque(bool opaque) {
+ mOpaque = opaque;
+}
+
+void CanvasContext::makeCurrent() {
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
+}
+
+void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
+ bool success = layerUpdater->apply();
+ LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
+ if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
+ mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
+ }
+}
+
+void CanvasContext::prepareTree(TreeInfo& info) {
+ mRenderThread.removeFrameCallback(this);
+
+ info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+ info.damageAccumulator = &mDamageAccumulator;
+ info.renderer = mCanvas;
+ mRootRenderNode->prepareTree(info);
+
+ int runningBehind = 0;
+ // TODO: This query is moderately expensive, investigate adding some sort
+ // of fast-path based off when we last called eglSwapBuffers() as well as
+ // last vsync time. Or something.
+ mNativeWindow->query(mNativeWindow.get(),
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+ info.out.canDrawThisFrame = !runningBehind;
+
+ if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+ if (!info.out.requiresUiRedraw) {
+ // If animationsNeedsRedraw is set don't bother posting for an RT anim
+ // as we will just end up fighting the UI thread.
+ mRenderThread.postFrameCallback(this);
+ }
+ }
+}
+
+void CanvasContext::stopDrawing() {
+ mRenderThread.removeFrameCallback(this);
+}
+
+void CanvasContext::notifyFramePending() {
+ ATRACE_CALL();
+ mRenderThread.pushBackFrameCallback(this);
+}
+
+void CanvasContext::draw() {
+ LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no canvas or surface!");
+
+ profiler().markPlaybackStart();
+
+ SkRect dirty;
+ mDamageAccumulator.finish(&dirty);
+
+ EGLint width, height;
+ mEglManager.beginFrame(mEglSurface, &width, &height);
+ if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
+ mCanvas->setViewport(width, height);
+ dirty.setEmpty();
+ } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
+ dirty.setEmpty();
+ } else {
+ if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
+ ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
+ SK_RECT_ARGS(dirty), width, height);
+ dirty.setEmpty();
+ }
+ profiler().unionDirty(&dirty);
+ }
+
+ status_t status;
+ if (!dirty.isEmpty()) {
+ status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+ dirty.fRight, dirty.fBottom, mOpaque);
+ } else {
+ status = mCanvas->prepare(mOpaque);
+ }
+
+ Rect outBounds;
+ status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
+
+ profiler().draw(mCanvas);
+
+ mCanvas->finish();
+
+ profiler().markPlaybackEnd();
+
+ if (status & DrawGlInfo::kStatusDrew) {
+ swapBuffers();
+ }
+
+ profiler().finishFrame();
+}
+
+// Called by choreographer to do an RT-driven animation
+void CanvasContext::doFrame() {
+ if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
+ return;
+ }
+
+ ATRACE_CALL();
+
+ profiler().startFrame();
+
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
+ prepareTree(info);
+ if (info.out.canDrawThisFrame) {
+ draw();
+ }
+}
+
+void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
+ ATRACE_CALL();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ thread.eglManager().requireGlContext();
+ mode = DrawGlInfo::kModeProcess;
+ }
+
+ thread.renderState().invokeFunctor(functor, mode, NULL);
+}
+
+bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ requireGlContext();
+ layer->apply();
+ return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
+}
+
+void CanvasContext::destroyHardwareResources() {
+ stopDrawing();
+ if (mEglManager.hasEglContext()) {
+ requireGlContext();
+ mRootRenderNode->destroyHardwareResources();
+ Caches::getInstance().flush(Caches::kFlushMode_Layers);
+ }
+}
+
+void CanvasContext::trimMemory(RenderThread& thread, int level) {
+ // No context means nothing to free
+ if (!thread.eglManager().hasEglContext()) return;
+
+ thread.eglManager().requireGlContext();
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ Caches::getInstance().flush(Caches::kFlushMode_Full);
+ thread.eglManager().destroy();
+ } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
+ Caches::getInstance().flush(Caches::kFlushMode_Moderate);
+ }
+}
+
+void CanvasContext::runWithGlContext(RenderTask* task) {
+ requireGlContext();
+ task->run();
+}
+
+Layer* CanvasContext::createRenderLayer(int width, int height) {
+ requireSurface();
+ return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height);
+}
+
+Layer* CanvasContext::createTextureLayer() {
+ requireSurface();
+ return LayerRenderer::createTextureLayer(mRenderThread.renderState());
+}
+
+void CanvasContext::requireGlContext() {
+ mEglManager.requireGlContext();
+}
+
+void CanvasContext::setTextureAtlas(RenderThread& thread,
+ const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
+ thread.eglManager().setTextureAtlas(buffer, map, mapSize);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
new file mode 100644
index 0000000..2a01027
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CANVASCONTEXT_H_
+#define CANVASCONTEXT_H_
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <SkBitmap.h>
+#include <utils/Functor.h>
+#include <utils/Vector.h>
+
+#include "../DamageAccumulator.h"
+#include "../DrawProfiler.h"
+#include "../RenderNode.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#define FUNCTOR_PROCESS_DELAY 4
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+
+namespace renderthread {
+
+class EglManager;
+
+// This per-renderer class manages the bridge between the global EGL context
+// and the render surface.
+class CanvasContext : public IFrameCallback {
+public:
+ CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode);
+ virtual ~CanvasContext();
+
+ bool initialize(ANativeWindow* window);
+ void updateSurface(ANativeWindow* window);
+ void pauseSurface(ANativeWindow* window);
+ void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
+ void setOpaque(bool opaque);
+ void makeCurrent();
+ void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
+ void prepareTree(TreeInfo& info);
+ void draw();
+ void destroyCanvasAndSurface();
+
+ // IFrameCallback, Chroreographer-driven frame callback entry point
+ virtual void doFrame();
+
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+
+ void destroyHardwareResources();
+ static void trimMemory(RenderThread& thread, int level);
+
+ static void invokeFunctor(RenderThread& thread, Functor* functor);
+
+ void runWithGlContext(RenderTask* task);
+
+ Layer* createRenderLayer(int width, int height);
+ Layer* createTextureLayer();
+
+ ANDROID_API static void setTextureAtlas(RenderThread& thread,
+ const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
+ void stopDrawing();
+ void notifyFramePending();
+
+ DrawProfiler& profiler() { return mProfiler; }
+
+private:
+ friend class RegisterFrameCallbackTask;
+
+ void setSurface(ANativeWindow* window);
+ void swapBuffers();
+ void requireSurface();
+
+ void requireGlContext();
+
+ RenderThread& mRenderThread;
+ EglManager& mEglManager;
+ sp<ANativeWindow> mNativeWindow;
+ EGLSurface mEglSurface;
+ bool mDirtyRegionsEnabled;
+
+ bool mOpaque;
+ OpenGLRenderer* mCanvas;
+ bool mHaveNewSurface;
+ DamageAccumulator mDamageAccumulator;
+
+ const sp<RenderNode> mRootRenderNode;
+
+ DrawProfiler mProfiler;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* CANVASCONTEXT_H_ */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
new file mode 100644
index 0000000..dd34e09
--- /dev/null
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 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 ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "DrawFrameTask.h"
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "../DeferredLayerUpdater.h"
+#include "../DisplayList.h"
+#include "../RenderNode.h"
+#include "CanvasContext.h"
+#include "RenderThread.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+DrawFrameTask::DrawFrameTask()
+ : mRenderThread(NULL)
+ , mContext(NULL)
+ , mFrameTimeNanos(0)
+ , mRecordDurationNanos(0)
+ , mDensity(1.0f) // safe enough default
+ , mSyncResult(kSync_OK) {
+}
+
+DrawFrameTask::~DrawFrameTask() {
+}
+
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+ mRenderThread = thread;
+ mContext = context;
+}
+
+void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
+ LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!");
+
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ if (mLayers[i].get() == layer) {
+ return;
+ }
+ }
+ mLayers.push_back(layer);
+}
+
+void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ if (mLayers[i].get() == layer) {
+ mLayers.erase(mLayers.begin() + i);
+ return;
+ }
+ }
+}
+
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
+ LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
+
+ mSyncResult = kSync_OK;
+ mFrameTimeNanos = frameTimeNanos;
+ mRecordDurationNanos = recordDurationNanos;
+ postAndWait();
+
+ // Reset the single-frame data
+ mFrameTimeNanos = 0;
+ mRecordDurationNanos = 0;
+
+ return mSyncResult;
+}
+
+void DrawFrameTask::postAndWait() {
+ AutoMutex _lock(mLock);
+ mRenderThread->queue(this);
+ mSignal.wait(mLock);
+}
+
+void DrawFrameTask::run() {
+ ATRACE_NAME("DrawFrame");
+
+ mContext->profiler().setDensity(mDensity);
+ mContext->profiler().startFrame(mRecordDurationNanos);
+
+ bool canUnblockUiThread;
+ bool canDrawThisFrame;
+ {
+ TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
+ canUnblockUiThread = syncFrameState(info);
+ canDrawThisFrame = info.out.canDrawThisFrame;
+ }
+
+ // Grab a copy of everything we need
+ CanvasContext* context = mContext;
+
+ // From this point on anything in "this" is *UNSAFE TO ACCESS*
+ if (canUnblockUiThread) {
+ unblockUiThread();
+ }
+
+ if (CC_LIKELY(canDrawThisFrame)) {
+ context->draw();
+ }
+
+ if (!canUnblockUiThread) {
+ unblockUiThread();
+ }
+}
+
+bool DrawFrameTask::syncFrameState(TreeInfo& info) {
+ ATRACE_CALL();
+ mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
+ mContext->makeCurrent();
+ Caches::getInstance().textureCache.resetMarkInUse();
+
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ mContext->processLayerUpdate(mLayers[i].get());
+ }
+ mLayers.clear();
+ mContext->prepareTree(info);
+
+ if (info.out.hasAnimations) {
+ if (info.out.requiresUiRedraw) {
+ mSyncResult |= kSync_UIRedrawRequired;
+ }
+ }
+ // If prepareTextures is false, we ran out of texture cache space
+ return info.prepareTextures;
+}
+
+void DrawFrameTask::unblockUiThread() {
+ AutoMutex _lock(mLock);
+ mSignal.signal();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
new file mode 100644
index 0000000..243cc5d
--- /dev/null
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef DRAWFRAMETASK_H
+#define DRAWFRAMETASK_H
+
+#include <vector>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
+#include "RenderTask.h"
+
+#include "../Rect.h"
+#include "../TreeInfo.h"
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class DisplayListData;
+class RenderNode;
+
+namespace renderthread {
+
+class CanvasContext;
+class RenderThread;
+
+enum SyncResult {
+ kSync_OK = 0,
+ kSync_UIRedrawRequired = 1 << 0,
+};
+
+/*
+ * This is a special Super Task. It is re-used multiple times by RenderProxy,
+ * and contains state (such as layer updaters & new DisplayListDatas) that is
+ * tracked across many frames not just a single frame.
+ * It is the sync-state task, and will kick off the post-sync draw
+ */
+class DrawFrameTask : public RenderTask {
+public:
+ DrawFrameTask();
+ virtual ~DrawFrameTask();
+
+ void setContext(RenderThread* thread, CanvasContext* context);
+
+ void pushLayerUpdate(DeferredLayerUpdater* layer);
+ void removeLayerUpdate(DeferredLayerUpdater* layer);
+
+ void setDensity(float density) { mDensity = density; }
+ int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
+
+ virtual void run();
+
+private:
+ void postAndWait();
+ bool syncFrameState(TreeInfo& info);
+ void unblockUiThread();
+
+ Mutex mLock;
+ Condition mSignal;
+
+ RenderThread* mRenderThread;
+ CanvasContext* mContext;
+
+ /*********************************************
+ * Single frame data
+ *********************************************/
+ nsecs_t mFrameTimeNanos;
+ nsecs_t mRecordDurationNanos;
+ float mDensity;
+ std::vector< sp<DeferredLayerUpdater> > mLayers;
+
+ int mSyncResult;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWFRAMETASK_H */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
new file mode 100644
index 0000000..05ca34d
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2014 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 "EglContext"
+
+#include "EglManager.h"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "../RenderState.h"
+#include "RenderThread.h"
+
+#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#define GLES_VERSION 2
+
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ERROR_CASE(x) case x: return #x;
+static const char* egl_error_str(EGLint error) {
+ switch (error) {
+ ERROR_CASE(EGL_SUCCESS)
+ ERROR_CASE(EGL_NOT_INITIALIZED)
+ ERROR_CASE(EGL_BAD_ACCESS)
+ ERROR_CASE(EGL_BAD_ALLOC)
+ ERROR_CASE(EGL_BAD_ATTRIBUTE)
+ ERROR_CASE(EGL_BAD_CONFIG)
+ ERROR_CASE(EGL_BAD_CONTEXT)
+ ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
+ ERROR_CASE(EGL_BAD_DISPLAY)
+ ERROR_CASE(EGL_BAD_MATCH)
+ ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
+ ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
+ ERROR_CASE(EGL_BAD_PARAMETER)
+ ERROR_CASE(EGL_BAD_SURFACE)
+ ERROR_CASE(EGL_CONTEXT_LOST)
+ default:
+ return "Unknown error";
+ }
+}
+static const char* egl_error_str() {
+ return egl_error_str(eglGetError());
+}
+
+static bool load_dirty_regions_property() {
+ char buf[PROPERTY_VALUE_MAX];
+ int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
+ return !strncasecmp("true", buf, len);
+}
+
+EglManager::EglManager(RenderThread& thread)
+ : mRenderThread(thread)
+ , mEglDisplay(EGL_NO_DISPLAY)
+ , mEglConfig(0)
+ , mEglContext(EGL_NO_CONTEXT)
+ , mPBufferSurface(EGL_NO_SURFACE)
+ , mRequestDirtyRegions(load_dirty_regions_property())
+ , mCurrentSurface(EGL_NO_SURFACE)
+ , mAtlasMap(NULL)
+ , mAtlasMapSize(0) {
+ mCanSetDirtyRegions = mRequestDirtyRegions;
+ ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+}
+
+void EglManager::initialize() {
+ if (hasEglContext()) return;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+
+ EGLint major, minor;
+ LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+ "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
+ ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+
+ loadConfig();
+ createContext();
+ usePBufferSurface();
+ mRenderThread.renderState().onGLContextCreated();
+ initAtlas();
+}
+
+bool EglManager::hasEglContext() {
+ return mEglDisplay != EGL_NO_DISPLAY;
+}
+
+void EglManager::requireGlContext() {
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context");
+
+ // We don't care *WHAT* surface is active, just that one is active to give
+ // us access to the GL context
+ if (mCurrentSurface == EGL_NO_SURFACE) {
+ usePBufferSurface();
+ }
+}
+
+void EglManager::loadConfig() {
+ EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint attribs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+ EGL_NONE
+ };
+
+ EGLint num_configs = 1;
+ if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
+ || num_configs != 1) {
+ // Failed to get a valid config
+ if (mCanSetDirtyRegions) {
+ ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ // Try again without dirty regions enabled
+ mCanSetDirtyRegions = false;
+ loadConfig();
+ } else {
+ LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+ }
+ }
+}
+
+void EglManager::createContext() {
+ EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+ mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
+ LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+ "Failed to create context, error = %s", egl_error_str());
+}
+
+void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+ int64_t* map, size_t mapSize) {
+
+ // Already initialized
+ if (mAtlasBuffer.get()) {
+ ALOGW("Multiple calls to setTextureAtlas!");
+ delete map;
+ return;
+ }
+
+ mAtlasBuffer = buffer;
+ mAtlasMap = map;
+ mAtlasMapSize = mapSize;
+
+ if (hasEglContext()) {
+ usePBufferSurface();
+ initAtlas();
+ }
+}
+
+void EglManager::initAtlas() {
+ if (mAtlasBuffer.get()) {
+ Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
+ }
+}
+
+void EglManager::usePBufferSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "usePBufferSurface() called on uninitialized GlobalContext!");
+
+ if (mPBufferSurface == EGL_NO_SURFACE) {
+ EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+ mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+ }
+ makeCurrent(mPBufferSurface);
+}
+
+EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
+ initialize();
+ EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Failed to create EGLSurface for window %p, eglErr = %s",
+ (void*) window, egl_error_str());
+ return surface;
+}
+
+void EglManager::destroySurface(EGLSurface surface) {
+ if (isCurrent(surface)) {
+ makeCurrent(EGL_NO_SURFACE);
+ }
+ if (!eglDestroySurface(mEglDisplay, surface)) {
+ ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+ }
+}
+
+void EglManager::destroy() {
+ if (mEglDisplay == EGL_NO_DISPLAY) return;
+
+ usePBufferSurface();
+ if (Caches::hasInstance()) {
+ Caches::getInstance().terminate();
+ }
+
+ eglDestroyContext(mEglDisplay, mEglContext);
+ eglDestroySurface(mEglDisplay, mPBufferSurface);
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ eglReleaseThread();
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+ mPBufferSurface = EGL_NO_SURFACE;
+ mCurrentSurface = EGL_NO_SURFACE;
+}
+
+bool EglManager::makeCurrent(EGLSurface surface) {
+ if (isCurrent(surface)) return false;
+
+ if (surface == EGL_NO_SURFACE) {
+ // If we are setting EGL_NO_SURFACE we don't care about any of the potential
+ // return errors, which would only happen if mEglDisplay had already been
+ // destroyed in which case the current context is already NO_CONTEXT
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
+ LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+ (void*)surface, egl_error_str());
+ }
+ mCurrentSurface = surface;
+ return true;
+}
+
+void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Tried to beginFrame on EGL_NO_SURFACE!");
+ makeCurrent(surface);
+ if (width) {
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
+ }
+ if (height) {
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+ }
+ eglBeginFrame(mEglDisplay, surface);
+}
+
+void EglManager::swapBuffers(EGLSurface surface) {
+ eglSwapBuffers(mEglDisplay, surface);
+ EGLint err = eglGetError();
+ LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+ "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
+}
+
+bool EglManager::enableDirtyRegions(EGLSurface surface) {
+ if (!mRequestDirtyRegions) return false;
+
+ if (mCanSetDirtyRegions) {
+ if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
+ ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+ (void*) surface, egl_error_str());
+ return false;
+ }
+ return true;
+ }
+ // Perhaps it is already enabled?
+ EGLint value;
+ if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
+ ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
+ (void*) surface, egl_error_str());
+ return false;
+ }
+ return value == EGL_BUFFER_PRESERVED;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
new file mode 100644
index 0000000..a844cfc
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef EGLMANAGER_H
+#define EGLMANAGER_H
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class contains the shared global EGL objects, such as EGLDisplay
+// and EGLConfig, which are re-used by CanvasContext
+class EglManager {
+public:
+ // Returns true on success, false on failure
+ void initialize();
+
+ bool hasEglContext();
+ void requireGlContext();
+
+ void usePBufferSurface();
+ EGLSurface createSurface(EGLNativeWindowType window);
+ void destroySurface(EGLSurface surface);
+
+ void destroy();
+
+ bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
+ // Returns true if the current surface changed, false if it was already current
+ bool makeCurrent(EGLSurface surface);
+ void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+ void swapBuffers(EGLSurface surface);
+
+ bool enableDirtyRegions(EGLSurface surface);
+
+ void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
+private:
+ friend class RenderThread;
+
+ EglManager(RenderThread& thread);
+ // EglContext is never destroyed, method is purposely not implemented
+ ~EglManager();
+
+ void loadConfig();
+ void createContext();
+ void initAtlas();
+
+ RenderThread& mRenderThread;
+
+ EGLDisplay mEglDisplay;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+ EGLSurface mPBufferSurface;
+
+ const bool mRequestDirtyRegions;
+ bool mCanSetDirtyRegions;
+
+ EGLSurface mCurrentSurface;
+
+ sp<GraphicBuffer> mAtlasBuffer;
+ int64_t* mAtlasMap;
+ size_t mAtlasMapSize;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* EGLMANAGER_H */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
new file mode 100644
index 0000000..d9b96f6
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "RenderProxy.h"
+
+#include "CanvasContext.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#include "../DeferredLayerUpdater.h"
+#include "../DisplayList.h"
+#include "../LayerRenderer.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ARGS(method) method ## Args
+
+#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,)
+#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
+#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
+#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
+#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
+#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,)
+#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,)
+#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,)
+#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
+ typedef struct { \
+ a1; a2; a3; a4; a5; a6; a7; a8; \
+ } ARGS(name); \
+ static void* Bridge_ ## name(ARGS(name)* args)
+
+#define SETUP_TASK(method) \
+ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
+ "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
+ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
+ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
+ ARGS(method) *args = (ARGS(method) *) task->payload()
+
+CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) {
+ return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode);
+}
+
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
+ : mRenderThread(RenderThread::getInstance())
+ , mContext(0) {
+ SETUP_TASK(createContext);
+ args->translucent = translucent;
+ args->rootRenderNode = rootRenderNode;
+ args->thread = &mRenderThread;
+ mContext = (CanvasContext*) postAndWait(task);
+ mDrawFrameTask.setContext(&mRenderThread, mContext);
+}
+
+RenderProxy::~RenderProxy() {
+ destroyContext();
+}
+
+CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
+ delete args->context;
+ return NULL;
+}
+
+void RenderProxy::destroyContext() {
+ if (mContext) {
+ SETUP_TASK(destroyContext);
+ args->context = mContext;
+ mContext = 0;
+ mDrawFrameTask.setContext(NULL, NULL);
+ // This is also a fence as we need to be certain that there are no
+ // outstanding mDrawFrame tasks posted before it is destroyed
+ postAndWait(task);
+ }
+}
+
+CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
+ args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
+ return NULL;
+}
+
+void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
+ SETUP_TASK(setFrameInterval);
+ args->thread = &mRenderThread;
+ args->frameIntervalNanos = frameIntervalNanos;
+ post(task);
+}
+
+CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
+ bool needsRedraw = false;
+ if (Caches::hasInstance()) {
+ needsRedraw = Caches::getInstance().initProperties();
+ }
+ if (args->context->profiler().loadSystemProperties()) {
+ needsRedraw = true;
+ }
+ return (void*) needsRedraw;
+}
+
+bool RenderProxy::loadSystemProperties() {
+ SETUP_TASK(loadSystemProperties);
+ args->context = mContext;
+ return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
+ return (void*) args->context->initialize(args->window);
+}
+
+bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
+ SETUP_TASK(initialize);
+ args->context = mContext;
+ args->window = window.get();
+ return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
+ args->context->updateSurface(args->window);
+ return NULL;
+}
+
+void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
+ SETUP_TASK(updateSurface);
+ args->context = mContext;
+ args->window = window.get();
+ postAndWait(task);
+}
+
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
+ args->context->pauseSurface(args->window);
+ return NULL;
+}
+
+void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
+ SETUP_TASK(pauseSurface);
+ args->context = mContext;
+ args->window = window.get();
+ postAndWait(task);
+}
+
+CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
+ Vector3 lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius,
+ args->ambientShadowAlpha, args->spotShadowAlpha);
+ return NULL;
+}
+
+void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ SETUP_TASK(setup);
+ args->context = mContext;
+ args->width = width;
+ args->height = height;
+ args->lightCenter = lightCenter;
+ args->lightRadius = lightRadius;
+ args->ambientShadowAlpha = ambientShadowAlpha;
+ args->spotShadowAlpha = spotShadowAlpha;
+ post(task);
+}
+
+CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
+ args->context->setOpaque(args->opaque);
+ return NULL;
+}
+
+void RenderProxy::setOpaque(bool opaque) {
+ SETUP_TASK(setOpaque);
+ args->context = mContext;
+ args->opaque = opaque;
+ post(task);
+}
+
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+ float density) {
+ mDrawFrameTask.setDensity(density);
+ return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
+}
+
+CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
+ args->context->destroyCanvasAndSurface();
+ return NULL;
+}
+
+void RenderProxy::destroyCanvasAndSurface() {
+ SETUP_TASK(destroyCanvasAndSurface);
+ args->context = mContext;
+ // destroyCanvasAndSurface() needs a fence as when it returns the
+ // underlying BufferQueue is going to be released from under
+ // the render thread.
+ postAndWait(task);
+}
+
+CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) {
+ CanvasContext::invokeFunctor(*args->thread, args->functor);
+ return NULL;
+}
+
+void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
+ ATRACE_CALL();
+ RenderThread& thread = RenderThread::getInstance();
+ SETUP_TASK(invokeFunctor);
+ args->thread = &thread;
+ args->functor = functor;
+ if (waitForCompletion) {
+ // waitForCompletion = true is expected to be fairly rare and only
+ // happen in destruction. Thus it should be fine to temporarily
+ // create a Mutex
+ Mutex mutex;
+ Condition condition;
+ SignalingRenderTask syncTask(task, &mutex, &condition);
+ AutoMutex _lock(mutex);
+ thread.queue(&syncTask);
+ condition.wait(mutex);
+ } else {
+ thread.queue(task);
+ }
+}
+
+CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) {
+ args->context->runWithGlContext(args->task);
+ return NULL;
+}
+
+void RenderProxy::runWithGlContext(RenderTask* gltask) {
+ SETUP_TASK(runWithGlContext);
+ args->context = mContext;
+ args->task = gltask;
+ postAndWait(task);
+}
+
+CREATE_BRIDGE1(destroyLayer, Layer* layer) {
+ LayerRenderer::destroyLayer(args->layer);
+ return NULL;
+}
+
+void RenderProxy::enqueueDestroyLayer(Layer* layer) {
+ SETUP_TASK(destroyLayer);
+ args->layer = layer;
+ RenderThread::getInstance().queue(task);
+}
+
+CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) {
+ Layer* layer = args->context->createRenderLayer(args->width, args->height);
+ if (!layer) return 0;
+ return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
+}
+
+DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
+ SETUP_TASK(createDisplayListLayer);
+ args->width = width;
+ args->height = height;
+ args->context = mContext;
+ void* retval = postAndWait(task);
+ DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
+ return layer;
+}
+
+CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
+ Layer* layer = args->context->createTextureLayer();
+ if (!layer) return 0;
+ return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
+}
+
+DeferredLayerUpdater* RenderProxy::createTextureLayer() {
+ SETUP_TASK(createTextureLayer);
+ args->context = mContext;
+ void* retval = postAndWait(task);
+ DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
+ return layer;
+}
+
+CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
+ SkBitmap* bitmap) {
+ bool success = args->context->copyLayerInto(args->layer, args->bitmap);
+ return (void*) success;
+}
+
+bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ SETUP_TASK(copyLayerInto);
+ args->context = mContext;
+ args->layer = layer;
+ args->bitmap = bitmap;
+ return (bool) postAndWait(task);
+}
+
+void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
+ mDrawFrameTask.pushLayerUpdate(layer);
+}
+
+void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
+ mDrawFrameTask.removeLayerUpdate(layer);
+}
+
+CREATE_BRIDGE1(detachSurfaceTexture, DeferredLayerUpdater* layer) {
+ args->layer->detachSurfaceTexture();
+ return NULL;
+}
+
+void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
+ SETUP_TASK(detachSurfaceTexture);
+ args->layer = layer;
+ postAndWait(task);
+}
+
+CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
+ args->context->destroyHardwareResources();
+ return NULL;
+}
+
+void RenderProxy::destroyHardwareResources() {
+ SETUP_TASK(destroyHardwareResources);
+ args->context = mContext;
+ post(task);
+}
+
+CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) {
+ CanvasContext::trimMemory(*args->thread, args->level);
+ return NULL;
+}
+
+void RenderProxy::trimMemory(int level) {
+ // Avoid creating a RenderThread to do a trimMemory.
+ if (RenderThread::hasInstance()) {
+ RenderThread& thread = RenderThread::getInstance();
+ SETUP_TASK(timMemory);
+ args->thread = &thread;
+ args->level = level;
+ thread.queue(task);
+ }
+}
+
+CREATE_BRIDGE0(fence) {
+ // Intentionally empty
+ return NULL;
+}
+
+void RenderProxy::fence() {
+ SETUP_TASK(fence);
+ postAndWait(task);
+}
+
+CREATE_BRIDGE1(stopDrawing, CanvasContext* context) {
+ args->context->stopDrawing();
+ return NULL;
+}
+
+void RenderProxy::stopDrawing() {
+ SETUP_TASK(stopDrawing);
+ args->context = mContext;
+ postAndWait(task);
+}
+
+CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
+ args->context->notifyFramePending();
+ return NULL;
+}
+
+void RenderProxy::notifyFramePending() {
+ SETUP_TASK(notifyFramePending);
+ args->context = mContext;
+ mRenderThread.queueAtFront(task);
+}
+
+CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
+ args->context->profiler().dumpData(args->fd);
+ return NULL;
+}
+
+void RenderProxy::dumpProfileInfo(int fd) {
+ SETUP_TASK(dumpProfileInfo);
+ args->context = mContext;
+ args->fd = fd;
+ postAndWait(task);
+}
+
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+ CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
+ args->buffer->decStrong(0);
+ return NULL;
+}
+
+void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) {
+ SETUP_TASK(setTextureAtlas);
+ args->thread = &mRenderThread;
+ args->buffer = buffer.get();
+ args->buffer->incStrong(0);
+ args->map = map;
+ args->size = size;
+ post(task);
+}
+
+void RenderProxy::post(RenderTask* task) {
+ mRenderThread.queue(task);
+}
+
+void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
+ void* retval;
+ task->setReturnPtr(&retval);
+ SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
+ AutoMutex _lock(mSyncMutex);
+ mRenderThread.queue(&syncTask);
+ mSyncCondition.wait(mSyncMutex);
+ return retval;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
new file mode 100644
index 0000000..28d0173
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef RENDERPROXY_H_
+#define RENDERPROXY_H_
+
+#include "RenderTask.h"
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <SkBitmap.h>
+#include <utils/Condition.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "../Caches.h"
+#include "DrawFrameTask.h"
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class RenderNode;
+class DisplayListData;
+class Layer;
+class Rect;
+
+namespace renderthread {
+
+class CanvasContext;
+class ErrorChannel;
+class RenderThread;
+class RenderProxyBridge;
+
+/*
+ * RenderProxy is strictly single threaded. All methods must be invoked on the owning
+ * thread. It is important to note that RenderProxy may be deleted while it has
+ * tasks post()'d as a result. Therefore any RenderTask that is post()'d must not
+ * reference RenderProxy or any of its fields. The exception here is that postAndWait()
+ * references RenderProxy fields. This is safe as RenderProxy cannot
+ * be deleted if it is blocked inside a call.
+ */
+class ANDROID_API RenderProxy {
+public:
+ ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
+ ANDROID_API virtual ~RenderProxy();
+
+ ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
+ ANDROID_API bool loadSystemProperties();
+
+ ANDROID_API bool initialize(const sp<ANativeWindow>& window);
+ ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
+ ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
+ ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
+ ANDROID_API void setOpaque(bool opaque);
+ ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+ float density);
+ ANDROID_API void destroyCanvasAndSurface();
+
+ ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
+
+ ANDROID_API void runWithGlContext(RenderTask* task);
+
+ static void enqueueDestroyLayer(Layer* layer);
+ ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
+ ANDROID_API DeferredLayerUpdater* createTextureLayer();
+ ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+ ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
+ ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
+ ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer);
+
+ ANDROID_API void destroyHardwareResources();
+ ANDROID_API static void trimMemory(int level);
+
+ ANDROID_API void fence();
+ ANDROID_API void stopDrawing();
+ ANDROID_API void notifyFramePending();
+
+ ANDROID_API void dumpProfileInfo(int fd);
+
+ ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+
+private:
+ RenderThread& mRenderThread;
+ CanvasContext* mContext;
+
+ DrawFrameTask mDrawFrameTask;
+
+ Mutex mSyncMutex;
+ Condition mSyncCondition;
+
+ void destroyContext();
+
+ void post(RenderTask* task);
+ void* postAndWait(MethodInvokeRenderTask* task);
+
+ // Friend class to help with bridging
+ friend class RenderProxyBridge;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERPROXY_H_ */
diff --git a/libs/input/InputApplication.cpp b/libs/hwui/renderthread/RenderTask.cpp
index a99e637..7ca61e4 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -14,29 +14,25 @@
* limitations under the License.
*/
-#define LOG_TAG "InputApplication"
+#define LOG_TAG "RenderTask"
-#include "InputApplication.h"
+#include "RenderTask.h"
-#include <cutils/log.h>
+#include <utils/Log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
namespace android {
-
-// --- InputApplicationHandle ---
-
-InputApplicationHandle::InputApplicationHandle() :
- mInfo(NULL) {
-}
-
-InputApplicationHandle::~InputApplicationHandle() {
- delete mInfo;
-}
-
-void InputApplicationHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = NULL;
- }
+namespace uirenderer {
+namespace renderthread {
+
+void SignalingRenderTask::run() {
+ mTask->run();
+ mLock->lock();
+ mSignal->signal();
+ mLock->unlock();
}
-} // namespace android
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
new file mode 100644
index 0000000..1554a16
--- /dev/null
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef RENDERTASK_H_
+#define RENDERTASK_H_
+
+#include <cutils/compiler.h>
+#include <utils/Timers.h>
+
+namespace android {
+class Mutex;
+class Condition;
+namespace uirenderer {
+namespace renderthread {
+
+#define METHOD_INVOKE_PAYLOAD_SIZE (8 * sizeof(void*))
+
+/*
+ * Notes about memory management
+ *
+ * RenderThread will only invoke RenderTask::run(). It is the responsibility
+ * of the RenderTask to know if it needs to suicide at the end of run() or
+ * if some other lifecycle is being used. As such, it is not valid to reference
+ * anything on RenderTask after the first call to run().
+ *
+ * For example SignalingRenderTask
+ * is expected to be stack allocated by the calling thread, so it does not
+ * suicide in run() but instead relies on the caller to destroy it.
+ *
+ * MethodInvokeRenderTask however is currently allocated with new, so it will
+ * suicide at the end of run(). TODO: Replace this with a small pool to avoid
+ * malloc/free churn of small objects?
+ */
+
+class ANDROID_API RenderTask {
+public:
+ ANDROID_API RenderTask() : mNext(0), mRunAt(0) {}
+ ANDROID_API virtual ~RenderTask() {}
+
+ ANDROID_API virtual void run() = 0;
+
+ RenderTask* mNext;
+ nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock
+};
+
+class SignalingRenderTask : public RenderTask {
+public:
+ // Takes ownership of task, caller owns lock and signal
+ SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
+ : mTask(task), mLock(lock), mSignal(signal) {}
+ virtual void run();
+
+private:
+ RenderTask* mTask;
+ Mutex* mLock;
+ Condition* mSignal;
+};
+
+typedef void* (*RunnableMethod)(void* data);
+
+class MethodInvokeRenderTask : public RenderTask {
+public:
+ MethodInvokeRenderTask(RunnableMethod method)
+ : mMethod(method), mReturnPtr(0) {}
+
+ void* payload() { return mData; }
+ void setReturnPtr(void** retptr) { mReturnPtr = retptr; }
+
+ virtual void run() {
+ void* retval = mMethod(mData);
+ if (mReturnPtr) {
+ *mReturnPtr = retval;
+ }
+ // Commit suicide
+ delete this;
+ }
+private:
+ RunnableMethod mMethod;
+ char mData[METHOD_INVOKE_PAYLOAD_SIZE];
+ void** mReturnPtr;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERTASK_H_ */
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
new file mode 100644
index 0000000..403e164
--- /dev/null
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "RenderThread.h"
+
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+
+#include "../RenderState.h"
+#include "CanvasContext.h"
+#include "EglManager.h"
+#include "RenderProxy.h"
+
+namespace android {
+using namespace uirenderer::renderthread;
+ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
+
+namespace uirenderer {
+namespace renderthread {
+
+// Number of events to read at a time from the DisplayEventReceiver pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+// Slight delay to give the UI time to push us a new frame before we replay
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 4;
+
+TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
+
+RenderTask* TaskQueue::next() {
+ RenderTask* ret = mHead;
+ if (ret) {
+ mHead = ret->mNext;
+ if (!mHead) {
+ mTail = 0;
+ }
+ ret->mNext = 0;
+ }
+ return ret;
+}
+
+RenderTask* TaskQueue::peek() {
+ return mHead;
+}
+
+void TaskQueue::queue(RenderTask* task) {
+ // Since the RenderTask itself forms the linked list it is not allowed
+ // to have the same task queued twice
+ LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
+ if (mTail) {
+ // Fast path if we can just append
+ if (mTail->mRunAt <= task->mRunAt) {
+ mTail->mNext = task;
+ mTail = task;
+ } else {
+ // Need to find the proper insertion point
+ RenderTask* previous = 0;
+ RenderTask* next = mHead;
+ while (next && next->mRunAt <= task->mRunAt) {
+ previous = next;
+ next = next->mNext;
+ }
+ if (!previous) {
+ task->mNext = mHead;
+ mHead = task;
+ } else {
+ previous->mNext = task;
+ if (next) {
+ task->mNext = next;
+ } else {
+ mTail = task;
+ }
+ }
+ }
+ } else {
+ mTail = mHead = task;
+ }
+}
+
+void TaskQueue::queueAtFront(RenderTask* task) {
+ if (mTail) {
+ task->mNext = mHead;
+ mHead = task;
+ } else {
+ mTail = mHead = task;
+ }
+}
+
+void TaskQueue::remove(RenderTask* task) {
+ // TaskQueue is strict here to enforce that users are keeping track of
+ // their RenderTasks due to how their memory is managed
+ LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task,
+ "Cannot remove a task that isn't in the queue!");
+
+ // If task is the head we can just call next() to pop it off
+ // Otherwise we need to scan through to find the task before it
+ if (peek() == task) {
+ next();
+ } else {
+ RenderTask* previous = mHead;
+ while (previous->mNext != task) {
+ previous = previous->mNext;
+ }
+ previous->mNext = task->mNext;
+ if (mTail == task) {
+ mTail = previous;
+ }
+ }
+}
+
+class DispatchFrameCallbacks : public RenderTask {
+private:
+ RenderThread* mRenderThread;
+public:
+ DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
+
+ virtual void run() {
+ mRenderThread->dispatchFrameCallbacks();
+ }
+};
+
+RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
+ , mNextWakeup(LLONG_MAX)
+ , mDisplayEventReceiver(0)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mFrameCallbackTask(0)
+ , mRenderState(NULL)
+ , mEglManager(NULL) {
+ mFrameCallbackTask = new DispatchFrameCallbacks(this);
+ mLooper = new Looper(false);
+ run("RenderThread");
+}
+
+RenderThread::~RenderThread() {
+ LOG_ALWAYS_FATAL("Can't destroy the render thread");
+}
+
+void RenderThread::initializeDisplayEventReceiver() {
+ LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
+ mDisplayEventReceiver = new DisplayEventReceiver();
+ status_t status = mDisplayEventReceiver->initCheck();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
+ "failed with status: %d", status);
+
+ // Register the FD
+ mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
+ Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
+}
+
+void RenderThread::initThreadLocals() {
+ initializeDisplayEventReceiver();
+ mEglManager = new EglManager(*this);
+ mRenderState = new RenderState();
+}
+
+int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x", events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x", events);
+ return 1; // keep the callback
+ }
+
+ reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
+
+ return 1; // keep the callback
+}
+
+static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ nsecs_t latest = 0;
+ ssize_t n;
+ while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ latest = ev.header.timestamp;
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+ }
+ return latest;
+}
+
+void RenderThread::drainDisplayEventQueue(bool skipCallbacks) {
+ ATRACE_CALL();
+ nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+ if (vsyncEvent > 0) {
+ mVsyncRequested = false;
+ mTimeLord.vsyncReceived(vsyncEvent);
+ if (!skipCallbacks && !mFrameCallbackTaskPending) {
+ ATRACE_NAME("queue mFrameCallbackTask");
+ mFrameCallbackTaskPending = true;
+ queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
+ }
+ }
+}
+
+void RenderThread::dispatchFrameCallbacks() {
+ ATRACE_CALL();
+ mFrameCallbackTaskPending = false;
+
+ std::set<IFrameCallback*> callbacks;
+ mFrameCallbacks.swap(callbacks);
+
+ for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
+ (*it)->doFrame();
+ }
+}
+
+void RenderThread::requestVsync() {
+ if (!mVsyncRequested) {
+ mVsyncRequested = true;
+ status_t status = mDisplayEventReceiver->requestNextVsync();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "requestNextVsync failed with status: %d", status);
+ }
+}
+
+bool RenderThread::threadLoop() {
+#if defined(HAVE_PTHREADS)
+ setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
+#endif
+ initThreadLocals();
+
+ int timeoutMillis = -1;
+ for (;;) {
+ int result = mLooper->pollOnce(timeoutMillis);
+ LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
+ "RenderThread Looper POLL_ERROR!");
+
+ nsecs_t nextWakeup;
+ // Process our queue, if we have anything
+ while (RenderTask* task = nextTask(&nextWakeup)) {
+ task->run();
+ // task may have deleted itself, do not reference it again
+ }
+ if (nextWakeup == LLONG_MAX) {
+ timeoutMillis = -1;
+ } else {
+ nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
+ if (timeoutMillis < 0) {
+ timeoutMillis = 0;
+ }
+ }
+
+ if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
+ drainDisplayEventQueue(true);
+ mFrameCallbacks.insert(
+ mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
+ mPendingRegistrationFrameCallbacks.clear();
+ requestVsync();
+ }
+ }
+
+ return false;
+}
+
+void RenderThread::queue(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.queue(task);
+ if (mNextWakeup && task->mRunAt < mNextWakeup) {
+ mNextWakeup = 0;
+ mLooper->wake();
+ }
+}
+
+void RenderThread::queueAtFront(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.queueAtFront(task);
+ mLooper->wake();
+}
+
+void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
+ queue(task);
+}
+
+void RenderThread::remove(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.remove(task);
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {
+ mPendingRegistrationFrameCallbacks.insert(callback);
+}
+
+void RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.erase(callback);
+ mPendingRegistrationFrameCallbacks.erase(callback);
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
+ if (mFrameCallbacks.erase(callback)) {
+ mPendingRegistrationFrameCallbacks.insert(callback);
+ }
+}
+
+RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
+ AutoMutex _lock(mLock);
+ RenderTask* next = mQueue.peek();
+ if (!next) {
+ mNextWakeup = LLONG_MAX;
+ } else {
+ mNextWakeup = next->mRunAt;
+ // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
+ if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
+ next = mQueue.next();
+ } else {
+ next = 0;
+ }
+ }
+ if (nextWakeup) {
+ *nextWakeup = mNextWakeup;
+ }
+ return next;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
new file mode 100644
index 0000000..0b91e9d
--- /dev/null
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef RENDERTHREAD_H_
+#define RENDERTHREAD_H_
+
+#include "RenderTask.h"
+
+#include <memory>
+#include <set>
+
+#include <cutils/compiler.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+#include <utils/Singleton.h>
+#include <utils/Thread.h>
+
+#include "TimeLord.h"
+
+namespace android {
+
+class DisplayEventReceiver;
+
+namespace uirenderer {
+
+class RenderState;
+
+namespace renderthread {
+
+class DispatchFrameCallbacks;
+class EglManager;
+class RenderProxy;
+
+class TaskQueue {
+public:
+ TaskQueue();
+
+ RenderTask* next();
+ void queue(RenderTask* task);
+ void queueAtFront(RenderTask* task);
+ RenderTask* peek();
+ void remove(RenderTask* task);
+
+private:
+ RenderTask* mHead;
+ RenderTask* mTail;
+};
+
+// Mimics android.view.Choreographer.FrameCallback
+class IFrameCallback {
+public:
+ virtual void doFrame() = 0;
+
+protected:
+ ~IFrameCallback() {}
+};
+
+class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> {
+public:
+ // RenderThread takes complete ownership of tasks that are queued
+ // and will delete them after they are run
+ ANDROID_API void queue(RenderTask* task);
+ ANDROID_API void queueAtFront(RenderTask* task);
+ void queueDelayed(RenderTask* task, int delayMs);
+ void remove(RenderTask* task);
+
+ // Mimics android.view.Choreographer
+ void postFrameCallback(IFrameCallback* callback);
+ void removeFrameCallback(IFrameCallback* callback);
+ // If the callback is currently registered, it will be pushed back until
+ // the next vsync. If it is not currently registered this does nothing.
+ void pushBackFrameCallback(IFrameCallback* callback);
+
+ TimeLord& timeLord() { return mTimeLord; }
+ RenderState& renderState() { return *mRenderState; }
+ EglManager& eglManager() { return *mEglManager; }
+
+protected:
+ virtual bool threadLoop();
+
+private:
+ friend class Singleton<RenderThread>;
+ friend class DispatchFrameCallbacks;
+ friend class RenderProxy;
+
+ RenderThread();
+ virtual ~RenderThread();
+
+ void initThreadLocals();
+ void initializeDisplayEventReceiver();
+ static int displayEventReceiverCallback(int fd, int events, void* data);
+ void drainDisplayEventQueue(bool skipCallbacks = false);
+ void dispatchFrameCallbacks();
+ void requestVsync();
+
+ // Returns the next task to be run. If this returns NULL nextWakeup is set
+ // to the time to requery for the nextTask to run. mNextWakeup is also
+ // set to this time
+ RenderTask* nextTask(nsecs_t* nextWakeup);
+
+ sp<Looper> mLooper;
+ Mutex mLock;
+
+ nsecs_t mNextWakeup;
+ TaskQueue mQueue;
+
+ DisplayEventReceiver* mDisplayEventReceiver;
+ bool mVsyncRequested;
+ std::set<IFrameCallback*> mFrameCallbacks;
+ // We defer the actual registration of these callbacks until
+ // both mQueue *and* mDisplayEventReceiver have been drained off all
+ // immediate events. This makes sure that we catch the next vsync, not
+ // the previous one
+ std::set<IFrameCallback*> mPendingRegistrationFrameCallbacks;
+ bool mFrameCallbackTaskPending;
+ DispatchFrameCallbacks* mFrameCallbackTask;
+
+ TimeLord mTimeLord;
+ RenderState* mRenderState;
+ EglManager* mEglManager;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERTHREAD_H_ */
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
new file mode 100644
index 0000000..758d96e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "TimeLord.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+TimeLord::TimeLord()
+ : mFrameIntervalNanos(0)
+ , mFrameTimeNanos(0) {
+}
+
+void TimeLord::vsyncReceived(nsecs_t vsync) {
+ if (vsync > mFrameTimeNanos) {
+ mFrameTimeNanos = vsync;
+ }
+}
+
+nsecs_t TimeLord::frameTimeMs() {
+ // Logic copied from Choreographer.java
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t jitterNanos = now - mFrameTimeNanos;
+ if (jitterNanos >= mFrameIntervalNanos) {
+ nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
+ mFrameTimeNanos = now - lastFrameOffset;
+ }
+ return nanoseconds_to_milliseconds(mFrameTimeNanos);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
new file mode 100644
index 0000000..52c6d9e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef TIMELORD_H
+#define TIMELORD_H
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class serves as a helper to filter & manage frame times from multiple sources
+// ensuring that time flows linearly and smoothly
+class TimeLord {
+public:
+ void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
+ void vsyncReceived(nsecs_t vsync);
+ nsecs_t frameTimeMs();
+
+private:
+ friend class RenderThread;
+
+ TimeLord();
+ ~TimeLord() {}
+
+ nsecs_t mFrameIntervalNanos;
+ nsecs_t mFrameTimeNanos;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TIMELORD_H */
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 9a211a2..30b6ff2 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_HWUI_TASK_H
#define ANDROID_HWUI_TASK_H
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <utils/RefBase.h>
#include <utils/Trace.h>
@@ -40,7 +38,7 @@ public:
virtual ~Task() { }
T getResult() const {
- ATRACE_NAME("waitForTask");
+ ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask");
return mFuture->get();
}
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 189895c..cb5401c 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -15,10 +15,14 @@
*/
#include <sys/sysinfo.h>
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
+#include "TaskManager.h"
#include "Task.h"
#include "TaskProcessor.h"
-#include "TaskManager.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -31,7 +35,8 @@ TaskManager::TaskManager() {
// Get the number of available CPUs. This value does not change over time.
int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
- for (int i = 0; i < cpuCount / 2; i++) {
+ int workerCount = MathUtils::max(1, cpuCount / 2);
+ for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
mThreads.add(new WorkerThread(name));
@@ -77,6 +82,13 @@ bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBa
// Thread
///////////////////////////////////////////////////////////////////////////////
+status_t TaskManager::WorkerThread::readyToRun() {
+#if defined(HAVE_PTHREADS)
+ setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
+#endif
+ return NO_ERROR;
+}
+
bool TaskManager::WorkerThread::threadLoop() {
mSignal.wait();
Vector<TaskWrapper> tasks;
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index f2a216f..5a933ab 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -84,6 +84,7 @@ private:
void exit();
private:
+ virtual status_t readyToRun();
virtual bool threadLoop();
// Lock for the list of tasks
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index 85d90d0..877a422 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -19,10 +19,47 @@
#include <math.h>
#include "Blur.h"
+#include "MathUtils.h"
namespace android {
namespace uirenderer {
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+float Blur::convertRadiusToSigma(float radius) {
+ return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+}
+
+float Blur::convertSigmaToRadius(float sigma) {
+ return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
+}
+
+// if the original radius was on an integer boundary and the resulting radius
+// is within the conversion error tolerance then we attempt to snap to the
+// original integer boundary.
+uint32_t Blur::convertRadiusToInt(float radius) {
+ const float radiusCeil = ceilf(radius);
+ if (MathUtils::areEqual(radiusCeil, radius)) {
+ return radiusCeil;
+ }
+ return radius;
+}
+
+/**
+ * HWUI has used a slightly different equation than Skia to generate the value
+ * for sigma and to preserve compatibility we have kept that logic.
+ *
+ * Based on some experimental radius and sigma values we approximate the
+ * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the
+ * radius gets, the more our gaussian blur will resemble a box blur since with
+ * large sigma the gaussian curve begins to lose its shape.
+ */
+static float legacyConvertRadiusToSigma(float radius) {
+ return radius > 0 ? 0.3f * radius + 0.6f : 0.0f;
+}
+
void Blur::generateGaussianWeights(float* weights, int32_t radius) {
// Compute gaussian weights for the blur
// e is the euler's number
@@ -31,13 +68,7 @@ void Blur::generateGaussianWeights(float* weights, int32_t radius) {
// g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
// x is of the form [-radius .. 0 .. radius]
// and sigma varies with radius.
- // Based on some experimental radius values and sigma's
- // we approximately fit sigma = f(radius) as
- // sigma = radius * 0.3 + 0.6
- // The larger the radius gets, the more our gaussian blur
- // will resemble a box blur since with large sigma
- // the gaussian curve begins to lose its shape
- float sigma = 0.3f * (float) radius + 0.6f;
+ float sigma = legacyConvertRadiusToSigma((float) radius);
// Now compute the coefficints
// We will store some redundant values to save some math during
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index 6c176e9..b145333 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -18,12 +18,22 @@
#define ANDROID_HWUI_BLUR_H
#include <stdint.h>
+#include <cutils/compiler.h>
namespace android {
namespace uirenderer {
class Blur {
public:
+ // If radius > 0, return the corresponding sigma, else return 0
+ ANDROID_API static float convertRadiusToSigma(float radius);
+ // If sigma > 0.5, return the corresponding radius, else return 0
+ ANDROID_API static float convertSigmaToRadius(float sigma);
+ // If the original radius was on an integer boundary then after the sigma to
+ // radius conversion a small rounding error may be introduced. This function
+ // accounts for that error and snaps to the appropriate integer boundary.
+ static uint32_t convertRadiusToInt(float radius);
+
static void generateGaussianWeights(float* weights, int32_t radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,
uint8_t* dest, int32_t width, int32_t height);
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
new file mode 100644
index 0000000..9b298ca
--- /dev/null
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Log.h>
+
+#include "GLUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+void GLUtils::dumpGLErrors() {
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ switch (status) {
+ case GL_INVALID_ENUM:
+ ALOGE("GL error: GL_INVALID_ENUM");
+ break;
+ case GL_INVALID_VALUE:
+ ALOGE("GL error: GL_INVALID_VALUE");
+ break;
+ case GL_INVALID_OPERATION:
+ ALOGE("GL error: GL_INVALID_OPERATION");
+ break;
+ case GL_OUT_OF_MEMORY:
+ ALOGE("GL error: Out of memory!");
+ break;
+ default:
+ ALOGE("GL error: 0x%x", status);
+ }
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
new file mode 100644
index 0000000..890e374
--- /dev/null
+++ b/libs/hwui/utils/GLUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef GLUTILS_H
+#define GLUTILS_H
+
+namespace android {
+namespace uirenderer {
+
+class GLUtils {
+private:
+public:
+ /**
+ * Print out any GL errors with ALOGE
+ */
+ static void dumpGLErrors();
+
+}; // class GLUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* GLUTILS_H */
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
new file mode 100644
index 0000000..5b7c87c
--- /dev/null
+++ b/libs/hwui/utils/Macros.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef MACROS_H
+#define MACROS_H
+
+#define PREVENT_COPY_AND_ASSIGN(Type) \
+ private: \
+ Type(const Type&); \
+ void operator=(const Type&)
+
+#define DESCRIPTION_TYPE(Type) \
+ int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
+ bool operator==(const Type& other) const { return compare(other) == 0; } \
+ bool operator!=(const Type& other) const { return compare(other) != 0; } \
+ friend inline int strictly_order_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs) < 0; } \
+ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
+ friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
+
+#endif /* MACROS_H */
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
new file mode 100644
index 0000000..6fb0411
--- /dev/null
+++ b/libs/hwui/utils/MathUtils.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef MATHUTILS_H
+#define MATHUTILS_H
+
+namespace android {
+namespace uirenderer {
+
+#define NON_ZERO_EPSILON (0.001f)
+#define ALPHA_EPSILON (0.001f)
+
+class MathUtils {
+public:
+ /**
+ * Check for floats that are close enough to zero.
+ */
+ inline static bool isZero(float value) {
+ return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
+ }
+
+ inline static bool isPositive(float value) {
+ return value >= NON_ZERO_EPSILON;
+ }
+
+ /**
+ * Clamps alpha value, and snaps when very near 0 or 1
+ */
+ inline static float clampAlpha(float alpha) {
+ if (alpha <= ALPHA_EPSILON) {
+ return 0;
+ } else if (alpha >= (1 - ALPHA_EPSILON)) {
+ return 1;
+ } else {
+ return alpha;
+ }
+ }
+
+ /*
+ * Clamps positive tessellation scale values
+ */
+ inline static float clampTessellationScale(float scale) {
+ const float MIN_SCALE = 0.0001;
+ const float MAX_SCALE = 1e10;
+ if (scale < MIN_SCALE) {
+ return MIN_SCALE;
+ } else if (scale > MAX_SCALE) {
+ return MAX_SCALE;
+ }
+ return scale;
+ }
+
+ inline static bool areEqual(float valueA, float valueB) {
+ return isZero(valueA - valueB);
+ }
+
+ inline static int max(int a, int b) {
+ return a > b ? a : b;
+ }
+
+ inline static int min(int a, int b) {
+ return a < b ? a : b;
+ }
+
+ inline static float lerp(float v1, float v2, float t) {
+ return v1 + ((v2 - v1) * t);
+ }
+}; // class MathUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* MATHUTILS_H */
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
index 09ecd3a..a7fb0e2 100644
--- a/libs/input/Android.mk
+++ b/libs/input/Android.mk
@@ -17,32 +17,24 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- EventHub.cpp \
- InputApplication.cpp \
- InputDispatcher.cpp \
- InputListener.cpp \
- InputManager.cpp \
- InputReader.cpp \
- InputWindow.cpp \
PointerController.cpp \
SpriteController.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
- libandroidfw \
libutils \
- libhardware \
- libhardware_legacy \
libskia \
libgui \
libui \
- libinput \
- libcrypto \
+ libinput \
+ libinputflinger
LOCAL_C_INCLUDES := \
- external/openssl/include \
- external/skia/include/core \
+ frameworks/native/services
+
+
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE:= libinputservice
diff --git a/libs/input/EventHub.cpp b/libs/input/EventHub.cpp
deleted file mode 100644
index e7a691d..0000000
--- a/libs/input/EventHub.cpp
+++ /dev/null
@@ -1,1676 +0,0 @@
-/*
- * Copyright (C) 2005 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 "EventHub"
-
-// #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
-#include <hardware_legacy/power.h>
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-#include <utils/Errors.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <memory.h>
-#include <errno.h>
-#include <assert.h>
-
-#include <input/KeyLayoutMap.h>
-#include <input/KeyCharacterMap.h>
-#include <input/VirtualKeyMap.h>
-
-#include <string.h>
-#include <stdint.h>
-#include <dirent.h>
-
-#include <sys/inotify.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <sys/limits.h>
-
-#include <openssl/sha.h>
-
-/* this macro is used to tell if "bit" is set in "array"
- * it selects a byte from the array, and does a boolean AND
- * operation with a byte that only has the relevant bit set.
- * eg. to check for the 12th bit, we do (array[1] & 1<<4)
- */
-#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
-
-/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) ((bits + 7) / 8)
-
-#define INDENT " "
-#define INDENT2 " "
-#define INDENT3 " "
-
-namespace android {
-
-static const char *WAKE_LOCK_ID = "KeyEvents";
-static const char *DEVICE_PATH = "/dev/input";
-
-/* return the larger integer */
-static inline int max(int v1, int v2)
-{
- return (v1 > v2) ? v1 : v2;
-}
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static String8 sha1(const String8& in) {
- SHA_CTX ctx;
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
- u_char digest[SHA_DIGEST_LENGTH];
- SHA1_Final(digest, &ctx);
-
- String8 out;
- for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) {
- out.appendFormat("%02x", digest[i]);
- }
- return out;
-}
-
-static void setDescriptor(InputDeviceIdentifier& identifier) {
- // Compute a device descriptor that uniquely identifies the device.
- // The descriptor is assumed to be a stable identifier. Its value should not
- // change between reboots, reconnections, firmware updates or new releases of Android.
- // Ideally, we also want the descriptor to be short and relatively opaque.
- String8 rawDescriptor;
- rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product);
- if (!identifier.uniqueId.isEmpty()) {
- rawDescriptor.append("uniqueId:");
- rawDescriptor.append(identifier.uniqueId);
- } if (identifier.vendor == 0 && identifier.product == 0) {
- // If we don't know the vendor and product id, then the device is probably
- // built-in so we need to rely on other information to uniquely identify
- // the input device. Usually we try to avoid relying on the device name or
- // location but for built-in input device, they are unlikely to ever change.
- if (!identifier.name.isEmpty()) {
- rawDescriptor.append("name:");
- rawDescriptor.append(identifier.name);
- } else if (!identifier.location.isEmpty()) {
- rawDescriptor.append("location:");
- rawDescriptor.append(identifier.location);
- }
- }
- identifier.descriptor = sha1(rawDescriptor);
- ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
- identifier.descriptor.string());
-}
-
-// --- Global Functions ---
-
-uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
- // Touch devices get dibs on touch-related axes.
- if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
- switch (axis) {
- case ABS_X:
- case ABS_Y:
- case ABS_PRESSURE:
- case ABS_TOOL_WIDTH:
- case ABS_DISTANCE:
- case ABS_TILT_X:
- case ABS_TILT_Y:
- case ABS_MT_SLOT:
- case ABS_MT_TOUCH_MAJOR:
- case ABS_MT_TOUCH_MINOR:
- case ABS_MT_WIDTH_MAJOR:
- case ABS_MT_WIDTH_MINOR:
- case ABS_MT_ORIENTATION:
- case ABS_MT_POSITION_X:
- case ABS_MT_POSITION_Y:
- case ABS_MT_TOOL_TYPE:
- case ABS_MT_BLOB_ID:
- case ABS_MT_TRACKING_ID:
- case ABS_MT_PRESSURE:
- case ABS_MT_DISTANCE:
- return INPUT_DEVICE_CLASS_TOUCH;
- }
- }
-
- // Joystick devices get the rest.
- return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
-}
-
-// --- EventHub::Device ---
-
-EventHub::Device::Device(int fd, int32_t id, const String8& path,
- const InputDeviceIdentifier& identifier) :
- next(NULL),
- fd(fd), id(id), path(path), identifier(identifier),
- classes(0), configuration(NULL), virtualKeyMap(NULL),
- ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
- timestampOverrideSec(0), timestampOverrideUsec(0) {
- memset(keyBitmask, 0, sizeof(keyBitmask));
- memset(absBitmask, 0, sizeof(absBitmask));
- memset(relBitmask, 0, sizeof(relBitmask));
- memset(swBitmask, 0, sizeof(swBitmask));
- memset(ledBitmask, 0, sizeof(ledBitmask));
- memset(ffBitmask, 0, sizeof(ffBitmask));
- memset(propBitmask, 0, sizeof(propBitmask));
-}
-
-EventHub::Device::~Device() {
- close();
- delete configuration;
- delete virtualKeyMap;
-}
-
-void EventHub::Device::close() {
- if (fd >= 0) {
- ::close(fd);
- fd = -1;
- }
-}
-
-
-// --- EventHub ---
-
-const uint32_t EventHub::EPOLL_ID_INOTIFY;
-const uint32_t EventHub::EPOLL_ID_WAKE;
-const int EventHub::EPOLL_SIZE_HINT;
-const int EventHub::EPOLL_MAX_EVENTS;
-
-EventHub::EventHub(void) :
- mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
- mOpeningDevices(0), mClosingDevices(0),
- mNeedToSendFinishedDeviceScan(false),
- mNeedToReopenDevices(false), mNeedToScanDevices(true),
- mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
-
- mINotifyFd = inotify_init();
- int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
- DEVICE_PATH, errno);
-
- struct epoll_event eventItem;
- memset(&eventItem, 0, sizeof(eventItem));
- eventItem.events = EPOLLIN;
- eventItem.data.u32 = EPOLL_ID_INOTIFY;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
-
- int wakeFds[2];
- result = pipe(wakeFds);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
-
- mWakeReadPipeFd = wakeFds[0];
- mWakeWritePipeFd = wakeFds[1];
-
- result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
- errno);
-
- result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
- errno);
-
- eventItem.data.u32 = EPOLL_ID_WAKE;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
- errno);
-}
-
-EventHub::~EventHub(void) {
- closeAllDevicesLocked();
-
- while (mClosingDevices) {
- Device* device = mClosingDevices;
- mClosingDevices = device->next;
- delete device;
- }
-
- ::close(mEpollFd);
- ::close(mINotifyFd);
- ::close(mWakeReadPipeFd);
- ::close(mWakeWritePipeFd);
-
- release_wake_lock(WAKE_LOCK_ID);
-}
-
-InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return InputDeviceIdentifier();
- return device->identifier;
-}
-
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return 0;
- return device->classes;
-}
-
-int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return 0;
- return device->controllerNumber;
-}
-
-void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device && device->configuration) {
- *outConfiguration = *device->configuration;
- } else {
- outConfiguration->clear();
- }
-}
-
-status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
- outAxisInfo->clear();
-
- if (axis >= 0 && axis <= ABS_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
- struct input_absinfo info;
- if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
- axis, device->identifier.name.string(), device->fd, errno);
- return -errno;
- }
-
- if (info.minimum != info.maximum) {
- outAxisInfo->valid = true;
- outAxisInfo->minValue = info.minimum;
- outAxisInfo->maxValue = info.maximum;
- outAxisInfo->flat = info.flat;
- outAxisInfo->fuzz = info.fuzz;
- outAxisInfo->resolution = info.resolution;
- }
- return OK;
- }
- }
- return -1;
-}
-
-bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
- if (axis >= 0 && axis <= REL_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(axis, device->relBitmask);
- }
- }
- return false;
-}
-
-bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
- if (property >= 0 && property <= INPUT_PROP_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(property, device->propBitmask);
- }
- }
- return false;
-}
-
-int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
- if (scanCode >= 0 && scanCode <= KEY_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual() && test_bit(scanCode, device->keyBitmask)) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
- return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
- }
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual() && device->keyMap.haveKeyLayout()) {
- Vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
- if (scanCodes.size() != 0) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
- for (size_t i = 0; i < scanCodes.size(); i++) {
- int32_t sc = scanCodes.itemAt(i);
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
- return AKEY_STATE_DOWN;
- }
- }
- return AKEY_STATE_UP;
- }
- }
- }
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
- if (sw >= 0 && sw <= SW_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual() && test_bit(sw, device->swBitmask)) {
- uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
- memset(swState, 0, sizeof(swState));
- if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
- return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
- }
- return AKEY_STATE_UNKNOWN;
-}
-
-status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
- *outValue = 0;
-
- if (axis >= 0 && axis <= ABS_MAX) {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
- struct input_absinfo info;
- if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
- axis, device->identifier.name.string(), device->fd, errno);
- return -errno;
- }
-
- *outValue = info.value;
- return OK;
- }
- }
- return -1;
-}
-
-bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) const {
- AutoMutex _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
- Vector<int32_t> scanCodes;
- for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- scanCodes.clear();
-
- status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(
- keyCodes[codeIndex], &scanCodes);
- if (! err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], device->keyBitmask)) {
- outFlags[codeIndex] = 1;
- break;
- }
- }
- }
- }
- return true;
- }
- return false;
-}
-
-status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t* outKeycode, uint32_t* outFlags) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
-
- if (device) {
- // Check the key character map first.
- sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
- if (kcm != NULL) {
- if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
- *outFlags = 0;
- return NO_ERROR;
- }
- }
-
- // Check the key layout next.
- if (device->keyMap.haveKeyLayout()) {
- if (!device->keyMap.keyLayoutMap->mapKey(
- scanCode, usageCode, outKeycode, outFlags)) {
- return NO_ERROR;
- }
- }
- }
-
- *outKeycode = 0;
- *outFlags = 0;
- return NAME_NOT_FOUND;
-}
-
-status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
-
- if (device && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
- if (err == NO_ERROR) {
- return NO_ERROR;
- }
- }
-
- return NAME_NOT_FOUND;
-}
-
-void EventHub::setExcludedDevices(const Vector<String8>& devices) {
- AutoMutex _l(mLock);
-
- mExcludedDevices = devices;
-}
-
-bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
- if (test_bit(scanCode, device->keyBitmask)) {
- return true;
- }
- }
- return false;
-}
-
-bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- int32_t sc;
- if (device && mapLed(device, led, &sc) == NO_ERROR) {
- if (test_bit(sc, device->ledBitmask)) {
- return true;
- }
- }
- return false;
-}
-
-void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- setLedStateLocked(device, led, on);
-}
-
-void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
- int32_t sc;
- if (device && !device->isVirtual() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
- struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
- ev.type = EV_LED;
- ev.code = sc;
- ev.value = on ? 1 : 0;
-
- ssize_t nWrite;
- do {
- nWrite = write(device->fd, &ev, sizeof(struct input_event));
- } while (nWrite == -1 && errno == EINTR);
- }
-}
-
-void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const {
- outVirtualKeys.clear();
-
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device && device->virtualKeyMap) {
- outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys());
- }
-}
-
-sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- return device->getKeyCharacterMap();
- }
- return NULL;
-}
-
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
- const sp<KeyCharacterMap>& map) {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- if (map != device->overlayKeyMap) {
- device->overlayKeyMap = map;
- device->combinedKeyMap = KeyCharacterMap::combine(
- device->keyMap.keyCharacterMap, map);
- return true;
- }
- }
- return false;
-}
-
-static String8 generateDescriptor(InputDeviceIdentifier& identifier) {
- String8 rawDescriptor;
- rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor,
- identifier.product);
- // TODO add handling for USB devices to not uniqueify kbs that show up twice
- if (!identifier.uniqueId.isEmpty()) {
- rawDescriptor.append("uniqueId:");
- rawDescriptor.append(identifier.uniqueId);
- } else if (identifier.nonce != 0) {
- rawDescriptor.appendFormat("nonce:%04x", identifier.nonce);
- }
-
- if (identifier.vendor == 0 && identifier.product == 0) {
- // If we don't know the vendor and product id, then the device is probably
- // built-in so we need to rely on other information to uniquely identify
- // the input device. Usually we try to avoid relying on the device name or
- // location but for built-in input device, they are unlikely to ever change.
- if (!identifier.name.isEmpty()) {
- rawDescriptor.append("name:");
- rawDescriptor.append(identifier.name);
- } else if (!identifier.location.isEmpty()) {
- rawDescriptor.append("location:");
- rawDescriptor.append(identifier.location);
- }
- }
- identifier.descriptor = sha1(rawDescriptor);
- return rawDescriptor;
-}
-
-void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) {
- // Compute a device descriptor that uniquely identifies the device.
- // The descriptor is assumed to be a stable identifier. Its value should not
- // change between reboots, reconnections, firmware updates or new releases
- // of Android. In practice we sometimes get devices that cannot be uniquely
- // identified. In this case we enforce uniqueness between connected devices.
- // Ideally, we also want the descriptor to be short and relatively opaque.
-
- identifier.nonce = 0;
- String8 rawDescriptor = generateDescriptor(identifier);
- if (identifier.uniqueId.isEmpty()) {
- // If it didn't have a unique id check for conflicts and enforce
- // uniqueness if necessary.
- while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) {
- identifier.nonce++;
- rawDescriptor = generateDescriptor(identifier);
- }
- }
- ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
- identifier.descriptor.string());
-}
-
-void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual()) {
- ff_effect effect;
- memset(&effect, 0, sizeof(effect));
- effect.type = FF_RUMBLE;
- effect.id = device->ffEffectId;
- effect.u.rumble.strong_magnitude = 0xc000;
- effect.u.rumble.weak_magnitude = 0xc000;
- effect.replay.length = (duration + 999999LL) / 1000000LL;
- effect.replay.delay = 0;
- if (ioctl(device->fd, EVIOCSFF, &effect)) {
- ALOGW("Could not upload force feedback effect to device %s due to error %d.",
- device->identifier.name.string(), errno);
- return;
- }
- device->ffEffectId = effect.id;
-
- struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
- ev.type = EV_FF;
- ev.code = device->ffEffectId;
- ev.value = 1;
- if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
- ALOGW("Could not start force feedback effect on device %s due to error %d.",
- device->identifier.name.string(), errno);
- return;
- }
- device->ffEffectPlaying = true;
- }
-}
-
-void EventHub::cancelVibrate(int32_t deviceId) {
- AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device && !device->isVirtual()) {
- if (device->ffEffectPlaying) {
- device->ffEffectPlaying = false;
-
- struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
- ev.type = EV_FF;
- ev.code = device->ffEffectId;
- ev.value = 0;
- if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
- ALOGW("Could not stop force feedback effect on device %s due to error %d.",
- device->identifier.name.string(), errno);
- return;
- }
- }
- }
-}
-
-EventHub::Device* EventHub::getDeviceByDescriptorLocked(String8& descriptor) const {
- size_t size = mDevices.size();
- for (size_t i = 0; i < size; i++) {
- Device* device = mDevices.valueAt(i);
- if (descriptor.compare(device->identifier.descriptor) == 0) {
- return device;
- }
- }
- return NULL;
-}
-
-EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
- if (deviceId == BUILT_IN_KEYBOARD_ID) {
- deviceId = mBuiltInKeyboardId;
- }
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : NULL;
-}
-
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
- if (device->path == devicePath) {
- return device;
- }
- }
- return NULL;
-}
-
-size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
- ALOG_ASSERT(bufferSize >= 1);
-
- AutoMutex _l(mLock);
-
- struct input_event readBuffer[bufferSize];
-
- RawEvent* event = buffer;
- size_t capacity = bufferSize;
- bool awoken = false;
- for (;;) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- // Reopen input devices if needed.
- if (mNeedToReopenDevices) {
- mNeedToReopenDevices = false;
-
- ALOGI("Reopening all input devices due to a configuration change.");
-
- closeAllDevicesLocked();
- mNeedToScanDevices = true;
- break; // return to the caller before we actually rescan
- }
-
- // Report any devices that had last been added/removed.
- while (mClosingDevices) {
- Device* device = mClosingDevices;
- ALOGV("Reporting device closed: id=%d, name=%s\n",
- device->id, device->path.string());
- mClosingDevices = device->next;
- event->when = now;
- event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
- event->type = DEVICE_REMOVED;
- event += 1;
- delete device;
- mNeedToSendFinishedDeviceScan = true;
- if (--capacity == 0) {
- break;
- }
- }
-
- if (mNeedToScanDevices) {
- mNeedToScanDevices = false;
- scanDevicesLocked();
- mNeedToSendFinishedDeviceScan = true;
- }
-
- while (mOpeningDevices != NULL) {
- Device* device = mOpeningDevices;
- ALOGV("Reporting device opened: id=%d, name=%s\n",
- device->id, device->path.string());
- mOpeningDevices = device->next;
- event->when = now;
- event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
- event->type = DEVICE_ADDED;
- event += 1;
- mNeedToSendFinishedDeviceScan = true;
- if (--capacity == 0) {
- break;
- }
- }
-
- if (mNeedToSendFinishedDeviceScan) {
- mNeedToSendFinishedDeviceScan = false;
- event->when = now;
- event->type = FINISHED_DEVICE_SCAN;
- event += 1;
- if (--capacity == 0) {
- break;
- }
- }
-
- // Grab the next input event.
- bool deviceChanged = false;
- while (mPendingEventIndex < mPendingEventCount) {
- const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
- if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
- if (eventItem.events & EPOLLIN) {
- mPendingINotify = true;
- } else {
- ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
- }
- continue;
- }
-
- if (eventItem.data.u32 == EPOLL_ID_WAKE) {
- if (eventItem.events & EPOLLIN) {
- ALOGV("awoken after wake()");
- awoken = true;
- char buffer[16];
- ssize_t nRead;
- do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
- } else {
- ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
- eventItem.events);
- }
- continue;
- }
-
- ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
- if (deviceIndex < 0) {
- ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
- eventItem.events, eventItem.data.u32);
- continue;
- }
-
- Device* device = mDevices.valueAt(deviceIndex);
- if (eventItem.events & EPOLLIN) {
- int32_t readSize = read(device->fd, readBuffer,
- sizeof(struct input_event) * capacity);
- if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
- // Device was removed before INotify noticed.
- ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d "
- "capacity: %zu errno: %d)\n",
- device->fd, readSize, bufferSize, capacity, errno);
- deviceChanged = true;
- closeDeviceLocked(device);
- } else if (readSize < 0) {
- if (errno != EAGAIN && errno != EINTR) {
- ALOGW("could not get event (errno=%d)", errno);
- }
- } else if ((readSize % sizeof(struct input_event)) != 0) {
- ALOGE("could not get event (wrong size: %d)", readSize);
- } else {
- int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
-
- size_t count = size_t(readSize) / sizeof(struct input_event);
- for (size_t i = 0; i < count; i++) {
- struct input_event& iev = readBuffer[i];
- ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
- device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec,
- iev.type, iev.code, iev.value);
-
- // Some input devices may have a better concept of the time
- // when an input event was actually generated than the kernel
- // which simply timestamps all events on entry to evdev.
- // This is a custom Android extension of the input protocol
- // mainly intended for use with uinput based device drivers.
- if (iev.type == EV_MSC) {
- if (iev.code == MSC_ANDROID_TIME_SEC) {
- device->timestampOverrideSec = iev.value;
- continue;
- } else if (iev.code == MSC_ANDROID_TIME_USEC) {
- device->timestampOverrideUsec = iev.value;
- continue;
- }
- }
- if (device->timestampOverrideSec || device->timestampOverrideUsec) {
- iev.time.tv_sec = device->timestampOverrideSec;
- iev.time.tv_usec = device->timestampOverrideUsec;
- if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
- device->timestampOverrideSec = 0;
- device->timestampOverrideUsec = 0;
- }
- ALOGV("applied override time %d.%06d",
- int(iev.time.tv_sec), int(iev.time.tv_usec));
- }
-
-#ifdef HAVE_POSIX_CLOCKS
- // Use the time specified in the event instead of the current time
- // so that downstream code can get more accurate estimates of
- // event dispatch latency from the time the event is enqueued onto
- // the evdev client buffer.
- //
- // The event's timestamp fortuitously uses the same monotonic clock
- // time base as the rest of Android. The kernel event device driver
- // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
- // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
- // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
- // system call that also queries ktime_get_ts().
- event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
- + nsecs_t(iev.time.tv_usec) * 1000LL;
- ALOGV("event time %lld, now %lld", event->when, now);
-
- // Bug 7291243: Add a guard in case the kernel generates timestamps
- // that appear to be far into the future because they were generated
- // using the wrong clock source.
- //
- // This can happen because when the input device is initially opened
- // it has a default clock source of CLOCK_REALTIME. Any input events
- // enqueued right after the device is opened will have timestamps
- // generated using CLOCK_REALTIME. We later set the clock source
- // to CLOCK_MONOTONIC but it is already too late.
- //
- // Invalid input event timestamps can result in ANRs, crashes and
- // and other issues that are hard to track down. We must not let them
- // propagate through the system.
- //
- // Log a warning so that we notice the problem and recover gracefully.
- if (event->when >= now + 10 * 1000000000LL) {
- // Double-check. Time may have moved on.
- nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
- if (event->when > time) {
- ALOGW("An input event from %s has a timestamp that appears to "
- "have been generated using the wrong clock source "
- "(expected CLOCK_MONOTONIC): "
- "event time %lld, current time %lld, call time %lld. "
- "Using current time instead.",
- device->path.string(), event->when, time, now);
- event->when = time;
- } else {
- ALOGV("Event time is ok but failed the fast path and required "
- "an extra call to systemTime: "
- "event time %lld, current time %lld, call time %lld.",
- event->when, time, now);
- }
- }
-#else
- event->when = now;
-#endif
- event->deviceId = deviceId;
- event->type = iev.type;
- event->code = iev.code;
- event->value = iev.value;
- event += 1;
- capacity -= 1;
- }
- if (capacity == 0) {
- // The result buffer is full. Reset the pending event index
- // so we will try to read the device again on the next iteration.
- mPendingEventIndex -= 1;
- break;
- }
- }
- } else if (eventItem.events & EPOLLHUP) {
- ALOGI("Removing device %s due to epoll hang-up event.",
- device->identifier.name.string());
- deviceChanged = true;
- closeDeviceLocked(device);
- } else {
- ALOGW("Received unexpected epoll event 0x%08x for device %s.",
- eventItem.events, device->identifier.name.string());
- }
- }
-
- // readNotify() will modify the list of devices so this must be done after
- // processing all other events to ensure that we read all remaining events
- // before closing the devices.
- if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
- mPendingINotify = false;
- readNotifyLocked();
- deviceChanged = true;
- }
-
- // Report added or removed devices immediately.
- if (deviceChanged) {
- continue;
- }
-
- // Return now if we have collected any events or if we were explicitly awoken.
- if (event != buffer || awoken) {
- break;
- }
-
- // Poll for events. Mind the wake lock dance!
- // We hold a wake lock at all times except during epoll_wait(). This works due to some
- // subtle choreography. When a device driver has pending (unread) events, it acquires
- // a kernel wake lock. However, once the last pending event has been read, the device
- // driver will release the kernel wake lock. To prevent the system from going to sleep
- // when this happens, the EventHub holds onto its own user wake lock while the client
- // is processing events. Thus the system can only sleep if there are no events
- // pending or currently being processed.
- //
- // The timeout is advisory only. If the device is asleep, it will not wake just to
- // service the timeout.
- mPendingEventIndex = 0;
-
- mLock.unlock(); // release lock before poll, must be before release_wake_lock
- release_wake_lock(WAKE_LOCK_ID);
-
- int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
-
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
-
- if (pollResult == 0) {
- // Timed out.
- mPendingEventCount = 0;
- break;
- }
-
- if (pollResult < 0) {
- // An error occurred.
- mPendingEventCount = 0;
-
- // Sleep after errors to avoid locking up the system.
- // Hopefully the error is transient.
- if (errno != EINTR) {
- ALOGW("poll failed (errno=%d)\n", errno);
- usleep(100000);
- }
- } else {
- // Some events occurred.
- mPendingEventCount = size_t(pollResult);
- }
- }
-
- // All done, return the number of events we read.
- return event - buffer;
-}
-
-void EventHub::wake() {
- ALOGV("wake() called");
-
- ssize_t nWrite;
- do {
- nWrite = write(mWakeWritePipeFd, "W", 1);
- } while (nWrite == -1 && errno == EINTR);
-
- if (nWrite != 1 && errno != EAGAIN) {
- ALOGW("Could not write wake signal, errno=%d", errno);
- }
-}
-
-void EventHub::scanDevicesLocked() {
- status_t res = scanDirLocked(DEVICE_PATH);
- if(res < 0) {
- ALOGE("scan dir failed for %s\n", DEVICE_PATH);
- }
- if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
- createVirtualKeyboardLocked();
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
- const uint8_t* end = array + endIndex;
- array += startIndex;
- while (array != end) {
- if (*(array++) != 0) {
- return true;
- }
- }
- return false;
-}
-
-static const int32_t GAMEPAD_KEYCODES[] = {
- AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
- AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
- AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
- AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
- AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
- AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
-};
-
-status_t EventHub::openDeviceLocked(const char *devicePath) {
- char buffer[80];
-
- ALOGV("Opening device: %s", devicePath);
-
- int fd = open(devicePath, O_RDWR | O_CLOEXEC);
- if(fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
- return -1;
- }
-
- InputDeviceIdentifier identifier;
-
- // Get device name.
- if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
- } else {
- buffer[sizeof(buffer) - 1] = '\0';
- identifier.name.setTo(buffer);
- }
-
- // Check to see if the device is on our excluded list
- for (size_t i = 0; i < mExcludedDevices.size(); i++) {
- const String8& item = mExcludedDevices.itemAt(i);
- if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
- close(fd);
- return -1;
- }
- }
-
- // Get device driver version.
- int driverVersion;
- if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
- ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
- close(fd);
- return -1;
- }
-
- // Get device identifier.
- struct input_id inputId;
- if(ioctl(fd, EVIOCGID, &inputId)) {
- ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
- close(fd);
- return -1;
- }
- identifier.bus = inputId.bustype;
- identifier.product = inputId.product;
- identifier.vendor = inputId.vendor;
- identifier.version = inputId.version;
-
- // Get device physical location.
- if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
- } else {
- buffer[sizeof(buffer) - 1] = '\0';
- identifier.location.setTo(buffer);
- }
-
- // Get device unique id.
- if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
- } else {
- buffer[sizeof(buffer) - 1] = '\0';
- identifier.uniqueId.setTo(buffer);
- }
-
- // Fill in the descriptor.
- assignDescriptorLocked(identifier);
-
- // Make file descriptor non-blocking for use with poll().
- if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
- ALOGE("Error %d making device file descriptor non-blocking.", errno);
- close(fd);
- return -1;
- }
-
- // Allocate device. (The device object takes ownership of the fd at this point.)
- int32_t deviceId = mNextDeviceId++;
- Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
-
- ALOGV("add device %d: %s\n", deviceId, devicePath);
- ALOGV(" bus: %04x\n"
- " vendor %04x\n"
- " product %04x\n"
- " version %04x\n",
- identifier.bus, identifier.vendor, identifier.product, identifier.version);
- ALOGV(" name: \"%s\"\n", identifier.name.string());
- ALOGV(" location: \"%s\"\n", identifier.location.string());
- ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
- ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
- ALOGV(" driver: v%d.%d.%d\n",
- driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
-
- // Load the configuration file for the device.
- loadConfigurationLocked(device);
-
- // Figure out the kinds of events the device reports.
- ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
- ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
- ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
- ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
- ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
- ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
-
- // See if this is a keyboard. Ignore everything in the button range except for
- // joystick and gamepad buttons which are handled like keyboards for the most part.
- bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
- || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX + 1));
- bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
- sizeof_bit_array(BTN_MOUSE))
- || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
- sizeof_bit_array(BTN_DIGI));
- if (haveKeyboardKeys || haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
- }
-
- // See if this is a cursor device such as a trackball or mouse.
- if (test_bit(BTN_MOUSE, device->keyBitmask)
- && test_bit(REL_X, device->relBitmask)
- && test_bit(REL_Y, device->relBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_CURSOR;
- }
-
- // See if this is a touch pad.
- // Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
- && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
- // Some joysticks such as the PS3 controller report axes that conflict
- // with the ABS_MT range. Try to confirm that the device really is
- // a touch screen.
- if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
- }
- // Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, device->keyBitmask)
- && test_bit(ABS_X, device->absBitmask)
- && test_bit(ABS_Y, device->absBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH;
- }
-
- // See if this device is a joystick.
- // Assumes that joysticks always have gamepad buttons in order to distinguish them
- // from other devices such as accelerometers that also have absolute axes.
- if (haveGamepadButtons) {
- uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
- for (int i = 0; i <= ABS_MAX; i++) {
- if (test_bit(i, device->absBitmask)
- && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
- device->classes = assumedClasses;
- break;
- }
- }
- }
-
- // Check whether this device has switches.
- for (int i = 0; i <= SW_MAX; i++) {
- if (test_bit(i, device->swBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_SWITCH;
- break;
- }
- }
-
- // Check whether this device supports the vibrator.
- if (test_bit(FF_RUMBLE, device->ffBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
- }
-
- // Configure virtual keys.
- if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
- // Load the virtual keys for the touch screen, if any.
- // We do this now so that we can make sure to load the keymap if necessary.
- status_t status = loadVirtualKeyMapLocked(device);
- if (!status) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
- }
- }
-
- // Load the key map.
- // We need to do this for joysticks too because the key layout may specify axes.
- status_t keyMapStatus = NAME_NOT_FOUND;
- if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
- // Load the keymap for the device.
- keyMapStatus = loadKeyMapLocked(device);
- }
-
- // Configure the keyboard, gamepad or virtual keyboard.
- if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
- // Register the keyboard as a built-in keyboard if it is eligible.
- if (!keyMapStatus
- && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
- && isEligibleBuiltInKeyboard(device->identifier,
- device->configuration, &device->keyMap)) {
- mBuiltInKeyboardId = device->id;
- }
-
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
- }
-
- // See if this device has a DPAD.
- if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
- device->classes |= INPUT_DEVICE_CLASS_DPAD;
- }
-
- // See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
- device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
- break;
- }
- }
-
- // Disable kernel key repeat since we handle it ourselves
- unsigned int repeatRate[] = {0,0};
- if (ioctl(fd, EVIOCSREP, repeatRate)) {
- ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));
- }
- }
-
- // If the device isn't recognized as something we handle, don't monitor it.
- if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'",
- deviceId, devicePath, device->identifier.name.string());
- delete device;
- return -1;
- }
-
- // Determine whether the device is external or internal.
- if (isExternalDeviceLocked(device)) {
- device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
- }
-
- if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
- && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- device->controllerNumber = getNextControllerNumberLocked(device);
- setLedForController(device);
- }
-
- // Register with epoll.
- struct epoll_event eventItem;
- memset(&eventItem, 0, sizeof(eventItem));
- eventItem.events = EPOLLIN;
- eventItem.data.u32 = deviceId;
- if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
- ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
- delete device;
- return -1;
- }
-
- // Enable wake-lock behavior on kernels that support it.
- // TODO: Only need this for devices that can really wake the system.
-#ifndef EVIOCSSUSPENDBLOCK
- // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
- // will use an epoll flag instead, so as long as we want to support
- // this feature, we need to be prepared to define the ioctl ourselves.
-#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
-#endif
- bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);
-
- // Tell the kernel that we want to use the monotonic clock for reporting timestamps
- // associated with input events. This is important because the input system
- // uses the timestamps extensively and assumes they were recorded using the monotonic
- // clock.
- //
- // In older kernel, before Linux 3.4, there was no way to tell the kernel which
- // clock to use to input event timestamps. The standard kernel behavior was to
- // record a real time timestamp, which isn't what we want. Android kernels therefore
- // contained a patch to the evdev_event() function in drivers/input/evdev.c to
- // replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic
- // clock to be used instead of the real time clock.
- //
- // As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.
- // Therefore, we no longer require the Android-specific kernel patch described above
- // as long as we make sure to set select the monotonic clock. We do that here.
- int clockId = CLOCK_MONOTONIC;
- bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
-
- ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
- "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
- "usingSuspendBlockIoctl=%s, usingClockIoctl=%s",
- deviceId, fd, devicePath, device->identifier.name.string(),
- device->classes,
- device->configurationFile.string(),
- device->keyMap.keyLayoutFile.string(),
- device->keyMap.keyCharacterMapFile.string(),
- toString(mBuiltInKeyboardId == deviceId),
- toString(usingSuspendBlockIoctl), toString(usingClockIoctl));
-
- addDeviceLocked(device);
- return 0;
-}
-
-void EventHub::createVirtualKeyboardLocked() {
- InputDeviceIdentifier identifier;
- identifier.name = "Virtual";
- identifier.uniqueId = "<virtual>";
- assignDescriptorLocked(identifier);
-
- Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier);
- device->classes = INPUT_DEVICE_CLASS_KEYBOARD
- | INPUT_DEVICE_CLASS_ALPHAKEY
- | INPUT_DEVICE_CLASS_DPAD
- | INPUT_DEVICE_CLASS_VIRTUAL;
- loadKeyMapLocked(device);
- addDeviceLocked(device);
-}
-
-void EventHub::addDeviceLocked(Device* device) {
- mDevices.add(device->id, device);
- device->next = mOpeningDevices;
- mOpeningDevices = device;
-}
-
-void EventHub::loadConfigurationLocked(Device* device) {
- device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
- if (device->configurationFile.isEmpty()) {
- ALOGD("No input device configuration file found for device '%s'.",
- device->identifier.name.string());
- } else {
- status_t status = PropertyMap::load(device->configurationFile,
- &device->configuration);
- if (status) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- device->identifier.name.string());
- }
- }
-}
-
-status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
- // The virtual key map is supplied by the kernel as a system board property file.
- String8 path;
- path.append("/sys/board_properties/virtualkeys.");
- path.append(device->identifier.name);
- if (access(path.string(), R_OK)) {
- return NAME_NOT_FOUND;
- }
- return VirtualKeyMap::load(path, &device->virtualKeyMap);
-}
-
-status_t EventHub::loadKeyMapLocked(Device* device) {
- return device->keyMap.load(device->identifier, device->configuration);
-}
-
-bool EventHub::isExternalDeviceLocked(Device* device) {
- if (device->configuration) {
- bool value;
- if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
- return !value;
- }
- }
- return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
-}
-
-int32_t EventHub::getNextControllerNumberLocked(Device* device) {
- if (mControllerNumbers.isFull()) {
- ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
- device->identifier.name.string());
- return 0;
- }
- // Since the controller number 0 is reserved for non-controllers, translate all numbers up by
- // one
- return static_cast<int32_t>(mControllerNumbers.markFirstUnmarkedBit() + 1);
-}
-
-void EventHub::releaseControllerNumberLocked(Device* device) {
- int32_t num = device->controllerNumber;
- device->controllerNumber= 0;
- if (num == 0) {
- return;
- }
- mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
-}
-
-void EventHub::setLedForController(Device* device) {
- for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
- setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1);
- }
-}
-
-bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
- if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
- return false;
- }
-
- Vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
- const size_t N = scanCodes.size();
- for (size_t i=0; i<N && i<=KEY_MAX; i++) {
- int32_t sc = scanCodes.itemAt(i);
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
- return true;
- }
- }
-
- return false;
-}
-
-status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const {
- if (!device->keyMap.haveKeyLayout() || !device->ledBitmask) {
- return NAME_NOT_FOUND;
- }
-
- int32_t scanCode;
- if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
- *outScanCode = scanCode;
- return NO_ERROR;
- }
- }
- return NAME_NOT_FOUND;
-}
-
-status_t EventHub::closeDeviceByPathLocked(const char *devicePath) {
- Device* device = getDeviceByPathLocked(devicePath);
- if (device) {
- closeDeviceLocked(device);
- return 0;
- }
- ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
- return -1;
-}
-
-void EventHub::closeAllDevicesLocked() {
- while (mDevices.size() > 0) {
- closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
- }
-}
-
-void EventHub::closeDeviceLocked(Device* device) {
- ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
- device->path.string(), device->identifier.name.string(), device->id,
- device->fd, device->classes);
-
- if (device->id == mBuiltInKeyboardId) {
- ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.string(), mBuiltInKeyboardId);
- mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
- }
-
- if (!device->isVirtual()) {
- if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
- ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
- }
- }
-
- releaseControllerNumberLocked(device);
-
- mDevices.removeItem(device->id);
- device->close();
-
- // Unlink for opening devices list if it is present.
- Device* pred = NULL;
- bool found = false;
- for (Device* entry = mOpeningDevices; entry != NULL; ) {
- if (entry == device) {
- found = true;
- break;
- }
- pred = entry;
- entry = entry->next;
- }
- if (found) {
- // Unlink the device from the opening devices list then delete it.
- // We don't need to tell the client that the device was closed because
- // it does not even know it was opened in the first place.
- ALOGI("Device %s was immediately closed after opening.", device->path.string());
- if (pred) {
- pred->next = device->next;
- } else {
- mOpeningDevices = device->next;
- }
- delete device;
- } else {
- // Link into closing devices list.
- // The device will be deleted later after we have informed the client.
- device->next = mClosingDevices;
- mClosingDevices = device;
- }
-}
-
-status_t EventHub::readNotifyLocked() {
- int res;
- char devname[PATH_MAX];
- char *filename;
- char event_buf[512];
- int event_size;
- int event_pos = 0;
- struct inotify_event *event;
-
- ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
- res = read(mINotifyFd, event_buf, sizeof(event_buf));
- if(res < (int)sizeof(*event)) {
- if(errno == EINTR)
- return 0;
- ALOGW("could not get event, %s\n", strerror(errno));
- return -1;
- }
- //printf("got %d bytes of event information\n", res);
-
- strcpy(devname, DEVICE_PATH);
- filename = devname + strlen(devname);
- *filename++ = '/';
-
- while(res >= (int)sizeof(*event)) {
- event = (struct inotify_event *)(event_buf + event_pos);
- //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
- if(event->len) {
- strcpy(filename, event->name);
- if(event->mask & IN_CREATE) {
- openDeviceLocked(devname);
- } else {
- ALOGI("Removing device '%s' due to inotify event\n", devname);
- closeDeviceByPathLocked(devname);
- }
- }
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
- }
- return 0;
-}
-
-status_t EventHub::scanDirLocked(const char *dirname)
-{
- char devname[PATH_MAX];
- char *filename;
- DIR *dir;
- struct dirent *de;
- dir = opendir(dirname);
- if(dir == NULL)
- return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while((de = readdir(dir))) {
- if(de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDeviceLocked(devname);
- }
- closedir(dir);
- return 0;
-}
-
-void EventHub::requestReopenDevices() {
- ALOGV("requestReopenDevices() called");
-
- AutoMutex _l(mLock);
- mNeedToReopenDevices = true;
-}
-
-void EventHub::dump(String8& dump) {
- dump.append("Event Hub State:\n");
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- dump.appendFormat(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
-
- dump.append(INDENT "Devices:\n");
-
- for (size_t i = 0; i < mDevices.size(); i++) {
- const Device* device = mDevices.valueAt(i);
- if (mBuiltInKeyboardId == device->id) {
- dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
- device->id, device->identifier.name.string());
- } else {
- dump.appendFormat(INDENT2 "%d: %s\n", device->id,
- device->identifier.name.string());
- }
- dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
- dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
- dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
- dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
- dump.appendFormat(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
- dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
- dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
- "product=0x%04x, version=0x%04x\n",
- device->identifier.bus, device->identifier.vendor,
- device->identifier.product, device->identifier.version);
- dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
- device->keyMap.keyLayoutFile.string());
- dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
- device->keyMap.keyCharacterMapFile.string());
- dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
- device->configurationFile.string());
- dump.appendFormat(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
- toString(device->overlayKeyMap != NULL));
- }
- } // release lock
-}
-
-void EventHub::monitor() {
- // Acquire and release the lock to ensure that the event hub has not deadlocked.
- mLock.lock();
- mLock.unlock();
-}
-
-
-}; // namespace android
diff --git a/libs/input/EventHub.h b/libs/input/EventHub.h
deleted file mode 100644
index 86c05af..0000000
--- a/libs/input/EventHub.h
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-#ifndef _RUNTIME_EVENT_HUB_H
-#define _RUNTIME_EVENT_HUB_H
-
-#include <input/Input.h>
-#include <input/InputDevice.h>
-#include <input/Keyboard.h>
-#include <input/KeyLayoutMap.h>
-#include <input/KeyCharacterMap.h>
-#include <input/VirtualKeyMap.h>
-#include <utils/String8.h>
-#include <utils/threads.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/List.h>
-#include <utils/Errors.h>
-#include <utils/PropertyMap.h>
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <utils/BitSet.h>
-
-#include <linux/input.h>
-#include <sys/epoll.h>
-
-/* Convenience constants. */
-
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f // last button code
-
-/*
- * These constants are used privately in Android to pass raw timestamps
- * through evdev from uinput device drivers because there is currently no
- * other way to transfer this information. The evdev driver automatically
- * timestamps all input events with the time they were posted and clobbers
- * whatever information was passed in.
- *
- * For the purposes of this hack, the timestamp is specified in the
- * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
- * seconds and microseconds.
- */
-#define MSC_ANDROID_TIME_SEC 0x6
-#define MSC_ANDROID_TIME_USEC 0x7
-
-namespace android {
-
-enum {
- // Device id of a special "virtual" keyboard that is always present.
- VIRTUAL_KEYBOARD_ID = -1,
- // Device id of the "built-in" keyboard if there is one.
- BUILT_IN_KEYBOARD_ID = 0,
-};
-
-/*
- * A raw event as retrieved from the EventHub.
- */
-struct RawEvent {
- nsecs_t when;
- int32_t deviceId;
- int32_t type;
- int32_t code;
- int32_t value;
-};
-
-/* Describes an absolute axis. */
-struct RawAbsoluteAxisInfo {
- bool valid; // true if the information is valid, false otherwise
-
- int32_t minValue; // minimum value
- int32_t maxValue; // maximum value
- int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
- int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
- int32_t resolution; // resolution in units per mm or radians per mm
-
- inline void clear() {
- valid = false;
- minValue = 0;
- maxValue = 0;
- flat = 0;
- fuzz = 0;
- resolution = 0;
- }
-};
-
-/*
- * Input device classes.
- */
-enum {
- /* The input device is a keyboard or has buttons. */
- INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
-
- /* The input device is an alpha-numeric keyboard (not just a dial pad). */
- INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
-
- /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
-
- /* The input device is a cursor device such as a trackball or mouse. */
- INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
-
- /* The input device is a multi-touch touchscreen. */
- INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
-
- /* The input device is a directional pad (implies keyboard, has DPAD keys). */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020,
-
- /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
-
- /* The input device has switches. */
- INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
-
- /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
-
- /* The input device has a vibrator (supports FF_RUMBLE). */
- INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
-
- /* The input device is virtual (not a real device, not part of UI configuration). */
- INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
-
- /* The input device is external (not built-in). */
- INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
-};
-
-/*
- * Gets the class that owns an axis, in cases where multiple classes might claim
- * the same axis for different purposes.
- */
-extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
-
-/*
- * Grand Central Station for events.
- *
- * The event hub aggregates input events received across all known input
- * devices on the system, including devices that may be emulated by the simulator
- * environment. In addition, the event hub generates fake input events to indicate
- * when devices are added or removed.
- *
- * The event hub provides a stream of input events (via the getEvent function).
- * It also supports querying the current actual state of input devices such as identifying
- * which keys are currently down. Finally, the event hub keeps track of the capabilities of
- * individual input devices, such as their class and the set of key codes that they support.
- */
-class EventHubInterface : public virtual RefBase {
-protected:
- EventHubInterface() { }
- virtual ~EventHubInterface() { }
-
-public:
- // Synthetic raw event type codes produced when devices are added or removed.
- enum {
- // Sent when a device is added.
- DEVICE_ADDED = 0x10000000,
- // Sent when a device is removed.
- DEVICE_REMOVED = 0x20000000,
- // Sent when all added/removed devices from the most recent scan have been reported.
- // This event is always sent at least once.
- FINISHED_DEVICE_SCAN = 0x30000000,
-
- FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
- };
-
- virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
-
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
-
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const = 0;
-
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
-
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const = 0;
-
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
-
- virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
-
- virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t* outKeycode, uint32_t* outFlags) const = 0;
-
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const = 0;
-
- // Sets devices that are excluded from opening.
- // This can be used to ignore input devices for sensors.
- virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
-
- /*
- * Wait for events to become available and returns them.
- * After returning, the EventHub holds onto a wake lock until the next call to getEvent.
- * This ensures that the device will not go to sleep while the event is being processed.
- * If the device needs to remain awake longer than that, then the caller is responsible
- * for taking care of it (say, by poking the power manager user activity timer).
- *
- * The timeout is advisory only. If the device is asleep, it will not wake just to
- * service the timeout.
- *
- * Returns the number of events obtained, or 0 if the timeout expired.
- */
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
-
- /*
- * Query current input state.
- */
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const = 0;
-
- /*
- * Examine key input devices for specific framework keycode support
- */
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const = 0;
-
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
-
- /* LED related functions expect Android LED constants, not scan codes or HID usages */
- virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
-
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
-
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
-
- /* Control the vibrator. */
- virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
- virtual void cancelVibrate(int32_t deviceId) = 0;
-
- /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
- virtual void requestReopenDevices() = 0;
-
- /* Wakes up getEvents() if it is blocked on a read. */
- virtual void wake() = 0;
-
- /* Dump EventHub state to a string. */
- virtual void dump(String8& dump) = 0;
-
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
- virtual void monitor() = 0;
-};
-
-class EventHub : public EventHubInterface
-{
-public:
- EventHub();
-
- virtual uint32_t getDeviceClasses(int32_t deviceId) const;
-
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
-
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const;
-
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
-
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const;
-
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
-
- virtual bool hasInputProperty(int32_t deviceId, int property) const;
-
- virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t* outKeycode, uint32_t* outFlags) const;
-
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const;
-
- virtual void setExcludedDevices(const Vector<String8>& devices);
-
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
-
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) const;
-
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
-
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
- virtual bool hasLed(int32_t deviceId, int32_t led) const;
- virtual void setLedState(int32_t deviceId, int32_t led, bool on);
-
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const;
-
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
-
- virtual void vibrate(int32_t deviceId, nsecs_t duration);
- virtual void cancelVibrate(int32_t deviceId);
-
- virtual void requestReopenDevices();
-
- virtual void wake();
-
- virtual void dump(String8& dump);
- virtual void monitor();
-
-protected:
- virtual ~EventHub();
-
-private:
- struct Device {
- Device* next;
-
- int fd; // may be -1 if device is virtual
- const int32_t id;
- const String8 path;
- const InputDeviceIdentifier identifier;
-
- uint32_t classes;
-
- uint8_t keyBitmask[(KEY_MAX + 1) / 8];
- uint8_t absBitmask[(ABS_MAX + 1) / 8];
- uint8_t relBitmask[(REL_MAX + 1) / 8];
- uint8_t swBitmask[(SW_MAX + 1) / 8];
- uint8_t ledBitmask[(LED_MAX + 1) / 8];
- uint8_t ffBitmask[(FF_MAX + 1) / 8];
- uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
-
- String8 configurationFile;
- PropertyMap* configuration;
- VirtualKeyMap* virtualKeyMap;
- KeyMap keyMap;
-
- sp<KeyCharacterMap> overlayKeyMap;
- sp<KeyCharacterMap> combinedKeyMap;
-
- bool ffEffectPlaying;
- int16_t ffEffectId; // initially -1
-
- int32_t controllerNumber;
-
- int32_t timestampOverrideSec;
- int32_t timestampOverrideUsec;
-
- Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
- ~Device();
-
- void close();
-
- inline bool isVirtual() const { return fd < 0; }
-
- const sp<KeyCharacterMap>& getKeyCharacterMap() const {
- if (combinedKeyMap != NULL) {
- return combinedKeyMap;
- }
- return keyMap.keyCharacterMap;
- }
- };
-
- status_t openDeviceLocked(const char *devicePath);
- void createVirtualKeyboardLocked();
- void addDeviceLocked(Device* device);
- void assignDescriptorLocked(InputDeviceIdentifier& identifier);
-
- status_t closeDeviceByPathLocked(const char *devicePath);
- void closeDeviceLocked(Device* device);
- void closeAllDevicesLocked();
-
- status_t scanDirLocked(const char *dirname);
- void scanDevicesLocked();
- status_t readNotifyLocked();
-
- Device* getDeviceByDescriptorLocked(String8& descriptor) const;
- Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const char* devicePath) const;
-
- bool hasKeycodeLocked(Device* device, int keycode) const;
-
- void loadConfigurationLocked(Device* device);
- status_t loadVirtualKeyMapLocked(Device* device);
- status_t loadKeyMapLocked(Device* device);
-
- bool isExternalDeviceLocked(Device* device);
-
- int32_t getNextControllerNumberLocked(Device* device);
- void releaseControllerNumberLocked(Device* device);
- void setLedForController(Device* device);
-
- status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
- void setLedStateLocked(Device* device, int32_t led, bool on);
-
- // Protect all internal state.
- mutable Mutex mLock;
-
- // The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none.
- // EventHub remaps the built-in keyboard to id 0 externally as required by the API.
- enum {
- // Must not conflict with any other assigned device ids, including
- // the virtual keyboard id (-1).
- NO_BUILT_IN_KEYBOARD = -2,
- };
- int32_t mBuiltInKeyboardId;
-
- int32_t mNextDeviceId;
-
- BitSet32 mControllerNumbers;
-
- KeyedVector<int32_t, Device*> mDevices;
-
- Device *mOpeningDevices;
- Device *mClosingDevices;
-
- bool mNeedToSendFinishedDeviceScan;
- bool mNeedToReopenDevices;
- bool mNeedToScanDevices;
- Vector<String8> mExcludedDevices;
-
- int mEpollFd;
- int mINotifyFd;
- int mWakeReadPipeFd;
- int mWakeWritePipeFd;
-
- // Ids used for epoll notifications not associated with devices.
- static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
- static const uint32_t EPOLL_ID_WAKE = 0x80000002;
-
- // Epoll FD list size hint.
- static const int EPOLL_SIZE_HINT = 8;
-
- // Maximum number of signalled FDs to handle at a time.
- static const int EPOLL_MAX_EVENTS = 16;
-
- // The array of pending epoll events and the index of the next event to be handled.
- struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
- size_t mPendingEventCount;
- size_t mPendingEventIndex;
- bool mPendingINotify;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_EVENT_HUB_H
diff --git a/libs/input/InputApplication.h b/libs/input/InputApplication.h
deleted file mode 100644
index 1f5504c..0000000
--- a/libs/input/InputApplication.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef _UI_INPUT_APPLICATION_H
-#define _UI_INPUT_APPLICATION_H
-
-#include <input/Input.h>
-
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/String8.h>
-
-namespace android {
-
-/*
- * Describes the properties of an application that can receive input.
- */
-struct InputApplicationInfo {
- String8 name;
- nsecs_t dispatchingTimeout;
-};
-
-
-/*
- * Handle for an application that can receive input.
- *
- * Used by the native input dispatcher as a handle for the window manager objects
- * that describe an application.
- */
-class InputApplicationHandle : public RefBase {
-public:
- inline const InputApplicationInfo* getInfo() const {
- return mInfo;
- }
-
- inline String8 getName() const {
- return mInfo ? mInfo->name : String8("<invalid>");
- }
-
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
- }
-
- /**
- * Requests that the state of this object be updated to reflect
- * the most current available information about the application.
- *
- * This method should only be called from within the input dispatcher's
- * critical section.
- *
- * Returns true on success, or false if the handle is no longer valid.
- */
- virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
-protected:
- InputApplicationHandle();
- virtual ~InputApplicationHandle();
-
- InputApplicationInfo* mInfo;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_APPLICATION_H
diff --git a/libs/input/InputDispatcher.cpp b/libs/input/InputDispatcher.cpp
deleted file mode 100644
index f594df2..0000000
--- a/libs/input/InputDispatcher.cpp
+++ /dev/null
@@ -1,4496 +0,0 @@
-/*
- * 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 "InputDispatcher"
-#define ATRACE_TAG ATRACE_TAG_INPUT
-
-//#define LOG_NDEBUG 0
-
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 0
-
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
-
-// Log debug messages about input event injection.
-#define DEBUG_INJECTION 0
-
-// Log debug messages about input focus tracking.
-#define DEBUG_FOCUS 0
-
-// Log debug messages about the app switch latency optimization.
-#define DEBUG_APP_SWITCH 0
-
-// Log debug messages about hover events.
-#define DEBUG_HOVER 0
-
-#include "InputDispatcher.h"
-
-#include <utils/Trace.h>
-#include <cutils/log.h>
-#include <androidfw/PowerManager.h>
-
-#include <stddef.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <time.h>
-
-#define INDENT " "
-#define INDENT2 " "
-#define INDENT3 " "
-#define INDENT4 " "
-
-namespace android {
-
-// Default input dispatching timeout if there is no focused application or paused window
-// from which to determine an appropriate dispatching timeout.
-const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
-
-// Amount of time to allow for all pending events to be processed when an app switch
-// key is on the way. This is used to preempt input dispatch and drop input events
-// when an application takes too long to respond and the user has pressed an app switch key.
-const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
-// Amount of time to allow for an event to be dispatched (measured since its eventTime)
-// before considering it stale and dropping it.
-const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
-
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished. This value extends the ANR timeout by the specified
-// amount. For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-const nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
-// Log a warning when an event takes longer than this to process, even if an ANR does not occur.
-const nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
-
-// Number of recent events to keep for debugging purposes.
-const size_t RECENT_QUEUE_MAX_SIZE = 10;
-
-static inline nsecs_t now() {
- return systemTime(SYSTEM_TIME_MONOTONIC);
-}
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
- return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-}
-
-static bool isValidKeyAction(int32_t action) {
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- case AKEY_EVENT_ACTION_UP:
- return true;
- default:
- return false;
- }
-}
-
-static bool validateKeyEvent(int32_t action) {
- if (! isValidKeyAction(action)) {
- ALOGE("Key event has invalid action code 0x%x", action);
- return false;
- }
- return true;
-}
-
-static bool isValidMotionAction(int32_t action, size_t pointerCount) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
- case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_EXIT:
- case AMOTION_EVENT_ACTION_SCROLL:
- return true;
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && size_t(index) < pointerCount;
- }
- default:
- return false;
- }
-}
-
-static bool validateMotionEvent(int32_t action, size_t pointerCount,
- const PointerProperties* pointerProperties) {
- if (! isValidMotionAction(action, pointerCount)) {
- ALOGE("Motion event has invalid action code 0x%x", action);
- return false;
- }
- if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
- ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.",
- pointerCount, MAX_POINTERS);
- return false;
- }
- BitSet32 pointerIdBits;
- for (size_t i = 0; i < pointerCount; i++) {
- int32_t id = pointerProperties[i].id;
- if (id < 0 || id > MAX_POINTER_ID) {
- ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
- id, MAX_POINTER_ID);
- return false;
- }
- if (pointerIdBits.hasBit(id)) {
- ALOGE("Motion event has duplicate pointer id %d", id);
- return false;
- }
- pointerIdBits.markBit(id);
- }
- return true;
-}
-
-static bool isMainDisplay(int32_t displayId) {
- return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
-static void dumpRegion(String8& dump, const SkRegion& region) {
- if (region.isEmpty()) {
- dump.append("<empty>");
- return;
- }
-
- bool first = true;
- for (SkRegion::Iterator it(region); !it.done(); it.next()) {
- if (first) {
- first = false;
- } else {
- dump.append("|");
- }
- const SkIRect& rect = it.rect();
- dump.appendFormat("[%d,%d][%d,%d]", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
- }
-}
-
-
-// --- InputDispatcher ---
-
-InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
- mPolicy(policy),
- mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
- mNextUnblockedEvent(NULL),
- mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
- mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
- mLooper = new Looper(false);
-
- mKeyRepeatState.lastKeyEntry = NULL;
-
- policy->getDispatcherConfiguration(&mConfig);
-}
-
-InputDispatcher::~InputDispatcher() {
- { // acquire lock
- AutoMutex _l(mLock);
-
- resetKeyRepeatLocked();
- releasePendingEventLocked();
- drainInboundQueueLocked();
- }
-
- while (mConnectionsByFd.size() != 0) {
- unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
- }
-}
-
-void InputDispatcher::dispatchOnce() {
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- mDispatcherIsAliveCondition.broadcast();
-
- // Run a dispatch loop if there are no pending commands.
- // The dispatch loop might enqueue commands to run afterwards.
- if (!haveCommandsLocked()) {
- dispatchOnceInnerLocked(&nextWakeupTime);
- }
-
- // Run all pending commands if there are any.
- // If any commands were run then force the next poll to wake up immediately.
- if (runCommandsLockedInterruptible()) {
- nextWakeupTime = LONG_LONG_MIN;
- }
- } // release lock
-
- // Wait for callback or timeout or wake. (make sure we round up, not down)
- nsecs_t currentTime = now();
- int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
- mLooper->pollOnce(timeoutMillis);
-}
-
-void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
- nsecs_t currentTime = now();
-
- // Reset the key repeat timer whenever normal dispatch is suspended while the
- // device is in a non-interactive state. This is to ensure that we abort a key
- // repeat if the device is just coming out of sleep.
- if (!mDispatchEnabled) {
- resetKeyRepeatLocked();
- }
-
- // If dispatching is frozen, do not process timeouts or try to deliver any new events.
- if (mDispatchFrozen) {
-#if DEBUG_FOCUS
- ALOGD("Dispatch frozen. Waiting some more.");
-#endif
- return;
- }
-
- // Optimize latency of app switches.
- // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
- // been pressed. When it expires, we preempt dispatch and drop all other pending events.
- bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
- if (mAppSwitchDueTime < *nextWakeupTime) {
- *nextWakeupTime = mAppSwitchDueTime;
- }
-
- // Ready to start a new event.
- // If we don't already have a pending event, go grab one.
- if (! mPendingEvent) {
- if (mInboundQueue.isEmpty()) {
- if (isAppSwitchDue) {
- // The inbound queue is empty so the app switch key we were waiting
- // for will never arrive. Stop waiting for it.
- resetPendingAppSwitchLocked(false);
- isAppSwitchDue = false;
- }
-
- // Synthesize a key repeat if appropriate.
- if (mKeyRepeatState.lastKeyEntry) {
- if (currentTime >= mKeyRepeatState.nextRepeatTime) {
- mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
- } else {
- if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
- *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
- }
- }
- }
-
- // Nothing to do if there is no pending event.
- if (!mPendingEvent) {
- return;
- }
- } else {
- // Inbound queue has at least one entry.
- mPendingEvent = mInboundQueue.dequeueAtHead();
- traceInboundQueueLengthLocked();
- }
-
- // Poke user activity for this event.
- if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
- pokeUserActivityLocked(mPendingEvent);
- }
-
- // Get ready to dispatch the event.
- resetANRTimeoutsLocked();
- }
-
- // Now we have an event to dispatch.
- // All events are eventually dequeued and processed this way, even if we intend to drop them.
- ALOG_ASSERT(mPendingEvent != NULL);
- bool done = false;
- DropReason dropReason = DROP_REASON_NOT_DROPPED;
- if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
- dropReason = DROP_REASON_POLICY;
- } else if (!mDispatchEnabled) {
- dropReason = DROP_REASON_DISABLED;
- }
-
- if (mNextUnblockedEvent == mPendingEvent) {
- mNextUnblockedEvent = NULL;
- }
-
- switch (mPendingEvent->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(mPendingEvent);
- done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
- dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
- break;
- }
-
- case EventEntry::TYPE_DEVICE_RESET: {
- DeviceResetEntry* typedEntry =
- static_cast<DeviceResetEntry*>(mPendingEvent);
- done = dispatchDeviceResetLocked(currentTime, typedEntry);
- dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
- break;
- }
-
- case EventEntry::TYPE_KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
- if (isAppSwitchDue) {
- if (isAppSwitchKeyEventLocked(typedEntry)) {
- resetPendingAppSwitchLocked(true);
- isAppSwitchDue = false;
- } else if (dropReason == DROP_REASON_NOT_DROPPED) {
- dropReason = DROP_REASON_APP_SWITCH;
- }
- }
- if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
- dropReason = DROP_REASON_STALE;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DROP_REASON_BLOCKED;
- }
- done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
- if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
- dropReason = DROP_REASON_APP_SWITCH;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
- dropReason = DROP_REASON_STALE;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DROP_REASON_BLOCKED;
- }
- done = dispatchMotionLocked(currentTime, typedEntry,
- &dropReason, nextWakeupTime);
- break;
- }
-
- default:
- ALOG_ASSERT(false);
- break;
- }
-
- if (done) {
- if (dropReason != DROP_REASON_NOT_DROPPED) {
- dropInboundEventLocked(mPendingEvent, dropReason);
- }
-
- releasePendingEventLocked();
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
-}
-
-bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
- bool needWake = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(entry);
- traceInboundQueueLengthLocked();
-
- switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- // Optimize app switch latency.
- // If the application takes too long to catch up then we drop all events preceding
- // the app switch key.
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
-#if DEBUG_APP_SWITCH
- ALOGD("App switch is pending!");
-#endif
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
- }
- }
- }
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- // Optimize case where the current application is unresponsive and the user
- // decides to touch a window in a different application.
- // If the application takes too long to catch up then we drop all events preceding
- // the touch into the other window.
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
- && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
- && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
- && mInputTargetWaitApplicationHandle != NULL) {
- int32_t displayId = motionEntry->displayId;
- int32_t x = int32_t(motionEntry->pointerCoords[0].
- getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(motionEntry->pointerCoords[0].
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
- if (touchedWindowHandle != NULL
- && touchedWindowHandle->inputApplicationHandle
- != mInputTargetWaitApplicationHandle) {
- // User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- mNextUnblockedEvent = motionEntry;
- needWake = true;
- }
- }
- break;
- }
- }
-
- return needWake;
-}
-
-void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
- entry->refCount += 1;
- mRecentQueue.enqueueAtTail(entry);
- if (mRecentQueue.count() > RECENT_QUEUE_MAX_SIZE) {
- mRecentQueue.dequeueAtHead()->release();
- }
-}
-
-sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
- int32_t x, int32_t y) {
- // Traverse windows from front to back to find touched window.
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo->displayId == displayId) {
- int32_t flags = windowInfo->layoutParamsFlags;
- int32_t privateFlags = windowInfo->layoutParamsPrivateFlags;
-
- if (windowInfo->visible) {
- if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
- | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
- if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- // Found window.
- return windowHandle;
- }
- }
- }
-
- if (privateFlags & InputWindowInfo::PRIVATE_FLAG_SYSTEM_ERROR) {
- // Error window is on top but not visible, so touch is dropped.
- return NULL;
- }
- }
- }
- return NULL;
-}
-
-void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
- const char* reason;
- switch (dropReason) {
- case DROP_REASON_POLICY:
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("Dropped event because policy consumed it.");
-#endif
- reason = "inbound event was dropped because the policy consumed it";
- break;
- case DROP_REASON_DISABLED:
- ALOGI("Dropped event because input dispatch is disabled.");
- reason = "inbound event was dropped because input dispatch is disabled";
- break;
- case DROP_REASON_APP_SWITCH:
- ALOGI("Dropped event because of pending overdue app switch.");
- reason = "inbound event was dropped because of pending overdue app switch";
- break;
- case DROP_REASON_BLOCKED:
- ALOGI("Dropped event because the current application is not responding and the user "
- "has started interacting with a different application.");
- reason = "inbound event was dropped because the current application is not responding "
- "and the user has started interacting with a different application";
- break;
- case DROP_REASON_STALE:
- ALOGI("Dropped event because it is stale.");
- reason = "inbound event was dropped because it is stale";
- break;
- default:
- ALOG_ASSERT(false);
- return;
- }
-
- switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- break;
- }
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- } else {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- }
- break;
- }
- }
-}
-
-bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) {
- return keyCode == AKEYCODE_HOME
- || keyCode == AKEYCODE_ENDCALL
- || keyCode == AKEYCODE_APP_SWITCH;
-}
-
-bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
- return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
- && isAppSwitchKeyCode(keyEntry->keyCode)
- && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
- && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
-}
-
-bool InputDispatcher::isAppSwitchPendingLocked() {
- return mAppSwitchDueTime != LONG_LONG_MAX;
-}
-
-void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
- mAppSwitchDueTime = LONG_LONG_MAX;
-
-#if DEBUG_APP_SWITCH
- if (handled) {
- ALOGD("App switch has arrived.");
- } else {
- ALOGD("App switch was abandoned.");
- }
-#endif
-}
-
-bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) {
- return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
-}
-
-bool InputDispatcher::haveCommandsLocked() const {
- return !mCommandQueue.isEmpty();
-}
-
-bool InputDispatcher::runCommandsLockedInterruptible() {
- if (mCommandQueue.isEmpty()) {
- return false;
- }
-
- do {
- CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
-
- Command command = commandEntry->command;
- (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
-
- commandEntry->connection.clear();
- delete commandEntry;
- } while (! mCommandQueue.isEmpty());
- return true;
-}
-
-InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
- CommandEntry* commandEntry = new CommandEntry(command);
- mCommandQueue.enqueueAtTail(commandEntry);
- return commandEntry;
-}
-
-void InputDispatcher::drainInboundQueueLocked() {
- while (! mInboundQueue.isEmpty()) {
- EventEntry* entry = mInboundQueue.dequeueAtHead();
- releaseInboundEventLocked(entry);
- }
- traceInboundQueueLengthLocked();
-}
-
-void InputDispatcher::releasePendingEventLocked() {
- if (mPendingEvent) {
- resetANRTimeoutsLocked();
- releaseInboundEventLocked(mPendingEvent);
- mPendingEvent = NULL;
- }
-}
-
-void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
- if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("Injected inbound event was dropped.");
-#endif
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
- }
- if (entry == mNextUnblockedEvent) {
- mNextUnblockedEvent = NULL;
- }
- addRecentEventLocked(entry);
- entry->release();
-}
-
-void InputDispatcher::resetKeyRepeatLocked() {
- if (mKeyRepeatState.lastKeyEntry) {
- mKeyRepeatState.lastKeyEntry->release();
- mKeyRepeatState.lastKeyEntry = NULL;
- }
-}
-
-InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
- KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
-
- // Reuse the repeated key entry if it is otherwise unreferenced.
- uint32_t policyFlags = (entry->policyFlags & POLICY_FLAG_RAW_MASK)
- | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED;
- if (entry->refCount == 1) {
- entry->recycle();
- entry->eventTime = currentTime;
- entry->policyFlags = policyFlags;
- entry->repeatCount += 1;
- } else {
- KeyEntry* newEntry = new KeyEntry(currentTime,
- entry->deviceId, entry->source, policyFlags,
- entry->action, entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount + 1, entry->downTime);
-
- mKeyRepeatState.lastKeyEntry = newEntry;
- entry->release();
-
- entry = newEntry;
- }
- entry->syntheticRepeat = true;
-
- // Increment reference count since we keep a reference to the event in
- // mKeyRepeatState.lastKeyEntry in addition to the one we return.
- entry->refCount += 1;
-
- mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
- return entry;
-}
-
-bool InputDispatcher::dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
-#endif
-
- // Reset key repeating in case a keyboard device was added or removed or something.
- resetKeyRepeatLocked();
-
- // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyConfigurationChangedInterruptible);
- commandEntry->eventTime = entry->eventTime;
- return true;
-}
-
-bool InputDispatcher::dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchDeviceReset - eventTime=%lld, deviceId=%d", entry->eventTime, entry->deviceId);
-#endif
-
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "device was reset");
- options.deviceId = entry->deviceId;
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- return true;
-}
-
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
- // Preprocessing.
- if (! entry->dispatchInProgress) {
- if (entry->repeatCount == 0
- && entry->action == AKEY_EVENT_ACTION_DOWN
- && (entry->policyFlags & POLICY_FLAG_TRUSTED)
- && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
- if (mKeyRepeatState.lastKeyEntry
- && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
- // We have seen two identical key downs in a row which indicates that the device
- // driver is automatically generating key repeats itself. We take note of the
- // repeat here, but we disable our own next key repeat timer since it is clear that
- // we will not need to synthesize key repeats ourselves.
- entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
- resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
- } else {
- // Not a repeat. Save key down state in case we do see a repeat later.
- resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
- }
- mKeyRepeatState.lastKeyEntry = entry;
- entry->refCount += 1;
- } else if (! entry->syntheticRepeat) {
- resetKeyRepeatLocked();
- }
-
- if (entry->repeatCount == 1) {
- entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
- } else {
- entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
- }
-
- entry->dispatchInProgress = true;
-
- logOutboundKeyDetailsLocked("dispatchKey - ", entry);
- }
-
- // Handle case where the policy asked us to try again later last time.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
- if (currentTime < entry->interceptKeyWakeupTime) {
- if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
- *nextWakeupTime = entry->interceptKeyWakeupTime;
- }
- return false; // wait until next wakeup
- }
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
- entry->interceptKeyWakeupTime = 0;
- }
-
- // Give the policy a chance to intercept the key.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
- if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- if (mFocusedWindowHandle != NULL) {
- commandEntry->inputWindowHandle = mFocusedWindowHandle;
- }
- commandEntry->keyEntry = entry;
- entry->refCount += 1;
- return false; // wait for the command to run
- } else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
- }
- } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
- if (*dropReason == DROP_REASON_NOT_DROPPED) {
- *dropReason = DROP_REASON_POLICY;
- }
- }
-
- // Clean up if dropping the event.
- if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
- ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
- return true;
- }
-
- // Identify targets.
- Vector<InputTarget> inputTargets;
- int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime);
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
- return false;
- }
-
- setInjectionResultLocked(entry, injectionResult);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- return true;
- }
-
- addMonitoringTargetsLocked(inputTargets);
-
- // Dispatch the key.
- dispatchEventLocked(currentTime, entry, inputTargets);
- return true;
-}
-
-void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
- "repeatCount=%d, downTime=%lld",
- prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
- entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
- entry->repeatCount, entry->downTime);
-#endif
-}
-
-bool InputDispatcher::dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
- // Preprocessing.
- if (! entry->dispatchInProgress) {
- entry->dispatchInProgress = true;
-
- logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
- }
-
- // Clean up if dropping the event.
- if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
- ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
- return true;
- }
-
- bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
-
- // Identify targets.
- Vector<InputTarget> inputTargets;
-
- bool conflictingPointerActions = false;
- int32_t injectionResult;
- if (isPointerEvent) {
- // Pointer event. (eg. touchscreen)
- injectionResult = findTouchedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
- } else {
- // Non touch event. (eg. trackball)
- injectionResult = findFocusedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime);
- }
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
- return false;
- }
-
- setInjectionResultLocked(entry, injectionResult);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- return true;
- }
-
- // TODO: support sending secondary display events to input monitors
- if (isMainDisplay(entry->displayId)) {
- addMonitoringTargetsLocked(inputTargets);
- }
-
- // Dispatch the motion.
- if (conflictingPointerActions) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "conflicting pointer actions");
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- }
- dispatchEventLocked(currentTime, entry, inputTargets);
- return true;
-}
-
-
-void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
- "action=0x%x, flags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, "
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
- prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
- entry->action, entry->flags,
- entry->metaState, entry->buttonState,
- entry->edgeFlags, entry->xPrecision, entry->yPrecision,
- entry->downTime);
-
- for (uint32_t i = 0; i < entry->pointerCount; i++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, entry->pointerProperties[i].id,
- entry->pointerProperties[i].toolType,
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
- }
-#endif
-}
-
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("dispatchEventToCurrentInputTargets");
-#endif
-
- ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
-
- pokeUserActivityLocked(eventEntry);
-
- for (size_t i = 0; i < inputTargets.size(); i++) {
- const InputTarget& inputTarget = inputTargets.itemAt(i);
-
- ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
- } else {
-#if DEBUG_FOCUS
- ALOGD("Dropping event delivery to target with channel '%s' because it "
- "is no longer registered with the input dispatcher.",
- inputTarget.inputChannel->getName().string());
-#endif
- }
- }
-}
-
-int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
- const EventEntry* entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason) {
- if (applicationHandle == NULL && windowHandle == NULL) {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
-#if DEBUG_FOCUS
- ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
-#endif
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
- }
- } else {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-#if DEBUG_FOCUS
- ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
- reason);
-#endif
- nsecs_t timeout;
- if (windowHandle != NULL) {
- timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else if (applicationHandle != NULL) {
- timeout = applicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else {
- timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- }
-
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
-
- if (windowHandle != NULL) {
- mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
- }
- if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
- mInputTargetWaitApplicationHandle = applicationHandle;
- }
- }
- }
-
- if (mInputTargetWaitTimeoutExpired) {
- return INPUT_EVENT_INJECTION_TIMED_OUT;
- }
-
- if (currentTime >= mInputTargetWaitTimeoutTime) {
- onANRLocked(currentTime, applicationHandle, windowHandle,
- entry->eventTime, mInputTargetWaitStartTime, reason);
-
- // Force poll loop to wake up immediately on next iteration once we get the
- // ANR response back from the policy.
- *nextWakeupTime = LONG_LONG_MIN;
- return INPUT_EVENT_INJECTION_PENDING;
- } else {
- // Force poll loop to wake up when timeout is due.
- if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = mInputTargetWaitTimeoutTime;
- }
- return INPUT_EVENT_INJECTION_PENDING;
- }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel) {
- if (newTimeout > 0) {
- // Extend the timeout.
- mInputTargetWaitTimeoutTime = now() + newTimeout;
- } else {
- // Give up.
- mInputTargetWaitTimeoutExpired = true;
-
- // Input state will not be realistic. Mark it out of sync.
- if (inputChannel.get()) {
- ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- sp<InputWindowHandle> windowHandle = connection->inputWindowHandle;
-
- if (windowHandle != NULL) {
- const InputWindowInfo* info = windowHandle->getInfo();
- if (info) {
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(info->displayId);
- if (stateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(stateIndex).removeWindow(
- windowHandle);
- }
- }
- }
-
- if (connection->status == Connection::STATUS_NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "application not responding");
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- }
- }
- }
-}
-
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
- nsecs_t currentTime) {
- if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- return currentTime - mInputTargetWaitStartTime;
- }
- return 0;
-}
-
-void InputDispatcher::resetANRTimeoutsLocked() {
-#if DEBUG_FOCUS
- ALOGD("Resetting ANR timeouts.");
-#endif
-
- // Reset input target wait timeout.
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationHandle.clear();
-}
-
-int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
- int32_t injectionResult;
-
- // If there is no currently focused window and no focused application
- // then drop the event.
- if (mFocusedWindowHandle == NULL) {
- if (mFocusedApplicationHandle != NULL) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplicationHandle, NULL, nextWakeupTime,
- "Waiting because no window has focus but there is a "
- "focused application that may eventually add a window "
- "when it finishes starting up.");
- goto Unresponsive;
- }
-
- ALOGI("Dropping event because there is no focused window or focused application.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
- }
-
- // Check permissions.
- if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- goto Failed;
- }
-
- // If the currently focused window is paused then keep waiting.
- if (mFocusedWindowHandle->getInfo()->paused) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime,
- "Waiting because the focused window is paused.");
- goto Unresponsive;
- }
-
- // If the currently focused window is still working on previous events then keep waiting.
- if (!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry)) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime,
- "Waiting because the focused window has not finished "
- "processing the input events that were previously delivered to it.");
- goto Unresponsive;
- }
-
- // Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- addWindowTargetLocked(mFocusedWindowHandle,
- InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
- inputTargets);
-
- // Done.
-Failed:
-Unresponsive:
- nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
- ALOGD("findFocusedWindow finished: injectionResult=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, timeSpentWaitingForApplication / 1000000.0);
-#endif
- return injectionResult;
-}
-
-int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) {
- enum InjectionPermission {
- INJECTION_PERMISSION_UNKNOWN,
- INJECTION_PERMISSION_GRANTED,
- INJECTION_PERMISSION_DENIED
- };
-
- nsecs_t startTime = now();
-
- // For security reasons, we defer updating the touch state until we are sure that
- // event injection will be allowed.
- int32_t displayId = entry->displayId;
- int32_t action = entry->action;
- int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
-
- // Update the touch state as needed based on the properties of the touch event.
- int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
- InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- sp<InputWindowHandle> newHoverWindowHandle;
-
- // Copy current touch state into mTempTouchState.
- // This state is always reset at the end of this function, so if we don't find state
- // for the specified display then our initial state will be empty.
- const TouchState* oldState = NULL;
- ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (oldStateIndex >= 0) {
- oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
- mTempTouchState.copyFrom(*oldState);
- }
-
- bool isSplit = mTempTouchState.split;
- bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0
- && (mTempTouchState.deviceId != entry->deviceId
- || mTempTouchState.source != entry->source
- || mTempTouchState.displayId != displayId);
- bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
- bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_SCROLL
- || isHoverAction);
- bool wrongDevice = false;
- if (newGesture) {
- bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (switchedDevice && mTempTouchState.down && !down) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because a pointer for a different device is already down.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- switchedDevice = false;
- wrongDevice = true;
- goto Failed;
- }
- mTempTouchState.reset();
- mTempTouchState.down = down;
- mTempTouchState.deviceId = entry->deviceId;
- mTempTouchState.source = entry->source;
- mTempTouchState.displayId = displayId;
- isSplit = false;
- }
-
- if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
- /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
-
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- int32_t x = int32_t(entry->pointerCoords[pointerIndex].
- getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(entry->pointerCoords[pointerIndex].
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> newTouchedWindowHandle;
- sp<InputWindowHandle> topErrorWindowHandle;
- bool isTouchModal = false;
-
- // Traverse windows from front to back to find touched window and outside targets.
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo->displayId != displayId) {
- continue; // wrong display
- }
-
- int32_t privateFlags = windowInfo->layoutParamsPrivateFlags;
- if (privateFlags & InputWindowInfo::PRIVATE_FLAG_SYSTEM_ERROR) {
- if (topErrorWindowHandle == NULL) {
- topErrorWindowHandle = windowHandle;
- }
- }
-
- int32_t flags = windowInfo->layoutParamsFlags;
- if (windowInfo->visible) {
- if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
- | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
- if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- newTouchedWindowHandle = windowHandle;
- break; // found touched window, exit window loop
- }
- }
-
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
- if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
- outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
-
- mTempTouchState.addOrUpdateWindow(
- windowHandle, outsideTargetFlags, BitSet32(0));
- }
- }
- }
-
- // If there is an error window but it is not taking focus (typically because
- // it is invisible) then wait for it. Any other focused window may in
- // fact be in ANR state.
- if (topErrorWindowHandle != NULL && newTouchedWindowHandle != topErrorWindowHandle) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, NULL, nextWakeupTime,
- "Waiting because a system error window is about to be displayed.");
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- goto Unresponsive;
- }
-
- // Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != NULL
- && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- // New window supports splitting.
- isSplit = true;
- } else if (isSplit) {
- // New window does not support splitting but we have already split events.
- // Ignore the new window.
- newTouchedWindowHandle = NULL;
- }
-
- // Handle the case where we did not find a window.
- if (newTouchedWindowHandle == NULL) {
- // Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
- if (newTouchedWindowHandle == NULL) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
- }
- }
-
- // Set target flags.
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
-
- // Update hover state.
- if (isHoverAction) {
- newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
- }
-
- // Update the temporary touch state.
- BitSet32 pointerIds;
- if (isSplit) {
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
- pointerIds.markBit(pointerId);
- }
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
- } else {
- /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
-
- // If the pointer is not currently down, then ignore the event.
- if (! mTempTouchState.down) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because the pointer is not down or we previously "
- "dropped the pointer down event.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
- }
-
- // Check whether touches should slip outside of the current foreground window.
- if (maskedAction == AMOTION_EVENT_ACTION_MOVE
- && entry->pointerCount == 1
- && mTempTouchState.isSlippery()) {
- int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-
- sp<InputWindowHandle> oldTouchedWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
- if (oldTouchedWindowHandle != newTouchedWindowHandle
- && newTouchedWindowHandle != NULL) {
-#if DEBUG_FOCUS
- ALOGD("Touch is slipping out of window %s into window %s.",
- oldTouchedWindowHandle->getName().string(),
- newTouchedWindowHandle->getName().string());
-#endif
- // Make a slippery exit from the old window.
- mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
-
- // Make a slippery entrance into the new window.
- if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- isSplit = true;
- }
-
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND
- | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
-
- BitSet32 pointerIds;
- if (isSplit) {
- pointerIds.markBit(entry->pointerProperties[0].id);
- }
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
- }
- }
- }
-
- if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != NULL) {
-#if DEBUG_HOVER
- ALOGD("Sending hover exit event to window %s.",
- mLastHoverWindowHandle->getName().string());
-#endif
- mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
- }
-
- // Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != NULL) {
-#if DEBUG_HOVER
- ALOGD("Sending hover enter event to window %s.",
- newHoverWindowHandle->getName().string());
-#endif
- mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
- }
- }
-
- // Check permission to inject into all touched foreground windows and ensure there
- // is at least one touched foreground window.
- {
- bool haveForegroundWindow = false;
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- haveForegroundWindow = true;
- if (! checkInjectionPermission(touchedWindow.windowHandle,
- entry->injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- goto Failed;
- }
- }
- }
- if (! haveForegroundWindow) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because there is no touched foreground window to receive it.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
- }
-
- // Permission granted to injection into all touched foreground windows.
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- }
-
- // Check whether windows listening for outside touches are owned by the same UID. If it is
- // set the policy flag that we will not reveal coordinate information to this window.
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
- const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
- sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
- if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
- }
- }
- }
- }
-
- // Ensure all touched foreground windows are ready for new input.
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- // If the touched window is paused then keep waiting.
- if (touchedWindow.windowHandle->getInfo()->paused) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, touchedWindow.windowHandle, nextWakeupTime,
- "Waiting because the touched window is paused.");
- goto Unresponsive;
- }
-
- // If the touched window is still working on previous events then keep waiting.
- if (!isWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry)) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, touchedWindow.windowHandle, nextWakeupTime,
- "Waiting because the touched window has not finished "
- "processing the input events that were previously delivered to it.");
- goto Unresponsive;
- }
- }
- }
-
- // If this is the first pointer going down and the touched window has a wallpaper
- // then also add the touched wallpaper windows so they are locked in for the duration
- // of the touch gesture.
- // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
- // engine only supports touch events. We would need to add a mechanism similar
- // to View.onGenericMotionEvent to enable wallpapers to handle these events.
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
- if (foregroundWindowHandle->getInfo()->hasWallpaper) {
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* info = windowHandle->getInfo();
- if (info->displayId == displayId
- && windowHandle->getInfo()->layoutParamsType
- == InputWindowInfo::TYPE_WALLPAPER) {
- mTempTouchState.addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_WINDOW_IS_OBSCURED
- | InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0));
- }
- }
- }
- }
-
- // Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, inputTargets);
- }
-
- // Drop the outside or hover touch windows since we will not care about them
- // in the next iteration.
- mTempTouchState.filterNonAsIsTouchWindows();
-
-Failed:
- // Check injection permission once and for all.
- if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(NULL, entry->injectionState)) {
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- } else {
- injectionPermission = INJECTION_PERMISSION_DENIED;
- }
- }
-
- // Update final pieces of touch state if the injector had permission.
- if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
- if (!wrongDevice) {
- if (switchedDevice) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Switched to a different device.");
-#endif
- *outConflictingPointerActions = true;
- }
-
- if (isHoverAction) {
- // Started hovering, therefore no longer down.
- if (oldState && oldState->down) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Hover received while pointer was down.");
-#endif
- *outConflictingPointerActions = true;
- }
- mTempTouchState.reset();
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mTempTouchState.deviceId = entry->deviceId;
- mTempTouchState.source = entry->source;
- mTempTouchState.displayId = displayId;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP
- || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
- // All pointers up or canceled.
- mTempTouchState.reset();
- } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- // First pointer went down.
- if (oldState && oldState->down) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Down received while already down.");
-#endif
- *outConflictingPointerActions = true;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- // One pointer went up.
- if (isSplit) {
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
- TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
- if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
- mTempTouchState.windows.removeAt(i);
- continue;
- }
- }
- i += 1;
- }
- }
- }
-
- // Save changes unless the action was scroll in which case the temporary touch
- // state was only valid for this one action.
- if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (mTempTouchState.displayId >= 0) {
- if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
- } else {
- mTouchStatesByDisplay.add(displayId, mTempTouchState);
- }
- } else if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
- }
- }
-
- // Update hover state.
- mLastHoverWindowHandle = newHoverWindowHandle;
- }
- } else {
-#if DEBUG_FOCUS
- ALOGD("Not updating touch focus because injection was denied.");
-#endif
- }
-
-Unresponsive:
- // Reset temporary touch state to ensure we release unnecessary references to input channels.
- mTempTouchState.reset();
-
- nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
- ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
-#endif
- return injectionResult;
-}
-
-void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
- inputTargets.push();
-
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget& target = inputTargets.editTop();
- target.inputChannel = windowInfo->inputChannel;
- target.flags = targetFlags;
- target.xOffset = - windowInfo->frameLeft;
- target.yOffset = - windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
- target.pointerIds = pointerIds;
-}
-
-void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- inputTargets.push();
-
- InputTarget& target = inputTargets.editTop();
- target.inputChannel = mMonitoringChannels[i];
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = 0;
- target.yOffset = 0;
- target.pointerIds.clear();
- target.scaleFactor = 1.0f;
- }
-}
-
-bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
- const InjectionState* injectionState) {
- if (injectionState
- && (windowHandle == NULL
- || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
- && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
- if (windowHandle != NULL) {
- ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
- "owned by uid %d",
- injectionState->injectorPid, injectionState->injectorUid,
- windowHandle->getName().string(),
- windowHandle->getInfo()->ownerUid);
- } else {
- ALOGW("Permission denied: injecting event from pid %d uid %d",
- injectionState->injectorPid, injectionState->injectorUid);
- }
- return false;
- }
- return true;
-}
-
-bool InputDispatcher::isWindowObscuredAtPointLocked(
- const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
- int32_t displayId = windowHandle->getInfo()->displayId;
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
- if (otherHandle == windowHandle) {
- break;
- }
-
- const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (otherInfo->displayId == displayId
- && otherInfo->visible && !otherInfo->isTrustedOverlay()
- && otherInfo->frameContainsPoint(x, y)) {
- return true;
- }
- }
- return false;
-}
-
-bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry) {
- ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- if (connection->inputPublisherBlocked) {
- return false;
- }
- if (eventEntry->type == EventEntry::TYPE_KEY) {
- // If the event is a key event, then we must wait for all previous events to
- // complete before delivering it because previous events may have the
- // side-effect of transferring focus to a different window and we want to
- // ensure that the following keys are sent to the new window.
- //
- // Suppose the user touches a button in a window then immediately presses "A".
- // If the button causes a pop-up window to appear then we want to ensure that
- // the "A" key is delivered to the new pop-up window. This is because users
- // often anticipate pending UI changes when typing on a keyboard.
- // To obtain this behavior, we must serialize key events with respect to all
- // prior input events.
- return connection->outboundQueue.isEmpty()
- && connection->waitQueue.isEmpty();
- }
- // Touch events can always be sent to a window immediately because the user intended
- // to touch whatever was visible at the time. Even if focus changes or a new
- // window appears moments later, the touch event was meant to be delivered to
- // whatever window happened to be on screen at the time.
- //
- // Generic motion events, such as trackball or joystick events are a little trickier.
- // Like key events, generic motion events are delivered to the focused window.
- // Unlike key events, generic motion events don't tend to transfer focus to other
- // windows and it is not important for them to be serialized. So we prefer to deliver
- // generic motion events as soon as possible to improve efficiency and reduce lag
- // through batching.
- //
- // The one case where we pause input event delivery is when the wait queue is piling
- // up with lots of events because the application is not responding.
- // This condition ensures that ANRs are detected reliably.
- if (!connection->waitQueue.isEmpty()
- && currentTime >= connection->waitQueue.head->deliveryTime
- + STREAM_AHEAD_EVENT_TIMEOUT) {
- return false;
- }
- }
- return true;
-}
-
-String8 InputDispatcher::getApplicationWindowLabelLocked(
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle) {
- if (applicationHandle != NULL) {
- if (windowHandle != NULL) {
- String8 label(applicationHandle->getName());
- label.append(" - ");
- label.append(windowHandle->getName());
- return label;
- } else {
- return applicationHandle->getName();
- }
- } else if (windowHandle != NULL) {
- return windowHandle->getName();
- } else {
- return String8("<unknown application or window>");
- }
-}
-
-void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
- if (mFocusedWindowHandle != NULL) {
- const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
- if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("Not poking user activity: disabled by window '%s'.", info->name.string());
-#endif
- return;
- }
- }
-
- int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
- switch (eventEntry->type) {
- case EventEntry::TYPE_MOTION: {
- const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
- return;
- }
-
- if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
- eventType = USER_ACTIVITY_EVENT_TOUCH;
- }
- break;
- }
- case EventEntry::TYPE_KEY: {
- const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
- if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
- return;
- }
- eventType = USER_ACTIVITY_EVENT_BUTTON;
- break;
- }
- }
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doPokeUserActivityLockedInterruptible);
- commandEntry->eventTime = eventEntry->eventTime;
- commandEntry->userActivityEventType = eventType;
-}
-
-void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, scaleFactor=%f, "
- "pointerIds=0x%x",
- connection->getInputChannelName(), inputTarget->flags,
- inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor, inputTarget->pointerIds.value);
-#endif
-
- // Skip this event if the connection status is not normal.
- // We don't want to enqueue additional outbound events if the connection is broken.
- if (connection->status != Connection::STATUS_NORMAL) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getInputChannelName(), connection->getStatusLabel());
-#endif
- return;
- }
-
- // Split a motion event if needed.
- if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
- ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
-
- MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
- if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
- MotionEntry* splitMotionEntry = splitMotionEvent(
- originalMotionEntry, inputTarget->pointerIds);
- if (!splitMotionEntry) {
- return; // split event was dropped
- }
-#if DEBUG_FOCUS
- ALOGD("channel '%s' ~ Split motion event.",
- connection->getInputChannelName());
- logOutboundMotionDetailsLocked(" ", splitMotionEntry);
-#endif
- enqueueDispatchEntriesLocked(currentTime, connection,
- splitMotionEntry, inputTarget);
- splitMotionEntry->release();
- return;
- }
- }
-
- // Not splitting. Enqueue dispatch entries for the event as is.
- enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
-}
-
-void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
- bool wasEmpty = connection->outboundQueue.isEmpty();
-
- // Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
-
- // If the outbound queue was previously empty, start the dispatch cycle going.
- if (wasEmpty && !connection->outboundQueue.isEmpty()) {
- startDispatchCycleLocked(currentTime, connection);
- }
-}
-
-void InputDispatcher::enqueueDispatchEntryLocked(
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
- int32_t dispatchMode) {
- int32_t inputTargetFlags = inputTarget->flags;
- if (!(inputTargetFlags & dispatchMode)) {
- return;
- }
- inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
-
- // This is a new event.
- // Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
- inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor);
-
- // Apply target flags and update the connection's input state.
- switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- dispatchEntry->resolvedAction = keyEntry->action;
- dispatchEntry->resolvedFlags = keyEntry->flags;
-
- if (!connection->inputState.trackKey(keyEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
- connection->getInputChannelName());
-#endif
- delete dispatchEntry;
- return; // skip the inconsistent event
- }
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
- if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
- } else {
- dispatchEntry->resolvedAction = motionEntry->action;
- }
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- && !connection->inputState.isHovering(
- motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
- connection->getInputChannelName());
-#endif
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- }
-
- dispatchEntry->resolvedFlags = motionEntry->flags;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- }
-
- if (!connection->inputState.trackMotion(motionEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
- connection->getInputChannelName());
-#endif
- delete dispatchEntry;
- return; // skip the inconsistent event
- }
- break;
- }
- }
-
- // Remember that we are waiting for this dispatch to complete.
- if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatchesLocked(eventEntry);
- }
-
- // Enqueue the dispatch entry.
- connection->outboundQueue.enqueueAtTail(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
-}
-
-void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ startDispatchCycle",
- connection->getInputChannelName());
-#endif
-
- while (connection->status == Connection::STATUS_NORMAL
- && !connection->outboundQueue.isEmpty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.head;
- dispatchEntry->deliveryTime = currentTime;
-
- // Publish the event.
- status_t status;
- EventEntry* eventEntry = dispatchEntry->eventEntry;
- switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-
- // Publish the key event.
- status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
- keyEntry->deviceId, keyEntry->source,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
- keyEntry->keyCode, keyEntry->scanCode,
- keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-
- PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry->pointerCoords;
-
- // Set the X and Y offset depending on the input source.
- float xOffset, yOffset, scaleFactor;
- if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
- && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- scaleFactor = dispatchEntry->scaleFactor;
- xOffset = dispatchEntry->xOffset * scaleFactor;
- yOffset = dispatchEntry->yOffset * scaleFactor;
- if (scaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
- }
- usingCoords = scaledCoords;
- }
- } else {
- xOffset = 0.0f;
- yOffset = 0.0f;
- scaleFactor = 1.0f;
-
- // We don't want the dispatch target to know.
- if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i].clear();
- }
- usingCoords = scaledCoords;
- }
- }
-
- // Publish the motion event.
- status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
- motionEntry->deviceId, motionEntry->source,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
- motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
- xOffset, yOffset,
- motionEntry->xPrecision, motionEntry->yPrecision,
- motionEntry->downTime, motionEntry->eventTime,
- motionEntry->pointerCount, motionEntry->pointerProperties,
- usingCoords);
- break;
- }
-
- default:
- ALOG_ASSERT(false);
- return;
- }
-
- // Check the result.
- if (status) {
- if (status == WOULD_BLOCK) {
- if (connection->waitQueue.isEmpty()) {
- ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
- "This is unexpected because the wait queue is empty, so the pipe "
- "should be empty and we shouldn't have any problems writing an "
- "event to it, status=%d", connection->getInputChannelName(), status);
- abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
- } else {
- // Pipe is full and we are waiting for the app to finish process some events
- // before sending more events to it.
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
- "waiting for the application to catch up",
- connection->getInputChannelName());
-#endif
- connection->inputPublisherBlocked = true;
- }
- } else {
- ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
- "status=%d", connection->getInputChannelName(), status);
- abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
- }
- return;
- }
-
- // Re-enqueue the event on the wait queue.
- connection->outboundQueue.dequeue(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
- connection->waitQueue.enqueueAtTail(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
- }
-}
-
-void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, uint32_t seq, bool handled) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
- connection->getInputChannelName(), seq, toString(handled));
-#endif
-
- connection->inputPublisherBlocked = false;
-
- if (connection->status == Connection::STATUS_BROKEN
- || connection->status == Connection::STATUS_ZOMBIE) {
- return;
- }
-
- // Notify other system components and prepare to start the next dispatch cycle.
- onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
-}
-
-void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, bool notify) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
- connection->getInputChannelName(), toString(notify));
-#endif
-
- // Clear the dispatch queues.
- drainDispatchQueueLocked(&connection->outboundQueue);
- traceOutboundQueueLengthLocked(connection);
- drainDispatchQueueLocked(&connection->waitQueue);
- traceWaitQueueLengthLocked(connection);
-
- // The connection appears to be unrecoverably broken.
- // Ignore already broken or zombie connections.
- if (connection->status == Connection::STATUS_NORMAL) {
- connection->status = Connection::STATUS_BROKEN;
-
- if (notify) {
- // Notify other system components.
- onDispatchCycleBrokenLocked(currentTime, connection);
- }
- }
-}
-
-void InputDispatcher::drainDispatchQueueLocked(Queue<DispatchEntry>* queue) {
- while (!queue->isEmpty()) {
- DispatchEntry* dispatchEntry = queue->dequeueAtHead();
- releaseDispatchEntryLocked(dispatchEntry);
- }
-}
-
-void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) {
- if (dispatchEntry->hasForegroundTarget()) {
- decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
- }
- delete dispatchEntry;
-}
-
-int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
- InputDispatcher* d = static_cast<InputDispatcher*>(data);
-
- { // acquire lock
- AutoMutex _l(d->mLock);
-
- ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
- if (connectionIndex < 0) {
- ALOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x", fd, events);
- return 0; // remove the callback
- }
-
- bool notify;
- sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
- if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
- if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", connection->getInputChannelName(), events);
- return 1;
- }
-
- nsecs_t currentTime = now();
- bool gotOne = false;
- status_t status;
- for (;;) {
- uint32_t seq;
- bool handled;
- status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
- if (status) {
- break;
- }
- d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
- gotOne = true;
- }
- if (gotOne) {
- d->runCommandsLockedInterruptible();
- if (status == WOULD_BLOCK) {
- return 1;
- }
- }
-
- notify = status != DEAD_OBJECT || !connection->monitor;
- if (notify) {
- ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
- connection->getInputChannelName(), status);
- }
- } else {
- // Monitor channels are never explicitly unregistered.
- // We do it automatically when the remote endpoint is closed so don't warn
- // about them.
- notify = !connection->monitor;
- if (notify) {
- ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x", connection->getInputChannelName(), events);
- }
- }
-
- // Unregister the channel.
- d->unregisterInputChannelLocked(connection->inputChannel, notify);
- return 0; // remove the callback
- } // release lock
-}
-
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options) {
- for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
- synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByFd.valueAt(i), options);
- }
-}
-
-void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, const CancelationOptions& options) {
- ssize_t index = getConnectionIndexLocked(channel);
- if (index >= 0) {
- synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByFd.valueAt(index), options);
- }
-}
-
-void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const sp<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::STATUS_BROKEN) {
- return;
- }
-
- nsecs_t currentTime = now();
-
- Vector<EventEntry*> cancelationEvents;
- connection->inputState.synthesizeCancelationEvents(currentTime,
- cancelationEvents, options);
-
- if (!cancelationEvents.isEmpty()) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
- "with reality: %s, mode=%d.",
- connection->getInputChannelName(), cancelationEvents.size(),
- options.reason, options.mode);
-#endif
- for (size_t i = 0; i < cancelationEvents.size(); i++) {
- EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i);
- switch (cancelationEventEntry->type) {
- case EventEntry::TYPE_KEY:
- logOutboundKeyDetailsLocked("cancel - ",
- static_cast<KeyEntry*>(cancelationEventEntry));
- break;
- case EventEntry::TYPE_MOTION:
- logOutboundMotionDetailsLocked("cancel - ",
- static_cast<MotionEntry*>(cancelationEventEntry));
- break;
- }
-
- InputTarget target;
- sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel);
- if (windowHandle != NULL) {
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
- } else {
- target.xOffset = 0;
- target.yOffset = 0;
- target.scaleFactor = 1.0f;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- &target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- cancelationEventEntry->release();
- }
-
- startDispatchCycleLocked(currentTime, connection);
- }
-}
-
-InputDispatcher::MotionEntry*
-InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
- ALOG_ASSERT(pointerIds.value != 0);
-
- uint32_t splitPointerIndexMap[MAX_POINTERS];
- PointerProperties splitPointerProperties[MAX_POINTERS];
- PointerCoords splitPointerCoords[MAX_POINTERS];
-
- uint32_t originalPointerCount = originalMotionEntry->pointerCount;
- uint32_t splitPointerCount = 0;
-
- for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
- originalPointerIndex++) {
- const PointerProperties& pointerProperties =
- originalMotionEntry->pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.hasBit(pointerId)) {
- splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
- splitPointerCoords[splitPointerCount].copyFrom(
- originalMotionEntry->pointerCoords[originalPointerIndex]);
- splitPointerCount += 1;
- }
- }
-
- if (splitPointerCount != pointerIds.count()) {
- // This is bad. We are missing some of the pointers that we expected to deliver.
- // Most likely this indicates that we received an ACTION_MOVE events that has
- // different pointer ids than we expected based on the previous ACTION_DOWN
- // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
- // in this way.
- ALOGW("Dropping split motion event because the pointer count is %d but "
- "we expected there to be %d pointers. This probably means we received "
- "a broken sequence of pointer ids from the input device.",
- splitPointerCount, pointerIds.count());
- return NULL;
- }
-
- int32_t action = originalMotionEntry->action;
- int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
- if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
- const PointerProperties& pointerProperties =
- originalMotionEntry->pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.hasBit(pointerId)) {
- if (pointerIds.count() == 1) {
- // The first/last pointer went down/up.
- action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
- } else {
- // A secondary pointer went down/up.
- uint32_t splitPointerIndex = 0;
- while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) {
- splitPointerIndex += 1;
- }
- action = maskedAction | (splitPointerIndex
- << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- // An unrelated pointer changed.
- action = AMOTION_EVENT_ACTION_MOVE;
- }
- }
-
- MotionEntry* splitMotionEntry = new MotionEntry(
- originalMotionEntry->eventTime,
- originalMotionEntry->deviceId,
- originalMotionEntry->source,
- originalMotionEntry->policyFlags,
- action,
- originalMotionEntry->flags,
- originalMotionEntry->metaState,
- originalMotionEntry->buttonState,
- originalMotionEntry->edgeFlags,
- originalMotionEntry->xPrecision,
- originalMotionEntry->yPrecision,
- originalMotionEntry->downTime,
- originalMotionEntry->displayId,
- splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
-
- if (originalMotionEntry->injectionState) {
- splitMotionEntry->injectionState = originalMotionEntry->injectionState;
- splitMotionEntry->injectionState->refCount += 1;
- }
-
- return splitMotionEntry;
-}
-
-void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyConfigurationChanged - eventTime=%lld", args->eventTime);
-#endif
-
- bool needWake;
- { // acquire lock
- AutoMutex _l(mLock);
-
- ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
- needWake = enqueueInboundEventLocked(newEntry);
- } // release lock
-
- if (needWake) {
- mLooper->wake();
- }
-}
-
-void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
- args->eventTime, args->deviceId, args->source, args->policyFlags,
- args->action, args->flags, args->keyCode, args->scanCode,
- args->metaState, args->downTime);
-#endif
- if (!validateKeyEvent(args->action)) {
- return;
- }
-
- uint32_t policyFlags = args->policyFlags;
- int32_t flags = args->flags;
- int32_t metaState = args->metaState;
- if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
- policyFlags |= POLICY_FLAG_VIRTUAL;
- flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
- }
- if (policyFlags & POLICY_FLAG_ALT) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;
- }
- if (policyFlags & POLICY_FLAG_ALT_GR) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;
- }
- if (policyFlags & POLICY_FLAG_SHIFT) {
- metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;
- }
- if (policyFlags & POLICY_FLAG_CAPS_LOCK) {
- metaState |= AMETA_CAPS_LOCK_ON;
- }
- if (policyFlags & POLICY_FLAG_FUNCTION) {
- metaState |= AMETA_FUNCTION_ON;
- }
-
- policyFlags |= POLICY_FLAG_TRUSTED;
-
- KeyEvent event;
- event.initialize(args->deviceId, args->source, args->action,
- flags, args->keyCode, args->scanCode, metaState, 0,
- args->downTime, args->eventTime);
-
- mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
-
- bool needWake;
- { // acquire lock
- mLock.lock();
-
- if (shouldSendKeyToInputFilterLocked(args)) {
- mLock.unlock();
-
- policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
- return; // event was consumed by the filter
- }
-
- mLock.lock();
- }
-
- int32_t repeatCount = 0;
- KeyEntry* newEntry = new KeyEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
- args->action, flags, args->keyCode, args->scanCode,
- metaState, repeatCount, args->downTime);
-
- needWake = enqueueInboundEventLocked(newEntry);
- mLock.unlock();
- } // release lock
-
- if (needWake) {
- mLooper->wake();
- }
-}
-
-bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) {
- return mInputFilterEnabled;
-}
-
-void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
- "action=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, edgeFlags=0x%x, "
- "xPrecision=%f, yPrecision=%f, downTime=%lld",
- args->eventTime, args->deviceId, args->source, args->policyFlags,
- args->action, args->flags, args->metaState, args->buttonState,
- args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
- for (uint32_t i = 0; i < args->pointerCount; i++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, args->pointerProperties[i].id,
- args->pointerProperties[i].toolType,
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
- }
-#endif
- if (!validateMotionEvent(args->action, args->pointerCount, args->pointerProperties)) {
- return;
- }
-
- uint32_t policyFlags = args->policyFlags;
- policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
-
- bool needWake;
- { // acquire lock
- mLock.lock();
-
- if (shouldSendMotionToInputFilterLocked(args)) {
- mLock.unlock();
-
- MotionEvent event;
- event.initialize(args->deviceId, args->source, args->action, args->flags,
- args->edgeFlags, args->metaState, args->buttonState, 0, 0,
- args->xPrecision, args->yPrecision,
- args->downTime, args->eventTime,
- args->pointerCount, args->pointerProperties, args->pointerCoords);
-
- policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
- return; // event was consumed by the filter
- }
-
- mLock.lock();
- }
-
- // Just enqueue a new motion event.
- MotionEntry* newEntry = new MotionEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
- args->action, args->flags, args->metaState, args->buttonState,
- args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
- args->displayId,
- args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
-
- needWake = enqueueInboundEventLocked(newEntry);
- mLock.unlock();
- } // release lock
-
- if (needWake) {
- mLooper->wake();
- }
-}
-
-bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
- // TODO: support sending secondary display events to input filter
- return mInputFilterEnabled && isMainDisplay(args->displayId);
-}
-
-void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchValues=0x%08x, switchMask=0x%08x",
- args->eventTime, args->policyFlags,
- args->switchValues, args->switchMask);
-#endif
-
- uint32_t policyFlags = args->policyFlags;
- policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->notifySwitch(args->eventTime,
- args->switchValues, args->switchMask, policyFlags);
-}
-
-void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyDeviceReset - eventTime=%lld, deviceId=%d",
- args->eventTime, args->deviceId);
-#endif
-
- bool needWake;
- { // acquire lock
- AutoMutex _l(mLock);
-
- DeviceResetEntry* newEntry = new DeviceResetEntry(args->eventTime, args->deviceId);
- needWake = enqueueInboundEventLocked(newEntry);
- } // release lock
-
- if (needWake) {
- mLooper->wake();
- }
-}
-
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
-#endif
-
- nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
-
- policyFlags |= POLICY_FLAG_INJECTED;
- if (hasInjectionPermission(injectorPid, injectorUid)) {
- policyFlags |= POLICY_FLAG_TRUSTED;
- }
-
- EventEntry* firstInjectedEntry;
- EventEntry* lastInjectedEntry;
- switch (event->getType()) {
- case AINPUT_EVENT_TYPE_KEY: {
- const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
- int32_t action = keyEvent->getAction();
- if (! validateKeyEvent(action)) {
- return INPUT_EVENT_INJECTION_FAILED;
- }
-
- int32_t flags = keyEvent->getFlags();
- if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
- policyFlags |= POLICY_FLAG_VIRTUAL;
- }
-
- if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
- }
-
- mLock.lock();
- firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
- keyEvent->getDeviceId(), keyEvent->getSource(),
- policyFlags, action, flags,
- keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), keyEvent->getDownTime());
- lastInjectedEntry = firstInjectedEntry;
- break;
- }
-
- case AINPUT_EVENT_TYPE_MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
- int32_t action = motionEvent->getAction();
- size_t pointerCount = motionEvent->getPointerCount();
- const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
- if (! validateMotionEvent(action, pointerCount, pointerProperties)) {
- return INPUT_EVENT_INJECTION_FAILED;
- }
-
- if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- nsecs_t eventTime = motionEvent->getEventTime();
- mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
- }
-
- mLock.lock();
- const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
- const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
- firstInjectedEntry = new MotionEntry(*sampleEventTimes,
- motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
- action, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getDownTime(), displayId,
- uint32_t(pointerCount), pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- lastInjectedEntry = firstInjectedEntry;
- for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
- sampleEventTimes += 1;
- samplePointerCoords += pointerCount;
- MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes,
- motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
- action, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getDownTime(), displayId,
- uint32_t(pointerCount), pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- lastInjectedEntry->next = nextInjectedEntry;
- lastInjectedEntry = nextInjectedEntry;
- }
- break;
- }
-
- default:
- ALOGW("Cannot inject event of type %d", event->getType());
- return INPUT_EVENT_INJECTION_FAILED;
- }
-
- InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
- injectionState->injectionIsAsync = true;
- }
-
- injectionState->refCount += 1;
- lastInjectedEntry->injectionState = injectionState;
-
- bool needWake = false;
- for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
- EventEntry* nextEntry = entry->next;
- needWake |= enqueueInboundEventLocked(entry);
- entry = nextEntry;
- }
-
- mLock.unlock();
-
- if (needWake) {
- mLooper->wake();
- }
-
- int32_t injectionResult;
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- } else {
- for (;;) {
- injectionResult = injectionState->injectionResult;
- if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
- break;
- }
-
- nsecs_t remainingTimeout = endTime - now();
- if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Timed out waiting for injection result "
- "to become available.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
- break;
- }
-
- mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
- }
-
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
- && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
- while (injectionState->pendingForegroundDispatches != 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectionState->pendingForegroundDispatches);
-#endif
- nsecs_t remainingTimeout = endTime - now();
- if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Timed out waiting for pending foreground "
- "dispatches to finish.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
- break;
- }
-
- mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
- }
- }
- }
-
- injectionState->release();
- } // release lock
-
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Finished with result %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectorPid, injectorUid);
-#endif
-
- return injectionResult;
-}
-
-bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
- return injectorUid == 0
- || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
-}
-
-void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
- InjectionState* injectionState = entry->injectionState;
- if (injectionState) {
-#if DEBUG_INJECTION
- ALOGD("Setting input event injection result to %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectionState->injectorPid, injectionState->injectorUid);
-#endif
-
- if (injectionState->injectionIsAsync
- && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
- // Log the outcome since the injector did not wait for the injection result.
- switch (injectionResult) {
- case INPUT_EVENT_INJECTION_SUCCEEDED:
- ALOGV("Asynchronous input event injection succeeded.");
- break;
- case INPUT_EVENT_INJECTION_FAILED:
- ALOGW("Asynchronous input event injection failed.");
- break;
- case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
- ALOGW("Asynchronous input event injection permission denied.");
- break;
- case INPUT_EVENT_INJECTION_TIMED_OUT:
- ALOGW("Asynchronous input event injection timed out.");
- break;
- }
- }
-
- injectionState->injectionResult = injectionResult;
- mInjectionResultAvailableCondition.broadcast();
- }
-}
-
-void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches += 1;
- }
-}
-
-void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches -= 1;
-
- if (injectionState->pendingForegroundDispatches == 0) {
- mInjectionSyncFinishedCondition.broadcast();
- }
- }
-}
-
-sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
- const sp<InputChannel>& inputChannel) const {
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- if (windowHandle->getInputChannel() == inputChannel) {
- return windowHandle;
- }
- }
- return NULL;
-}
-
-bool InputDispatcher::hasWindowHandleLocked(
- const sp<InputWindowHandle>& windowHandle) const {
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- if (mWindowHandles.itemAt(i) == windowHandle) {
- return true;
- }
- }
- return false;
-}
-
-void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
-#if DEBUG_FOCUS
- ALOGD("setInputWindows");
-#endif
- { // acquire lock
- AutoMutex _l(mLock);
-
- Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
- mWindowHandles = inputWindowHandles;
-
- sp<InputWindowHandle> newFocusedWindowHandle;
- bool foundHoveredWindow = false;
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
- mWindowHandles.removeAt(i--);
- continue;
- }
- if (windowHandle->getInfo()->hasFocus) {
- newFocusedWindowHandle = windowHandle;
- }
- if (windowHandle == mLastHoverWindowHandle) {
- foundHoveredWindow = true;
- }
- }
-
- if (!foundHoveredWindow) {
- mLastHoverWindowHandle = NULL;
- }
-
- if (mFocusedWindowHandle != newFocusedWindowHandle) {
- if (mFocusedWindowHandle != NULL) {
-#if DEBUG_FOCUS
- ALOGD("Focus left window: %s",
- mFocusedWindowHandle->getName().string());
-#endif
- sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
- if (focusedInputChannel != NULL) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForInputChannelLocked(
- focusedInputChannel, options);
- }
- }
- if (newFocusedWindowHandle != NULL) {
-#if DEBUG_FOCUS
- ALOGD("Focus entered window: %s",
- newFocusedWindowHandle->getName().string());
-#endif
- }
- mFocusedWindowHandle = newFocusedWindowHandle;
- }
-
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- for (size_t i = 0; i < state.windows.size(); i++) {
- TouchedWindow& touchedWindow = state.windows.editItemAt(i);
- if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
-#if DEBUG_FOCUS
- ALOGD("Touched window was removed: %s",
- touchedWindow.windowHandle->getName().string());
-#endif
- sp<InputChannel> touchedInputChannel =
- touchedWindow.windowHandle->getInputChannel();
- if (touchedInputChannel != NULL) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "touched window was removed");
- synthesizeCancelationEventsForInputChannelLocked(
- touchedInputChannel, options);
- }
- state.windows.removeAt(i--);
- }
- }
- }
-
- // Release information for windows that are no longer present.
- // This ensures that unused input channels are released promptly.
- // Otherwise, they might stick around until the window handle is destroyed
- // which might not happen until the next GC.
- for (size_t i = 0; i < oldWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
- if (!hasWindowHandleLocked(oldWindowHandle)) {
-#if DEBUG_FOCUS
- ALOGD("Window went away: %s", oldWindowHandle->getName().string());
-#endif
- oldWindowHandle->releaseInfo();
- }
- }
- } // release lock
-
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
-}
-
-void InputDispatcher::setFocusedApplication(
- const sp<InputApplicationHandle>& inputApplicationHandle) {
-#if DEBUG_FOCUS
- ALOGD("setFocusedApplication");
-#endif
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
- if (mFocusedApplicationHandle != inputApplicationHandle) {
- if (mFocusedApplicationHandle != NULL) {
- resetANRTimeoutsLocked();
- mFocusedApplicationHandle->releaseInfo();
- }
- mFocusedApplicationHandle = inputApplicationHandle;
- }
- } else if (mFocusedApplicationHandle != NULL) {
- resetANRTimeoutsLocked();
- mFocusedApplicationHandle->releaseInfo();
- mFocusedApplicationHandle.clear();
- }
-
-#if DEBUG_FOCUS
- //logDispatchStateLocked();
-#endif
- } // release lock
-
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
-}
-
-void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
- ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
-
- bool changed;
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
- if (mDispatchFrozen && !frozen) {
- resetANRTimeoutsLocked();
- }
-
- if (mDispatchEnabled && !enabled) {
- resetAndDropEverythingLocked("dispatcher is being disabled");
- }
-
- mDispatchEnabled = enabled;
- mDispatchFrozen = frozen;
- changed = true;
- } else {
- changed = false;
- }
-
-#if DEBUG_FOCUS
- //logDispatchStateLocked();
-#endif
- } // release lock
-
- if (changed) {
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
- }
-}
-
-void InputDispatcher::setInputFilterEnabled(bool enabled) {
-#if DEBUG_FOCUS
- ALOGD("setInputFilterEnabled: enabled=%d", enabled);
-#endif
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (mInputFilterEnabled == enabled) {
- return;
- }
-
- mInputFilterEnabled = enabled;
- resetAndDropEverythingLocked("input filter is being enabled or disabled");
- } // release lock
-
- // Wake up poll loop since there might be work to do to drop everything.
- mLooper->wake();
-}
-
-bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel) {
-#if DEBUG_FOCUS
- ALOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
- fromChannel->getName().string(), toChannel->getName().string());
-#endif
- { // acquire lock
- AutoMutex _l(mLock);
-
- sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel);
- sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel);
- if (fromWindowHandle == NULL || toWindowHandle == NULL) {
-#if DEBUG_FOCUS
- ALOGD("Cannot transfer focus because from or to window not found.");
-#endif
- return false;
- }
- if (fromWindowHandle == toWindowHandle) {
-#if DEBUG_FOCUS
- ALOGD("Trivial transfer to same window.");
-#endif
- return true;
- }
- if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-#if DEBUG_FOCUS
- ALOGD("Cannot transfer focus because windows are on different displays.");
-#endif
- return false;
- }
-
- bool found = false;
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- for (size_t i = 0; i < state.windows.size(); i++) {
- const TouchedWindow& touchedWindow = state.windows[i];
- if (touchedWindow.windowHandle == fromWindowHandle) {
- int32_t oldTargetFlags = touchedWindow.targetFlags;
- BitSet32 pointerIds = touchedWindow.pointerIds;
-
- state.windows.removeAt(i);
-
- int32_t newTargetFlags = oldTargetFlags
- & (InputTarget::FLAG_FOREGROUND
- | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
- state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
- found = true;
- goto Found;
- }
- }
- }
-Found:
-
- if (! found) {
-#if DEBUG_FOCUS
- ALOGD("Focus transfer failed because from window did not have focus.");
-#endif
- return false;
- }
-
- ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
- ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
- if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
- sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
- sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
-
- fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "transferring touch focus from this window to another window");
- synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
- }
-
-#if DEBUG_FOCUS
- logDispatchStateLocked();
-#endif
- } // release lock
-
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
- return true;
-}
-
-void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
-#if DEBUG_FOCUS
- ALOGD("Resetting and dropping all events (%s).", reason);
-#endif
-
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
-
- resetKeyRepeatLocked();
- releasePendingEventLocked();
- drainInboundQueueLocked();
- resetANRTimeoutsLocked();
-
- mTouchStatesByDisplay.clear();
- mLastHoverWindowHandle.clear();
-}
-
-void InputDispatcher::logDispatchStateLocked() {
- String8 dump;
- dumpDispatchStateLocked(dump);
-
- char* text = dump.lockBuffer(dump.size());
- char* start = text;
- while (*start != '\0') {
- char* end = strchr(start, '\n');
- if (*end == '\n') {
- *(end++) = '\0';
- }
- ALOGD("%s", start);
- start = end;
- }
-}
-
-void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
- dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
- dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
-
- if (mFocusedApplicationHandle != NULL) {
- dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
- mFocusedApplicationHandle->getName().string(),
- mFocusedApplicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
- } else {
- dump.append(INDENT "FocusedApplication: <null>\n");
- }
- dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
- mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().string() : "<null>");
-
- if (!mTouchStatesByDisplay.isEmpty()) {
- dump.appendFormat(INDENT "TouchStatesByDisplay:\n");
- for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(i);
- dump.appendFormat(INDENT2 "%zu: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
- state.displayId, toString(state.down), toString(state.split),
- state.deviceId, state.source);
- if (!state.windows.isEmpty()) {
- dump.append(INDENT3 "Windows:\n");
- for (size_t i = 0; i < state.windows.size(); i++) {
- const TouchedWindow& touchedWindow = state.windows[i];
- dump.appendFormat(INDENT4 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
- i, touchedWindow.windowHandle->getName().string(),
- touchedWindow.pointerIds.value,
- touchedWindow.targetFlags);
- }
- } else {
- dump.append(INDENT3 "Windows: <none>\n");
- }
- }
- } else {
- dump.append(INDENT "TouchStates: <no displays touched>\n");
- }
-
- if (!mWindowHandles.isEmpty()) {
- dump.append(INDENT "Windows:\n");
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
-
- dump.appendFormat(INDENT2 "%zu: name='%s', displayId=%d, "
- "paused=%s, hasFocus=%s, hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], scale=%f, "
- "touchableRegion=",
- i, windowInfo->name.string(), windowInfo->displayId,
- toString(windowInfo->paused),
- toString(windowInfo->hasFocus),
- toString(windowInfo->hasWallpaper),
- toString(windowInfo->visible),
- toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
- windowInfo->layer,
- windowInfo->frameLeft, windowInfo->frameTop,
- windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->scaleFactor);
- dumpRegion(dump, windowInfo->touchableRegion);
- dump.appendFormat(", inputFeatures=0x%08x", windowInfo->inputFeatures);
- dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
- windowInfo->ownerPid, windowInfo->ownerUid,
- windowInfo->dispatchingTimeout / 1000000.0);
- }
- } else {
- dump.append(INDENT "Windows: <none>\n");
- }
-
- if (!mMonitoringChannels.isEmpty()) {
- dump.append(INDENT "MonitoringChannels:\n");
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- const sp<InputChannel>& channel = mMonitoringChannels[i];
- dump.appendFormat(INDENT2 "%zu: '%s'\n", i, channel->getName().string());
- }
- } else {
- dump.append(INDENT "MonitoringChannels: <none>\n");
- }
-
- nsecs_t currentTime = now();
-
- // Dump recently dispatched or dropped events from oldest to newest.
- if (!mRecentQueue.isEmpty()) {
- dump.appendFormat(INDENT "RecentQueue: length=%u\n", mRecentQueue.count());
- for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) {
- dump.append(INDENT2);
- entry->appendDescription(dump);
- dump.appendFormat(", age=%0.1fms\n",
- (currentTime - entry->eventTime) * 0.000001f);
- }
- } else {
- dump.append(INDENT "RecentQueue: <empty>\n");
- }
-
- // Dump event currently being dispatched.
- if (mPendingEvent) {
- dump.append(INDENT "PendingEvent:\n");
- dump.append(INDENT2);
- mPendingEvent->appendDescription(dump);
- dump.appendFormat(", age=%0.1fms\n",
- (currentTime - mPendingEvent->eventTime) * 0.000001f);
- } else {
- dump.append(INDENT "PendingEvent: <none>\n");
- }
-
- // Dump inbound events from oldest to newest.
- if (!mInboundQueue.isEmpty()) {
- dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
- for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) {
- dump.append(INDENT2);
- entry->appendDescription(dump);
- dump.appendFormat(", age=%0.1fms\n",
- (currentTime - entry->eventTime) * 0.000001f);
- }
- } else {
- dump.append(INDENT "InboundQueue: <empty>\n");
- }
-
- if (!mConnectionsByFd.isEmpty()) {
- dump.append(INDENT "Connections:\n");
- for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
- const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
- dump.appendFormat(INDENT2 "%zu: channelName='%s', windowName='%s', "
- "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
- i, connection->getInputChannelName(), connection->getWindowName(),
- connection->getStatusLabel(), toString(connection->monitor),
- toString(connection->inputPublisherBlocked));
-
- if (!connection->outboundQueue.isEmpty()) {
- dump.appendFormat(INDENT3 "OutboundQueue: length=%u\n",
- connection->outboundQueue.count());
- for (DispatchEntry* entry = connection->outboundQueue.head; entry;
- entry = entry->next) {
- dump.append(INDENT4);
- entry->eventEntry->appendDescription(dump);
- dump.appendFormat(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
- entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f);
- }
- } else {
- dump.append(INDENT3 "OutboundQueue: <empty>\n");
- }
-
- if (!connection->waitQueue.isEmpty()) {
- dump.appendFormat(INDENT3 "WaitQueue: length=%u\n",
- connection->waitQueue.count());
- for (DispatchEntry* entry = connection->waitQueue.head; entry;
- entry = entry->next) {
- dump.append(INDENT4);
- entry->eventEntry->appendDescription(dump);
- dump.appendFormat(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%0.1fms, wait=%0.1fms\n",
- entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f,
- (currentTime - entry->deliveryTime) * 0.000001f);
- }
- } else {
- dump.append(INDENT3 "WaitQueue: <empty>\n");
- }
- }
- } else {
- dump.append(INDENT "Connections: <none>\n");
- }
-
- if (isAppSwitchPendingLocked()) {
- dump.appendFormat(INDENT "AppSwitch: pending, due in %0.1fms\n",
- (mAppSwitchDueTime - now()) / 1000000.0);
- } else {
- dump.append(INDENT "AppSwitch: not pending\n");
- }
-
- dump.append(INDENT "Configuration:\n");
- dump.appendFormat(INDENT2 "KeyRepeatDelay: %0.1fms\n",
- mConfig.keyRepeatDelay * 0.000001f);
- dump.appendFormat(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
- mConfig.keyRepeatTimeout * 0.000001f);
-}
-
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
- toString(monitor));
-#endif
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (getConnectionIndexLocked(inputChannel) >= 0) {
- ALOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
-
- sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
-
- int fd = inputChannel->getFd();
- mConnectionsByFd.add(fd, connection);
-
- if (monitor) {
- mMonitoringChannels.push(inputChannel);
- }
-
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- } // release lock
-
- // Wake the looper because some connections have changed.
- mLooper->wake();
- return OK;
-}
-
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
-#endif
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
- if (status) {
- return status;
- }
- } // release lock
-
- // Wake the poll loop because removing the connection may have changed the current
- // synchronization state.
- mLooper->wake();
- return OK;
-}
-
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
- bool notify) {
- ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
- if (connectionIndex < 0) {
- ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
-
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- mConnectionsByFd.removeItemsAt(connectionIndex);
-
- if (connection->monitor) {
- removeMonitorChannelLocked(inputChannel);
- }
-
- mLooper->removeFd(inputChannel->getFd());
-
- nsecs_t currentTime = now();
- abortBrokenDispatchCycleLocked(currentTime, connection, notify);
-
- connection->status = Connection::STATUS_ZOMBIE;
- return OK;
-}
-
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- if (mMonitoringChannels[i] == inputChannel) {
- mMonitoringChannels.removeAt(i);
- break;
- }
- }
-}
-
-ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
- ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- if (connection->inputChannel.get() == inputChannel.get()) {
- return connectionIndex;
- }
- }
-
- return -1;
-}
-
-void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
- commandEntry->connection = connection;
- commandEntry->eventTime = currentTime;
- commandEntry->seq = seq;
- commandEntry->handled = handled;
-}
-
-void InputDispatcher::onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection) {
- ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
- connection->getInputChannelName());
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
- commandEntry->connection = connection;
-}
-
-void InputDispatcher::onANRLocked(
- nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
- float dispatchLatency = (currentTime - eventTime) * 0.000001f;
- float waitDuration = (currentTime - waitStartTime) * 0.000001f;
- ALOGI("Application is not responding: %s. "
- "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
- dispatchLatency, waitDuration, reason);
-
- // Capture a record of the InputDispatcher state at the time of the ANR.
- time_t t = time(NULL);
- struct tm tm;
- localtime_r(&t, &tm);
- char timestr[64];
- strftime(timestr, sizeof(timestr), "%F %T", &tm);
- mLastANRState.clear();
- mLastANRState.append(INDENT "ANR:\n");
- mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr);
- mLastANRState.appendFormat(INDENT2 "Window: %s\n",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
- mLastANRState.appendFormat(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
- mLastANRState.appendFormat(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
- mLastANRState.appendFormat(INDENT2 "Reason: %s\n", reason);
- dumpDispatchStateLocked(mLastANRState);
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyANRLockedInterruptible);
- commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputWindowHandle = windowHandle;
- commandEntry->reason = reason;
-}
-
-void InputDispatcher::doNotifyConfigurationChangedInterruptible(
- CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
- CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
-
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);
-
- mLock.lock();
- }
-}
-
-void InputDispatcher::doNotifyANRLockedInterruptible(
- CommandEntry* commandEntry) {
- mLock.unlock();
-
- nsecs_t newTimeout = mPolicy->notifyANR(
- commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
- commandEntry->reason);
-
- mLock.lock();
-
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
- commandEntry->inputWindowHandle != NULL
- ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
-}
-
-void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
- CommandEntry* commandEntry) {
- KeyEntry* entry = commandEntry->keyEntry;
-
- KeyEvent event;
- initializeKeyEvent(&event, entry);
-
- mLock.unlock();
-
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
- &event, entry->policyFlags);
-
- mLock.lock();
-
- if (delay < 0) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
- } else if (!delay) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
- } else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
- entry->interceptKeyWakeupTime = now() + delay;
- }
- entry->release();
-}
-
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
- CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
- nsecs_t finishTime = commandEntry->eventTime;
- uint32_t seq = commandEntry->seq;
- bool handled = commandEntry->handled;
-
- // Handle post-event policy actions.
- DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
- if (dispatchEntry) {
- nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
- if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- String8 msg;
- msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ",
- connection->getWindowName(), eventDuration * 0.000001f);
- dispatchEntry->eventEntry->appendDescription(msg);
- ALOGI("%s", msg.string());
- }
-
- bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
- restartEvent = afterKeyEventLockedInterruptible(connection,
- dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
- restartEvent = afterMotionEventLockedInterruptible(connection,
- dispatchEntry, motionEntry, handled);
- } else {
- restartEvent = false;
- }
-
- // Dequeue the event and start the next cycle.
- // Note that because the lock might have been released, it is possible that the
- // contents of the wait queue to have been drained, so we need to double-check
- // a few things.
- if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
- connection->waitQueue.dequeue(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
- if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
- connection->outboundQueue.enqueueAtHead(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
- } else {
- releaseDispatchEntryLocked(dispatchEntry);
- }
- }
-
- // Start the next dispatch cycle for this connection.
- startDispatchCycleLocked(now(), connection);
- }
-}
-
-bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) {
- if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
- // Get the fallback key state.
- // Clear it out after dispatching the UP.
- int32_t originalKeyCode = keyEntry->keyCode;
- int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- connection->inputState.removeFallbackKey(originalKeyCode);
- }
-
- if (handled || !dispatchEntry->hasForegroundTarget()) {
- // If the application handles the original key for which we previously
- // generated a fallback or if the window is not a foreground window,
- // then cancel the associated fallback key, if any.
- if (fallbackKeyCode != -1) {
- // Dispatch the unhandled key to the policy with the cancel flag.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
-#endif
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
- event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
-
- mLock.unlock();
-
- mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
- &event, keyEntry->policyFlags, &event);
-
- mLock.lock();
-
- // Cancel the fallback key.
- if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
- "application handled the original non-fallback key "
- "or is no longer a foreground target, "
- "canceling previously dispatched fallback key");
- options.keyCode = fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- connection->inputState.removeFallbackKey(originalKeyCode);
- }
- } else {
- // If the application did not handle a non-fallback key, first check
- // that we are in a good state to perform unhandled key event processing
- // Then ask the policy what to do with it.
- bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
- && keyEntry->repeatCount == 0;
- if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Skipping unhandled key event processing "
- "since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
-#endif
- return false;
- }
-
- // Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to perform fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
-#endif
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
-
- mLock.unlock();
-
- bool fallback = mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
- &event, keyEntry->policyFlags, &event);
-
- mLock.lock();
-
- if (connection->status != Connection::STATUS_NORMAL) {
- connection->inputState.removeFallbackKey(originalKeyCode);
- return false;
- }
-
- // Latch the fallback keycode for this key on an initial down.
- // The fallback keycode cannot change at any other point in the lifecycle.
- if (initialDown) {
- if (fallback) {
- fallbackKeyCode = event.getKeyCode();
- } else {
- fallbackKeyCode = AKEYCODE_UNKNOWN;
- }
- connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
- }
-
- ALOG_ASSERT(fallbackKeyCode != -1);
-
- // Cancel the fallback key if the policy decides not to send it anymore.
- // We will continue to dispatch the key to the policy but we will no
- // longer dispatch a fallback key to the application.
- if (fallbackKeyCode != AKEYCODE_UNKNOWN
- && (!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- if (fallback) {
- ALOGD("Unhandled key event: Policy requested to send key %d"
- "as a fallback for %d, but on the DOWN it had requested "
- "to send %d instead. Fallback canceled.",
- event.getKeyCode(), originalKeyCode, fallbackKeyCode);
- } else {
- ALOGD("Unhandled key event: Policy did not request fallback for %d, "
- "but on the DOWN it had requested to send %d. "
- "Fallback canceled.",
- originalKeyCode, fallbackKeyCode);
- }
-#endif
-
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
- "canceling fallback, policy no longer desires it");
- options.keyCode = fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
-
- fallback = false;
- fallbackKeyCode = AKEYCODE_UNKNOWN;
- if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
- connection->inputState.setFallbackKey(originalKeyCode,
- fallbackKeyCode);
- }
- }
-
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- {
- String8 msg;
- const KeyedVector<int32_t, int32_t>& fallbackKeys =
- connection->inputState.getFallbackKeys();
- for (size_t i = 0; i < fallbackKeys.size(); i++) {
- msg.appendFormat(", %d->%d", fallbackKeys.keyAt(i),
- fallbackKeys.valueAt(i));
- }
- ALOGD("Unhandled key event: %d currently tracked fallback keys%s.",
- fallbackKeys.size(), msg.string());
- }
-#endif
-
- if (fallback) {
- // Restart the dispatch cycle using the fallback key.
- keyEntry->eventTime = event.getEventTime();
- keyEntry->deviceId = event.getDeviceId();
- keyEntry->source = event.getSource();
- keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = fallbackKeyCode;
- keyEntry->scanCode = event.getScanCode();
- keyEntry->metaState = event.getMetaState();
- keyEntry->repeatCount = event.getRepeatCount();
- keyEntry->downTime = event.getDownTime();
- keyEntry->syntheticRepeat = false;
-
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry->metaState);
-#endif
- return true; // restart the event
- } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: No fallback key.");
-#endif
- }
- }
- }
- return false;
-}
-
-bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) {
- return false;
-}
-
-void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
-
- mLock.lock();
-}
-
-void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
- event->initialize(entry->deviceId, entry->source, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
- entry->downTime, entry->eventTime);
-}
-
-void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
- int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
- // TODO Write some statistics about how long we spend waiting.
-}
-
-void InputDispatcher::traceInboundQueueLengthLocked() {
- if (ATRACE_ENABLED()) {
- ATRACE_INT("iq", mInboundQueue.count());
- }
-}
-
-void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& connection) {
- if (ATRACE_ENABLED()) {
- char counterName[40];
- snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName());
- ATRACE_INT(counterName, connection->outboundQueue.count());
- }
-}
-
-void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connection) {
- if (ATRACE_ENABLED()) {
- char counterName[40];
- snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName());
- ATRACE_INT(counterName, connection->waitQueue.count());
- }
-}
-
-void InputDispatcher::dump(String8& dump) {
- AutoMutex _l(mLock);
-
- dump.append("Input Dispatcher State:\n");
- dumpDispatchStateLocked(dump);
-
- if (!mLastANRState.isEmpty()) {
- dump.append("\nInput Dispatcher State at time of last ANR:\n");
- dump.append(mLastANRState);
- }
-}
-
-void InputDispatcher::monitor() {
- // Acquire and release the lock to ensure that the dispatcher has not deadlocked.
- mLock.lock();
- mLooper->wake();
- mDispatcherIsAliveCondition.wait(mLock);
- mLock.unlock();
-}
-
-
-// --- InputDispatcher::Queue ---
-
-template <typename T>
-uint32_t InputDispatcher::Queue<T>::count() const {
- uint32_t result = 0;
- for (const T* entry = head; entry; entry = entry->next) {
- result += 1;
- }
- return result;
-}
-
-
-// --- InputDispatcher::InjectionState ---
-
-InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) :
- refCount(1),
- injectorPid(injectorPid), injectorUid(injectorUid),
- injectionResult(INPUT_EVENT_INJECTION_PENDING), injectionIsAsync(false),
- pendingForegroundDispatches(0) {
-}
-
-InputDispatcher::InjectionState::~InjectionState() {
-}
-
-void InputDispatcher::InjectionState::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
-
-// --- InputDispatcher::EventEntry ---
-
-InputDispatcher::EventEntry::EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags) :
- refCount(1), type(type), eventTime(eventTime), policyFlags(policyFlags),
- injectionState(NULL), dispatchInProgress(false) {
-}
-
-InputDispatcher::EventEntry::~EventEntry() {
- releaseInjectionState();
-}
-
-void InputDispatcher::EventEntry::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
-void InputDispatcher::EventEntry::releaseInjectionState() {
- if (injectionState) {
- injectionState->release();
- injectionState = NULL;
- }
-}
-
-
-// --- InputDispatcher::ConfigurationChangedEntry ---
-
-InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(nsecs_t eventTime) :
- EventEntry(TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
-}
-
-InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {
-}
-
-void InputDispatcher::ConfigurationChangedEntry::appendDescription(String8& msg) const {
- msg.append("ConfigurationChangedEvent(), policyFlags=0x%08x",
- policyFlags);
-}
-
-
-// --- InputDispatcher::DeviceResetEntry ---
-
-InputDispatcher::DeviceResetEntry::DeviceResetEntry(nsecs_t eventTime, int32_t deviceId) :
- EventEntry(TYPE_DEVICE_RESET, eventTime, 0),
- deviceId(deviceId) {
-}
-
-InputDispatcher::DeviceResetEntry::~DeviceResetEntry() {
-}
-
-void InputDispatcher::DeviceResetEntry::appendDescription(String8& msg) const {
- msg.appendFormat("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x",
- deviceId, policyFlags);
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime) :
- EventEntry(TYPE_KEY, eventTime, policyFlags),
- deviceId(deviceId), source(source), action(action), flags(flags),
- keyCode(keyCode), scanCode(scanCode), metaState(metaState),
- repeatCount(repeatCount), downTime(downTime),
- syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
- interceptKeyWakeupTime(0) {
-}
-
-InputDispatcher::KeyEntry::~KeyEntry() {
-}
-
-void InputDispatcher::KeyEntry::appendDescription(String8& msg) const {
- msg.appendFormat("KeyEvent(deviceId=%d, source=0x%08x, action=%d, "
- "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
- "repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, action, flags, keyCode, scanCode, metaState,
- repeatCount, policyFlags);
-}
-
-void InputDispatcher::KeyEntry::recycle() {
- releaseInjectionState();
-
- dispatchInProgress = false;
- syntheticRepeat = false;
- interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
- interceptKeyWakeupTime = 0;
-}
-
-
-// --- InputDispatcher::MotionEntry ---
-
-InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, float xPrecision, float yPrecision,
- nsecs_t downTime, int32_t displayId, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xOffset, float yOffset) :
- EventEntry(TYPE_MOTION, eventTime, policyFlags),
- eventTime(eventTime),
- deviceId(deviceId), source(source), action(action), flags(flags),
- metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags),
- xPrecision(xPrecision), yPrecision(yPrecision),
- downTime(downTime), displayId(displayId), pointerCount(pointerCount) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
- if (xOffset || yOffset) {
- this->pointerCoords[i].applyOffset(xOffset, yOffset);
- }
- }
-}
-
-InputDispatcher::MotionEntry::~MotionEntry() {
-}
-
-void InputDispatcher::MotionEntry::appendDescription(String8& msg) const {
- msg.appendFormat("MotionEvent(deviceId=%d, source=0x%08x, action=%d, "
- "flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, edgeFlags=0x%08x, "
- "xPrecision=%.1f, yPrecision=%.1f, displayId=%d, pointers=[",
- deviceId, source, action, flags, metaState, buttonState, edgeFlags,
- xPrecision, yPrecision, displayId);
- for (uint32_t i = 0; i < pointerCount; i++) {
- if (i) {
- msg.append(", ");
- }
- msg.appendFormat("%d: (%.1f, %.1f)", pointerProperties[i].id,
- pointerCoords[i].getX(), pointerCoords[i].getY());
- }
- msg.appendFormat("]), policyFlags=0x%08x", policyFlags);
-}
-
-
-// --- InputDispatcher::DispatchEntry ---
-
-volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
-
-InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
- seq(nextSeq()),
- eventEntry(eventEntry), targetFlags(targetFlags),
- xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
- deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
- eventEntry->refCount += 1;
-}
-
-InputDispatcher::DispatchEntry::~DispatchEntry() {
- eventEntry->release();
-}
-
-uint32_t InputDispatcher::DispatchEntry::nextSeq() {
- // Sequence number 0 is reserved and will never be returned.
- uint32_t seq;
- do {
- seq = android_atomic_inc(&sNextSeqAtomic);
- } while (!seq);
- return seq;
-}
-
-
-// --- InputDispatcher::InputState ---
-
-InputDispatcher::InputState::InputState() {
-}
-
-InputDispatcher::InputState::~InputState() {
-}
-
-bool InputDispatcher::InputState::isNeutral() const {
- return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
-}
-
-bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source,
- int32_t displayId) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
- if (memento.deviceId == deviceId
- && memento.source == source
- && memento.displayId == displayId
- && memento.hovering) {
- return true;
- }
- }
- return false;
-}
-
-bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
- int32_t action, int32_t flags) {
- switch (action) {
- case AKEY_EVENT_ACTION_UP: {
- if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
- for (size_t i = 0; i < mFallbackKeys.size(); ) {
- if (mFallbackKeys.valueAt(i) == entry->keyCode) {
- mFallbackKeys.removeItemsAt(i);
- } else {
- i += 1;
- }
- }
- }
- ssize_t index = findKeyMemento(entry);
- if (index >= 0) {
- mKeyMementos.removeAt(index);
- return true;
- }
- /* FIXME: We can't just drop the key up event because that prevents creating
- * popup windows that are automatically shown when a key is held and then
- * dismissed when the key is released. The problem is that the popup will
- * not have received the original key down, so the key up will be considered
- * to be inconsistent with its observed state. We could perhaps handle this
- * by synthesizing a key down but that will cause other problems.
- *
- * So for now, allow inconsistent key up events to be dispatched.
- *
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
- "keyCode=%d, scanCode=%d",
- entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
-#endif
- return false;
- */
- return true;
- }
-
- case AKEY_EVENT_ACTION_DOWN: {
- ssize_t index = findKeyMemento(entry);
- if (index >= 0) {
- mKeyMementos.removeAt(index);
- }
- addKeyMemento(entry, flags);
- return true;
- }
-
- default:
- return true;
- }
-}
-
-bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry,
- int32_t action, int32_t flags) {
- int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
- switch (actionMasked) {
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL: {
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
- if (index >= 0) {
- mMotionMementos.removeAt(index);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
- "actionMasked=%d",
- entry->deviceId, entry->source, actionMasked);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_DOWN: {
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
- if (index >= 0) {
- mMotionMementos.removeAt(index);
- }
- addMotionMemento(entry, flags, false /*hovering*/);
- return true;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_MOVE: {
- if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
- // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to
- // generate cancellation events for these since they're based in relative rather than
- // absolute units.
- return true;
- }
-
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
-
- if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
- // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
- // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any
- // other value and we need to track the motion so we can send cancellation events for
- // anything generating fallback events (e.g. DPad keys for joystick movements).
- if (index >= 0) {
- if (entry->pointerCoords[0].isEmpty()) {
- mMotionMementos.removeAt(index);
- } else {
- MotionMemento& memento = mMotionMementos.editItemAt(index);
- memento.setPointers(entry);
- }
- } else if (!entry->pointerCoords[0].isEmpty()) {
- addMotionMemento(entry, flags, false /*hovering*/);
- }
-
- // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
- return true;
- }
- if (index >= 0) {
- MotionMemento& memento = mMotionMementos.editItemAt(index);
- memento.setPointers(entry);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion pointer up/down or move event: "
- "deviceId=%d, source=%08x, actionMasked=%d",
- entry->deviceId, entry->source, actionMasked);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_HOVER_EXIT: {
- ssize_t index = findMotionMemento(entry, true /*hovering*/);
- if (index >= 0) {
- mMotionMementos.removeAt(index);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x",
- entry->deviceId, entry->source);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE: {
- ssize_t index = findMotionMemento(entry, true /*hovering*/);
- if (index >= 0) {
- mMotionMementos.removeAt(index);
- }
- addMotionMemento(entry, flags, true /*hovering*/);
- return true;
- }
-
- default:
- return true;
- }
-}
-
-ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
- for (size_t i = 0; i < mKeyMementos.size(); i++) {
- const KeyMemento& memento = mKeyMementos.itemAt(i);
- if (memento.deviceId == entry->deviceId
- && memento.source == entry->source
- && memento.keyCode == entry->keyCode
- && memento.scanCode == entry->scanCode) {
- return i;
- }
- }
- return -1;
-}
-
-ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
- bool hovering) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
- if (memento.deviceId == entry->deviceId
- && memento.source == entry->source
- && memento.displayId == entry->displayId
- && memento.hovering == hovering) {
- return i;
- }
- }
- return -1;
-}
-
-void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
- mKeyMementos.push();
- KeyMemento& memento = mKeyMementos.editTop();
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.keyCode = entry->keyCode;
- memento.scanCode = entry->scanCode;
- memento.metaState = entry->metaState;
- memento.flags = flags;
- memento.downTime = entry->downTime;
- memento.policyFlags = entry->policyFlags;
-}
-
-void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
- int32_t flags, bool hovering) {
- mMotionMementos.push();
- MotionMemento& memento = mMotionMementos.editTop();
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.flags = flags;
- memento.xPrecision = entry->xPrecision;
- memento.yPrecision = entry->yPrecision;
- memento.downTime = entry->downTime;
- memento.displayId = entry->displayId;
- memento.setPointers(entry);
- memento.hovering = hovering;
- memento.policyFlags = entry->policyFlags;
-}
-
-void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
- pointerCount = entry->pointerCount;
- for (uint32_t i = 0; i < entry->pointerCount; i++) {
- pointerProperties[i].copyFrom(entry->pointerProperties[i]);
- pointerCoords[i].copyFrom(entry->pointerCoords[i]);
- }
-}
-
-void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
- Vector<EventEntry*>& outEvents, const CancelationOptions& options) {
- for (size_t i = 0; i < mKeyMementos.size(); i++) {
- const KeyMemento& memento = mKeyMementos.itemAt(i);
- if (shouldCancelKey(memento, options)) {
- outEvents.push(new KeyEntry(currentTime,
- memento.deviceId, memento.source, memento.policyFlags,
- AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
- memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
- }
- }
-
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
- if (shouldCancelMotion(memento, options)) {
- outEvents.push(new MotionEntry(currentTime,
- memento.deviceId, memento.source, memento.policyFlags,
- memento.hovering
- ? AMOTION_EVENT_ACTION_HOVER_EXIT
- : AMOTION_EVENT_ACTION_CANCEL,
- memento.flags, 0, 0, 0,
- memento.xPrecision, memento.yPrecision, memento.downTime,
- memento.displayId,
- memento.pointerCount, memento.pointerProperties, memento.pointerCoords,
- 0, 0));
- }
- }
-}
-
-void InputDispatcher::InputState::clear() {
- mKeyMementos.clear();
- mMotionMementos.clear();
- mFallbackKeys.clear();
-}
-
-void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
- if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
- for (size_t j = 0; j < other.mMotionMementos.size(); ) {
- const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j);
- if (memento.deviceId == otherMemento.deviceId
- && memento.source == otherMemento.source
- && memento.displayId == otherMemento.displayId) {
- other.mMotionMementos.removeAt(j);
- } else {
- j += 1;
- }
- }
- other.mMotionMementos.push(memento);
- }
- }
-}
-
-int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) {
- ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
- return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
-}
-
-void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode,
- int32_t fallbackKeyCode) {
- ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
- if (index >= 0) {
- mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
- } else {
- mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
- }
-}
-
-void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) {
- mFallbackKeys.removeItem(originalKeyCode);
-}
-
-bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
- const CancelationOptions& options) {
- if (options.keyCode != -1 && memento.keyCode != options.keyCode) {
- return false;
- }
-
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
- return false;
- }
-
- switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
- return true;
- case CancelationOptions::CANCEL_FALLBACK_EVENTS:
- return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
- default:
- return false;
- }
-}
-
-bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
- const CancelationOptions& options) {
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
- return false;
- }
-
- switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- return true;
- case CancelationOptions::CANCEL_POINTER_EVENTS:
- return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
- return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
- default:
- return false;
- }
-}
-
-
-// --- InputDispatcher::Connection ---
-
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
- status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
- monitor(monitor),
- inputPublisher(inputChannel), inputPublisherBlocked(false) {
-}
-
-InputDispatcher::Connection::~Connection() {
-}
-
-const char* InputDispatcher::Connection::getWindowName() const {
- if (inputWindowHandle != NULL) {
- return inputWindowHandle->getName().string();
- }
- if (monitor) {
- return "monitor";
- }
- return "?";
-}
-
-const char* InputDispatcher::Connection::getStatusLabel() const {
- switch (status) {
- case STATUS_NORMAL:
- return "NORMAL";
-
- case STATUS_BROKEN:
- return "BROKEN";
-
- case STATUS_ZOMBIE:
- return "ZOMBIE";
-
- default:
- return "UNKNOWN";
- }
-}
-
-InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
- for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) {
- if (entry->seq == seq) {
- return entry;
- }
- }
- return NULL;
-}
-
-
-// --- InputDispatcher::CommandEntry ---
-
-InputDispatcher::CommandEntry::CommandEntry(Command command) :
- command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
- seq(0), handled(false) {
-}
-
-InputDispatcher::CommandEntry::~CommandEntry() {
-}
-
-
-// --- InputDispatcher::TouchState ---
-
-InputDispatcher::TouchState::TouchState() :
- down(false), split(false), deviceId(-1), source(0), displayId(-1) {
-}
-
-InputDispatcher::TouchState::~TouchState() {
-}
-
-void InputDispatcher::TouchState::reset() {
- down = false;
- split = false;
- deviceId = -1;
- source = 0;
- displayId = -1;
- windows.clear();
-}
-
-void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
- down = other.down;
- split = other.split;
- deviceId = other.deviceId;
- source = other.source;
- displayId = other.displayId;
- windows = other.windows;
-}
-
-void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds) {
- if (targetFlags & InputTarget::FLAG_SPLIT) {
- split = true;
- }
-
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows.editItemAt(i);
- if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.targetFlags |= targetFlags;
- if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
- }
- touchedWindow.pointerIds.value |= pointerIds.value;
- return;
- }
- }
-
- windows.push();
-
- TouchedWindow& touchedWindow = windows.editTop();
- touchedWindow.windowHandle = windowHandle;
- touchedWindow.targetFlags = targetFlags;
- touchedWindow.pointerIds = pointerIds;
-}
-
-void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
- for (size_t i = 0; i < windows.size(); i++) {
- if (windows.itemAt(i).windowHandle == windowHandle) {
- windows.removeAt(i);
- return;
- }
- }
-}
-
-void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
- for (size_t i = 0 ; i < windows.size(); ) {
- TouchedWindow& window = windows.editItemAt(i);
- if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
- | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
- window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
- window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
- i += 1;
- } else {
- windows.removeAt(i);
- }
- }
-}
-
-sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const {
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows.itemAt(i);
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
- return window.windowHandle;
- }
- }
- return NULL;
-}
-
-bool InputDispatcher::TouchState::isSlippery() const {
- // Must have exactly one foreground window.
- bool haveSlipperyForegroundWindow = false;
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows.itemAt(i);
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
- if (haveSlipperyForegroundWindow
- || !(window.windowHandle->getInfo()->layoutParamsFlags
- & InputWindowInfo::FLAG_SLIPPERY)) {
- return false;
- }
- haveSlipperyForegroundWindow = true;
- }
- }
- return haveSlipperyForegroundWindow;
-}
-
-
-// --- InputDispatcherThread ---
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
- Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
-}
-
-InputDispatcherThread::~InputDispatcherThread() {
-}
-
-bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
-}
-
-} // namespace android
diff --git a/libs/input/InputDispatcher.h b/libs/input/InputDispatcher.h
deleted file mode 100644
index 9439124..0000000
--- a/libs/input/InputDispatcher.h
+++ /dev/null
@@ -1,1121 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _UI_INPUT_DISPATCHER_H
-#define _UI_INPUT_DISPATCHER_H
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-#include <utils/KeyedVector.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Looper.h>
-#include <utils/BitSet.h>
-#include <cutils/atomic.h>
-
-#include <stddef.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "InputWindow.h"
-#include "InputApplication.h"
-#include "InputListener.h"
-
-
-namespace android {
-
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
- /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
- INPUT_EVENT_INJECTION_PENDING = -1,
-
- /* Injection succeeded. */
- INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
- /* Injection failed because the injector did not have permission to inject
- * into the application with input focus. */
- INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
- /* Injection failed because there were no available input targets. */
- INPUT_EVENT_INJECTION_FAILED = 2,
-
- /* Injection failed due to a timeout. */
- INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
- /* Injection is asynchronous and is assumed always to be successful. */
- INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
- /* Waits for previous events to be dispatched so that the input dispatcher can determine
- * whether input event injection willbe permitted based on the current input focus.
- * Does not wait for the input event to finish processing. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
- /* Waits for the input event to be completely processed. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
-
-
-/*
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- */
-struct InputTarget {
- enum {
- /* This flag indicates that the event is being delivered to a foreground application. */
- FLAG_FOREGROUND = 1 << 0,
-
- /* This flag indicates that the target of a MotionEvent is partly or wholly
- * obscured by another visible window above it. The motion event should be
- * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
- FLAG_WINDOW_IS_OBSCURED = 1 << 1,
-
- /* This flag indicates that a motion event is being split across multiple windows. */
- FLAG_SPLIT = 1 << 2,
-
- /* This flag indicates that the pointer coordinates dispatched to the application
- * will be zeroed out to avoid revealing information to an application. This is
- * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
- * the same UID from watching all touches. */
- FLAG_ZERO_COORDS = 1 << 3,
-
- /* This flag indicates that the event should be sent as is.
- * Should always be set unless the event is to be transmuted. */
- FLAG_DISPATCH_AS_IS = 1 << 8,
-
- /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
- * of the area of this target and so should instead be delivered as an
- * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
- FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
-
- /* This flag indicates that a hover sequence is starting in the given window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
-
- /* This flag indicates that a hover event happened outside of a window which handled
- * previous hover events, signifying the end of the current hover sequence for that
- * window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
-
- /* This flag indicates that the event should be canceled.
- * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
- * outside of a window. */
- FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
-
- /* This flag indicates that the event should be dispatched as an initial down.
- * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
- * into a new window. */
- FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
-
- /* Mask for all dispatch modes. */
- FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
- | FLAG_DISPATCH_AS_OUTSIDE
- | FLAG_DISPATCH_AS_HOVER_ENTER
- | FLAG_DISPATCH_AS_HOVER_EXIT
- | FLAG_DISPATCH_AS_SLIPPERY_EXIT
- | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
- };
-
- // The input channel to be targeted.
- sp<InputChannel> inputChannel;
-
- // Flags for the input target.
- int32_t flags;
-
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset, yOffset;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float scaleFactor;
-
- // The subset of pointer ids to include in motion events dispatched to this input target
- // if FLAG_SPLIT is set.
- BitSet32 pointerIds;
-};
-
-
-/*
- * Input dispatcher configuration.
- *
- * Specifies various options that modify the behavior of the input dispatcher.
- * The values provided here are merely defaults. The actual values will come from ViewConfiguration
- * and are passed into the dispatcher during initialization.
- */
-struct InputDispatcherConfiguration {
- // The key repeat initial timeout.
- nsecs_t keyRepeatTimeout;
-
- // The key repeat inter-key delay.
- nsecs_t keyRepeatDelay;
-
- InputDispatcherConfiguration() :
- keyRepeatTimeout(500 * 1000000LL),
- keyRepeatDelay(50 * 1000000LL) { }
-};
-
-
-/*
- * Input dispatcher policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI. This interface is also mocked in the unit tests.
- */
-class InputDispatcherPolicyInterface : public virtual RefBase {
-protected:
- InputDispatcherPolicyInterface() { }
- virtual ~InputDispatcherPolicyInterface() { }
-
-public:
- /* Notifies the system that a configuration change has occurred. */
- virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-
- /* Notifies the system that an application is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputWindowHandle>& inputWindowHandle,
- const String8& reason) = 0;
-
- /* Notifies the system that an input channel is unrecoverably broken. */
- virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) = 0;
-
- /* Gets the input dispatcher configuration. */
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
-
- /* Filters an input event.
- * Return true to dispatch the event unmodified, false to consume the event.
- * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
- * to injectInputEvent.
- */
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
-
- /* Intercepts a key event immediately before queueing it.
- * The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing such as updating policy flags.
- *
- * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
- * should be dispatched to applications.
- */
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
-
- /* Intercepts a touch, trackball or other motion event before queueing it.
- * The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing such as updating policy flags.
- *
- * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
- * should be dispatched to applications.
- */
- virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
-
- /* Allows the policy a chance to intercept a key before dispatching. */
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
- const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
-
- /* Allows the policy a chance to perform default processing for an unhandled key.
- * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
- virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
- const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
-
- /* Notifies the policy about switch events.
- */
- virtual void notifySwitch(nsecs_t when,
- uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0;
-
- /* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
-
- /* Checks whether a given application pid/uid has permission to inject input events
- * into other applications.
- *
- * This method is special in that its implementation promises to be non-reentrant and
- * is safe to call while holding other locks. (Most other methods make no such guarantees!)
- */
- virtual bool checkInjectEventsPermissionNonReentrant(
- int32_t injectorPid, int32_t injectorUid) = 0;
-};
-
-
-/* Notifies the system about input events generated by the input reader.
- * The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
-protected:
- InputDispatcherInterface() { }
- virtual ~InputDispatcherInterface() { }
-
-public:
- /* Dumps the state of the input dispatcher.
- *
- * This method may be called on any thread (usually by the input manager). */
- virtual void dump(String8& dump) = 0;
-
- /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
- virtual void monitor() = 0;
-
- /* Runs a single iteration of the dispatch loop.
- * Nominally processes one queued event, a timeout, or a response from an input consumer.
- *
- * This method should only be called on the input dispatcher thread.
- */
- virtual void dispatchOnce() = 0;
-
- /* Injects an input event and optionally waits for sync.
- * The synchronization mode determines whether the method blocks while waiting for
- * input injection to proceed.
- * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) = 0;
-
- /* Sets the list of input windows.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0;
-
- /* Sets the focused application.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setFocusedApplication(
- const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
-
- /* Sets the input dispatching mode.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
-
- /* Sets whether input event filtering is enabled.
- * When enabled, incoming input events are sent to the policy's filterInputEvent
- * method instead of being dispatched. The filter is expected to use
- * injectInputEvent to inject the events it would like to have dispatched.
- * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
- */
- virtual void setInputFilterEnabled(bool enabled) = 0;
-
- /* Transfers touch focus from the window associated with one channel to the
- * window associated with the other channel.
- *
- * Returns true on success. False if the window did not actually have touch focus.
- */
- virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel) = 0;
-
- /* Registers or unregister input channels that may be used as targets for input events.
- * If monitor is true, the channel will receive a copy of all input events.
- *
- * These methods may be called on any thread (usually by the input manager).
- */
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0;
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-};
-
-/* Dispatches events to input targets. Some functions of the input dispatcher, such as
- * identifying input targets, are controlled by a separate policy object.
- *
- * IMPORTANT INVARIANT:
- * Because the policy can potentially block or cause re-entrance into the input dispatcher,
- * the input dispatcher never calls into the policy while holding its internal locks.
- * The implementation is also carefully designed to recover from scenarios such as an
- * input channel becoming unregistered while identifying input targets or processing timeouts.
- *
- * Methods marked 'Locked' must be called with the lock acquired.
- *
- * Methods marked 'LockedInterruptible' must be called with the lock acquired but
- * may during the course of their execution release the lock, call into the policy, and
- * then reacquire the lock. The caller is responsible for recovering gracefully.
- *
- * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
- */
-class InputDispatcher : public InputDispatcherInterface {
-protected:
- virtual ~InputDispatcher();
-
-public:
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
-
- virtual void dump(String8& dump);
- virtual void monitor();
-
- virtual void dispatchOnce();
-
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
-
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags);
-
- virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles);
- virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
- virtual void setInputDispatchMode(bool enabled, bool frozen);
- virtual void setInputFilterEnabled(bool enabled);
-
- virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel);
-
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
-
-private:
- template <typename T>
- struct Link {
- T* next;
- T* prev;
-
- protected:
- inline Link() : next(NULL), prev(NULL) { }
- };
-
- struct InjectionState {
- mutable int32_t refCount;
-
- int32_t injectorPid;
- int32_t injectorUid;
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
- bool injectionIsAsync; // set to true if injection is not waiting for the result
- int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
-
- InjectionState(int32_t injectorPid, int32_t injectorUid);
- void release();
-
- private:
- ~InjectionState();
- };
-
- struct EventEntry : Link<EventEntry> {
- enum {
- TYPE_CONFIGURATION_CHANGED,
- TYPE_DEVICE_RESET,
- TYPE_KEY,
- TYPE_MOTION
- };
-
- mutable int32_t refCount;
- int32_t type;
- nsecs_t eventTime;
- uint32_t policyFlags;
- InjectionState* injectionState;
-
- bool dispatchInProgress; // initially false, set to true while dispatching
-
- inline bool isInjected() const { return injectionState != NULL; }
-
- void release();
-
- virtual void appendDescription(String8& msg) const = 0;
-
- protected:
- EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags);
- virtual ~EventEntry();
- void releaseInjectionState();
- };
-
- struct ConfigurationChangedEntry : EventEntry {
- ConfigurationChangedEntry(nsecs_t eventTime);
- virtual void appendDescription(String8& msg) const;
-
- protected:
- virtual ~ConfigurationChangedEntry();
- };
-
- struct DeviceResetEntry : EventEntry {
- int32_t deviceId;
-
- DeviceResetEntry(nsecs_t eventTime, int32_t deviceId);
- virtual void appendDescription(String8& msg) const;
-
- protected:
- virtual ~DeviceResetEntry();
- };
-
- struct KeyEntry : EventEntry {
- int32_t deviceId;
- uint32_t source;
- int32_t action;
- int32_t flags;
- int32_t keyCode;
- int32_t scanCode;
- int32_t metaState;
- int32_t repeatCount;
- nsecs_t downTime;
-
- bool syntheticRepeat; // set to true for synthetic key repeats
-
- enum InterceptKeyResult {
- INTERCEPT_KEY_RESULT_UNKNOWN,
- INTERCEPT_KEY_RESULT_SKIP,
- INTERCEPT_KEY_RESULT_CONTINUE,
- INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
- };
- InterceptKeyResult interceptKeyResult; // set based on the interception result
- nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
-
- KeyEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime);
- virtual void appendDescription(String8& msg) const;
- void recycle();
-
- protected:
- virtual ~KeyEntry();
- };
-
- struct MotionEntry : EventEntry {
- nsecs_t eventTime;
- int32_t deviceId;
- uint32_t source;
- int32_t action;
- int32_t flags;
- int32_t metaState;
- int32_t buttonState;
- int32_t edgeFlags;
- float xPrecision;
- float yPrecision;
- nsecs_t downTime;
- int32_t displayId;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
-
- MotionEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags,
- int32_t action, int32_t flags,
- int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- float xPrecision, float yPrecision,
- nsecs_t downTime, int32_t displayId, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xOffset, float yOffset);
- virtual void appendDescription(String8& msg) const;
-
- protected:
- virtual ~MotionEntry();
- };
-
- // Tracks the progress of dispatching a particular event to a particular connection.
- struct DispatchEntry : Link<DispatchEntry> {
- const uint32_t seq; // unique sequence number, never 0
-
- EventEntry* eventEntry; // the event to dispatch
- int32_t targetFlags;
- float xOffset;
- float yOffset;
- float scaleFactor;
- nsecs_t deliveryTime; // time when the event was actually delivered
-
- // Set to the resolved action and flags when the event is enqueued.
- int32_t resolvedAction;
- int32_t resolvedFlags;
-
- DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
- ~DispatchEntry();
-
- inline bool hasForegroundTarget() const {
- return targetFlags & InputTarget::FLAG_FOREGROUND;
- }
-
- inline bool isSplit() const {
- return targetFlags & InputTarget::FLAG_SPLIT;
- }
-
- private:
- static volatile int32_t sNextSeqAtomic;
-
- static uint32_t nextSeq();
- };
-
- // A command entry captures state and behavior for an action to be performed in the
- // dispatch loop after the initial processing has taken place. It is essentially
- // a kind of continuation used to postpone sensitive policy interactions to a point
- // in the dispatch loop where it is safe to release the lock (generally after finishing
- // the critical parts of the dispatch cycle).
- //
- // The special thing about commands is that they can voluntarily release and reacquire
- // the dispatcher lock at will. Initially when the command starts running, the
- // dispatcher lock is held. However, if the command needs to call into the policy to
- // do some work, it can release the lock, do the work, then reacquire the lock again
- // before returning.
- //
- // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
- // never calls into the policy while holding its lock.
- //
- // Commands are implicitly 'LockedInterruptible'.
- struct CommandEntry;
- typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
-
- class Connection;
- struct CommandEntry : Link<CommandEntry> {
- CommandEntry(Command command);
- ~CommandEntry();
-
- Command command;
-
- // parameters for the command (usage varies by command)
- sp<Connection> connection;
- nsecs_t eventTime;
- KeyEntry* keyEntry;
- sp<InputApplicationHandle> inputApplicationHandle;
- sp<InputWindowHandle> inputWindowHandle;
- String8 reason;
- int32_t userActivityEventType;
- uint32_t seq;
- bool handled;
- };
-
- // Generic queue implementation.
- template <typename T>
- struct Queue {
- T* head;
- T* tail;
-
- inline Queue() : head(NULL), tail(NULL) {
- }
-
- inline bool isEmpty() const {
- return !head;
- }
-
- inline void enqueueAtTail(T* entry) {
- entry->prev = tail;
- if (tail) {
- tail->next = entry;
- } else {
- head = entry;
- }
- entry->next = NULL;
- tail = entry;
- }
-
- inline void enqueueAtHead(T* entry) {
- entry->next = head;
- if (head) {
- head->prev = entry;
- } else {
- tail = entry;
- }
- entry->prev = NULL;
- head = entry;
- }
-
- inline void dequeue(T* entry) {
- if (entry->prev) {
- entry->prev->next = entry->next;
- } else {
- head = entry->next;
- }
- if (entry->next) {
- entry->next->prev = entry->prev;
- } else {
- tail = entry->prev;
- }
- }
-
- inline T* dequeueAtHead() {
- T* entry = head;
- head = entry->next;
- if (head) {
- head->prev = NULL;
- } else {
- tail = NULL;
- }
- return entry;
- }
-
- uint32_t count() const;
- };
-
- /* Specifies which events are to be canceled and why. */
- struct CancelationOptions {
- enum Mode {
- CANCEL_ALL_EVENTS = 0,
- CANCEL_POINTER_EVENTS = 1,
- CANCEL_NON_POINTER_EVENTS = 2,
- CANCEL_FALLBACK_EVENTS = 3,
- };
-
- // The criterion to use to determine which events should be canceled.
- Mode mode;
-
- // Descriptive reason for the cancelation.
- const char* reason;
-
- // The specific keycode of the key event to cancel, or -1 to cancel any key event.
- int32_t keyCode;
-
- // The specific device id of events to cancel, or -1 to cancel events from any device.
- int32_t deviceId;
-
- CancelationOptions(Mode mode, const char* reason) :
- mode(mode), reason(reason), keyCode(-1), deviceId(-1) { }
- };
-
- /* Tracks dispatched key and motion event state so that cancelation events can be
- * synthesized when events are dropped. */
- class InputState {
- public:
- InputState();
- ~InputState();
-
- // Returns true if there is no state to be canceled.
- bool isNeutral() const;
-
- // Returns true if the specified source is known to have received a hover enter
- // motion event.
- bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
-
- // Records tracking information for a key event that has just been published.
- // Returns true if the event should be delivered, false if it is inconsistent
- // and should be skipped.
- bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
-
- // Records tracking information for a motion event that has just been published.
- // Returns true if the event should be delivered, false if it is inconsistent
- // and should be skipped.
- bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
-
- // Synthesizes cancelation events for the current state and resets the tracked state.
- void synthesizeCancelationEvents(nsecs_t currentTime,
- Vector<EventEntry*>& outEvents, const CancelationOptions& options);
-
- // Clears the current state.
- void clear();
-
- // Copies pointer-related parts of the input state to another instance.
- void copyPointerStateTo(InputState& other) const;
-
- // Gets the fallback key associated with a keycode.
- // Returns -1 if none.
- // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
- int32_t getFallbackKey(int32_t originalKeyCode);
-
- // Sets the fallback key for a particular keycode.
- void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
-
- // Removes the fallback key for a particular keycode.
- void removeFallbackKey(int32_t originalKeyCode);
-
- inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const {
- return mFallbackKeys;
- }
-
- private:
- struct KeyMemento {
- int32_t deviceId;
- uint32_t source;
- int32_t keyCode;
- int32_t scanCode;
- int32_t metaState;
- int32_t flags;
- nsecs_t downTime;
- uint32_t policyFlags;
- };
-
- struct MotionMemento {
- int32_t deviceId;
- uint32_t source;
- int32_t flags;
- float xPrecision;
- float yPrecision;
- nsecs_t downTime;
- int32_t displayId;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- bool hovering;
- uint32_t policyFlags;
-
- void setPointers(const MotionEntry* entry);
- };
-
- Vector<KeyMemento> mKeyMementos;
- Vector<MotionMemento> mMotionMementos;
- KeyedVector<int32_t, int32_t> mFallbackKeys;
-
- ssize_t findKeyMemento(const KeyEntry* entry) const;
- ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
-
- void addKeyMemento(const KeyEntry* entry, int32_t flags);
- void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
-
- static bool shouldCancelKey(const KeyMemento& memento,
- const CancelationOptions& options);
- static bool shouldCancelMotion(const MotionMemento& memento,
- const CancelationOptions& options);
- };
-
- /* Manages the dispatch state associated with a single input channel. */
- class Connection : public RefBase {
- protected:
- virtual ~Connection();
-
- public:
- enum Status {
- // Everything is peachy.
- STATUS_NORMAL,
- // An unrecoverable communication error has occurred.
- STATUS_BROKEN,
- // The input channel has been unregistered.
- STATUS_ZOMBIE
- };
-
- Status status;
- sp<InputChannel> inputChannel; // never null
- sp<InputWindowHandle> inputWindowHandle; // may be null
- bool monitor;
- InputPublisher inputPublisher;
- InputState inputState;
-
- // True if the socket is full and no further events can be published until
- // the application consumes some of the input.
- bool inputPublisherBlocked;
-
- // Queue of events that need to be published to the connection.
- Queue<DispatchEntry> outboundQueue;
-
- // Queue of events that have been published to the connection but that have not
- // yet received a "finished" response from the application.
- Queue<DispatchEntry> waitQueue;
-
- explicit Connection(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
-
- inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
-
- const char* getWindowName() const;
- const char* getStatusLabel() const;
-
- DispatchEntry* findWaitQueueEntry(uint32_t seq);
- };
-
- enum DropReason {
- DROP_REASON_NOT_DROPPED = 0,
- DROP_REASON_POLICY = 1,
- DROP_REASON_APP_SWITCH = 2,
- DROP_REASON_DISABLED = 3,
- DROP_REASON_BLOCKED = 4,
- DROP_REASON_STALE = 5,
- };
-
- sp<InputDispatcherPolicyInterface> mPolicy;
- InputDispatcherConfiguration mConfig;
-
- Mutex mLock;
-
- Condition mDispatcherIsAliveCondition;
-
- sp<Looper> mLooper;
-
- EventEntry* mPendingEvent;
- Queue<EventEntry> mInboundQueue;
- Queue<EventEntry> mRecentQueue;
- Queue<CommandEntry> mCommandQueue;
-
- void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
-
- // Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry);
-
- // Cleans up input state when dropping an inbound event.
- void dropInboundEventLocked(EventEntry* entry, DropReason dropReason);
-
- // Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry);
-
- // App switch latency optimization.
- bool mAppSwitchSawKeyDown;
- nsecs_t mAppSwitchDueTime;
-
- static bool isAppSwitchKeyCode(int32_t keyCode);
- bool isAppSwitchKeyEventLocked(KeyEntry* keyEntry);
- bool isAppSwitchPendingLocked();
- void resetPendingAppSwitchLocked(bool handled);
-
- // Stale event latency optimization.
- static bool isStaleEventLocked(nsecs_t currentTime, EventEntry* entry);
-
- // Blocked event latency optimization. Drops old events when the user intends
- // to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent;
-
- sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
-
- // All registered connections mapped by channel file descriptor.
- KeyedVector<int, sp<Connection> > mConnectionsByFd;
-
- ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
-
- // Input channels that will receive a copy of all input events.
- Vector<sp<InputChannel> > mMonitoringChannels;
-
- // Event injection and synchronization.
- Condition mInjectionResultAvailableCondition;
- bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
-
- Condition mInjectionSyncFinishedCondition;
- void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
- void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
-
- // Key repeat tracking.
- struct KeyRepeatState {
- KeyEntry* lastKeyEntry; // or null if no repeat
- nsecs_t nextRepeatTime;
- } mKeyRepeatState;
-
- void resetKeyRepeatLocked();
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
-
- // Deferred command processing.
- bool haveCommandsLocked() const;
- bool runCommandsLockedInterruptible();
- CommandEntry* postCommandLocked(Command command);
-
- // Input filter processing.
- bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args);
- bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args);
-
- // Inbound event processing.
- void drainInboundQueueLocked();
- void releasePendingEventLocked();
- void releaseInboundEventLocked(EventEntry* entry);
-
- // Dispatch state.
- bool mDispatchEnabled;
- bool mDispatchFrozen;
- bool mInputFilterEnabled;
-
- Vector<sp<InputWindowHandle> > mWindowHandles;
-
- sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
-
- // Focus tracking for keys, trackball, etc.
- sp<InputWindowHandle> mFocusedWindowHandle;
-
- // Focus tracking for touch.
- struct TouchedWindow {
- sp<InputWindowHandle> windowHandle;
- int32_t targetFlags;
- BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
- };
- struct TouchState {
- bool down;
- bool split;
- int32_t deviceId; // id of the device that is currently down, others are rejected
- uint32_t source; // source of the device that is current down, others are rejected
- int32_t displayId; // id to the display that currently has a touch, others are rejected
- Vector<TouchedWindow> windows;
-
- TouchState();
- ~TouchState();
- void reset();
- void copyFrom(const TouchState& other);
- void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds);
- void removeWindow(const sp<InputWindowHandle>& windowHandle);
- void filterNonAsIsTouchWindows();
- sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
- bool isSlippery() const;
- };
-
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay;
- TouchState mTempTouchState;
-
- // Focused application.
- sp<InputApplicationHandle> mFocusedApplicationHandle;
-
- // Dispatcher state at time of last ANR.
- String8 mLastANRState;
-
- // Dispatch inbound events.
- bool dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry);
- bool dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry);
- bool dispatchKeyLocked(
- nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
- bool dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
- void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
- const Vector<InputTarget>& inputTargets);
-
- void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
- void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
-
- // Keeping track of ANR timeouts.
- enum InputTargetWaitCause {
- INPUT_TARGET_WAIT_CAUSE_NONE,
- INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
- INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
- };
-
- InputTargetWaitCause mInputTargetWaitCause;
- nsecs_t mInputTargetWaitStartTime;
- nsecs_t mInputTargetWaitTimeoutTime;
- bool mInputTargetWaitTimeoutExpired;
- sp<InputApplicationHandle> mInputTargetWaitApplicationHandle;
-
- // Contains the last window which received a hover event.
- sp<InputWindowHandle> mLastHoverWindowHandle;
-
- // Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason);
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel);
- nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
- void resetANRTimeoutsLocked();
-
- int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
- Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
- int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
- Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions);
-
- void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
- void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
-
- void pokeUserActivityLocked(const EventEntry* eventEntry);
- bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
- const InjectionState* injectionState);
- bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t x, int32_t y) const;
- bool isWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry);
- String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle);
-
- // Manage the dispatch cycle for a single connection.
- // These methods are deliberately not Interruptible because doing all of the work
- // with the mutex held makes it easier to ensure that connection invariants are maintained.
- // If needed, the methods post commands to run later once the critical bits are done.
- void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
- void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool notify);
- void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
- void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
- static int handleReceiveCallback(int fd, int events, void* data);
-
- void synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options);
- void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options);
- void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- const CancelationOptions& options);
-
- // Splitting motion events across windows.
- MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
-
- // Reset and drop everything the dispatcher is doing.
- void resetAndDropEverythingLocked(const char* reason);
-
- // Dump state.
- void dumpDispatchStateLocked(String8& dump);
- void logDispatchStateLocked();
-
- // Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify);
-
- // Add or remove a connection to the mActiveConnections vector.
- void activateConnectionLocked(Connection* connection);
- void deactivateConnectionLocked(Connection* connection);
-
- // Interesting events that we might like to log or tell the framework about.
- void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
- void onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection);
- void onANRLocked(
- nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t eventTime, nsecs_t waitStartTime, const char* reason);
-
- // Outbound policy interactions.
- void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
- void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
- void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
- bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled);
- bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled);
- void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
- void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
-
- // Statistics gathering.
- void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
- int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
- void traceInboundQueueLengthLocked();
- void traceOutboundQueueLengthLocked(const sp<Connection>& connection);
- void traceWaitQueueLengthLocked(const sp<Connection>& connection);
-};
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
- ~InputDispatcherThread();
-
-private:
- virtual bool threadLoop();
-
- sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DISPATCHER_H
diff --git a/libs/input/InputListener.cpp b/libs/input/InputListener.cpp
deleted file mode 100644
index 85bb0ed..0000000
--- a/libs/input/InputListener.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2011 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 "InputListener"
-
-//#define LOG_NDEBUG 0
-
-#include "InputListener.h"
-
-#include <cutils/log.h>
-
-namespace android {
-
-// --- NotifyConfigurationChangedArgs ---
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) :
- eventTime(eventTime) {
-}
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
- const NotifyConfigurationChangedArgs& other) :
- eventTime(other.eventTime) {
-}
-
-void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyConfigurationChanged(this);
-}
-
-
-// --- NotifyKeyArgs ---
-
-NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
- uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, nsecs_t downTime) :
- eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
- action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
- metaState(metaState), downTime(downTime) {
-}
-
-NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
- policyFlags(other.policyFlags),
- action(other.action), flags(other.flags),
- keyCode(other.keyCode), scanCode(other.scanCode),
- metaState(other.metaState), downTime(other.downTime) {
-}
-
-void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyKey(this);
-}
-
-
-// --- NotifyMotionArgs ---
-
-NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
- uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime) :
- eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
- action(action), flags(flags), metaState(metaState), buttonState(buttonState),
- edgeFlags(edgeFlags), displayId(displayId), pointerCount(pointerCount),
- xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
- }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
- policyFlags(other.policyFlags),
- action(other.action), flags(other.flags),
- metaState(other.metaState), buttonState(other.buttonState),
- edgeFlags(other.edgeFlags), displayId(other.displayId),
- pointerCount(other.pointerCount),
- xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
- }
-}
-
-void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyMotion(this);
-}
-
-
-// --- NotifySwitchArgs ---
-
-NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
- uint32_t switchValues, uint32_t switchMask) :
- eventTime(eventTime), policyFlags(policyFlags),
- switchValues(switchValues), switchMask(switchMask) {
-}
-
-NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
- eventTime(other.eventTime), policyFlags(other.policyFlags),
- switchValues(other.switchValues), switchMask(other.switchMask) {
-}
-
-void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifySwitch(this);
-}
-
-
-// --- NotifyDeviceResetArgs ---
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId) :
- eventTime(eventTime), deviceId(deviceId) {
-}
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId) {
-}
-
-void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyDeviceReset(this);
-}
-
-
-// --- QueuedInputListener ---
-
-QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
- mInnerListener(innerListener) {
-}
-
-QueuedInputListener::~QueuedInputListener() {
- size_t count = mArgsQueue.size();
- for (size_t i = 0; i < count; i++) {
- delete mArgsQueue[i];
- }
-}
-
-void QueuedInputListener::notifyConfigurationChanged(
- const NotifyConfigurationChangedArgs* args) {
- mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
-}
-
-void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
- mArgsQueue.push(new NotifyKeyArgs(*args));
-}
-
-void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
- mArgsQueue.push(new NotifyMotionArgs(*args));
-}
-
-void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
- mArgsQueue.push(new NotifySwitchArgs(*args));
-}
-
-void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mArgsQueue.push(new NotifyDeviceResetArgs(*args));
-}
-
-void QueuedInputListener::flush() {
- size_t count = mArgsQueue.size();
- for (size_t i = 0; i < count; i++) {
- NotifyArgs* args = mArgsQueue[i];
- args->notify(mInnerListener);
- delete args;
- }
- mArgsQueue.clear();
-}
-
-
-} // namespace android
diff --git a/libs/input/InputListener.h b/libs/input/InputListener.h
deleted file mode 100644
index 78ae10f..0000000
--- a/libs/input/InputListener.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef _UI_INPUT_LISTENER_H
-#define _UI_INPUT_LISTENER_H
-
-#include <input/Input.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-class InputListenerInterface;
-
-
-/* Superclass of all input event argument objects */
-struct NotifyArgs {
- virtual ~NotifyArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
-};
-
-
-/* Describes a configuration change event. */
-struct NotifyConfigurationChangedArgs : public NotifyArgs {
- nsecs_t eventTime;
-
- inline NotifyConfigurationChangedArgs() { }
-
- NotifyConfigurationChangedArgs(nsecs_t eventTime);
-
- NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
-
- virtual ~NotifyConfigurationChangedArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const;
-};
-
-
-/* Describes a key event. */
-struct NotifyKeyArgs : public NotifyArgs {
- nsecs_t eventTime;
- int32_t deviceId;
- uint32_t source;
- uint32_t policyFlags;
- int32_t action;
- int32_t flags;
- int32_t keyCode;
- int32_t scanCode;
- int32_t metaState;
- nsecs_t downTime;
-
- inline NotifyKeyArgs() { }
-
- NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, nsecs_t downTime);
-
- NotifyKeyArgs(const NotifyKeyArgs& other);
-
- virtual ~NotifyKeyArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const;
-};
-
-
-/* Describes a motion event. */
-struct NotifyMotionArgs : public NotifyArgs {
- nsecs_t eventTime;
- int32_t deviceId;
- uint32_t source;
- uint32_t policyFlags;
- int32_t action;
- int32_t flags;
- int32_t metaState;
- int32_t buttonState;
- int32_t edgeFlags;
- int32_t displayId;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- float xPrecision;
- float yPrecision;
- nsecs_t downTime;
-
- inline NotifyMotionArgs() { }
-
- NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime);
-
- NotifyMotionArgs(const NotifyMotionArgs& other);
-
- virtual ~NotifyMotionArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const;
-};
-
-
-/* Describes a switch event. */
-struct NotifySwitchArgs : public NotifyArgs {
- nsecs_t eventTime;
- uint32_t policyFlags;
- uint32_t switchValues;
- uint32_t switchMask;
-
- inline NotifySwitchArgs() { }
-
- NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
- uint32_t switchValues, uint32_t switchMask);
-
- NotifySwitchArgs(const NotifySwitchArgs& other);
-
- virtual ~NotifySwitchArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const;
-};
-
-
-/* Describes a device reset event, such as when a device is added,
- * reconfigured, or removed. */
-struct NotifyDeviceResetArgs : public NotifyArgs {
- nsecs_t eventTime;
- int32_t deviceId;
-
- inline NotifyDeviceResetArgs() { }
-
- NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId);
-
- NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
-
- virtual ~NotifyDeviceResetArgs() { }
-
- virtual void notify(const sp<InputListenerInterface>& listener) const;
-};
-
-
-/*
- * The interface used by the InputReader to notify the InputListener about input events.
- */
-class InputListenerInterface : public virtual RefBase {
-protected:
- InputListenerInterface() { }
- virtual ~InputListenerInterface() { }
-
-public:
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
- virtual void notifyKey(const NotifyKeyArgs* args) = 0;
- virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
- virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
-};
-
-
-/*
- * An implementation of the listener interface that queues up and defers dispatch
- * of decoded events until flushed.
- */
-class QueuedInputListener : public InputListenerInterface {
-protected:
- virtual ~QueuedInputListener();
-
-public:
- QueuedInputListener(const sp<InputListenerInterface>& innerListener);
-
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
-
- void flush();
-
-private:
- sp<InputListenerInterface> mInnerListener;
- Vector<NotifyArgs*> mArgsQueue;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_LISTENER_H
diff --git a/libs/input/InputManager.cpp b/libs/input/InputManager.cpp
deleted file mode 100644
index 6a6547b..0000000
--- a/libs/input/InputManager.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 "InputManager"
-
-//#define LOG_NDEBUG 0
-
-#include "InputManager.h"
-
-#include <cutils/log.h>
-
-namespace android {
-
-InputManager::InputManager(
- const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
- mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- initialize();
-}
-
-InputManager::InputManager(
- const sp<InputReaderInterface>& reader,
- const sp<InputDispatcherInterface>& dispatcher) :
- mReader(reader),
- mDispatcher(dispatcher) {
- initialize();
-}
-
-InputManager::~InputManager() {
- stop();
-}
-
-void InputManager::initialize() {
- mReaderThread = new InputReaderThread(mReader);
- mDispatcherThread = new InputDispatcherThread(mDispatcher);
-}
-
-status_t InputManager::start() {
- status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
- if (result) {
- ALOGE("Could not start InputDispatcher thread due to error %d.", result);
- return result;
- }
-
- result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
- if (result) {
- ALOGE("Could not start InputReader thread due to error %d.", result);
-
- mDispatcherThread->requestExit();
- return result;
- }
-
- return OK;
-}
-
-status_t InputManager::stop() {
- status_t result = mReaderThread->requestExitAndWait();
- if (result) {
- ALOGW("Could not stop InputReader thread due to error %d.", result);
- }
-
- result = mDispatcherThread->requestExitAndWait();
- if (result) {
- ALOGW("Could not stop InputDispatcher thread due to error %d.", result);
- }
-
- return OK;
-}
-
-sp<InputReaderInterface> InputManager::getReader() {
- return mReader;
-}
-
-sp<InputDispatcherInterface> InputManager::getDispatcher() {
- return mDispatcher;
-}
-
-} // namespace android
diff --git a/libs/input/InputManager.h b/libs/input/InputManager.h
deleted file mode 100644
index a213b2d..0000000
--- a/libs/input/InputManager.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _UI_INPUT_MANAGER_H
-#define _UI_INPUT_MANAGER_H
-
-/**
- * Native input manager.
- */
-
-#include "EventHub.h"
-#include "InputReader.h"
-#include "InputDispatcher.h"
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-namespace android {
-
-/*
- * The input manager is the core of the system event processing.
- *
- * The input manager uses two threads.
- *
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- * applies policy, and posts messages to a queue managed by the DispatcherThread.
- * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
- * queue and asynchronously dispatches them to applications.
- *
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state. Moreover, all communication is done one way from the InputReaderThread
- * into the InputDispatcherThread and never the reverse. Both classes may interact with the
- * InputDispatchPolicy, however.
- *
- * The InputManager class never makes any calls into Java itself. Instead, the
- * InputDispatchPolicy is responsible for performing all external interactions with the
- * system, including calling DVM services.
- */
-class InputManagerInterface : public virtual RefBase {
-protected:
- InputManagerInterface() { }
- virtual ~InputManagerInterface() { }
-
-public:
- /* Starts the input manager threads. */
- virtual status_t start() = 0;
-
- /* Stops the input manager threads and waits for them to exit. */
- virtual status_t stop() = 0;
-
- /* Gets the input reader. */
- virtual sp<InputReaderInterface> getReader() = 0;
-
- /* Gets the input dispatcher. */
- virtual sp<InputDispatcherInterface> getDispatcher() = 0;
-};
-
-class InputManager : public InputManagerInterface {
-protected:
- virtual ~InputManager();
-
-public:
- InputManager(
- const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
-
- // (used for testing purposes)
- InputManager(
- const sp<InputReaderInterface>& reader,
- const sp<InputDispatcherInterface>& dispatcher);
-
- virtual status_t start();
- virtual status_t stop();
-
- virtual sp<InputReaderInterface> getReader();
- virtual sp<InputDispatcherInterface> getDispatcher();
-
-private:
- sp<InputReaderInterface> mReader;
- sp<InputReaderThread> mReaderThread;
-
- sp<InputDispatcherInterface> mDispatcher;
- sp<InputDispatcherThread> mDispatcherThread;
-
- void initialize();
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_MANAGER_H
diff --git a/libs/input/InputReader.cpp b/libs/input/InputReader.cpp
deleted file mode 100644
index a4093e9..0000000
--- a/libs/input/InputReader.cpp
+++ /dev/null
@@ -1,6543 +0,0 @@
-/*
- * 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 "InputReader"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
-
-// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
-
-// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
-
-// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
-
-// Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
-
-// Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
-
-#include "InputReader.h"
-
-#include <cutils/log.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <math.h>
-
-#define INDENT " "
-#define INDENT2 " "
-#define INDENT3 " "
-#define INDENT4 " "
-#define INDENT5 " "
-
-namespace android {
-
-// --- Constants ---
-
-// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static const size_t MAX_SLOTS = 32;
-
-// --- Static Functions ---
-
-template<typename T>
-inline static T abs(const T& value) {
- return value < 0 ? - value : value;
-}
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
-
-template<typename T>
-inline static void swap(T& a, T& b) {
- T temp = a;
- a = b;
- b = temp;
-}
-
-inline static float avg(float x, float y) {
- return (x + y) / 2;
-}
-
-inline static float distance(float x1, float y1, float x2, float y2) {
- return hypotf(x1 - x2, y1 - y2);
-}
-
-inline static int32_t signExtendNybble(int32_t value) {
- return value >= 8 ? value - 16 : value;
-}
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
- const int32_t map[][4], size_t mapSize) {
- if (orientation != DISPLAY_ORIENTATION_0) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][orientation];
- }
- }
- }
- return value;
-}
-
-static const int32_t keyCodeRotationMap[][4] = {
- // key codes enumerated counter-clockwise with the original (unrotated) key first
- // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
- { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
- { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
- { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
- { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
-};
-static const size_t keyCodeRotationMapSize =
- sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
- return rotateValueUsingRotationMap(keyCode, orientation,
- keyCodeRotationMap, keyCodeRotationMapSize);
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case DISPLAY_ORIENTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case DISPLAY_ORIENTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
- }
-}
-
-static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
- return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
-}
-
-// Returns true if the pointer should be reported as being down given the specified
-// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY
- | AMOTION_EVENT_BUTTON_TERTIARY);
-}
-
-static float calculateCommonVector(float a, float b) {
- if (a > 0 && b > 0) {
- return a < b ? a : b;
- } else if (a < 0 && b < 0) {
- return a > b ? a : b;
- } else {
- return 0;
- }
-}
-
-static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
- int32_t buttonState, int32_t keyCode) {
- if (
- (action == AKEY_EVENT_ACTION_DOWN
- && !(lastButtonState & buttonState)
- && (currentButtonState & buttonState))
- || (action == AKEY_EVENT_ACTION_UP
- && (lastButtonState & buttonState)
- && !(currentButtonState & buttonState))) {
- NotifyKeyArgs args(when, deviceId, source, policyFlags,
- action, 0, keyCode, 0, context->getGlobalMetaState(), when);
- context->getListener()->notifyKey(&args);
- }
-}
-
-static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
-}
-
-
-// --- InputReaderConfiguration ---
-
-bool InputReaderConfiguration::getDisplayInfo(bool external, DisplayViewport* outViewport) const {
- const DisplayViewport& viewport = external ? mExternalDisplay : mInternalDisplay;
- if (viewport.displayId >= 0) {
- *outViewport = viewport;
- return true;
- }
- return false;
-}
-
-void InputReaderConfiguration::setDisplayInfo(bool external, const DisplayViewport& viewport) {
- DisplayViewport& v = external ? mExternalDisplay : mInternalDisplay;
- v = viewport;
-}
-
-
-// --- InputReader ---
-
-InputReader::InputReader(const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) :
- mContext(this), mEventHub(eventHub), mPolicy(policy),
- mGlobalMetaState(0), mGeneration(1),
- mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
- mConfigurationChangesToRefresh(0) {
- mQueuedListener = new QueuedInputListener(listener);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- refreshConfigurationLocked(0);
- updateGlobalMetaStateLocked();
- } // release lock
-}
-
-InputReader::~InputReader() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
- }
-}
-
-void InputReader::loopOnce() {
- int32_t oldGeneration;
- int32_t timeoutMillis;
- bool inputDevicesChanged = false;
- Vector<InputDeviceInfo> inputDevices;
- { // acquire lock
- AutoMutex _l(mLock);
-
- oldGeneration = mGeneration;
- timeoutMillis = -1;
-
- uint32_t changes = mConfigurationChangesToRefresh;
- if (changes) {
- mConfigurationChangesToRefresh = 0;
- timeoutMillis = 0;
- refreshConfigurationLocked(changes);
- } else if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
- }
- } // release lock
-
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
-
- { // acquire lock
- AutoMutex _l(mLock);
- mReaderIsAliveCondition.broadcast();
-
- if (count) {
- processEventsLocked(mEventBuffer, count);
- }
-
- if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
- mNextTimeout = LLONG_MAX;
- timeoutExpiredLocked(now);
- }
- }
-
- if (oldGeneration != mGeneration) {
- inputDevicesChanged = true;
- getInputDevicesLocked(inputDevices);
- }
- } // release lock
-
- // Send out a message that the describes the changed input devices.
- if (inputDevicesChanged) {
- mPolicy->notifyInputDevicesChanged(inputDevices);
- }
-
- // Flush queued events out to the listener.
- // This must happen outside of the lock because the listener could potentially call
- // back into the InputReader's methods, such as getScanCodeState, or become blocked
- // on another thread similarly waiting to acquire the InputReader lock thereby
- // resulting in a deadlock. This situation is actually quite plausible because the
- // listener is actually the input dispatcher, which calls into the window manager,
- // which occasionally calls into the input reader.
- mQueuedListener->flush();
-}
-
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
- for (const RawEvent* rawEvent = rawEvents; count;) {
- int32_t type = rawEvent->type;
- size_t batchSize = 1;
- if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
- int32_t deviceId = rawEvent->deviceId;
- while (batchSize < count) {
- if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
- || rawEvent[batchSize].deviceId != deviceId) {
- break;
- }
- batchSize += 1;
- }
-#if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %d Count: %d", batchSize, count);
-#endif
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
- } else {
- switch (rawEvent->type) {
- case EventHubInterface::DEVICE_ADDED:
- addDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::DEVICE_REMOVED:
- removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChangedLocked(rawEvent->when);
- break;
- default:
- ALOG_ASSERT(false); // can't happen
- break;
- }
- }
- count -= batchSize;
- rawEvent += batchSize;
- }
-}
-
-void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
- return;
- }
-
- InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
- uint32_t classes = mEventHub->getDeviceClasses(deviceId);
- int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
-
- InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
- device->configure(when, &mConfig, 0);
- device->reset(when);
-
- if (device->isIgnored()) {
- ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
- identifier.name.string());
- } else {
- ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
- identifier.name.string(), device->getSources());
- }
-
- mDevices.add(deviceId, device);
- bumpGenerationLocked();
-}
-
-void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
- InputDevice* device = NULL;
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
- return;
- }
-
- device = mDevices.valueAt(deviceIndex);
- mDevices.removeItemsAt(deviceIndex, 1);
- bumpGenerationLocked();
-
- if (device->isIgnored()) {
- ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
- device->getId(), device->getName().string());
- } else {
- ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
- device->getId(), device->getName().string(), device->getSources());
- }
-
- device->reset(when);
- delete device;
-}
-
-InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes) {
- InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
- controllerNumber, identifier, classes);
-
- // External devices.
- if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
- device->setExternal(true);
- }
-
- // Switch-like devices.
- if (classes & INPUT_DEVICE_CLASS_SWITCH) {
- device->addMapper(new SwitchInputMapper(device));
- }
-
- // Vibrator-like devices.
- if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
- device->addMapper(new VibratorInputMapper(device));
- }
-
- // Keyboard-like devices.
- uint32_t keyboardSource = 0;
- int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
- if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
- keyboardSource |= AINPUT_SOURCE_KEYBOARD;
- }
- if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
- keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
- }
- if (classes & INPUT_DEVICE_CLASS_DPAD) {
- keyboardSource |= AINPUT_SOURCE_DPAD;
- }
- if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- keyboardSource |= AINPUT_SOURCE_GAMEPAD;
- }
-
- if (keyboardSource != 0) {
- device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
- }
-
- // Cursor-like devices.
- if (classes & INPUT_DEVICE_CLASS_CURSOR) {
- device->addMapper(new CursorInputMapper(device));
- }
-
- // Touchscreens and touchpad devices.
- if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
- device->addMapper(new MultiTouchInputMapper(device));
- } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
- device->addMapper(new SingleTouchInputMapper(device));
- }
-
- // Joystick-like devices.
- if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
- device->addMapper(new JoystickInputMapper(device));
- }
-
- return device;
-}
-
-void InputReader::processEventsForDeviceLocked(int32_t deviceId,
- const RawEvent* rawEvents, size_t count) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Discarding event for unknown deviceId %d.", deviceId);
- return;
- }
-
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (device->isIgnored()) {
- //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
- return;
- }
-
- device->process(rawEvents, count);
-}
-
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (!device->isIgnored()) {
- device->timeoutExpired(when);
- }
- }
-}
-
-void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
- // Reset global meta state because it depends on the list of all configured devices.
- updateGlobalMetaStateLocked();
-
- // Enqueue configuration changed.
- NotifyConfigurationChangedArgs args(when);
- mQueuedListener->notifyConfigurationChanged(&args);
-}
-
-void InputReader::refreshConfigurationLocked(uint32_t changes) {
- mPolicy->getReaderConfiguration(&mConfig);
- mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
-
- if (changes) {
- ALOGI("Reconfiguring input devices. changes=0x%08x", changes);
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
- mEventHub->requestReopenDevices();
- } else {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- device->configure(now, &mConfig, changes);
- }
- }
- }
-}
-
-void InputReader::updateGlobalMetaStateLocked() {
- mGlobalMetaState = 0;
-
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- mGlobalMetaState |= device->getMetaState();
- }
-}
-
-int32_t InputReader::getGlobalMetaStateLocked() {
- return mGlobalMetaState;
-}
-
-void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
- mDisableVirtualKeysTimeout = time;
-}
-
-bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) {
- if (now < mDisableVirtualKeysTimeout) {
- ALOGI("Dropping virtual key from device %s because virtual keys are "
- "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d",
- device->getName().string(),
- (mDisableVirtualKeysTimeout - now) * 0.000001,
- keyCode, scanCode);
- return true;
- } else {
- return false;
- }
-}
-
-void InputReader::fadePointerLocked() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- device->fadePointer();
- }
-}
-
-void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
- if (when < mNextTimeout) {
- mNextTimeout = when;
- mEventHub->wake();
- }
-}
-
-int32_t InputReader::bumpGenerationLocked() {
- return ++mGeneration;
-}
-
-void InputReader::getInputDevices(Vector<InputDeviceInfo>& outInputDevices) {
- AutoMutex _l(mLock);
- getInputDevicesLocked(outInputDevices);
-}
-
-void InputReader::getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices) {
- outInputDevices.clear();
-
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (!device->isIgnored()) {
- outInputDevices.push();
- device->getDeviceInfo(&outInputDevices.editTop());
- }
- }
-}
-
-int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
-}
-
-int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
-}
-
-int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
-}
-
-int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
- GetStateFunc getStateFunc) {
- int32_t result = AKEY_STATE_UNKNOWN;
- if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result = (device->*getStateFunc)(sourceMask, code);
- }
- }
- } else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
- // value. Otherwise, return AKEY_STATE_UP as long as one device reports it.
- int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
- if (currentResult >= AKEY_STATE_DOWN) {
- return currentResult;
- } else if (currentResult == AKEY_STATE_UP) {
- result = currentResult;
- }
- }
- }
- }
- return result;
-}
-
-bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
- AutoMutex _l(mLock);
-
- memset(outFlags, 0, numCodes);
- return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
-}
-
-bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
- if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result = device->markSupportedKeyCodes(sourceMask,
- numCodes, keyCodes, outFlags);
- }
- }
- } else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result |= device->markSupportedKeyCodes(sourceMask,
- numCodes, keyCodes, outFlags);
- }
- }
- }
- return result;
-}
-
-void InputReader::requestRefreshConfiguration(uint32_t changes) {
- AutoMutex _l(mLock);
-
- if (changes) {
- bool needWake = !mConfigurationChangesToRefresh;
- mConfigurationChangesToRefresh |= changes;
-
- if (needWake) {
- mEventHub->wake();
- }
- }
-}
-
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- device->vibrate(pattern, patternSize, repeat, token);
- }
-}
-
-void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- device->cancelVibrate(token);
- }
-}
-
-void InputReader::dump(String8& dump) {
- AutoMutex _l(mLock);
-
- mEventHub->dump(dump);
- dump.append("\n");
-
- dump.append("Input Reader State:\n");
-
- for (size_t i = 0; i < mDevices.size(); i++) {
- mDevices.valueAt(i)->dump(dump);
- }
-
- dump.append(INDENT "Configuration:\n");
- dump.append(INDENT2 "ExcludedDeviceNames: [");
- for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
- if (i != 0) {
- dump.append(", ");
- }
- dump.append(mConfig.excludedDeviceNames.itemAt(i).string());
- }
- dump.append("]\n");
- dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
- mConfig.virtualKeyQuietTime * 0.000001f);
-
- dump.appendFormat(INDENT2 "PointerVelocityControlParameters: "
- "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
- mConfig.pointerVelocityControlParameters.scale,
- mConfig.pointerVelocityControlParameters.lowThreshold,
- mConfig.pointerVelocityControlParameters.highThreshold,
- mConfig.pointerVelocityControlParameters.acceleration);
-
- dump.appendFormat(INDENT2 "WheelVelocityControlParameters: "
- "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
- mConfig.wheelVelocityControlParameters.scale,
- mConfig.wheelVelocityControlParameters.lowThreshold,
- mConfig.wheelVelocityControlParameters.highThreshold,
- mConfig.wheelVelocityControlParameters.acceleration);
-
- dump.appendFormat(INDENT2 "PointerGesture:\n");
- dump.appendFormat(INDENT3 "Enabled: %s\n",
- toString(mConfig.pointerGesturesEnabled));
- dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
- mConfig.pointerGestureQuietInterval * 0.000001f);
- dump.appendFormat(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
- mConfig.pointerGestureDragMinSwitchSpeed);
- dump.appendFormat(INDENT3 "TapInterval: %0.1fms\n",
- mConfig.pointerGestureTapInterval * 0.000001f);
- dump.appendFormat(INDENT3 "TapDragInterval: %0.1fms\n",
- mConfig.pointerGestureTapDragInterval * 0.000001f);
- dump.appendFormat(INDENT3 "TapSlop: %0.1fpx\n",
- mConfig.pointerGestureTapSlop);
- dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
- mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
- dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
- mConfig.pointerGestureMultitouchMinDistance);
- dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
- mConfig.pointerGestureSwipeMaxWidthRatio);
- dump.appendFormat(INDENT3 "MovementSpeedRatio: %0.1f\n",
- mConfig.pointerGestureMovementSpeedRatio);
- dump.appendFormat(INDENT3 "ZoomSpeedRatio: %0.1f\n",
- mConfig.pointerGestureZoomSpeedRatio);
-}
-
-void InputReader::monitor() {
- // Acquire and release the lock to ensure that the reader has not deadlocked.
- mLock.lock();
- mEventHub->wake();
- mReaderIsAliveCondition.wait(mLock);
- mLock.unlock();
-
- // Check the EventHub
- mEventHub->monitor();
-}
-
-
-// --- InputReader::ContextImpl ---
-
-InputReader::ContextImpl::ContextImpl(InputReader* reader) :
- mReader(reader) {
-}
-
-void InputReader::ContextImpl::updateGlobalMetaState() {
- // lock is already held by the input loop
- mReader->updateGlobalMetaStateLocked();
-}
-
-int32_t InputReader::ContextImpl::getGlobalMetaState() {
- // lock is already held by the input loop
- return mReader->getGlobalMetaStateLocked();
-}
-
-void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
- // lock is already held by the input loop
- mReader->disableVirtualKeysUntilLocked(time);
-}
-
-bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) {
- // lock is already held by the input loop
- return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
-}
-
-void InputReader::ContextImpl::fadePointer() {
- // lock is already held by the input loop
- mReader->fadePointerLocked();
-}
-
-void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
- // lock is already held by the input loop
- mReader->requestTimeoutAtTimeLocked(when);
-}
-
-int32_t InputReader::ContextImpl::bumpGeneration() {
- // lock is already held by the input loop
- return mReader->bumpGenerationLocked();
-}
-
-InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
- return mReader->mPolicy.get();
-}
-
-InputListenerInterface* InputReader::ContextImpl::getListener() {
- return mReader->mQueuedListener.get();
-}
-
-EventHubInterface* InputReader::ContextImpl::getEventHub() {
- return mReader->mEventHub.get();
-}
-
-
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
-}
-
-
-// --- InputDevice ---
-
-InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
- int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) :
- mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber),
- mIdentifier(identifier), mClasses(classes),
- mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
-}
-
-InputDevice::~InputDevice() {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- delete mMappers[i];
- }
- mMappers.clear();
-}
-
-void InputDevice::dump(String8& dump) {
- InputDeviceInfo deviceInfo;
- getDeviceInfo(& deviceInfo);
-
- dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
- deviceInfo.getDisplayName().string());
- dump.appendFormat(INDENT2 "Generation: %d\n", mGeneration);
- dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
- dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
- dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
-
- const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
- if (!ranges.isEmpty()) {
- dump.append(INDENT2 "Motion Ranges:\n");
- for (size_t i = 0; i < ranges.size(); i++) {
- const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
- const char* label = getAxisLabel(range.axis);
- char name[32];
- if (label) {
- strncpy(name, label, sizeof(name));
- name[sizeof(name) - 1] = '\0';
- } else {
- snprintf(name, sizeof(name), "%d", range.axis);
- }
- dump.appendFormat(INDENT3 "%s: source=0x%08x, "
- "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
- name, range.source, range.min, range.max, range.flat, range.fuzz,
- range.resolution);
- }
- }
-
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->dump(dump);
- }
-}
-
-void InputDevice::addMapper(InputMapper* mapper) {
- mMappers.add(mapper);
-}
-
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
- mSources = 0;
-
- if (!isIgnored()) {
- if (!changes) { // first time only
- mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- sp<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
- bumpGeneration();
- }
- }
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
- if (mAlias != alias) {
- mAlias = alias;
- bumpGeneration();
- }
- }
- }
-
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->configure(when, config, changes);
- mSources |= mapper->getSources();
- }
- }
-}
-
-void InputDevice::reset(nsecs_t when) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->reset(when);
- }
-
- mContext->updateGlobalMetaState();
-
- notifyReset(when);
-}
-
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
- // Process all of the events in order for each mapper.
- // We cannot simply ask each mapper to process them in bulk because mappers may
- // have side-effects that must be interleaved. For example, joystick movement events and
- // gamepad button presses are handled by different mappers but they should be dispatched
- // in the order received.
- size_t numMappers = mMappers.size();
- for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
- rawEvent->when);
-#endif
-
- if (mDropUntilNextSync) {
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
- ALOGD("Recovered from input event buffer overrun.");
-#endif
- } else {
-#if DEBUG_RAW_EVENTS
- ALOGD("Dropped input event while waiting for next input sync.");
-#endif
- }
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
- ALOGI("Detected input event buffer overrun for device %s.", getName().string());
- mDropUntilNextSync = true;
- reset(rawEvent->when);
- } else {
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
- }
- }
- }
-}
-
-void InputDevice::timeoutExpired(nsecs_t when) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->timeoutExpired(when);
- }
-}
-
-void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
- outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias,
- mIsExternal);
-
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->populateDeviceInfo(outDeviceInfo);
- }
-}
-
-int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
-}
-
-int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
-}
-
-int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
-}
-
-int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
- int32_t result = AKEY_STATE_UNKNOWN;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
- // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
- // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
- int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
- if (currentResult >= AKEY_STATE_DOWN) {
- return currentResult;
- } else if (currentResult == AKEY_STATE_UP) {
- result = currentResult;
- }
- }
- }
- return result;
-}
-
-bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
- result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
- }
- }
- return result;
-}
-
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->vibrate(pattern, patternSize, repeat, token);
- }
-}
-
-void InputDevice::cancelVibrate(int32_t token) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->cancelVibrate(token);
- }
-}
-
-int32_t InputDevice::getMetaState() {
- int32_t result = 0;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- result |= mapper->getMetaState();
- }
- return result;
-}
-
-void InputDevice::fadePointer() {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->fadePointer();
- }
-}
-
-void InputDevice::bumpGeneration() {
- mGeneration = mContext->bumpGeneration();
-}
-
-void InputDevice::notifyReset(nsecs_t when) {
- NotifyDeviceResetArgs args(when, mId);
- mContext->getListener()->notifyDeviceReset(&args);
-}
-
-
-// --- CursorButtonAccumulator ---
-
-CursorButtonAccumulator::CursorButtonAccumulator() {
- clearButtons();
-}
-
-void CursorButtonAccumulator::reset(InputDevice* device) {
- mBtnLeft = device->isKeyPressed(BTN_LEFT);
- mBtnRight = device->isKeyPressed(BTN_RIGHT);
- mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
- mBtnBack = device->isKeyPressed(BTN_BACK);
- mBtnSide = device->isKeyPressed(BTN_SIDE);
- mBtnForward = device->isKeyPressed(BTN_FORWARD);
- mBtnExtra = device->isKeyPressed(BTN_EXTRA);
- mBtnTask = device->isKeyPressed(BTN_TASK);
-}
-
-void CursorButtonAccumulator::clearButtons() {
- mBtnLeft = 0;
- mBtnRight = 0;
- mBtnMiddle = 0;
- mBtnBack = 0;
- mBtnSide = 0;
- mBtnForward = 0;
- mBtnExtra = 0;
- mBtnTask = 0;
-}
-
-void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
- case BTN_LEFT:
- mBtnLeft = rawEvent->value;
- break;
- case BTN_RIGHT:
- mBtnRight = rawEvent->value;
- break;
- case BTN_MIDDLE:
- mBtnMiddle = rawEvent->value;
- break;
- case BTN_BACK:
- mBtnBack = rawEvent->value;
- break;
- case BTN_SIDE:
- mBtnSide = rawEvent->value;
- break;
- case BTN_FORWARD:
- mBtnForward = rawEvent->value;
- break;
- case BTN_EXTRA:
- mBtnExtra = rawEvent->value;
- break;
- case BTN_TASK:
- mBtnTask = rawEvent->value;
- break;
- }
- }
-}
-
-uint32_t CursorButtonAccumulator::getButtonState() const {
- uint32_t result = 0;
- if (mBtnLeft) {
- result |= AMOTION_EVENT_BUTTON_PRIMARY;
- }
- if (mBtnRight) {
- result |= AMOTION_EVENT_BUTTON_SECONDARY;
- }
- if (mBtnMiddle) {
- result |= AMOTION_EVENT_BUTTON_TERTIARY;
- }
- if (mBtnBack || mBtnSide) {
- result |= AMOTION_EVENT_BUTTON_BACK;
- }
- if (mBtnForward || mBtnExtra) {
- result |= AMOTION_EVENT_BUTTON_FORWARD;
- }
- return result;
-}
-
-
-// --- CursorMotionAccumulator ---
-
-CursorMotionAccumulator::CursorMotionAccumulator() {
- clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::reset(InputDevice* device) {
- clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::clearRelativeAxes() {
- mRelX = 0;
- mRelY = 0;
-}
-
-void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
- case REL_X:
- mRelX = rawEvent->value;
- break;
- case REL_Y:
- mRelY = rawEvent->value;
- break;
- }
- }
-}
-
-void CursorMotionAccumulator::finishSync() {
- clearRelativeAxes();
-}
-
-
-// --- CursorScrollAccumulator ---
-
-CursorScrollAccumulator::CursorScrollAccumulator() :
- mHaveRelWheel(false), mHaveRelHWheel(false) {
- clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::configure(InputDevice* device) {
- mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
- mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
-}
-
-void CursorScrollAccumulator::reset(InputDevice* device) {
- clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::clearRelativeAxes() {
- mRelWheel = 0;
- mRelHWheel = 0;
-}
-
-void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
- case REL_WHEEL:
- mRelWheel = rawEvent->value;
- break;
- case REL_HWHEEL:
- mRelHWheel = rawEvent->value;
- break;
- }
- }
-}
-
-void CursorScrollAccumulator::finishSync() {
- clearRelativeAxes();
-}
-
-
-// --- TouchButtonAccumulator ---
-
-TouchButtonAccumulator::TouchButtonAccumulator() :
- mHaveBtnTouch(false), mHaveStylus(false) {
- clearButtons();
-}
-
-void TouchButtonAccumulator::configure(InputDevice* device) {
- mHaveBtnTouch = device->hasKey(BTN_TOUCH);
- mHaveStylus = device->hasKey(BTN_TOOL_PEN)
- || device->hasKey(BTN_TOOL_RUBBER)
- || device->hasKey(BTN_TOOL_BRUSH)
- || device->hasKey(BTN_TOOL_PENCIL)
- || device->hasKey(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDevice* device) {
- mBtnTouch = device->isKeyPressed(BTN_TOUCH);
- mBtnStylus = device->isKeyPressed(BTN_STYLUS);
- mBtnStylus2 = device->isKeyPressed(BTN_STYLUS);
- mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
- mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
- mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
- mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
- mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
- mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
- mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
- mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
- mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
- mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
- mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
- mBtnTouch = 0;
- mBtnStylus = 0;
- mBtnStylus2 = 0;
- mBtnToolFinger = 0;
- mBtnToolPen = 0;
- mBtnToolRubber = 0;
- mBtnToolBrush = 0;
- mBtnToolPencil = 0;
- mBtnToolAirbrush = 0;
- mBtnToolMouse = 0;
- mBtnToolLens = 0;
- mBtnToolDoubleTap = 0;
- mBtnToolTripleTap = 0;
- mBtnToolQuadTap = 0;
-}
-
-void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
- case BTN_TOUCH:
- mBtnTouch = rawEvent->value;
- break;
- case BTN_STYLUS:
- mBtnStylus = rawEvent->value;
- break;
- case BTN_STYLUS2:
- mBtnStylus2 = rawEvent->value;
- break;
- case BTN_TOOL_FINGER:
- mBtnToolFinger = rawEvent->value;
- break;
- case BTN_TOOL_PEN:
- mBtnToolPen = rawEvent->value;
- break;
- case BTN_TOOL_RUBBER:
- mBtnToolRubber = rawEvent->value;
- break;
- case BTN_TOOL_BRUSH:
- mBtnToolBrush = rawEvent->value;
- break;
- case BTN_TOOL_PENCIL:
- mBtnToolPencil = rawEvent->value;
- break;
- case BTN_TOOL_AIRBRUSH:
- mBtnToolAirbrush = rawEvent->value;
- break;
- case BTN_TOOL_MOUSE:
- mBtnToolMouse = rawEvent->value;
- break;
- case BTN_TOOL_LENS:
- mBtnToolLens = rawEvent->value;
- break;
- case BTN_TOOL_DOUBLETAP:
- mBtnToolDoubleTap = rawEvent->value;
- break;
- case BTN_TOOL_TRIPLETAP:
- mBtnToolTripleTap = rawEvent->value;
- break;
- case BTN_TOOL_QUADTAP:
- mBtnToolQuadTap = rawEvent->value;
- break;
- }
- }
-}
-
-uint32_t TouchButtonAccumulator::getButtonState() const {
- uint32_t result = 0;
- if (mBtnStylus) {
- result |= AMOTION_EVENT_BUTTON_SECONDARY;
- }
- if (mBtnStylus2) {
- result |= AMOTION_EVENT_BUTTON_TERTIARY;
- }
- return result;
-}
-
-int32_t TouchButtonAccumulator::getToolType() const {
- if (mBtnToolMouse || mBtnToolLens) {
- return AMOTION_EVENT_TOOL_TYPE_MOUSE;
- }
- if (mBtnToolRubber) {
- return AMOTION_EVENT_TOOL_TYPE_ERASER;
- }
- if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
- return AMOTION_EVENT_TOOL_TYPE_STYLUS;
- }
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
- return AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-bool TouchButtonAccumulator::isToolActive() const {
- return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber
- || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush
- || mBtnToolMouse || mBtnToolLens
- || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
-}
-
-bool TouchButtonAccumulator::isHovering() const {
- return mHaveBtnTouch && !mBtnTouch;
-}
-
-bool TouchButtonAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
-
-// --- RawPointerAxes ---
-
-RawPointerAxes::RawPointerAxes() {
- clear();
-}
-
-void RawPointerAxes::clear() {
- x.clear();
- y.clear();
- pressure.clear();
- touchMajor.clear();
- touchMinor.clear();
- toolMajor.clear();
- toolMinor.clear();
- orientation.clear();
- distance.clear();
- tiltX.clear();
- tiltY.clear();
- trackingId.clear();
- slot.clear();
-}
-
-
-// --- RawPointerData ---
-
-RawPointerData::RawPointerData() {
- clear();
-}
-
-void RawPointerData::clear() {
- pointerCount = 0;
- clearIdBits();
-}
-
-void RawPointerData::copyFrom(const RawPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointers[i] = other.pointers[i];
-
- int id = pointers[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
-void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
- float x = 0, y = 0;
- uint32_t count = touchingIdBits.count();
- if (count) {
- for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const Pointer& pointer = pointerForId(id);
- x += pointer.x;
- y += pointer.y;
- }
- x /= count;
- y /= count;
- }
- *outX = x;
- *outY = y;
-}
-
-
-// --- CookedPointerData ---
-
-CookedPointerData::CookedPointerData() {
- clear();
-}
-
-void CookedPointerData::clear() {
- pointerCount = 0;
- hoveringIdBits.clear();
- touchingIdBits.clear();
-}
-
-void CookedPointerData::copyFrom(const CookedPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
-
- int id = pointerProperties[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
-
-// --- SingleTouchMotionAccumulator ---
-
-SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
- clearAbsoluteAxes();
-}
-
-void SingleTouchMotionAccumulator::reset(InputDevice* device) {
- mAbsX = device->getAbsoluteAxisValue(ABS_X);
- mAbsY = device->getAbsoluteAxisValue(ABS_Y);
- mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
- mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
- mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
- mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
- mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
-}
-
-void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
- mAbsX = 0;
- mAbsY = 0;
- mAbsPressure = 0;
- mAbsToolWidth = 0;
- mAbsDistance = 0;
- mAbsTiltX = 0;
- mAbsTiltY = 0;
-}
-
-void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- switch (rawEvent->code) {
- case ABS_X:
- mAbsX = rawEvent->value;
- break;
- case ABS_Y:
- mAbsY = rawEvent->value;
- break;
- case ABS_PRESSURE:
- mAbsPressure = rawEvent->value;
- break;
- case ABS_TOOL_WIDTH:
- mAbsToolWidth = rawEvent->value;
- break;
- case ABS_DISTANCE:
- mAbsDistance = rawEvent->value;
- break;
- case ABS_TILT_X:
- mAbsTiltX = rawEvent->value;
- break;
- case ABS_TILT_Y:
- mAbsTiltY = rawEvent->value;
- break;
- }
- }
-}
-
-
-// --- MultiTouchMotionAccumulator ---
-
-MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() :
- mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false),
- mHaveStylus(false) {
-}
-
-MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
- delete[] mSlots;
-}
-
-void MultiTouchMotionAccumulator::configure(InputDevice* device,
- size_t slotCount, bool usingSlotsProtocol) {
- mSlotCount = slotCount;
- mUsingSlotsProtocol = usingSlotsProtocol;
- mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
-
- delete[] mSlots;
- mSlots = new Slot[slotCount];
-}
-
-void MultiTouchMotionAccumulator::reset(InputDevice* device) {
- // Unfortunately there is no way to read the initial contents of the slots.
- // So when we reset the accumulator, we must assume they are all zeroes.
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot
- // before we start reading events from the device. It is possible that the
- // current slot index will not be the same as it was when the first event was
- // written into the evdev buffer, which means the input mapper could start
- // out of sync with the initial state of the events in the evdev buffer.
- // In the extremely unlikely case that this happens, the data from
- // two slots will be confused until the next ABS_MT_SLOT event is received.
- // This can cause the touch point to "jump", but at least there will be
- // no stuck touches.
- int32_t initialSlot;
- status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(),
- ABS_MT_SLOT, &initialSlot);
- if (status) {
- ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
- initialSlot = -1;
- }
- clearSlots(initialSlot);
- } else {
- clearSlots(-1);
- }
-}
-
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
- if (mSlots) {
- for (size_t i = 0; i < mSlotCount; i++) {
- mSlots[i].clear();
- }
- }
- mCurrentSlot = initialSlot;
-}
-
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- bool newSlot = false;
- if (mUsingSlotsProtocol) {
- if (rawEvent->code == ABS_MT_SLOT) {
- mCurrentSlot = rawEvent->value;
- newSlot = true;
- }
- } else if (mCurrentSlot < 0) {
- mCurrentSlot = 0;
- }
-
- if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
- if (newSlot) {
- ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %d; ignoring this slot.",
- mCurrentSlot, mSlotCount - 1);
- }
-#endif
- } else {
- Slot* slot = &mSlots[mCurrentSlot];
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot->mInUse = true;
- slot->mAbsMTPositionX = rawEvent->value;
- break;
- case ABS_MT_POSITION_Y:
- slot->mInUse = true;
- slot->mAbsMTPositionY = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot->mInUse = true;
- slot->mAbsMTTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot->mInUse = true;
- slot->mAbsMTTouchMinor = rawEvent->value;
- slot->mHaveAbsMTTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot->mInUse = true;
- slot->mAbsMTWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot->mInUse = true;
- slot->mAbsMTWidthMinor = rawEvent->value;
- slot->mHaveAbsMTWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot->mInUse = true;
- slot->mAbsMTOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot->mInUse = false;
- } else {
- slot->mInUse = true;
- slot->mAbsMTTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot->mInUse = true;
- slot->mAbsMTPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot->mInUse = true;
- slot->mAbsMTDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot->mInUse = true;
- slot->mAbsMTToolType = rawEvent->value;
- slot->mHaveAbsMTToolType = true;
- break;
- }
- }
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
- // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
- mCurrentSlot += 1;
- }
-}
-
-void MultiTouchMotionAccumulator::finishSync() {
- if (!mUsingSlotsProtocol) {
- clearSlots(-1);
- }
-}
-
-bool MultiTouchMotionAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
-
-// --- MultiTouchMotionAccumulator::Slot ---
-
-MultiTouchMotionAccumulator::Slot::Slot() {
- clear();
-}
-
-void MultiTouchMotionAccumulator::Slot::clear() {
- mInUse = false;
- mHaveAbsMTTouchMinor = false;
- mHaveAbsMTWidthMinor = false;
- mHaveAbsMTToolType = false;
- mAbsMTPositionX = 0;
- mAbsMTPositionY = 0;
- mAbsMTTouchMajor = 0;
- mAbsMTTouchMinor = 0;
- mAbsMTWidthMajor = 0;
- mAbsMTWidthMinor = 0;
- mAbsMTOrientation = 0;
- mAbsMTTrackingId = -1;
- mAbsMTPressure = 0;
- mAbsMTDistance = 0;
- mAbsMTToolType = 0;
-}
-
-int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
- if (mHaveAbsMTToolType) {
- switch (mAbsMTToolType) {
- case MT_TOOL_FINGER:
- return AMOTION_EVENT_TOOL_TYPE_FINGER;
- case MT_TOOL_PEN:
- return AMOTION_EVENT_TOOL_TYPE_STYLUS;
- }
- }
- return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-
-// --- InputMapper ---
-
-InputMapper::InputMapper(InputDevice* device) :
- mDevice(device), mContext(device->getContext()) {
-}
-
-InputMapper::~InputMapper() {
-}
-
-void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- info->addSource(getSources());
-}
-
-void InputMapper::dump(String8& dump) {
-}
-
-void InputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
-}
-
-void InputMapper::reset(nsecs_t when) {
-}
-
-void InputMapper::timeoutExpired(nsecs_t when) {
-}
-
-int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- return false;
-}
-
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
-}
-
-void InputMapper::cancelVibrate(int32_t token) {
-}
-
-int32_t InputMapper::getMetaState() {
- return 0;
-}
-
-void InputMapper::fadePointer() {
-}
-
-status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
- return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
-}
-
-void InputMapper::bumpGeneration() {
- mDevice->bumpGeneration();
-}
-
-void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
- const RawAbsoluteAxisInfo& axis, const char* name) {
- if (axis.valid) {
- dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n",
- name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution);
- } else {
- dump.appendFormat(INDENT4 "%s: unknown range\n", name);
- }
-}
-
-
-// --- SwitchInputMapper ---
-
-SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
- InputMapper(device), mUpdatedSwitchValues(0), mUpdatedSwitchMask(0) {
-}
-
-SwitchInputMapper::~SwitchInputMapper() {
-}
-
-uint32_t SwitchInputMapper::getSources() {
- return AINPUT_SOURCE_SWITCH;
-}
-
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_SW:
- processSwitch(rawEvent->code, rawEvent->value);
- break;
-
- case EV_SYN:
- if (rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
- }
-}
-
-void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
- if (switchCode >= 0 && switchCode < 32) {
- if (switchValue) {
- mUpdatedSwitchValues |= 1 << switchCode;
- }
- mUpdatedSwitchMask |= 1 << switchCode;
- }
-}
-
-void SwitchInputMapper::sync(nsecs_t when) {
- if (mUpdatedSwitchMask) {
- NotifySwitchArgs args(when, 0, mUpdatedSwitchValues, mUpdatedSwitchMask);
- getListener()->notifySwitch(&args);
-
- mUpdatedSwitchValues = 0;
- mUpdatedSwitchMask = 0;
- }
-}
-
-int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return getEventHub()->getSwitchState(getDeviceId(), switchCode);
-}
-
-
-// --- VibratorInputMapper ---
-
-VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
- InputMapper(device), mVibrating(false) {
-}
-
-VibratorInputMapper::~VibratorInputMapper() {
-}
-
-uint32_t VibratorInputMapper::getSources() {
- return 0;
-}
-
-void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- info->setVibrator(true);
-}
-
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
- // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
-}
-
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
-#if DEBUG_VIBRATOR
- String8 patternStr;
- for (size_t i = 0; i < patternSize; i++) {
- if (i != 0) {
- patternStr.append(", ");
- }
- patternStr.appendFormat("%lld", pattern[i]);
- }
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
- getDeviceId(), patternStr.string(), repeat, token);
-#endif
-
- mVibrating = true;
- memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
- mPatternSize = patternSize;
- mRepeat = repeat;
- mToken = token;
- mIndex = -1;
-
- nextStep();
-}
-
-void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
-
- if (mVibrating && mToken == token) {
- stopVibrating();
- }
-}
-
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
- if (mVibrating) {
- if (when >= mNextStepTime) {
- nextStep();
- } else {
- getContext()->requestTimeoutAtTime(mNextStepTime);
- }
- }
-}
-
-void VibratorInputMapper::nextStep() {
- mIndex += 1;
- if (size_t(mIndex) >= mPatternSize) {
- if (mRepeat < 0) {
- // We are done.
- stopVibrating();
- return;
- }
- mIndex = mRepeat;
- }
-
- bool vibratorOn = mIndex & 1;
- nsecs_t duration = mPattern[mIndex];
- if (vibratorOn) {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
- getDeviceId(), duration);
-#endif
- getEventHub()->vibrate(getDeviceId(), duration);
- } else {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
- getEventHub()->cancelVibrate(getDeviceId());
- }
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- mNextStepTime = now + duration;
- getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
-#endif
-}
-
-void VibratorInputMapper::stopVibrating() {
- mVibrating = false;
-#if DEBUG_VIBRATOR
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
- getEventHub()->cancelVibrate(getDeviceId());
-}
-
-void VibratorInputMapper::dump(String8& dump) {
- dump.append(INDENT2 "Vibrator Input Mapper:\n");
- dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating));
-}
-
-
-// --- KeyboardInputMapper ---
-
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
- uint32_t source, int32_t keyboardType) :
- InputMapper(device), mSource(source),
- mKeyboardType(keyboardType) {
-}
-
-KeyboardInputMapper::~KeyboardInputMapper() {
-}
-
-uint32_t KeyboardInputMapper::getSources() {
- return mSource;
-}
-
-void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- info->setKeyboardType(mKeyboardType);
- info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
-}
-
-void KeyboardInputMapper::dump(String8& dump) {
- dump.append(INDENT2 "Keyboard Input Mapper:\n");
- dumpParameters(dump);
- dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
- dump.appendFormat(INDENT3 "Orientation: %d\n", mOrientation);
- dump.appendFormat(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
- dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mMetaState);
- dump.appendFormat(INDENT3 "DownTime: %lld\n", mDownTime);
-}
-
-
-void KeyboardInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- // Configure basic parameters.
- configureParameters();
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- DisplayViewport v;
- if (config->getDisplayInfo(false /*external*/, &v)) {
- mOrientation = v.orientation;
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- }
-}
-
-void KeyboardInputMapper::configureParameters() {
- mParameters.orientationAware = false;
- getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"),
- mParameters.orientationAware);
-
- mParameters.hasAssociatedDisplay = false;
- if (mParameters.orientationAware) {
- mParameters.hasAssociatedDisplay = true;
- }
-
- mParameters.handlesKeyRepeat = false;
- getDevice()->getConfiguration().tryGetProperty(String8("keyboard.handlesKeyRepeat"),
- mParameters.handlesKeyRepeat);
-}
-
-void KeyboardInputMapper::dumpParameters(String8& dump) {
- dump.append(INDENT3 "Parameters:\n");
- dump.appendFormat(INDENT4 "HasAssociatedDisplay: %s\n",
- toString(mParameters.hasAssociatedDisplay));
- dump.appendFormat(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
- dump.appendFormat(INDENT4 "HandlesKeyRepeat: %s\n",
- toString(mParameters.handlesKeyRepeat));
-}
-
-void KeyboardInputMapper::reset(nsecs_t when) {
- mMetaState = AMETA_NONE;
- mDownTime = 0;
- mKeyDowns.clear();
- mCurrentHidUsage = 0;
-
- resetLedState();
-
- InputMapper::reset(when);
-}
-
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_KEY: {
- int32_t scanCode = rawEvent->code;
- int32_t usageCode = mCurrentHidUsage;
- mCurrentHidUsage = 0;
-
- if (isKeyboardOrGamepadKey(scanCode)) {
- int32_t keyCode;
- uint32_t flags;
- if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
- keyCode = AKEYCODE_UNKNOWN;
- flags = 0;
- }
- processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
- }
- break;
- }
- case EV_MSC: {
- if (rawEvent->code == MSC_SCAN) {
- mCurrentHidUsage = rawEvent->value;
- }
- break;
- }
- case EV_SYN: {
- if (rawEvent->code == SYN_REPORT) {
- mCurrentHidUsage = 0;
- }
- }
- }
-}
-
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
- return scanCode < BTN_MOUSE
- || scanCode >= KEY_OK
- || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE)
- || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
- int32_t scanCode, uint32_t policyFlags) {
-
- if (down) {
- // Rotate key codes according to orientation if needed.
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- keyCode = rotateKeyCode(keyCode, mOrientation);
- }
-
- // Add key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
- // key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
- } else {
- // key down
- if ((policyFlags & POLICY_FLAG_VIRTUAL)
- && mContext->shouldDropVirtualKey(when,
- getDevice(), keyCode, scanCode)) {
- return;
- }
-
- mKeyDowns.push();
- KeyDown& keyDown = mKeyDowns.editTop();
- keyDown.keyCode = keyCode;
- keyDown.scanCode = scanCode;
- }
-
- mDownTime = when;
- } else {
- // Remove key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
- // key up, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
- mKeyDowns.removeAt(size_t(keyDownIndex));
- } else {
- // key was not actually down
- ALOGI("Dropping key up from device %s because the key was not down. "
- "keyCode=%d, scanCode=%d",
- getDeviceName().string(), keyCode, scanCode);
- return;
- }
- }
-
- int32_t oldMetaState = mMetaState;
- int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
- bool metaStateChanged = oldMetaState != newMetaState;
- if (metaStateChanged) {
- mMetaState = newMetaState;
- updateLedState(false);
- }
-
- nsecs_t downTime = mDownTime;
-
- // Key down on external an keyboard should wake the device.
- // We don't do this for internal keyboards to prevent them from waking up in your pocket.
- // For internal keyboards, the key layout file should specify the policy flags for
- // each wake key individually.
- // TODO: Use the input device configuration to control this behavior more finely.
- if (down && getDevice()->isExternal()
- && !(policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED))) {
- policyFlags |= POLICY_FLAG_WAKE_DROPPED;
- }
-
- if (mParameters.handlesKeyRepeat) {
- policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
- }
-
- if (metaStateChanged) {
- getContext()->updateGlobalMetaState();
- }
-
- if (down && !isMetaKey(keyCode)) {
- getContext()->fadePointer();
- }
-
- NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
- getListener()->notifyKey(&args);
-}
-
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
- size_t n = mKeyDowns.size();
- for (size_t i = 0; i < n; i++) {
- if (mKeyDowns[i].scanCode == scanCode) {
- return i;
- }
- }
- return -1;
-}
-
-int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
-}
-
-int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
-}
-
-bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
-}
-
-int32_t KeyboardInputMapper::getMetaState() {
- return mMetaState;
-}
-
-void KeyboardInputMapper::resetLedState() {
- initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
- initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
- initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK);
-
- updateLedState(true);
-}
-
-void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
- ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
- ledState.on = false;
-}
-
-void KeyboardInputMapper::updateLedState(bool reset) {
- updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK,
- AMETA_CAPS_LOCK_ON, reset);
- updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK,
- AMETA_NUM_LOCK_ON, reset);
- updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK,
- AMETA_SCROLL_LOCK_ON, reset);
-}
-
-void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState,
- int32_t led, int32_t modifier, bool reset) {
- if (ledState.avail) {
- bool desiredState = (mMetaState & modifier) != 0;
- if (reset || ledState.on != desiredState) {
- getEventHub()->setLedState(getDeviceId(), led, desiredState);
- ledState.on = desiredState;
- }
- }
-}
-
-
-// --- CursorInputMapper ---
-
-CursorInputMapper::CursorInputMapper(InputDevice* device) :
- InputMapper(device) {
-}
-
-CursorInputMapper::~CursorInputMapper() {
-}
-
-uint32_t CursorInputMapper::getSources() {
- return mSource;
-}
-
-void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- if (mParameters.mode == Parameters::MODE_POINTER) {
- float minX, minY, maxX, maxY;
- if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
- }
- } else {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
- }
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-
- if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
- }
- if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
- }
-}
-
-void CursorInputMapper::dump(String8& dump) {
- dump.append(INDENT2 "Cursor Input Mapper:\n");
- dumpParameters(dump);
- dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale);
- dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale);
- dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
- dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
- dump.appendFormat(INDENT3 "HaveVWheel: %s\n",
- toString(mCursorScrollAccumulator.haveRelativeVWheel()));
- dump.appendFormat(INDENT3 "HaveHWheel: %s\n",
- toString(mCursorScrollAccumulator.haveRelativeHWheel()));
- dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
- dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
- dump.appendFormat(INDENT3 "Orientation: %d\n", mOrientation);
- dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
- dump.appendFormat(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
- dump.appendFormat(INDENT3 "DownTime: %lld\n", mDownTime);
-}
-
-void CursorInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- mCursorScrollAccumulator.configure(getDevice());
-
- // Configure basic parameters.
- configureParameters();
-
- // Configure device mode.
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- mSource = AINPUT_SOURCE_MOUSE;
- mXPrecision = 1.0f;
- mYPrecision = 1.0f;
- mXScale = 1.0f;
- mYScale = 1.0f;
- mPointerController = getPolicy()->obtainPointerController(getDeviceId());
- break;
- case Parameters::MODE_NAVIGATION:
- mSource = AINPUT_SOURCE_TRACKBALL;
- mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
- mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
- break;
- }
-
- mVWheelScale = 1.0f;
- mHWheelScale = 1.0f;
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
- mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
- mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- DisplayViewport v;
- if (config->getDisplayInfo(false /*external*/, &v)) {
- mOrientation = v.orientation;
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- bumpGeneration();
- }
-}
-
-void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
- String8 cursorModeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
- if (cursorModeString == "navigation") {
- mParameters.mode = Parameters::MODE_NAVIGATION;
- } else if (cursorModeString != "pointer" && cursorModeString != "default") {
- ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
- }
- }
-
- mParameters.orientationAware = false;
- getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
- mParameters.orientationAware);
-
- mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
- mParameters.hasAssociatedDisplay = true;
- }
-}
-
-void CursorInputMapper::dumpParameters(String8& dump) {
- dump.append(INDENT3 "Parameters:\n");
- dump.appendFormat(INDENT4 "HasAssociatedDisplay: %s\n",
- toString(mParameters.hasAssociatedDisplay));
-
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- dump.append(INDENT4 "Mode: pointer\n");
- break;
- case Parameters::MODE_NAVIGATION:
- dump.append(INDENT4 "Mode: navigation\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- dump.appendFormat(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
-}
-
-void CursorInputMapper::reset(nsecs_t when) {
- mButtonState = 0;
- mDownTime = 0;
-
- mPointerVelocityControl.reset();
- mWheelXVelocityControl.reset();
- mWheelYVelocityControl.reset();
-
- mCursorButtonAccumulator.reset(getDevice());
- mCursorMotionAccumulator.reset(getDevice());
- mCursorScrollAccumulator.reset(getDevice());
-
- InputMapper::reset(when);
-}
-
-void CursorInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorMotionAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void CursorInputMapper::sync(nsecs_t when) {
- int32_t lastButtonState = mButtonState;
- int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
- mButtonState = currentButtonState;
-
- bool wasDown = isPointerDown(lastButtonState);
- bool down = isPointerDown(currentButtonState);
- bool downChanged;
- if (!wasDown && down) {
- mDownTime = when;
- downChanged = true;
- } else if (wasDown && !down) {
- downChanged = true;
- } else {
- downChanged = false;
- }
- nsecs_t downTime = mDownTime;
- bool buttonsChanged = currentButtonState != lastButtonState;
- bool buttonsPressed = currentButtonState & ~lastButtonState;
-
- float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
- float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
- bool moved = deltaX != 0 || deltaY != 0;
-
- // Rotate delta according to orientation if needed.
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay
- && (deltaX != 0.0f || deltaY != 0.0f)) {
- rotateDelta(mOrientation, &deltaX, &deltaY);
- }
-
- // Move the pointer.
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
- float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
- bool scrolled = vscroll != 0 || hscroll != 0;
-
- mWheelYVelocityControl.move(when, NULL, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, NULL);
-
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- int32_t displayId;
- if (mPointerController != NULL) {
- if (moved || scrolled || buttonsChanged) {
- mPointerController->setPresentation(
- PointerControllerInterface::PRESENTATION_POINTER);
-
- if (moved) {
- mPointerController->move(deltaX, deltaY);
- }
-
- if (buttonsChanged) {
- mPointerController->setButtonState(currentButtonState);
- }
-
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- }
-
- float x, y;
- mPointerController->getPosition(&x, &y);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- displayId = ADISPLAY_ID_DEFAULT;
- } else {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
- displayId = ADISPLAY_ID_NONE;
- }
-
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
-
- // Moving an external trackball or mouse should wake the device.
- // We don't do this for internal cursor devices to prevent them from waking up
- // the device in your pocket.
- // TODO: Use the input device configuration to control this behavior more finely.
- uint32_t policyFlags = 0;
- if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
- policyFlags |= POLICY_FLAG_WAKE_DROPPED;
- }
-
- // Synthesize key down from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- policyFlags, lastButtonState, currentButtonState);
-
- // Send motion event.
- if (downChanged || moved || scrolled || buttonsChanged) {
- int32_t metaState = mContext->getGlobalMetaState();
- int32_t motionEventAction;
- if (downChanged) {
- motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
- } else if (down || mPointerController == NULL) {
- motionEventAction = AMOTION_EVENT_ACTION_MOVE;
- } else {
- motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
- }
-
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- motionEventAction, 0, metaState, currentButtonState, 0,
- displayId, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
- getListener()->notifyMotion(&args);
-
- // Send hover move after UP to tell the application that the mouse is hovering now.
- if (motionEventAction == AMOTION_EVENT_ACTION_UP
- && mPointerController != NULL) {
- NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
- getListener()->notifyMotion(&hoverArgs);
- }
-
- // Send scroll events.
- if (scrolled) {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
- NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
- getListener()->notifyMotion(&scrollArgs);
- }
- }
-
- // Synthesize key up from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- policyFlags, lastButtonState, currentButtonState);
-
- mCursorMotionAccumulator.finishSync();
- mCursorScrollAccumulator.finishSync();
-}
-
-int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
- return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
- } else {
- return AKEY_STATE_UNKNOWN;
- }
-}
-
-void CursorInputMapper::fadePointer() {
- if (mPointerController != NULL) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
-}
-
-
-// --- TouchInputMapper ---
-
-TouchInputMapper::TouchInputMapper(InputDevice* device) :
- InputMapper(device),
- mSource(0), mDeviceMode(DEVICE_MODE_DISABLED),
- mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0),
- mSurfaceOrientation(DISPLAY_ORIENTATION_0) {
-}
-
-TouchInputMapper::~TouchInputMapper() {
-}
-
-uint32_t TouchInputMapper::getSources() {
- return mSource;
-}
-
-void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- if (mDeviceMode != DEVICE_MODE_DISABLED) {
- info->addMotionRange(mOrientedRanges.x);
- info->addMotionRange(mOrientedRanges.y);
- info->addMotionRange(mOrientedRanges.pressure);
-
- if (mOrientedRanges.haveSize) {
- info->addMotionRange(mOrientedRanges.size);
- }
-
- if (mOrientedRanges.haveTouchSize) {
- info->addMotionRange(mOrientedRanges.touchMajor);
- info->addMotionRange(mOrientedRanges.touchMinor);
- }
-
- if (mOrientedRanges.haveToolSize) {
- info->addMotionRange(mOrientedRanges.toolMajor);
- info->addMotionRange(mOrientedRanges.toolMinor);
- }
-
- if (mOrientedRanges.haveOrientation) {
- info->addMotionRange(mOrientedRanges.orientation);
- }
-
- if (mOrientedRanges.haveDistance) {
- info->addMotionRange(mOrientedRanges.distance);
- }
-
- if (mOrientedRanges.haveTilt) {
- info->addMotionRange(mOrientedRanges.tilt);
- }
-
- if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- }
- info->setButtonUnderPad(mParameters.hasButtonUnderPad);
- }
-}
-
-void TouchInputMapper::dump(String8& dump) {
- dump.append(INDENT2 "Touch Input Mapper:\n");
- dumpParameters(dump);
- dumpVirtualKeys(dump);
- dumpRawPointerAxes(dump);
- dumpCalibration(dump);
- dumpSurface(dump);
-
- dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
- dump.appendFormat(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
- dump.appendFormat(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
- dump.appendFormat(INDENT4 "XScale: %0.3f\n", mXScale);
- dump.appendFormat(INDENT4 "YScale: %0.3f\n", mYScale);
- dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
- dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
- dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
- dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
- dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
- dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
- dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
- dump.appendFormat(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt));
- dump.appendFormat(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter);
- dump.appendFormat(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale);
- dump.appendFormat(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
- dump.appendFormat(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
-
- dump.appendFormat(INDENT3 "Last Button State: 0x%08x\n", mLastButtonState);
-
- dump.appendFormat(INDENT3 "Last Raw Touch: pointerCount=%d\n",
- mLastRawPointerData.pointerCount);
- for (uint32_t i = 0; i < mLastRawPointerData.pointerCount; i++) {
- const RawPointerData::Pointer& pointer = mLastRawPointerData.pointers[i];
- dump.appendFormat(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
- "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
- "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
- "toolType=%d, isHovering=%s\n", i,
- pointer.id, pointer.x, pointer.y, pointer.pressure,
- pointer.touchMajor, pointer.touchMinor,
- pointer.toolMajor, pointer.toolMinor,
- pointer.orientation, pointer.tiltX, pointer.tiltY, pointer.distance,
- pointer.toolType, toString(pointer.isHovering));
- }
-
- dump.appendFormat(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
- mLastCookedPointerData.pointerCount);
- for (uint32_t i = 0; i < mLastCookedPointerData.pointerCount; i++) {
- const PointerProperties& pointerProperties = mLastCookedPointerData.pointerProperties[i];
- const PointerCoords& pointerCoords = mLastCookedPointerData.pointerCoords[i];
- dump.appendFormat(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
- "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, "
- "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
- "toolType=%d, isHovering=%s\n", i,
- pointerProperties.id,
- pointerCoords.getX(),
- pointerCoords.getY(),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
- pointerProperties.toolType,
- toString(mLastCookedPointerData.isHovering(i)));
- }
-
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
- dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
- mPointerXMovementScale);
- dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n",
- mPointerYMovementScale);
- dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n",
- mPointerXZoomScale);
- dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n",
- mPointerYZoomScale);
- dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
- mPointerGestureMaxSwipeWidth);
- }
-}
-
-void TouchInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- mConfig = *config;
-
- if (!changes) { // first time only
- // Configure basic parameters.
- configureParameters();
-
- // Configure common accumulators.
- mCursorScrollAccumulator.configure(getDevice());
- mTouchButtonAccumulator.configure(getDevice());
-
- // Configure absolute axis information.
- configureRawPointerAxes();
-
- // Prepare input device calibration.
- parseCalibration();
- resolveCalibration();
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
- // Update pointer speed.
- mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
- mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
- }
-
- bool resetNeeded = false;
- if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
- | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
- | InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {
- // Configure device sources, surface dimensions, orientation and
- // scaling factors.
- configureSurface(when, &resetNeeded);
- }
-
- if (changes && resetNeeded) {
- // Send reset, unless this is the first time the device has been configured,
- // in which case the reader will call reset itself after all mappers are ready.
- getDevice()->notifyReset(when);
- }
-}
-
-void TouchInputMapper::configureParameters() {
- // Use the pointer presentation mode for devices that do not support distinct
- // multitouch. The spot-based presentation relies on being able to accurately
- // locate two or more fingers on the touch pad.
- mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
- ? Parameters::GESTURE_MODE_POINTER : Parameters::GESTURE_MODE_SPOTS;
-
- String8 gestureModeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
- gestureModeString)) {
- if (gestureModeString == "pointer") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER;
- } else if (gestureModeString == "spots") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;
- } else if (gestureModeString != "default") {
- ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
- }
- }
-
- if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
- // The device is a touch screen.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
- // The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
- || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
- // The device is a cursor device with a touch pad attached.
- // By default don't use the touch pad to move the pointer.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
- } else {
- // The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- }
-
- mParameters.hasButtonUnderPad=
- getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
-
- String8 deviceTypeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
- deviceTypeString)) {
- if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- } else if (deviceTypeString == "touchPad") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
- } else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
- } else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- } else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
- }
- }
-
- mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
- mParameters.orientationAware);
-
- mParameters.hasAssociatedDisplay = false;
- mParameters.associatedDisplayIsExternal = false;
- if (mParameters.orientationAware
- || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
- || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
- mParameters.hasAssociatedDisplay = true;
- mParameters.associatedDisplayIsExternal =
- mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
- && getDevice()->isExternal();
- }
-
- // Initial downs on external touch devices should wake the device.
- // Normally we don't do this for internal touch screens to prevent them from waking
- // up in your pocket but you can enable it using the input device configuration.
- mParameters.wake = getDevice()->isExternal();
- getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"),
- mParameters.wake);
-}
-
-void TouchInputMapper::dumpParameters(String8& dump) {
- dump.append(INDENT3 "Parameters:\n");
-
- switch (mParameters.gestureMode) {
- case Parameters::GESTURE_MODE_POINTER:
- dump.append(INDENT4 "GestureMode: pointer\n");
- break;
- case Parameters::GESTURE_MODE_SPOTS:
- dump.append(INDENT4 "GestureMode: spots\n");
- break;
- default:
- assert(false);
- }
-
- switch (mParameters.deviceType) {
- case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
- dump.append(INDENT4 "DeviceType: touchScreen\n");
- break;
- case Parameters::DEVICE_TYPE_TOUCH_PAD:
- dump.append(INDENT4 "DeviceType: touchPad\n");
- break;
- case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
- dump.append(INDENT4 "DeviceType: touchNavigation\n");
- break;
- case Parameters::DEVICE_TYPE_POINTER:
- dump.append(INDENT4 "DeviceType: pointer\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- dump.appendFormat(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s\n",
- toString(mParameters.hasAssociatedDisplay),
- toString(mParameters.associatedDisplayIsExternal));
- dump.appendFormat(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
-}
-
-void TouchInputMapper::configureRawPointerAxes() {
- mRawPointerAxes.clear();
-}
-
-void TouchInputMapper::dumpRawPointerAxes(String8& dump) {
- dump.append(INDENT3 "Raw Touch Axes:\n");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
-}
-
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
- int32_t oldDeviceMode = mDeviceMode;
-
- // Determine device mode.
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
- && mConfig.pointerGesturesEnabled) {
- mSource = AINPUT_SOURCE_MOUSE;
- mDeviceMode = DEVICE_MODE_POINTER;
- if (hasStylus()) {
- mSource |= AINPUT_SOURCE_STYLUS;
- }
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
- && mParameters.hasAssociatedDisplay) {
- mSource = AINPUT_SOURCE_TOUCHSCREEN;
- mDeviceMode = DEVICE_MODE_DIRECT;
- if (hasStylus()) {
- mSource |= AINPUT_SOURCE_STYLUS;
- }
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
- mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_NAVIGATION;
- } else {
- mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DEVICE_MODE_UNSCALED;
- }
-
- // Ensure we have valid X and Y axes.
- if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis! "
- "The device will be inoperable.", getDeviceName().string());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
-
- // Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
- int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
-
- // Get associated display dimensions.
- DisplayViewport newViewport;
- if (mParameters.hasAssociatedDisplay) {
- if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal, &newViewport)) {
- ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
- "display. The device will be inoperable until the display size "
- "becomes available.",
- getDeviceName().string());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
- } else {
- newViewport.setNonDisplayViewport(rawWidth, rawHeight);
- }
- bool viewportChanged = mViewport != newViewport;
- if (viewportChanged) {
- mViewport = newViewport;
-
- if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
- // Convert rotated viewport to natural surface coordinates.
- int32_t naturalLogicalWidth, naturalLogicalHeight;
- int32_t naturalPhysicalWidth, naturalPhysicalHeight;
- int32_t naturalPhysicalLeft, naturalPhysicalTop;
- int32_t naturalDeviceWidth, naturalDeviceHeight;
- switch (mViewport.orientation) {
- case DISPLAY_ORIENTATION_90:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalPhysicalTop = mViewport.physicalLeft;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_180:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
- naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- case DISPLAY_ORIENTATION_270:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.physicalTop;
- naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_0:
- default:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.physicalLeft;
- naturalPhysicalTop = mViewport.physicalTop;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- }
-
- mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
-
- mSurfaceOrientation = mParameters.orientationAware ?
- mViewport.orientation : DISPLAY_ORIENTATION_0;
- } else {
- mSurfaceWidth = rawWidth;
- mSurfaceHeight = rawHeight;
- mSurfaceLeft = 0;
- mSurfaceTop = 0;
- mSurfaceOrientation = DISPLAY_ORIENTATION_0;
- }
- }
-
- // If moving between pointer modes, need to reset some state.
- bool deviceModeChanged = mDeviceMode != oldDeviceMode;
- if (deviceModeChanged) {
- mOrientedRanges.clear();
- }
-
- // Create pointer controller if needed.
- if (mDeviceMode == DEVICE_MODE_POINTER ||
- (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
- if (mPointerController == NULL) {
- mPointerController = getPolicy()->obtainPointerController(getDeviceId());
- }
- } else {
- mPointerController.clear();
- }
-
- if (viewportChanged || deviceModeChanged) {
- ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
- "display id %d",
- getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight,
- mSurfaceOrientation, mDeviceMode, mViewport.displayId);
-
- // Configure X and Y factors.
- mXScale = float(mSurfaceWidth) / rawWidth;
- mYScale = float(mSurfaceHeight) / rawHeight;
- mXTranslate = -mSurfaceLeft;
- mYTranslate = -mSurfaceTop;
- mXPrecision = 1.0f / mXScale;
- mYPrecision = 1.0f / mYScale;
-
- mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
- mOrientedRanges.x.source = mSource;
- mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
- mOrientedRanges.y.source = mSource;
-
- configureVirtualKeys();
-
- // Scale factor for terms that are not oriented in a particular axis.
- // If the pixels are square then xScale == yScale otherwise we fake it
- // by choosing an average.
- mGeometricScale = avg(mXScale, mYScale);
-
- // Size of diagonal axis.
- float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
-
- // Size factors.
- if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
- if (mRawPointerAxes.touchMajor.valid
- && mRawPointerAxes.touchMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
- } else if (mRawPointerAxes.toolMajor.valid
- && mRawPointerAxes.toolMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
- } else {
- mSizeScale = 0.0f;
- }
-
- mOrientedRanges.haveTouchSize = true;
- mOrientedRanges.haveToolSize = true;
- mOrientedRanges.haveSize = true;
-
- mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
- mOrientedRanges.touchMajor.source = mSource;
- mOrientedRanges.touchMajor.min = 0;
- mOrientedRanges.touchMajor.max = diagonalSize;
- mOrientedRanges.touchMajor.flat = 0;
- mOrientedRanges.touchMajor.fuzz = 0;
- mOrientedRanges.touchMajor.resolution = 0;
-
- mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
- mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
-
- mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
- mOrientedRanges.toolMajor.source = mSource;
- mOrientedRanges.toolMajor.min = 0;
- mOrientedRanges.toolMajor.max = diagonalSize;
- mOrientedRanges.toolMajor.flat = 0;
- mOrientedRanges.toolMajor.fuzz = 0;
- mOrientedRanges.toolMajor.resolution = 0;
-
- mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
- mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
-
- mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
- mOrientedRanges.size.source = mSource;
- mOrientedRanges.size.min = 0;
- mOrientedRanges.size.max = 1.0;
- mOrientedRanges.size.flat = 0;
- mOrientedRanges.size.fuzz = 0;
- mOrientedRanges.size.resolution = 0;
- } else {
- mSizeScale = 0.0f;
- }
-
- // Pressure factors.
- mPressureScale = 0;
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
- || mCalibration.pressureCalibration
- == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
- if (mCalibration.havePressureScale) {
- mPressureScale = mCalibration.pressureScale;
- } else if (mRawPointerAxes.pressure.valid
- && mRawPointerAxes.pressure.maxValue != 0) {
- mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
- }
- }
-
- mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
- mOrientedRanges.pressure.source = mSource;
- mOrientedRanges.pressure.min = 0;
- mOrientedRanges.pressure.max = 1.0;
- mOrientedRanges.pressure.flat = 0;
- mOrientedRanges.pressure.fuzz = 0;
- mOrientedRanges.pressure.resolution = 0;
-
- // Tilt
- mTiltXCenter = 0;
- mTiltXScale = 0;
- mTiltYCenter = 0;
- mTiltYScale = 0;
- mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
- if (mHaveTilt) {
- mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue,
- mRawPointerAxes.tiltX.maxValue);
- mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue,
- mRawPointerAxes.tiltY.maxValue);
- mTiltXScale = M_PI / 180;
- mTiltYScale = M_PI / 180;
-
- mOrientedRanges.haveTilt = true;
-
- mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
- mOrientedRanges.tilt.source = mSource;
- mOrientedRanges.tilt.min = 0;
- mOrientedRanges.tilt.max = M_PI_2;
- mOrientedRanges.tilt.flat = 0;
- mOrientedRanges.tilt.fuzz = 0;
- mOrientedRanges.tilt.resolution = 0;
- }
-
- // Orientation
- mOrientationScale = 0;
- if (mHaveTilt) {
- mOrientedRanges.haveOrientation = true;
-
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI;
- mOrientedRanges.orientation.max = M_PI;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
- } else if (mCalibration.orientationCalibration !=
- Calibration::ORIENTATION_CALIBRATION_NONE) {
- if (mCalibration.orientationCalibration
- == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
- if (mRawPointerAxes.orientation.valid) {
- if (mRawPointerAxes.orientation.maxValue > 0) {
- mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
- } else if (mRawPointerAxes.orientation.minValue < 0) {
- mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
- } else {
- mOrientationScale = 0;
- }
- }
- }
-
- mOrientedRanges.haveOrientation = true;
-
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI_2;
- mOrientedRanges.orientation.max = M_PI_2;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
- }
-
- // Distance
- mDistanceScale = 0;
- if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
- if (mCalibration.distanceCalibration
- == Calibration::DISTANCE_CALIBRATION_SCALED) {
- if (mCalibration.haveDistanceScale) {
- mDistanceScale = mCalibration.distanceScale;
- } else {
- mDistanceScale = 1.0f;
- }
- }
-
- mOrientedRanges.haveDistance = true;
-
- mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
- mOrientedRanges.distance.source = mSource;
- mOrientedRanges.distance.min =
- mRawPointerAxes.distance.minValue * mDistanceScale;
- mOrientedRanges.distance.max =
- mRawPointerAxes.distance.maxValue * mDistanceScale;
- mOrientedRanges.distance.flat = 0;
- mOrientedRanges.distance.fuzz =
- mRawPointerAxes.distance.fuzz * mDistanceScale;
- mOrientedRanges.distance.resolution = 0;
- }
-
- // Compute oriented precision, scales and ranges.
- // Note that the maximum value reported is an inclusive maximum value so it is one
- // unit less than the total width or height of surface.
- switch (mSurfaceOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- mOrientedXPrecision = mYPrecision;
- mOrientedYPrecision = mXPrecision;
-
- mOrientedRanges.x.min = mYTranslate;
- mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
-
- mOrientedRanges.y.min = mXTranslate;
- mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
- break;
-
- default:
- mOrientedXPrecision = mXPrecision;
- mOrientedYPrecision = mYPrecision;
-
- mOrientedRanges.x.min = mXTranslate;
- mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
-
- mOrientedRanges.y.min = mYTranslate;
- mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
- break;
- }
-
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- // Compute pointer gesture detection parameters.
- float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
-
- // Scale movements such that one whole swipe of the touch pad covers a
- // given area relative to the diagonal size of the display when no acceleration
- // is applied.
- // Assume that the touch pad has a square aspect ratio such that movements in
- // X and Y of the same number of raw units cover the same physical distance.
- mPointerXMovementScale = mConfig.pointerGestureMovementSpeedRatio
- * displayDiagonal / rawDiagonal;
- mPointerYMovementScale = mPointerXMovementScale;
-
- // Scale zooms to cover a smaller range of the display than movements do.
- // This value determines the area around the pointer that is affected by freeform
- // pointer gestures.
- mPointerXZoomScale = mConfig.pointerGestureZoomSpeedRatio
- * displayDiagonal / rawDiagonal;
- mPointerYZoomScale = mPointerXZoomScale;
-
- // Max width between pointers to detect a swipe gesture is more than some fraction
- // of the diagonal axis of the touch pad. Touches that are wider than this are
- // translated into freeform gestures.
- mPointerGestureMaxSwipeWidth =
- mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
-
- // Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
- }
-
- // Inform the dispatcher about the changes.
- *outResetNeeded = true;
- bumpGeneration();
- }
-}
-
-void TouchInputMapper::dumpSurface(String8& dump) {
- dump.appendFormat(INDENT3 "Viewport: displayId=%d, orientation=%d, "
- "logicalFrame=[%d, %d, %d, %d], "
- "physicalFrame=[%d, %d, %d, %d], "
- "deviceSize=[%d, %d]\n",
- mViewport.displayId, mViewport.orientation,
- mViewport.logicalLeft, mViewport.logicalTop,
- mViewport.logicalRight, mViewport.logicalBottom,
- mViewport.physicalLeft, mViewport.physicalTop,
- mViewport.physicalRight, mViewport.physicalBottom,
- mViewport.deviceWidth, mViewport.deviceHeight);
-
- dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
- dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
- dump.appendFormat(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
- dump.appendFormat(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
- dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
-}
-
-void TouchInputMapper::configureVirtualKeys() {
- Vector<VirtualKeyDefinition> virtualKeyDefinitions;
- getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
-
- mVirtualKeys.clear();
-
- if (virtualKeyDefinitions.size() == 0) {
- return;
- }
-
- mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
-
- int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
- int32_t touchScreenTop = mRawPointerAxes.y.minValue;
- int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
- int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
-
- for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
- const VirtualKeyDefinition& virtualKeyDefinition =
- virtualKeyDefinitions[i];
-
- mVirtualKeys.add();
- VirtualKey& virtualKey = mVirtualKeys.editTop();
-
- virtualKey.scanCode = virtualKeyDefinition.scanCode;
- int32_t keyCode;
- uint32_t flags;
- if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, &keyCode, &flags)) {
- ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
- virtualKey.scanCode);
- mVirtualKeys.pop(); // drop the key
- continue;
- }
-
- virtualKey.keyCode = keyCode;
- virtualKey.flags = flags;
-
- // convert the key definition's display coordinates into touch coordinates for a hit box
- int32_t halfWidth = virtualKeyDefinition.width / 2;
- int32_t halfHeight = virtualKeyDefinition.height / 2;
-
- virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
- * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
- virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
- * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
- virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
- * touchScreenHeight / mSurfaceHeight + touchScreenTop;
- virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
- * touchScreenHeight / mSurfaceHeight + touchScreenTop;
- }
-}
-
-void TouchInputMapper::dumpVirtualKeys(String8& dump) {
- if (!mVirtualKeys.isEmpty()) {
- dump.append(INDENT3 "Virtual Keys:\n");
-
- for (size_t i = 0; i < mVirtualKeys.size(); i++) {
- const VirtualKey& virtualKey = mVirtualKeys.itemAt(i);
- dump.appendFormat(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
- "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
- i, virtualKey.scanCode, virtualKey.keyCode,
- virtualKey.hitLeft, virtualKey.hitRight,
- virtualKey.hitTop, virtualKey.hitBottom);
- }
- }
-}
-
-void TouchInputMapper::parseCalibration() {
- const PropertyMap& in = getDevice()->getConfiguration();
- Calibration& out = mCalibration;
-
- // Size
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
- String8 sizeCalibrationString;
- if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
- if (sizeCalibrationString == "none") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
- } else if (sizeCalibrationString == "geometric") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
- } else if (sizeCalibrationString == "diameter") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
- } else if (sizeCalibrationString == "box") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
- } else if (sizeCalibrationString == "area") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
- } else if (sizeCalibrationString != "default") {
- ALOGW("Invalid value for touch.size.calibration: '%s'",
- sizeCalibrationString.string());
- }
- }
-
- out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"),
- out.sizeScale);
- out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"),
- out.sizeBias);
- out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"),
- out.sizeIsSummed);
-
- // Pressure
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
- String8 pressureCalibrationString;
- if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
- if (pressureCalibrationString == "none") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
- } else if (pressureCalibrationString == "physical") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
- } else if (pressureCalibrationString == "amplitude") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
- } else if (pressureCalibrationString != "default") {
- ALOGW("Invalid value for touch.pressure.calibration: '%s'",
- pressureCalibrationString.string());
- }
- }
-
- out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
- out.pressureScale);
-
- // Orientation
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
- String8 orientationCalibrationString;
- if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
- if (orientationCalibrationString == "none") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
- } else if (orientationCalibrationString == "interpolated") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
- } else if (orientationCalibrationString == "vector") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
- } else if (orientationCalibrationString != "default") {
- ALOGW("Invalid value for touch.orientation.calibration: '%s'",
- orientationCalibrationString.string());
- }
- }
-
- // Distance
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
- String8 distanceCalibrationString;
- if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
- if (distanceCalibrationString == "none") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
- } else if (distanceCalibrationString == "scaled") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
- } else if (distanceCalibrationString != "default") {
- ALOGW("Invalid value for touch.distance.calibration: '%s'",
- distanceCalibrationString.string());
- }
- }
-
- out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"),
- out.distanceScale);
-
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
- String8 coverageCalibrationString;
- if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
- if (coverageCalibrationString == "none") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
- } else if (coverageCalibrationString == "box") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
- } else if (coverageCalibrationString != "default") {
- ALOGW("Invalid value for touch.coverage.calibration: '%s'",
- coverageCalibrationString.string());
- }
- }
-}
-
-void TouchInputMapper::resolveCalibration() {
- // Size
- if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
- }
- } else {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
- }
-
- // Pressure
- if (mRawPointerAxes.pressure.valid) {
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
- }
- } else {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
- }
-
- // Orientation
- if (mRawPointerAxes.orientation.valid) {
- if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
- }
- } else {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
- }
-
- // Distance
- if (mRawPointerAxes.distance.valid) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
- }
- } else {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
- }
-
- // Coverage
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
- mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
- }
-}
-
-void TouchInputMapper::dumpCalibration(String8& dump) {
- dump.append(INDENT3 "Calibration:\n");
-
- // Size
- switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_NONE:
- dump.append(INDENT4 "touch.size.calibration: none\n");
- break;
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- dump.append(INDENT4 "touch.size.calibration: geometric\n");
- break;
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- dump.append(INDENT4 "touch.size.calibration: diameter\n");
- break;
- case Calibration::SIZE_CALIBRATION_BOX:
- dump.append(INDENT4 "touch.size.calibration: box\n");
- break;
- case Calibration::SIZE_CALIBRATION_AREA:
- dump.append(INDENT4 "touch.size.calibration: area\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.haveSizeScale) {
- dump.appendFormat(INDENT4 "touch.size.scale: %0.3f\n",
- mCalibration.sizeScale);
- }
-
- if (mCalibration.haveSizeBias) {
- dump.appendFormat(INDENT4 "touch.size.bias: %0.3f\n",
- mCalibration.sizeBias);
- }
-
- if (mCalibration.haveSizeIsSummed) {
- dump.appendFormat(INDENT4 "touch.size.isSummed: %s\n",
- toString(mCalibration.sizeIsSummed));
- }
-
- // Pressure
- switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_NONE:
- dump.append(INDENT4 "touch.pressure.calibration: none\n");
- break;
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- dump.append(INDENT4 "touch.pressure.calibration: physical\n");
- break;
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.havePressureScale) {
- dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
- mCalibration.pressureScale);
- }
-
- // Orientation
- switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_NONE:
- dump.append(INDENT4 "touch.orientation.calibration: none\n");
- break;
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
- break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR:
- dump.append(INDENT4 "touch.orientation.calibration: vector\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- // Distance
- switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_NONE:
- dump.append(INDENT4 "touch.distance.calibration: none\n");
- break;
- case Calibration::DISTANCE_CALIBRATION_SCALED:
- dump.append(INDENT4 "touch.distance.calibration: scaled\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.haveDistanceScale) {
- dump.appendFormat(INDENT4 "touch.distance.scale: %0.3f\n",
- mCalibration.distanceScale);
- }
-
- switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_NONE:
- dump.append(INDENT4 "touch.coverage.calibration: none\n");
- break;
- case Calibration::COVERAGE_CALIBRATION_BOX:
- dump.append(INDENT4 "touch.coverage.calibration: box\n");
- break;
- default:
- ALOG_ASSERT(false);
- }
-}
-
-void TouchInputMapper::reset(nsecs_t when) {
- mCursorButtonAccumulator.reset(getDevice());
- mCursorScrollAccumulator.reset(getDevice());
- mTouchButtonAccumulator.reset(getDevice());
-
- mPointerVelocityControl.reset();
- mWheelXVelocityControl.reset();
- mWheelYVelocityControl.reset();
-
- mCurrentRawPointerData.clear();
- mLastRawPointerData.clear();
- mCurrentCookedPointerData.clear();
- mLastCookedPointerData.clear();
- mCurrentButtonState = 0;
- mLastButtonState = 0;
- mCurrentRawVScroll = 0;
- mCurrentRawHScroll = 0;
- mCurrentFingerIdBits.clear();
- mLastFingerIdBits.clear();
- mCurrentStylusIdBits.clear();
- mLastStylusIdBits.clear();
- mCurrentMouseIdBits.clear();
- mLastMouseIdBits.clear();
- mPointerUsage = POINTER_USAGE_NONE;
- mSentHoverEnter = false;
- mDownTime = 0;
-
- mCurrentVirtualKey.down = false;
-
- mPointerGesture.reset();
- mPointerSimple.reset();
-
- if (mPointerController != NULL) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- mPointerController->clearSpots();
- }
-
- InputMapper::reset(when);
-}
-
-void TouchInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
- mTouchButtonAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void TouchInputMapper::sync(nsecs_t when) {
- // Sync button state.
- mCurrentButtonState = mTouchButtonAccumulator.getButtonState()
- | mCursorButtonAccumulator.getButtonState();
-
- // Sync scroll state.
- mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
- mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
- mCursorScrollAccumulator.finishSync();
-
- // Sync touch state.
- bool havePointerIds = true;
- mCurrentRawPointerData.clear();
- syncTouch(when, &havePointerIds);
-
-#if DEBUG_RAW_EVENTS
- if (!havePointerIds) {
- ALOGD("syncTouch: pointerCount %d -> %d, no pointer ids",
- mLastRawPointerData.pointerCount,
- mCurrentRawPointerData.pointerCount);
- } else {
- ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x",
- mLastRawPointerData.pointerCount,
- mCurrentRawPointerData.pointerCount,
- mLastRawPointerData.touchingIdBits.value,
- mCurrentRawPointerData.touchingIdBits.value,
- mLastRawPointerData.hoveringIdBits.value,
- mCurrentRawPointerData.hoveringIdBits.value);
- }
-#endif
-
- // Reset state that we will compute below.
- mCurrentFingerIdBits.clear();
- mCurrentStylusIdBits.clear();
- mCurrentMouseIdBits.clear();
- mCurrentCookedPointerData.clear();
-
- if (mDeviceMode == DEVICE_MODE_DISABLED) {
- // Drop all input if the device is disabled.
- mCurrentRawPointerData.clear();
- mCurrentButtonState = 0;
- } else {
- // Preprocess pointer data.
- if (!havePointerIds) {
- assignPointerIds();
- }
-
- // Handle policy on initial down or hover events.
- uint32_t policyFlags = 0;
- bool initialDown = mLastRawPointerData.pointerCount == 0
- && mCurrentRawPointerData.pointerCount != 0;
- bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;
- if (initialDown || buttonsPressed) {
- // If this is a touch screen, hide the pointer on an initial down.
- if (mDeviceMode == DEVICE_MODE_DIRECT) {
- getContext()->fadePointer();
- }
-
- if (mParameters.wake) {
- policyFlags |= POLICY_FLAG_WAKE_DROPPED;
- }
- }
-
- // Synthesize key down from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- policyFlags, mLastButtonState, mCurrentButtonState);
-
- // Consume raw off-screen touches before cooking pointer data.
- // If touches are consumed, subsequent code will not receive any pointer data.
- if (consumeRawTouches(when, policyFlags)) {
- mCurrentRawPointerData.clear();
- }
-
- // Cook pointer data. This call populates the mCurrentCookedPointerData structure
- // with cooked pointer data that has the same ids and indices as the raw data.
- // The following code can use either the raw or cooked data, as needed.
- cookPointerData();
-
- // Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
- mCurrentStylusIdBits.markBit(id);
- } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- mCurrentFingerIdBits.markBit(id);
- } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
- mCurrentMouseIdBits.markBit(id);
- }
- }
- for (BitSet32 idBits(mCurrentRawPointerData.hoveringIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
- mCurrentStylusIdBits.markBit(id);
- }
- }
-
- // Stylus takes precedence over all tools, then mouse, then finger.
- PointerUsage pointerUsage = mPointerUsage;
- if (!mCurrentStylusIdBits.isEmpty()) {
- mCurrentMouseIdBits.clear();
- mCurrentFingerIdBits.clear();
- pointerUsage = POINTER_USAGE_STYLUS;
- } else if (!mCurrentMouseIdBits.isEmpty()) {
- mCurrentFingerIdBits.clear();
- pointerUsage = POINTER_USAGE_MOUSE;
- } else if (!mCurrentFingerIdBits.isEmpty() || isPointerDown(mCurrentButtonState)) {
- pointerUsage = POINTER_USAGE_GESTURES;
- }
-
- dispatchPointerUsage(when, policyFlags, pointerUsage);
- } else {
- if (mDeviceMode == DEVICE_MODE_DIRECT
- && mConfig.showTouches && mPointerController != NULL) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-
- mPointerController->setButtonState(mCurrentButtonState);
- mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- mCurrentCookedPointerData.touchingIdBits);
- }
-
- dispatchHoverExit(when, policyFlags);
- dispatchTouches(when, policyFlags);
- dispatchHoverEnterAndMove(when, policyFlags);
- }
-
- // Synthesize key up from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- policyFlags, mLastButtonState, mCurrentButtonState);
- }
-
- // Copy current touch to last touch in preparation for the next cycle.
- mLastRawPointerData.copyFrom(mCurrentRawPointerData);
- mLastCookedPointerData.copyFrom(mCurrentCookedPointerData);
- mLastButtonState = mCurrentButtonState;
- mLastFingerIdBits = mCurrentFingerIdBits;
- mLastStylusIdBits = mCurrentStylusIdBits;
- mLastMouseIdBits = mCurrentMouseIdBits;
-
- // Clear some transient state.
- mCurrentRawVScroll = 0;
- mCurrentRawHScroll = 0;
-}
-
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- if (mPointerUsage == POINTER_USAGE_GESTURES) {
- dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
- }
- }
-}
-
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
- // Check for release of a virtual key.
- if (mCurrentVirtualKey.down) {
- if (mCurrentRawPointerData.touchingIdBits.isEmpty()) {
- // Pointer went up while virtual key was down.
- mCurrentVirtualKey.down = false;
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- }
- return true;
- }
-
- if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
- uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
- const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
- if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
- // Pointer is still within the space of the virtual key.
- return true;
- }
- }
-
- // Pointer left virtual key area or another pointer also went down.
- // Send key cancellation but do not consume the touch yet.
- // This is useful when the user swipes through from the virtual key area
- // into the main display surface.
- mCurrentVirtualKey.down = false;
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
- | AKEY_EVENT_FLAG_CANCELED);
- }
- }
-
- if (mLastRawPointerData.touchingIdBits.isEmpty()
- && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
- // Pointer just went down. Check for virtual key press or off-screen touches.
- uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
- if (!isPointInsideSurface(pointer.x, pointer.y)) {
- // If exactly one pointer went down, check for virtual key hit.
- // Otherwise we will drop the entire stroke.
- if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
- const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
- if (virtualKey) {
- mCurrentVirtualKey.down = true;
- mCurrentVirtualKey.downTime = when;
- mCurrentVirtualKey.keyCode = virtualKey->keyCode;
- mCurrentVirtualKey.scanCode = virtualKey->scanCode;
- mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey(
- when, getDevice(), virtualKey->keyCode, virtualKey->scanCode);
-
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode,
- mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- }
- }
- }
- return true;
- }
- }
-
- // Disable all virtual key touches that happen within a short time interval of the
- // most recent touch within the screen area. The idea is to filter out stray
- // virtual key presses when interacting with the touch screen.
- //
- // Problems we're trying to solve:
- //
- // 1. While scrolling a list or dragging the window shade, the user swipes down into a
- // virtual key area that is implemented by a separate touch panel and accidentally
- // triggers a virtual key.
- //
- // 2. While typing in the on screen keyboard, the user taps slightly outside the screen
- // area and accidentally triggers a virtual key. This often happens when virtual keys
- // are layed out below the screen near to where the on screen keyboard's space bar
- // is displayed.
- if (mConfig.virtualKeyQuietTime > 0 && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
- mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
- }
- return false;
-}
-
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags) {
- int32_t keyCode = mCurrentVirtualKey.keyCode;
- int32_t scanCode = mCurrentVirtualKey.scanCode;
- nsecs_t downTime = mCurrentVirtualKey.downTime;
- int32_t metaState = mContext->getGlobalMetaState();
- policyFlags |= POLICY_FLAG_VIRTUAL;
-
- NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
- keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
- getListener()->notifyKey(&args);
-}
-
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
- BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits;
- BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits;
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentButtonState;
-
- if (currentIdBits == lastIdBits) {
- if (!currentIdBits.isEmpty()) {
- // No pointer id changes so this is a move event.
- // The listener takes care of batching moves so we don't have to deal with that here.
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
- } else {
- // There may be pointers going up and pointers going down and pointers moving
- // all at the same time.
- BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
- BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
- BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
- BitSet32 dispatchedIdBits(lastIdBits.value);
-
- // Update last coordinates of pointers that have moved so that we observe the new
- // pointer positions at the same time as other pointers that have just gone up.
- bool moveNeeded = updateMovedPointers(
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- mLastCookedPointerData.pointerProperties,
- mLastCookedPointerData.pointerCoords,
- mLastCookedPointerData.idToIndex,
- moveIdBits);
- if (buttonState != mLastButtonState) {
- moveNeeded = true;
- }
-
- // Dispatch pointer up events.
- while (!upIdBits.isEmpty()) {
- uint32_t upId = upIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0,
- mLastCookedPointerData.pointerProperties,
- mLastCookedPointerData.pointerCoords,
- mLastCookedPointerData.idToIndex,
- dispatchedIdBits, upId,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- dispatchedIdBits.clearBit(upId);
- }
-
- // Dispatch move events if any of the remaining pointers moved from their old locations.
- // Although applications receive new locations as part of individual pointer up
- // events, they do not generally handle them except when presented in a move event.
- if (moveNeeded) {
- ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- dispatchedIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-
- // Dispatch pointer down events using the new pointer locations.
- while (!downIdBits.isEmpty()) {
- uint32_t downId = downIdBits.clearFirstMarkedBit();
- dispatchedIdBits.markBit(downId);
-
- if (dispatchedIdBits.count() == 1) {
- // First pointer is going down. Set down time.
- mDownTime = when;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- dispatchedIdBits, downId,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
- }
-}
-
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
- if (mSentHoverEnter &&
- (mCurrentCookedPointerData.hoveringIdBits.isEmpty()
- || !mCurrentCookedPointerData.touchingIdBits.isEmpty())) {
- int32_t metaState = getContext()->getGlobalMetaState();
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
- mLastCookedPointerData.pointerProperties,
- mLastCookedPointerData.pointerCoords,
- mLastCookedPointerData.idToIndex,
- mLastCookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- mSentHoverEnter = false;
- }
-}
-
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
- if (mCurrentCookedPointerData.touchingIdBits.isEmpty()
- && !mCurrentCookedPointerData.hoveringIdBits.isEmpty()) {
- int32_t metaState = getContext()->getGlobalMetaState();
- if (!mSentHoverEnter) {
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- mCurrentCookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- mSentHoverEnter = true;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- mCurrentCookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-}
-
-void TouchInputMapper::cookPointerData() {
- uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
-
- mCurrentCookedPointerData.clear();
- mCurrentCookedPointerData.pointerCount = currentPointerCount;
- mCurrentCookedPointerData.hoveringIdBits = mCurrentRawPointerData.hoveringIdBits;
- mCurrentCookedPointerData.touchingIdBits = mCurrentRawPointerData.touchingIdBits;
-
- // Walk through the the active pointers and map device coordinates onto
- // surface coordinates and adjust for display orientation.
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- const RawPointerData::Pointer& in = mCurrentRawPointerData.pointers[i];
-
- // Size
- float touchMajor, touchMinor, toolMajor, toolMinor, size;
- switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- case Calibration::SIZE_CALIBRATION_BOX:
- case Calibration::SIZE_CALIBRATION_AREA:
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
- touchMajor = in.touchMajor;
- touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
- toolMajor = in.toolMajor;
- toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.touchMinor.valid
- ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
- } else if (mRawPointerAxes.touchMajor.valid) {
- toolMajor = touchMajor = in.touchMajor;
- toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid
- ? in.touchMinor : in.touchMajor;
- size = mRawPointerAxes.touchMinor.valid
- ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
- } else if (mRawPointerAxes.toolMajor.valid) {
- touchMajor = toolMajor = in.toolMajor;
- touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid
- ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.toolMinor.valid
- ? avg(in.toolMajor, in.toolMinor) : in.toolMajor;
- } else {
- ALOG_ASSERT(false, "No touch or tool axes. "
- "Size calibration should have been resolved to NONE.");
- touchMajor = 0;
- touchMinor = 0;
- toolMajor = 0;
- toolMinor = 0;
- size = 0;
- }
-
- if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
- uint32_t touchingCount = mCurrentRawPointerData.touchingIdBits.count();
- if (touchingCount > 1) {
- touchMajor /= touchingCount;
- touchMinor /= touchingCount;
- toolMajor /= touchingCount;
- toolMinor /= touchingCount;
- size /= touchingCount;
- }
- }
-
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
- touchMajor *= mGeometricScale;
- touchMinor *= mGeometricScale;
- toolMajor *= mGeometricScale;
- toolMinor *= mGeometricScale;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
- touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
- touchMinor = touchMajor;
- toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
- toolMinor = toolMajor;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
- touchMinor = touchMajor;
- toolMinor = toolMajor;
- }
-
- mCalibration.applySizeScaleAndBias(&touchMajor);
- mCalibration.applySizeScaleAndBias(&touchMinor);
- mCalibration.applySizeScaleAndBias(&toolMajor);
- mCalibration.applySizeScaleAndBias(&toolMinor);
- size *= mSizeScale;
- break;
- default:
- touchMajor = 0;
- touchMinor = 0;
- toolMajor = 0;
- toolMinor = 0;
- size = 0;
- break;
- }
-
- // Pressure
- float pressure;
- switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- pressure = in.pressure * mPressureScale;
- break;
- default:
- pressure = in.isHovering ? 0 : 1;
- break;
- }
-
- // Tilt and Orientation
- float tilt;
- float orientation;
- if (mHaveTilt) {
- float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
- float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
- orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
- tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
- } else {
- tilt = 0;
-
- switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- orientation = in.orientation * mOrientationScale;
- break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
- int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
- int32_t c2 = signExtendNybble(in.orientation & 0x0f);
- if (c1 != 0 || c2 != 0) {
- orientation = atan2f(c1, c2) * 0.5f;
- float confidence = hypotf(c1, c2);
- float scale = 1.0f + confidence / 16.0f;
- touchMajor *= scale;
- touchMinor /= scale;
- toolMajor *= scale;
- toolMinor /= scale;
- } else {
- orientation = 0;
- }
- break;
- }
- default:
- orientation = 0;
- }
- }
-
- // Distance
- float distance;
- switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_SCALED:
- distance = in.distance * mDistanceScale;
- break;
- default:
- distance = 0;
- }
-
- // Coverage
- int32_t rawLeft, rawTop, rawRight, rawBottom;
- switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_BOX:
- rawLeft = (in.toolMinor & 0xffff0000) >> 16;
- rawRight = in.toolMinor & 0x0000ffff;
- rawBottom = in.toolMajor & 0x0000ffff;
- rawTop = (in.toolMajor & 0xffff0000) >> 16;
- break;
- default:
- rawLeft = rawTop = rawRight = rawBottom = 0;
- break;
- }
-
- // X, Y, and the bounding box for coverage information
- // Adjust coords for surface orientation.
- float x, y, left, top, right, bottom;
- switch (mSurfaceOrientation) {
- case DISPLAY_ORIENTATION_90:
- x = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
- left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
- top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
- orientation -= M_PI_2;
- if (orientation < mOrientedRanges.orientation.min) {
- orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- case DISPLAY_ORIENTATION_180:
- x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
- y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
- left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
- right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
- bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
- top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
- orientation -= M_PI;
- if (orientation < mOrientedRanges.orientation.min) {
- orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- case DISPLAY_ORIENTATION_270:
- x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
- y = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
- right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
- bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- orientation += M_PI_2;
- if (orientation > mOrientedRanges.orientation.max) {
- orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- default:
- x = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- y = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- break;
- }
-
- // Write output coords.
- PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];
- out.clear();
- out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
- out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
- out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
- out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
- out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
- } else {
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
- }
-
- // Write output properties.
- PointerProperties& properties = mCurrentCookedPointerData.pointerProperties[i];
- uint32_t id = in.id;
- properties.clear();
- properties.id = id;
- properties.toolType = in.toolType;
-
- // Write id index.
- mCurrentCookedPointerData.idToIndex[id] = i;
- }
-}
-
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
- PointerUsage pointerUsage) {
- if (pointerUsage != mPointerUsage) {
- abortPointerUsage(when, policyFlags);
- mPointerUsage = pointerUsage;
- }
-
- switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
- dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
- break;
- case POINTER_USAGE_STYLUS:
- dispatchPointerStylus(when, policyFlags);
- break;
- case POINTER_USAGE_MOUSE:
- dispatchPointerMouse(when, policyFlags);
- break;
- default:
- break;
- }
-}
-
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
- switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
- abortPointerGestures(when, policyFlags);
- break;
- case POINTER_USAGE_STYLUS:
- abortPointerStylus(when, policyFlags);
- break;
- case POINTER_USAGE_MOUSE:
- abortPointerMouse(when, policyFlags);
- break;
- default:
- break;
- }
-
- mPointerUsage = POINTER_USAGE_NONE;
-}
-
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
- bool isTimeout) {
- // Update current gesture coordinates.
- bool cancelPreviousGesture, finishPreviousGesture;
- bool sendEvents = preparePointerGestures(when,
- &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
- if (!sendEvents) {
- return;
- }
- if (finishPreviousGesture) {
- cancelPreviousGesture = false;
- }
-
- // Update the pointer presentation and spots.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
- if (finishPreviousGesture || cancelPreviousGesture) {
- mPointerController->clearSpots();
- }
- mPointerController->setSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits);
- } else {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
- }
-
- // Show or hide the pointer if needed.
- switch (mPointerGesture.currentGestureMode) {
- case PointerGesture::NEUTRAL:
- case PointerGesture::QUIET:
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
- && (mPointerGesture.lastGestureMode == PointerGesture::SWIPE
- || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)) {
- // Remind the user of where the pointer is after finishing a gesture with spots.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
- break;
- case PointerGesture::TAP:
- case PointerGesture::TAP_DRAG:
- case PointerGesture::BUTTON_CLICK_OR_DRAG:
- case PointerGesture::HOVER:
- case PointerGesture::PRESS:
- // Unfade the pointer when the current gesture manipulates the
- // area directly under the pointer.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- break;
- case PointerGesture::SWIPE:
- case PointerGesture::FREEFORM:
- // Fade the pointer when the current gesture manipulates a different
- // area and there are spots to guide the user experience.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- } else {
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- }
- break;
- }
-
- // Send events!
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentButtonState;
-
- // Update last coordinates of pointers that have moved so that we observe the new
- // pointer positions at the same time as other pointers that have just gone up.
- bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP
- || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG
- || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
- || mPointerGesture.currentGestureMode == PointerGesture::PRESS
- || mPointerGesture.currentGestureMode == PointerGesture::SWIPE
- || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
- bool moveNeeded = false;
- if (down && !cancelPreviousGesture && !finishPreviousGesture
- && !mPointerGesture.lastGestureIdBits.isEmpty()
- && !mPointerGesture.currentGestureIdBits.isEmpty()) {
- BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value
- & mPointerGesture.lastGestureIdBits.value);
- moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- movedGestureIdBits);
- if (buttonState != mLastButtonState) {
- moveNeeded = true;
- }
- }
-
- // Send motion events for all pointers that went up or were canceled.
- BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
- if (!dispatchedGestureIdBits.isEmpty()) {
- if (cancelPreviousGesture) {
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_CANCEL, 0, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- dispatchedGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime);
-
- dispatchedGestureIdBits.clear();
- } else {
- BitSet32 upGestureIdBits;
- if (finishPreviousGesture) {
- upGestureIdBits = dispatchedGestureIdBits;
- } else {
- upGestureIdBits.value = dispatchedGestureIdBits.value
- & ~mPointerGesture.currentGestureIdBits.value;
- }
- while (!upGestureIdBits.isEmpty()) {
- uint32_t id = upGestureIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- dispatchedGestureIdBits, id,
- 0, 0, mPointerGesture.downTime);
-
- dispatchedGestureIdBits.clearBit(id);
- }
- }
- }
-
- // Send motion events for all pointers that moved.
- if (moveNeeded) {
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- dispatchedGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime);
- }
-
- // Send motion events for all pointers that went down.
- if (down) {
- BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value
- & ~dispatchedGestureIdBits.value);
- while (!downGestureIdBits.isEmpty()) {
- uint32_t id = downGestureIdBits.clearFirstMarkedBit();
- dispatchedGestureIdBits.markBit(id);
-
- if (dispatchedGestureIdBits.count() == 1) {
- mPointerGesture.downTime = when;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- dispatchedGestureIdBits, id,
- 0, 0, mPointerGesture.downTime);
- }
- }
-
- // Send motion events for hover.
- if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime);
- } else if (dispatchedGestureIdBits.isEmpty()
- && !mPointerGesture.lastGestureIdBits.isEmpty()) {
- // Synthesize a hover move event after all pointers go up to indicate that
- // the pointer is hovering again even if the user is not currently touching
- // the touch pad. This ensures that a view will receive a fresh hover enter
- // event after a tap.
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mViewport.displayId, 1, &pointerProperties, &pointerCoords,
- 0, 0, mPointerGesture.downTime);
- getListener()->notifyMotion(&args);
- }
-
- // Update state.
- mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
- if (!down) {
- mPointerGesture.lastGestureIdBits.clear();
- } else {
- mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
- for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- mPointerGesture.lastGestureProperties[index].copyFrom(
- mPointerGesture.currentGestureProperties[index]);
- mPointerGesture.lastGestureCoords[index].copyFrom(
- mPointerGesture.currentGestureCoords[index]);
- mPointerGesture.lastGestureIdToIndex[id] = index;
- }
- }
-}
-
-void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
- // Cancel previously dispatches pointers.
- if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentButtonState;
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_CANCEL, 0, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- mPointerGesture.lastGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime);
- }
-
- // Reset the current pointer gesture.
- mPointerGesture.reset();
- mPointerVelocityControl.reset();
-
- // Remove any current spots.
- if (mPointerController != NULL) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- mPointerController->clearSpots();
- }
-}
-
-bool TouchInputMapper::preparePointerGestures(nsecs_t when,
- bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) {
- *outCancelPreviousGesture = false;
- *outFinishPreviousGesture = false;
-
- // Handle TAP timeout.
- if (isTimeout) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Processing timeout");
-#endif
-
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
- if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- // The tap/drag timeout has not yet expired.
- getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime
- + mConfig.pointerGestureTapDragInterval);
- } else {
- // The tap is finished.
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP finished");
-#endif
- *outFinishPreviousGesture = true;
-
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
- mPointerGesture.currentGestureIdBits.clear();
-
- mPointerVelocityControl.reset();
- return true;
- }
- }
-
- // We did not handle this timeout.
- return false;
- }
-
- const uint32_t currentFingerCount = mCurrentFingerIdBits.count();
- const uint32_t lastFingerCount = mLastFingerIdBits.count();
-
- // Update the velocity tracker.
- {
- VelocityTracker::Position positions[MAX_POINTERS];
- uint32_t count = 0;
- for (BitSet32 idBits(mCurrentFingerIdBits); !idBits.isEmpty(); count++) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
- positions[count].x = pointer.x * mPointerXMovementScale;
- positions[count].y = pointer.y * mPointerYMovementScale;
- }
- mPointerGesture.velocityTracker.addMovement(when,
- mCurrentFingerIdBits, positions);
- }
-
- // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
- // to NEUTRAL, then we should not generate tap event.
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER
- && mPointerGesture.lastGestureMode != PointerGesture::TAP
- && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
- mPointerGesture.resetTap();
- }
-
- // Pick a new active touch id if needed.
- // Choose an arbitrary pointer that just went down, if there is one.
- // Otherwise choose an arbitrary remaining pointer.
- // This guarantees we always have an active touch id when there is at least one pointer.
- // We keep the same active touch id for as long as possible.
- bool activeTouchChanged = false;
- int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
- int32_t activeTouchId = lastActiveTouchId;
- if (activeTouchId < 0) {
- if (!mCurrentFingerIdBits.isEmpty()) {
- activeTouchChanged = true;
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentFingerIdBits.firstMarkedBit();
- mPointerGesture.firstTouchTime = when;
- }
- } else if (!mCurrentFingerIdBits.hasBit(activeTouchId)) {
- activeTouchChanged = true;
- if (!mCurrentFingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentFingerIdBits.firstMarkedBit();
- } else {
- activeTouchId = mPointerGesture.activeTouchId = -1;
- }
- }
-
- // Determine whether we are in quiet time.
- bool isQuietTime = false;
- if (activeTouchId < 0) {
- mPointerGesture.resetQuietTime();
- } else {
- isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
- if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
- || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
- || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)
- && currentFingerCount < 2) {
- // Enter quiet time when exiting swipe or freeform state.
- // This is to prevent accidentally entering the hover state and flinging the
- // pointer when finishing a swipe and there is still one pointer left onscreen.
- isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
- && currentFingerCount >= 2
- && !isPointerDown(mCurrentButtonState)) {
- // Enter quiet time when releasing the button and there are still two or more
- // fingers down. This may indicate that one finger was used to press the button
- // but it has not gone up yet.
- isQuietTime = true;
- }
- if (isQuietTime) {
- mPointerGesture.quietTime = when;
- }
- }
- }
-
- // Switch states based on button and pointer state.
- if (isQuietTime) {
- // Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
- ALOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
- + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
- *outFinishPreviousGesture = true;
- }
-
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::QUIET;
- mPointerGesture.currentGestureIdBits.clear();
-
- mPointerVelocityControl.reset();
- } else if (isPointerDown(mCurrentButtonState)) {
- // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
- // The pointer follows the active touch point.
- // Emit DOWN, MOVE, UP events at the pointer location.
- //
- // Only the active touch matters; other fingers are ignored. This policy helps
- // to handle the case where the user places a second finger on the touch pad
- // to apply the necessary force to depress an integrated button below the surface.
- // We don't want the second finger to be delivered to applications.
- //
- // For this to work well, we need to make sure to track the pointer that is really
- // active. If the user first puts one finger down to click then adds another
- // finger to drag then the active pointer should switch to the finger that is
- // being dragged.
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
- "currentFingerCount=%d", activeTouchId, currentFingerCount);
-#endif
- // Reset state when just starting.
- if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
- *outFinishPreviousGesture = true;
- mPointerGesture.activeGestureId = 0;
- }
-
- // Switch pointers if needed.
- // Find the fastest pointer and follow it.
- if (activeTouchId >= 0 && currentFingerCount > 1) {
- int32_t bestId = -1;
- float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
- for (BitSet32 idBits(mCurrentFingerIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- float vx, vy;
- if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
- float speed = hypotf(vx, vy);
- if (speed > bestSpeed) {
- bestId = id;
- bestSpeed = speed;
- }
- }
- }
- if (bestId >= 0 && bestId != activeTouchId) {
- mPointerGesture.activeTouchId = activeTouchId = bestId;
- activeTouchChanged = true;
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
- "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
-#endif
- }
- }
-
- if (activeTouchId >= 0 && mLastFingerIdBits.hasBit(activeTouchId)) {
- const RawPointerData::Pointer& currentPointer =
- mCurrentRawPointerData.pointerForId(activeTouchId);
- const RawPointerData::Pointer& lastPointer =
- mLastRawPointerData.pointerForId(activeTouchId);
- float deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
- float deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- // Move the pointer using a relative motion.
- // When using spots, the click will occur at the position of the anchor
- // spot and all other spots will move there.
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (currentFingerCount == 0) {
- // Case 3. No fingers down and button is not pressed. (NEUTRAL)
- if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
- *outFinishPreviousGesture = true;
- }
-
- // Watch for taps coming out of HOVER or TAP_DRAG mode.
- // Checking for taps after TAP_DRAG allows us to detect double-taps.
- bool tapped = false;
- if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
- || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
- && lastFingerCount == 1) {
- if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
- && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP");
-#endif
-
- mPointerGesture.tapUpTime = when;
- getContext()->requestTimeoutAtTime(when
- + mConfig.pointerGestureTapDragInterval);
-
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.currentGestureMode = PointerGesture::TAP;
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(
- mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[
- mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id =
- mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
- tapped = true;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
- }
- } else {
-#if DEBUG_GESTURES
- if (mPointerGesture.tapDownTime != LLONG_MIN) {
- ALOGD("Gestures: Not a TAP, %0.3fms since down",
- (when - mPointerGesture.tapDownTime) * 0.000001f);
- } else {
- ALOGD("Gestures: Not a TAP, incompatible mode transitions");
- }
-#endif
- }
- }
-
- mPointerVelocityControl.reset();
-
- if (!tapped) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: NEUTRAL");
-#endif
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
- mPointerGesture.currentGestureIdBits.clear();
- }
- } else if (currentFingerCount == 1) {
- // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
- // The pointer follows the active touch point.
- // When in HOVER, emit HOVER_MOVE events at the pointer location.
- // When in TAP_DRAG, emit MOVE events at the pointer location.
- ALOG_ASSERT(activeTouchId >= 0);
-
- mPointerGesture.currentGestureMode = PointerGesture::HOVER;
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
- if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
- && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
- }
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
- (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
- }
- } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
- }
-
- if (mLastFingerIdBits.hasBit(activeTouchId)) {
- const RawPointerData::Pointer& currentPointer =
- mCurrentRawPointerData.pointerForId(activeTouchId);
- const RawPointerData::Pointer& lastPointer =
- mLastRawPointerData.pointerForId(activeTouchId);
- float deltaX = (currentPointer.x - lastPointer.x)
- * mPointerXMovementScale;
- float deltaY = (currentPointer.y - lastPointer.y)
- * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- // Move the pointer using a relative motion.
- // When using spots, the hover or drag will occur at the position of the anchor spot.
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- bool down;
- if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP_DRAG");
-#endif
- down = true;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: HOVER");
-#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
- *outFinishPreviousGesture = true;
- }
- mPointerGesture.activeGestureId = 0;
- down = false;
- }
-
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- down ? 1.0f : 0.0f);
-
- if (lastFingerCount == 0 && currentFingerCount != 0) {
- mPointerGesture.resetTap();
- mPointerGesture.tapDownTime = when;
- mPointerGesture.tapX = x;
- mPointerGesture.tapY = y;
- }
- } else {
- // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
- // We need to provide feedback for each finger that goes down so we cannot wait
- // for the fingers to move before deciding what to do.
- //
- // The ambiguous case is deciding what to do when there are two fingers down but they
- // have not moved enough to determine whether they are part of a drag or part of a
- // freeform gesture, or just a press or long-press at the pointer location.
- //
- // When there are two fingers we start with the PRESS hypothesis and we generate a
- // down at the pointer location.
- //
- // When the two fingers move enough or when additional fingers are added, we make
- // a decision to transition into SWIPE or FREEFORM mode accordingly.
- ALOG_ASSERT(activeTouchId >= 0);
-
- bool settled = when >= mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
- && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
- && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
- *outFinishPreviousGesture = true;
- } else if (!settled && currentFingerCount > lastFingerCount) {
- // Additional pointers have gone down but not yet settled.
- // Reset the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval - when)
- * 0.000001f);
-#endif
- *outCancelPreviousGesture = true;
- } else {
- // Continue previous gesture.
- mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
- }
-
- if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::PRESS;
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.referenceIdBits.clear();
- mPointerVelocityControl.reset();
-
- // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval - when)
- * 0.000001f);
-#endif
- mCurrentRawPointerData.getCentroidOfTouchingPointers(
- &mPointerGesture.referenceTouchX,
- &mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
-
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentFingerIdBits.value
- & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentFingerIdBits;
-
- // Add delta for all fingers and calculate a common movement delta.
- float commonDeltaX = 0, commonDeltaY = 0;
- BitSet32 commonIdBits(mLastFingerIdBits.value
- & mCurrentFingerIdBits.value);
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- bool first = (idBits == commonIdBits);
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& cpd = mCurrentRawPointerData.pointerForId(id);
- const RawPointerData::Pointer& lpd = mLastRawPointerData.pointerForId(id);
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
- }
-
- // Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
- float dist[MAX_POINTER_ID + 1];
- int32_t distOverThreshold = 0;
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- dist[id] = hypotf(delta.dx * mPointerXZoomScale,
- delta.dy * mPointerYZoomScale);
- if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
- distOverThreshold += 1;
- }
- }
-
- // Only transition when at least two pointers have moved further than
- // the minimum distance threshold.
- if (distOverThreshold >= 2) {
- if (currentFingerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else {
- // There are exactly two pointers.
- BitSet32 idBits(mCurrentFingerIdBits);
- uint32_t id1 = idBits.clearFirstMarkedBit();
- uint32_t id2 = idBits.firstMarkedBit();
- const RawPointerData::Pointer& p1 = mCurrentRawPointerData.pointerForId(id1);
- const RawPointerData::Pointer& p2 = mCurrentRawPointerData.pointerForId(id2);
- float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
- if (mutualDistance > mPointerGestureMaxSwipeWidth) {
- // There are two pointers but they are too far apart for a SWIPE,
- // switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- float dist1 = dist[id1];
- float dist2 = dist[id2];
- if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
- && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
- // Calculate the dot product of the displacement vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
- PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
- PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
- float dx1 = delta1.dx * mPointerXZoomScale;
- float dy1 = delta1.dy * mPointerYZoomScale;
- float dx2 = delta2.dx * mPointerXZoomScale;
- float dy2 = delta2.dy * mPointerYZoomScale;
- float dot = dx1 * dx2 + dy1 * dy2;
- float cosine = dot / (dist1 * dist2); // denominator always > 0
- if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance,
- dist2, mConfig.pointerGestureMultitouchMinDistance,
- cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance,
- dist2, mConfig.pointerGestureMultitouchMinDistance,
- cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- }
- }
- }
- }
- }
- } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
- // Switch from SWIPE to FREEFORM if additional pointers go down.
- // Cancel previous gesture.
- if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- }
- }
-
- // Move the reference points based on the overall group motion of the fingers
- // except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
- && (commonDeltaX || commonDeltaY)) {
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
-
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mPointerXMovementScale;
- commonDeltaY *= mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
- mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
- }
-
- // Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS
- || mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
- // PRESS or SWIPE mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
- // FREEFORM mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
-
- BitSet32 mappedTouchIdBits;
- BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
- // Initially, assign the active gesture id to the active touch point
- // if there is one. No other touch id bits are mapped yet.
- if (!*outCancelPreviousGesture) {
- mappedTouchIdBits.markBit(activeTouchId);
- usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
- mPointerGesture.activeGestureId;
- } else {
- mPointerGesture.activeGestureId = -1;
- }
- } else {
- // Otherwise, assume we mapped all touches from the previous frame.
- // Reuse all mappings that are still applicable.
- mappedTouchIdBits.value = mLastFingerIdBits.value
- & mCurrentFingerIdBits.value;
- usedGestureIdBits = mPointerGesture.lastGestureIdBits;
-
- // Check whether we need to choose a new active gesture id because the
- // current went went up.
- for (BitSet32 upTouchIdBits(mLastFingerIdBits.value
- & ~mCurrentFingerIdBits.value);
- !upTouchIdBits.isEmpty(); ) {
- uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
- uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
- if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
- mPointerGesture.activeGestureId = -1;
- break;
- }
- }
- }
-
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM follow up "
- "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
- "activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-#endif
-
- BitSet32 idBits(mCurrentFingerIdBits);
- for (uint32_t i = 0; i < currentFingerCount; i++) {
- uint32_t touchId = idBits.clearFirstMarkedBit();
- uint32_t gestureId;
- if (!mappedTouchIdBits.hasBit(touchId)) {
- gestureId = usedGestureIdBits.markFirstUnmarkedBit();
- mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
- } else {
- gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
- }
- mPointerGesture.currentGestureIdBits.markBit(gestureId);
- mPointerGesture.currentGestureIdToIndex[gestureId] = i;
-
- const RawPointerData::Pointer& pointer =
- mCurrentRawPointerData.pointerForId(touchId);
- float deltaX = (pointer.x - mPointerGesture.referenceTouchX)
- * mPointerXZoomScale;
- float deltaY = (pointer.y - mPointerGesture.referenceTouchY)
- * mPointerYZoomScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-
- mPointerGesture.currentGestureProperties[i].clear();
- mPointerGesture.currentGestureProperties[i].id = gestureId;
- mPointerGesture.currentGestureProperties[i].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[i].clear();
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX);
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY);
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- }
-
- if (mPointerGesture.activeGestureId < 0) {
- mPointerGesture.activeGestureId =
- mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM new "
- "activeGestureId=%d", mPointerGesture.activeGestureId);
-#endif
- }
- }
- }
-
- mPointerController->setButtonState(mCurrentButtonState);
-
-#if DEBUG_GESTURES
- ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
- "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
- "lastGestureMode=%d, lastGestureIdBits=0x%08x",
- toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
- mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
- mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
- for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
- ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType,
- coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
- for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
- ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType,
- coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
-#endif
- return true;
-}
-
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- bool down, hovering;
- if (!mCurrentStylusIdBits.isEmpty()) {
- uint32_t id = mCurrentStylusIdBits.firstMarkedBit();
- uint32_t index = mCurrentCookedPointerData.idToIndex[id];
- float x = mCurrentCookedPointerData.pointerCoords[index].getX();
- float y = mCurrentCookedPointerData.pointerCoords[index].getY();
- mPointerController->setPosition(x, y);
-
- hovering = mCurrentCookedPointerData.hoveringIdBits.hasBit(id);
- down = !hovering;
-
- mPointerController->getPosition(&x, &y);
- mPointerSimple.currentCoords.copyFrom(mCurrentCookedPointerData.pointerCoords[index]);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerSimple.currentProperties.id = 0;
- mPointerSimple.currentProperties.toolType =
- mCurrentCookedPointerData.pointerProperties[index].toolType;
- } else {
- down = false;
- hovering = false;
- }
-
- dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
-}
-
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- bool down, hovering;
- if (!mCurrentMouseIdBits.isEmpty()) {
- uint32_t id = mCurrentMouseIdBits.firstMarkedBit();
- uint32_t currentIndex = mCurrentRawPointerData.idToIndex[id];
- if (mLastMouseIdBits.hasBit(id)) {
- uint32_t lastIndex = mCurrentRawPointerData.idToIndex[id];
- float deltaX = (mCurrentRawPointerData.pointers[currentIndex].x
- - mLastRawPointerData.pointers[lastIndex].x)
- * mPointerXMovementScale;
- float deltaY = (mCurrentRawPointerData.pointers[currentIndex].y
- - mLastRawPointerData.pointers[lastIndex].y)
- * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- down = isPointerDown(mCurrentButtonState);
- hovering = !down;
-
- float x, y;
- mPointerController->getPosition(&x, &y);
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedPointerData.pointerCoords[currentIndex]);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- hovering ? 0.0f : 1.0f);
- mPointerSimple.currentProperties.id = 0;
- mPointerSimple.currentProperties.toolType =
- mCurrentCookedPointerData.pointerProperties[currentIndex].toolType;
- } else {
- mPointerVelocityControl.reset();
-
- down = false;
- hovering = false;
- }
-
- dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
-
- mPointerVelocityControl.reset();
-}
-
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
- bool down, bool hovering) {
- int32_t metaState = getContext()->getGlobalMetaState();
-
- if (mPointerController != NULL) {
- if (down || hovering) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
- mPointerController->clearSpots();
- mPointerController->setButtonState(mCurrentButtonState);
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
- }
-
- if (mPointerSimple.down && !down) {
- mPointerSimple.down = false;
-
- // Send up.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_UP, 0, metaState, mLastButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- if (mPointerSimple.hovering && !hovering) {
- mPointerSimple.hovering = false;
-
- // Send hover exit.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- if (down) {
- if (!mPointerSimple.down) {
- mPointerSimple.down = true;
- mPointerSimple.downTime = when;
-
- // Send down.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_DOWN, 0, metaState, mCurrentButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- // Send move.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, mCurrentButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- if (hovering) {
- if (!mPointerSimple.hovering) {
- mPointerSimple.hovering = true;
-
- // Send hover enter.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- // Send hover move.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- if (mCurrentRawVScroll || mCurrentRawHScroll) {
- float vscroll = mCurrentRawVScroll;
- float hscroll = mCurrentRawHScroll;
- mWheelYVelocityControl.move(when, NULL, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, NULL);
-
- // Send scroll.
- PointerCoords pointerCoords;
- pointerCoords.copyFrom(mPointerSimple.currentCoords);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, metaState, mCurrentButtonState, 0,
- mViewport.displayId,
- 1, &mPointerSimple.currentProperties, &pointerCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
- getListener()->notifyMotion(&args);
- }
-
- // Save state.
- if (down || hovering) {
- mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
- mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
- } else {
- mPointerSimple.reset();
- }
-}
-
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- dispatchPointerSimple(when, policyFlags, false, false);
-}
-
-void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties, const PointerCoords* coords,
- const uint32_t* idToIndex, BitSet32 idBits,
- int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {
- PointerCoords pointerCoords[MAX_POINTERS];
- PointerProperties pointerProperties[MAX_POINTERS];
- uint32_t pointerCount = 0;
- while (!idBits.isEmpty()) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = idToIndex[id];
- pointerProperties[pointerCount].copyFrom(properties[index]);
- pointerCoords[pointerCount].copyFrom(coords[index]);
-
- if (changedId >= 0 && id == uint32_t(changedId)) {
- action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- }
-
- pointerCount += 1;
- }
-
- ALOG_ASSERT(pointerCount != 0);
-
- if (changedId >= 0 && pointerCount == 1) {
- // Replace initial down and final up action.
- // We can compare the action without masking off the changed pointer index
- // because we know the index is 0.
- if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
- action = AMOTION_EVENT_ACTION_DOWN;
- } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
- action = AMOTION_EVENT_ACTION_UP;
- } else {
- // Can't happen.
- ALOG_ASSERT(false);
- }
- }
-
- NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
- action, flags, metaState, buttonState, edgeFlags,
- mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
- xPrecision, yPrecision, downTime);
- getListener()->notifyMotion(&args);
-}
-
-bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
- const PointerCoords* inCoords, const uint32_t* inIdToIndex,
- PointerProperties* outProperties, PointerCoords* outCoords, const uint32_t* outIdToIndex,
- BitSet32 idBits) const {
- bool changed = false;
- while (!idBits.isEmpty()) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t inIndex = inIdToIndex[id];
- uint32_t outIndex = outIdToIndex[id];
-
- const PointerProperties& curInProperties = inProperties[inIndex];
- const PointerCoords& curInCoords = inCoords[inIndex];
- PointerProperties& curOutProperties = outProperties[outIndex];
- PointerCoords& curOutCoords = outCoords[outIndex];
-
- if (curInProperties != curOutProperties) {
- curOutProperties.copyFrom(curInProperties);
- changed = true;
- }
-
- if (curInCoords != curOutCoords) {
- curOutCoords.copyFrom(curInCoords);
- changed = true;
- }
- }
- return changed;
-}
-
-void TouchInputMapper::fadePointer() {
- if (mPointerController != NULL) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
-}
-
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
- return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue
- && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue;
-}
-
-const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(
- int32_t x, int32_t y) {
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
-
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y,
- virtualKey.keyCode, virtualKey.scanCode,
- virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
-#endif
-
- if (virtualKey.isHit(x, y)) {
- return & virtualKey;
- }
- }
-
- return NULL;
-}
-
-void TouchInputMapper::assignPointerIds() {
- uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
- uint32_t lastPointerCount = mLastRawPointerData.pointerCount;
-
- mCurrentRawPointerData.clearIdBits();
-
- if (currentPointerCount == 0) {
- // No pointers to assign.
- return;
- }
-
- if (lastPointerCount == 0) {
- // All pointers are new.
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- uint32_t id = i;
- mCurrentRawPointerData.pointers[i].id = id;
- mCurrentRawPointerData.idToIndex[id] = i;
- mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(i));
- }
- return;
- }
-
- if (currentPointerCount == 1 && lastPointerCount == 1
- && mCurrentRawPointerData.pointers[0].toolType
- == mLastRawPointerData.pointers[0].toolType) {
- // Only one pointer and no change in count so it must have the same id as before.
- uint32_t id = mLastRawPointerData.pointers[0].id;
- mCurrentRawPointerData.pointers[0].id = id;
- mCurrentRawPointerData.idToIndex[id] = 0;
- mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(0));
- return;
- }
-
- // General case.
- // We build a heap of squared euclidean distances between current and last pointers
- // associated with the current and last pointer indices. Then, we find the best
- // match (by distance) for each current pointer.
- // The pointers must have the same tool type but it is possible for them to
- // transition from hovering to touching or vice-versa while retaining the same id.
- PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
- uint32_t heapSize = 0;
- for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
- currentPointerIndex++) {
- for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
- lastPointerIndex++) {
- const RawPointerData::Pointer& currentPointer =
- mCurrentRawPointerData.pointers[currentPointerIndex];
- const RawPointerData::Pointer& lastPointer =
- mLastRawPointerData.pointers[lastPointerIndex];
- if (currentPointer.toolType == lastPointer.toolType) {
- int64_t deltaX = currentPointer.x - lastPointer.x;
- int64_t deltaY = currentPointer.y - lastPointer.y;
-
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
- // Insert new element into the heap (sift up).
- heap[heapSize].currentPointerIndex = currentPointerIndex;
- heap[heapSize].lastPointerIndex = lastPointerIndex;
- heap[heapSize].distance = distance;
- heapSize += 1;
- }
- }
- }
-
- // Heapify
- for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
- startIndex -= 1;
- for (uint32_t parentIndex = startIndex; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
-
- // Pull matches out by increasing order of distance.
- // To avoid reassigning pointers that have already been matched, the loop keeps track
- // of which last and current pointers have been matched using the matchedXXXBits variables.
- // It also tracks the used pointer id bits.
- BitSet32 matchedLastBits(0);
- BitSet32 matchedCurrentBits(0);
- BitSet32 usedIdBits(0);
- bool first = true;
- for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
- while (heapSize > 0) {
- if (first) {
- // The first time through the loop, we just consume the root element of
- // the heap (the one with smallest distance).
- first = false;
- } else {
- // Previous iterations consumed the root element of the heap.
- // Pop root element off of the heap (sift down).
- heap[0] = heap[heapSize];
- for (uint32_t parentIndex = 0; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
- }
-
- heapSize -= 1;
-
- uint32_t currentPointerIndex = heap[0].currentPointerIndex;
- if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
- uint32_t lastPointerIndex = heap[0].lastPointerIndex;
- if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
- matchedCurrentBits.markBit(currentPointerIndex);
- matchedLastBits.markBit(lastPointerIndex);
-
- uint32_t id = mLastRawPointerData.pointers[lastPointerIndex].id;
- mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
- mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
- mCurrentRawPointerData.markIdBit(id,
- mCurrentRawPointerData.isHovering(currentPointerIndex));
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
- break;
- }
- }
-
- // Assign fresh ids to pointers that were not matched in the process.
- for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
- uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
- uint32_t id = usedIdBits.markFirstUnmarkedBit();
-
- mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
- mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
- mCurrentRawPointerData.markIdBit(id,
- mCurrentRawPointerData.isHovering(currentPointerIndex));
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - assigned: cur=%d, id=%d",
- currentPointerIndex, id);
-#endif
- }
-}
-
-int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
- return AKEY_STATE_VIRTUAL;
- }
-
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
- if (virtualKey.keyCode == keyCode) {
- return AKEY_STATE_UP;
- }
- }
-
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
- return AKEY_STATE_VIRTUAL;
- }
-
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
- if (virtualKey.scanCode == scanCode) {
- return AKEY_STATE_UP;
- }
- }
-
- return AKEY_STATE_UNKNOWN;
-}
-
-bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
-
- for (size_t i = 0; i < numCodes; i++) {
- if (virtualKey.keyCode == keyCodes[i]) {
- outFlags[i] = 1;
- }
- }
- }
-
- return true;
-}
-
-
-// --- SingleTouchInputMapper ---
-
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
- TouchInputMapper(device) {
-}
-
-SingleTouchInputMapper::~SingleTouchInputMapper() {
-}
-
-void SingleTouchInputMapper::reset(nsecs_t when) {
- mSingleTouchMotionAccumulator.reset(getDevice());
-
- TouchInputMapper::reset(when);
-}
-
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
-
- mSingleTouchMotionAccumulator.process(rawEvent);
-}
-
-void SingleTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
- if (mTouchButtonAccumulator.isToolActive()) {
- mCurrentRawPointerData.pointerCount = 1;
- mCurrentRawPointerData.idToIndex[0] = 0;
-
- bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
- && (mTouchButtonAccumulator.isHovering()
- || (mRawPointerAxes.pressure.valid
- && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
- mCurrentRawPointerData.markIdBit(0, isHovering);
-
- RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[0];
- outPointer.id = 0;
- outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
- outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
- outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
- outPointer.touchMajor = 0;
- outPointer.touchMinor = 0;
- outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
- outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
- outPointer.orientation = 0;
- outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();
- outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();
- outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();
- outPointer.toolType = mTouchButtonAccumulator.getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- outPointer.isHovering = isHovering;
- }
-}
-
-void SingleTouchInputMapper::configureRawPointerAxes() {
- TouchInputMapper::configureRawPointerAxes();
-
- getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
- getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
-}
-
-bool SingleTouchInputMapper::hasStylus() const {
- return mTouchButtonAccumulator.hasStylus();
-}
-
-
-// --- MultiTouchInputMapper ---
-
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
- TouchInputMapper(device) {
-}
-
-MultiTouchInputMapper::~MultiTouchInputMapper() {
-}
-
-void MultiTouchInputMapper::reset(nsecs_t when) {
- mMultiTouchMotionAccumulator.reset(getDevice());
-
- mPointerIdBits.clear();
-
- TouchInputMapper::reset(when);
-}
-
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
-
- mMultiTouchMotionAccumulator.process(rawEvent);
-}
-
-void MultiTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
- size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
- size_t outCount = 0;
- BitSet32 newPointerIdBits;
-
- for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
- const MultiTouchMotionAccumulator::Slot* inSlot =
- mMultiTouchMotionAccumulator.getSlot(inIndex);
- if (!inSlot->isInUse()) {
- continue;
- }
-
- if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
- ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
- "ignoring the rest.",
- getDeviceName().string(), MAX_POINTERS);
-#endif
- break; // too many fingers!
- }
-
- RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[outCount];
- outPointer.x = inSlot->getX();
- outPointer.y = inSlot->getY();
- outPointer.pressure = inSlot->getPressure();
- outPointer.touchMajor = inSlot->getTouchMajor();
- outPointer.touchMinor = inSlot->getTouchMinor();
- outPointer.toolMajor = inSlot->getToolMajor();
- outPointer.toolMinor = inSlot->getToolMinor();
- outPointer.orientation = inSlot->getOrientation();
- outPointer.distance = inSlot->getDistance();
- outPointer.tiltX = 0;
- outPointer.tiltY = 0;
-
- outPointer.toolType = inSlot->getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = mTouchButtonAccumulator.getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- }
-
- bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
- && (mTouchButtonAccumulator.isHovering()
- || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
- outPointer.isHovering = isHovering;
-
- // Assign pointer id using tracking id if available.
- if (*outHavePointerIds) {
- int32_t trackingId = inSlot->getTrackingId();
- int32_t id = -1;
- if (trackingId >= 0) {
- for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
- uint32_t n = idBits.clearFirstMarkedBit();
- if (mPointerTrackingIdMap[n] == trackingId) {
- id = n;
- }
- }
-
- if (id < 0 && !mPointerIdBits.isFull()) {
- id = mPointerIdBits.markFirstUnmarkedBit();
- mPointerTrackingIdMap[id] = trackingId;
- }
- }
- if (id < 0) {
- *outHavePointerIds = false;
- mCurrentRawPointerData.clearIdBits();
- newPointerIdBits.clear();
- } else {
- outPointer.id = id;
- mCurrentRawPointerData.idToIndex[id] = outCount;
- mCurrentRawPointerData.markIdBit(id, isHovering);
- newPointerIdBits.markBit(id);
- }
- }
-
- outCount += 1;
- }
-
- mCurrentRawPointerData.pointerCount = outCount;
- mPointerIdBits = newPointerIdBits;
-
- mMultiTouchMotionAccumulator.finishSync();
-}
-
-void MultiTouchInputMapper::configureRawPointerAxes() {
- TouchInputMapper::configureRawPointerAxes();
-
- getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
- getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
- getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
- getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
-
- if (mRawPointerAxes.trackingId.valid
- && mRawPointerAxes.slot.valid
- && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
- size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
- if (slotCount > MAX_SLOTS) {
- ALOGW("MultiTouch Device %s reported %zu slots but the framework "
- "only supports a maximum of %zu slots at this time.",
- getDeviceName().string(), slotCount, MAX_SLOTS);
- slotCount = MAX_SLOTS;
- }
- mMultiTouchMotionAccumulator.configure(getDevice(),
- slotCount, true /*usingSlotsProtocol*/);
- } else {
- mMultiTouchMotionAccumulator.configure(getDevice(),
- MAX_POINTERS, false /*usingSlotsProtocol*/);
- }
-}
-
-bool MultiTouchInputMapper::hasStylus() const {
- return mMultiTouchMotionAccumulator.hasStylus()
- || mTouchButtonAccumulator.hasStylus();
-}
-
-
-// --- JoystickInputMapper ---
-
-JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
- InputMapper(device) {
-}
-
-JoystickInputMapper::~JoystickInputMapper() {
-}
-
-uint32_t JoystickInputMapper::getSources() {
- return AINPUT_SOURCE_JOYSTICK;
-}
-
-void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- for (size_t i = 0; i < mAxes.size(); i++) {
- const Axis& axis = mAxes.valueAt(i);
- addMotionRange(axis.axisInfo.axis, axis, info);
-
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- addMotionRange(axis.axisInfo.highAxis, axis, info);
-
- }
- }
-}
-
-void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis,
- InputDeviceInfo* info) {
- info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- /* In order to ease the transition for developers from using the old axes
- * to the newer, more semantically correct axes, we'll continue to register
- * the old axes as duplicates of their corresponding new ones. */
- int32_t compatAxis = getCompatAxis(axisId);
- if (compatAxis >= 0) {
- info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- }
-}
-
-/* A mapping from axes the joystick actually has to the axes that should be
- * artificially created for compatibility purposes.
- * Returns -1 if no compatibility axis is needed. */
-int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
- switch(axis) {
- case AMOTION_EVENT_AXIS_LTRIGGER:
- return AMOTION_EVENT_AXIS_BRAKE;
- case AMOTION_EVENT_AXIS_RTRIGGER:
- return AMOTION_EVENT_AXIS_GAS;
- }
- return -1;
-}
-
-void JoystickInputMapper::dump(String8& dump) {
- dump.append(INDENT2 "Joystick Input Mapper:\n");
-
- dump.append(INDENT3 "Axes:\n");
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- const char* label = getAxisLabel(axis.axisInfo.axis);
- if (label) {
- dump.appendFormat(INDENT4 "%s", label);
- } else {
- dump.appendFormat(INDENT4 "%d", axis.axisInfo.axis);
- }
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- label = getAxisLabel(axis.axisInfo.highAxis);
- if (label) {
- dump.appendFormat(" / %s (split at %d)", label, axis.axisInfo.splitValue);
- } else {
- dump.appendFormat(" / %d (split at %d)", axis.axisInfo.highAxis,
- axis.axisInfo.splitValue);
- }
- } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
- dump.append(" (invert)");
- }
-
- dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, "
- "highScale=%0.5f, highOffset=%0.5f\n",
- axis.scale, axis.offset, axis.highScale, axis.highOffset);
- dump.appendFormat(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
- "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
- mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
- axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution);
- }
-}
-
-void JoystickInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- // Collect all axes.
- for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
- if (!(getAbsAxisUsage(abs, getDevice()->getClasses())
- & INPUT_DEVICE_CLASS_JOYSTICK)) {
- continue; // axis must be claimed by a different device
- }
-
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
- // Map axis.
- AxisInfo axisInfo;
- bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
- if (!explicitlyMapped) {
- // Axis is not explicitly mapped, will choose a generic axis later.
- axisInfo.mode = AxisInfo::MODE_NORMAL;
- axisInfo.axis = -1;
- }
-
- // Apply flat override.
- int32_t rawFlat = axisInfo.flatOverride < 0
- ? rawAxisInfo.flat : axisInfo.flatOverride;
-
- // Calculate scaling factors and limits.
- Axis axis;
- if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
- float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
- float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, 0.0f, highScale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else if (isCenteredAxis(axisInfo.axis)) {
- float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, offset, scale, offset,
- -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else {
- float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, 0.0f, scale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- }
-
- // To eliminate noise while the joystick is at rest, filter out small variations
- // in axis values up front.
- axis.filter = axis.flat * 0.25f;
-
- mAxes.add(abs, axis);
- }
- }
-
- // If there are too many axes, start dropping them.
- // Prefer to keep explicitly mapped axes.
- if (mAxes.size() > PointerCoords::MAX_AXES) {
- ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
- getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
- pruneAxes(true);
- pruneAxes(false);
- }
-
- // Assign generic axis ids to remaining axes.
- int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- if (axis.axisInfo.axis < 0) {
- while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
- && haveAxis(nextGenericAxisId)) {
- nextGenericAxisId += 1;
- }
-
- if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
- axis.axisInfo.axis = nextGenericAxisId;
- nextGenericAxisId += 1;
- } else {
- ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
- "have already been assigned to other axes.",
- getDeviceName().string(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i--);
- numAxes -= 1;
- }
- }
- }
- }
-}
-
-bool JoystickInputMapper::haveAxis(int32_t axisId) {
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- if (axis.axisInfo.axis == axisId
- || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT
- && axis.axisInfo.highAxis == axisId)) {
- return true;
- }
- }
- return false;
-}
-
-void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
- size_t i = mAxes.size();
- while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
- if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
- continue;
- }
- ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().string(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i);
- }
-}
-
-bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- case AMOTION_EVENT_AXIS_Y:
- case AMOTION_EVENT_AXIS_Z:
- case AMOTION_EVENT_AXIS_RX:
- case AMOTION_EVENT_AXIS_RY:
- case AMOTION_EVENT_AXIS_RZ:
- case AMOTION_EVENT_AXIS_HAT_X:
- case AMOTION_EVENT_AXIS_HAT_Y:
- case AMOTION_EVENT_AXIS_ORIENTATION:
- case AMOTION_EVENT_AXIS_RUDDER:
- case AMOTION_EVENT_AXIS_WHEEL:
- return true;
- default:
- return false;
- }
-}
-
-void JoystickInputMapper::reset(nsecs_t when) {
- // Recenter all axes.
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- axis.resetValue();
- }
-
- InputMapper::reset(when);
-}
-
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_ABS: {
- ssize_t index = mAxes.indexOfKey(rawEvent->code);
- if (index >= 0) {
- Axis& axis = mAxes.editValueAt(index);
- float newValue, highNewValue;
- switch (axis.axisInfo.mode) {
- case AxisInfo::MODE_INVERT:
- newValue = (axis.rawAxisInfo.maxValue - rawEvent->value)
- * axis.scale + axis.offset;
- highNewValue = 0.0f;
- break;
- case AxisInfo::MODE_SPLIT:
- if (rawEvent->value < axis.axisInfo.splitValue) {
- newValue = (axis.axisInfo.splitValue - rawEvent->value)
- * axis.scale + axis.offset;
- highNewValue = 0.0f;
- } else if (rawEvent->value > axis.axisInfo.splitValue) {
- newValue = 0.0f;
- highNewValue = (rawEvent->value - axis.axisInfo.splitValue)
- * axis.highScale + axis.highOffset;
- } else {
- newValue = 0.0f;
- highNewValue = 0.0f;
- }
- break;
- default:
- newValue = rawEvent->value * axis.scale + axis.offset;
- highNewValue = 0.0f;
- break;
- }
- axis.newValue = newValue;
- axis.highNewValue = highNewValue;
- }
- break;
- }
-
- case EV_SYN:
- switch (rawEvent->code) {
- case SYN_REPORT:
- sync(rawEvent->when, false /*force*/);
- break;
- }
- break;
- }
-}
-
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
- if (!filterAxes(force)) {
- return;
- }
-
- int32_t metaState = mContext->getGlobalMetaState();
- int32_t buttonState = 0;
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
- axis.highCurrentValue);
- }
- }
-
- // Moving a joystick axis should not wake the device because joysticks can
- // be fairly noisy even when not in use. On the other hand, pushing a gamepad
- // button will likely wake the device.
- // TODO: Use the input device configuration to control this behavior more finely.
- uint32_t policyFlags = 0;
-
- NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- ADISPLAY_ID_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, 0);
- getListener()->notifyMotion(&args);
-}
-
-void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords,
- int32_t axis, float value) {
- pointerCoords->setAxisValue(axis, value);
- /* In order to ease the transition for developers from using the old axes
- * to the newer, more semantically correct axes, we'll continue to produce
- * values for the old axes as mirrors of the value of their corresponding
- * new axes. */
- int32_t compatAxis = getCompatAxis(axis);
- if (compatAxis >= 0) {
- pointerCoords->setAxisValue(compatAxis, value);
- }
-}
-
-bool JoystickInputMapper::filterAxes(bool force) {
- bool atLeastOneSignificantChange = force;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- if (force || hasValueChangedSignificantly(axis.filter,
- axis.newValue, axis.currentValue, axis.min, axis.max)) {
- axis.currentValue = axis.newValue;
- atLeastOneSignificantChange = true;
- }
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- if (force || hasValueChangedSignificantly(axis.filter,
- axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) {
- axis.highCurrentValue = axis.highNewValue;
- atLeastOneSignificantChange = true;
- }
- }
- }
- return atLeastOneSignificantChange;
-}
-
-bool JoystickInputMapper::hasValueChangedSignificantly(
- float filter, float newValue, float currentValue, float min, float max) {
- if (newValue != currentValue) {
- // Filter out small changes in value unless the value is converging on the axis
- // bounds or center point. This is intended to reduce the amount of information
- // sent to applications by particularly noisy joysticks (such as PS3).
- if (fabs(newValue - currentValue) > filter
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min)
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max)
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
- return true;
- }
- }
- return false;
-}
-
-bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(
- float filter, float newValue, float currentValue, float thresholdValue) {
- float newDistance = fabs(newValue - thresholdValue);
- if (newDistance < filter) {
- float oldDistance = fabs(currentValue - thresholdValue);
- if (newDistance < oldDistance) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace android
diff --git a/libs/input/InputReader.h b/libs/input/InputReader.h
deleted file mode 100644
index c90d483..0000000
--- a/libs/input/InputReader.h
+++ /dev/null
@@ -1,1820 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _UI_INPUT_READER_H
-#define _UI_INPUT_READER_H
-
-#include "EventHub.h"
-#include "PointerController.h"
-#include "InputListener.h"
-
-#include <input/Input.h>
-#include <input/VelocityControl.h>
-#include <input/VelocityTracker.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/BitSet.h>
-
-#include <stddef.h>
-#include <unistd.h>
-
-// Maximum supported size of a vibration pattern.
-// Must be at least 2.
-#define MAX_VIBRATE_PATTERN_SIZE 100
-
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
-
-namespace android {
-
-class InputDevice;
-class InputMapper;
-
-/*
- * Describes how coordinates are mapped on a physical display.
- * See com.android.server.display.DisplayViewport.
- */
-struct DisplayViewport {
- int32_t displayId; // -1 if invalid
- int32_t orientation;
- int32_t logicalLeft;
- int32_t logicalTop;
- int32_t logicalRight;
- int32_t logicalBottom;
- int32_t physicalLeft;
- int32_t physicalTop;
- int32_t physicalRight;
- int32_t physicalBottom;
- int32_t deviceWidth;
- int32_t deviceHeight;
-
- DisplayViewport() :
- displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
- logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
- physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
- deviceWidth(0), deviceHeight(0) {
- }
-
- bool operator==(const DisplayViewport& other) const {
- return displayId == other.displayId
- && orientation == other.orientation
- && logicalLeft == other.logicalLeft
- && logicalTop == other.logicalTop
- && logicalRight == other.logicalRight
- && logicalBottom == other.logicalBottom
- && physicalLeft == other.physicalLeft
- && physicalTop == other.physicalTop
- && physicalRight == other.physicalRight
- && physicalBottom == other.physicalBottom
- && deviceWidth == other.deviceWidth
- && deviceHeight == other.deviceHeight;
- }
-
- bool operator!=(const DisplayViewport& other) const {
- return !(*this == other);
- }
-
- inline bool isValid() const {
- return displayId >= 0;
- }
-
- void setNonDisplayViewport(int32_t width, int32_t height) {
- displayId = ADISPLAY_ID_NONE;
- orientation = DISPLAY_ORIENTATION_0;
- logicalLeft = 0;
- logicalTop = 0;
- logicalRight = width;
- logicalBottom = height;
- physicalLeft = 0;
- physicalTop = 0;
- physicalRight = width;
- physicalBottom = height;
- deviceWidth = width;
- deviceHeight = height;
- }
-};
-
-/*
- * Input reader configuration.
- *
- * Specifies various options that modify the behavior of the input reader.
- */
-struct InputReaderConfiguration {
- // Describes changes that have occurred.
- enum {
- // The pointer speed changed.
- CHANGE_POINTER_SPEED = 1 << 0,
-
- // The pointer gesture control changed.
- CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
-
- // The display size or orientation changed.
- CHANGE_DISPLAY_INFO = 1 << 2,
-
- // The visible touches option changed.
- CHANGE_SHOW_TOUCHES = 1 << 3,
-
- // The keyboard layouts must be reloaded.
- CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
-
- // The device name alias supplied by the may have changed for some devices.
- CHANGE_DEVICE_ALIAS = 1 << 5,
-
- // All devices must be reopened.
- CHANGE_MUST_REOPEN = 1 << 31,
- };
-
- // Gets the amount of time to disable virtual keys after the screen is touched
- // in order to filter out accidental virtual key presses due to swiping gestures
- // or taps near the edge of the display. May be 0 to disable the feature.
- nsecs_t virtualKeyQuietTime;
-
- // The excluded device names for the platform.
- // Devices with these names will be ignored.
- Vector<String8> excludedDeviceNames;
-
- // Velocity control parameters for mouse pointer movements.
- VelocityControlParameters pointerVelocityControlParameters;
-
- // Velocity control parameters for mouse wheel movements.
- VelocityControlParameters wheelVelocityControlParameters;
-
- // True if pointer gestures are enabled.
- bool pointerGesturesEnabled;
-
- // Quiet time between certain pointer gesture transitions.
- // Time to allow for all fingers or buttons to settle into a stable state before
- // starting a new gesture.
- nsecs_t pointerGestureQuietInterval;
-
- // The minimum speed that a pointer must travel for us to consider switching the active
- // touch pointer to it during a drag. This threshold is set to avoid switching due
- // to noise from a finger resting on the touch pad (perhaps just pressing it down).
- float pointerGestureDragMinSwitchSpeed; // in pixels per second
-
- // Tap gesture delay time.
- // The time between down and up must be less than this to be considered a tap.
- nsecs_t pointerGestureTapInterval;
-
- // Tap drag gesture delay time.
- // The time between the previous tap's up and the next down must be less than
- // this to be considered a drag. Otherwise, the previous tap is finished and a
- // new tap begins.
- //
- // Note that the previous tap will be held down for this entire duration so this
- // interval must be shorter than the long press timeout.
- nsecs_t pointerGestureTapDragInterval;
-
- // The distance in pixels that the pointer is allowed to move from initial down
- // to up and still be called a tap.
- float pointerGestureTapSlop; // in pixels
-
- // Time after the first touch points go down to settle on an initial centroid.
- // This is intended to be enough time to handle cases where the user puts down two
- // fingers at almost but not quite exactly the same time.
- nsecs_t pointerGestureMultitouchSettleInterval;
-
- // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
- // at least two pointers have moved at least this far from their starting place.
- float pointerGestureMultitouchMinDistance; // in pixels
-
- // The transition from PRESS to SWIPE gesture mode can only occur when the
- // cosine of the angle between the two vectors is greater than or equal to than this value
- // which indicates that the vectors are oriented in the same direction.
- // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
- // (In exactly opposite directions, the cosine is -1.0.)
- float pointerGestureSwipeTransitionAngleCosine;
-
- // The transition from PRESS to SWIPE gesture mode can only occur when the
- // fingers are no more than this far apart relative to the diagonal size of
- // the touch pad. For example, a ratio of 0.5 means that the fingers must be
- // no more than half the diagonal size of the touch pad apart.
- float pointerGestureSwipeMaxWidthRatio;
-
- // The gesture movement speed factor relative to the size of the display.
- // Movement speed applies when the fingers are moving in the same direction.
- // Without acceleration, a full swipe of the touch pad diagonal in movement mode
- // will cover this portion of the display diagonal.
- float pointerGestureMovementSpeedRatio;
-
- // The gesture zoom speed factor relative to the size of the display.
- // Zoom speed applies when the fingers are mostly moving relative to each other
- // to execute a scale gesture or similar.
- // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
- // will cover this portion of the display diagonal.
- float pointerGestureZoomSpeedRatio;
-
- // True to show the location of touches on the touch screen as spots.
- bool showTouches;
-
- InputReaderConfiguration() :
- virtualKeyQuietTime(0),
- pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
- wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
- pointerGesturesEnabled(true),
- pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
- pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
- pointerGestureTapInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapSlop(10.0f), // 10 pixels
- pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinDistance(15), // 15 pixels
- pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
- pointerGestureSwipeMaxWidthRatio(0.25f),
- pointerGestureMovementSpeedRatio(0.8f),
- pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false) { }
-
- bool getDisplayInfo(bool external, DisplayViewport* outViewport) const;
- void setDisplayInfo(bool external, const DisplayViewport& viewport);
-
-private:
- DisplayViewport mInternalDisplay;
- DisplayViewport mExternalDisplay;
-};
-
-
-/*
- * Input reader policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI. This interface is also mocked in the unit tests.
- *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
- */
-class InputReaderPolicyInterface : public virtual RefBase {
-protected:
- InputReaderPolicyInterface() { }
- virtual ~InputReaderPolicyInterface() { }
-
-public:
- /* Gets the input reader configuration. */
- virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
-
- /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
-
- /* Notifies the input reader policy that some input devices have changed
- * and provides information about all current input devices.
- */
- virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
-
- /* Gets the keyboard layout for a particular input device. */
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
-
- /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
- virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
-};
-
-
-/* Processes raw input events and sends cooked event data to an input listener. */
-class InputReaderInterface : public virtual RefBase {
-protected:
- InputReaderInterface() { }
- virtual ~InputReaderInterface() { }
-
-public:
- /* Dumps the state of the input reader.
- *
- * This method may be called on any thread (usually by the input manager). */
- virtual void dump(String8& dump) = 0;
-
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
- virtual void monitor() = 0;
-
- /* Runs a single iteration of the processing loop.
- * Nominally reads and processes one incoming message from the EventHub.
- *
- * This method should be called on the input reader thread.
- */
- virtual void loopOnce() = 0;
-
- /* Gets information about all input devices.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
-
- /* Query current input state. */
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) = 0;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw) = 0;
-
- /* Determine whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
-
- /* Requests that a reconfiguration of all input devices.
- * The changes flag is a bitfield that indicates what has changed and whether
- * the input devices must all be reopened. */
- virtual void requestRefreshConfiguration(uint32_t changes) = 0;
-
- /* Controls the vibrator of a particular input device. */
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) = 0;
- virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
-};
-
-
-/* Internal interface used by individual input devices to access global input device state
- * and parameters maintained by the input reader.
- */
-class InputReaderContext {
-public:
- InputReaderContext() { }
- virtual ~InputReaderContext() { }
-
- virtual void updateGlobalMetaState() = 0;
- virtual int32_t getGlobalMetaState() = 0;
-
- virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
- virtual bool shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
-
- virtual void fadePointer() = 0;
-
- virtual void requestTimeoutAtTime(nsecs_t when) = 0;
- virtual int32_t bumpGeneration() = 0;
-
- virtual InputReaderPolicyInterface* getPolicy() = 0;
- virtual InputListenerInterface* getListener() = 0;
- virtual EventHubInterface* getEventHub() = 0;
-};
-
-
-/* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input listener. Some functions of the input reader, such as early
- * event filtering in low power states, are controlled by a separate policy object.
- *
- * The InputReader owns a collection of InputMappers. Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
- * components running on arbitrary threads. To keep things manageable, the InputReader
- * uses a single Mutex to guard its state. The Mutex may be held while calling into the
- * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
- */
-class InputReader : public InputReaderInterface {
-public:
- InputReader(const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener);
- virtual ~InputReader();
-
- virtual void dump(String8& dump);
- virtual void monitor();
-
- virtual void loopOnce();
-
- virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices);
-
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode);
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode);
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw);
-
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual void requestRefreshConfiguration(uint32_t changes);
-
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token);
- virtual void cancelVibrate(int32_t deviceId, int32_t token);
-
-protected:
- // These members are protected so they can be instrumented by test cases.
- virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes);
-
- class ContextImpl : public InputReaderContext {
- InputReader* mReader;
-
- public:
- ContextImpl(InputReader* reader);
-
- virtual void updateGlobalMetaState();
- virtual int32_t getGlobalMetaState();
- virtual void disableVirtualKeysUntil(nsecs_t time);
- virtual bool shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode);
- virtual void fadePointer();
- virtual void requestTimeoutAtTime(nsecs_t when);
- virtual int32_t bumpGeneration();
- virtual InputReaderPolicyInterface* getPolicy();
- virtual InputListenerInterface* getListener();
- virtual EventHubInterface* getEventHub();
- } mContext;
-
- friend class ContextImpl;
-
-private:
- Mutex mLock;
-
- Condition mReaderIsAliveCondition;
-
- sp<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<QueuedInputListener> mQueuedListener;
-
- InputReaderConfiguration mConfig;
-
- // The event queue.
- static const int EVENT_BUFFER_SIZE = 256;
- RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
-
- KeyedVector<int32_t, InputDevice*> mDevices;
-
- // low-level input event decoding and device management
- void processEventsLocked(const RawEvent* rawEvents, size_t count);
-
- void addDeviceLocked(nsecs_t when, int32_t deviceId);
- void removeDeviceLocked(nsecs_t when, int32_t deviceId);
- void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
- void timeoutExpiredLocked(nsecs_t when);
-
- void handleConfigurationChangedLocked(nsecs_t when);
-
- int32_t mGlobalMetaState;
- void updateGlobalMetaStateLocked();
- int32_t getGlobalMetaStateLocked();
-
- void fadePointerLocked();
-
- int32_t mGeneration;
- int32_t bumpGenerationLocked();
-
- void getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices);
-
- nsecs_t mDisableVirtualKeysTimeout;
- void disableVirtualKeysUntilLocked(nsecs_t time);
- bool shouldDropVirtualKeyLocked(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode);
-
- nsecs_t mNextTimeout;
- void requestTimeoutAtTimeLocked(nsecs_t when);
-
- uint32_t mConfigurationChangesToRefresh;
- void refreshConfigurationLocked(uint32_t changes);
-
- // state queries
- typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
- int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
- GetStateFunc getStateFunc);
- bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-};
-
-
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
- InputReaderThread(const sp<InputReaderInterface>& reader);
- virtual ~InputReaderThread();
-
-private:
- sp<InputReaderInterface> mReader;
-
- virtual bool threadLoop();
-};
-
-
-/* Represents the state of a single input device. */
-class InputDevice {
-public:
- InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t
- controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes);
- ~InputDevice();
-
- inline InputReaderContext* getContext() { return mContext; }
- inline int32_t getId() const { return mId; }
- inline int32_t getControllerNumber() const { return mControllerNumber; }
- inline int32_t getGeneration() const { return mGeneration; }
- inline const String8& getName() const { return mIdentifier.name; }
- inline uint32_t getClasses() const { return mClasses; }
- inline uint32_t getSources() const { return mSources; }
-
- inline bool isExternal() { return mIsExternal; }
- inline void setExternal(bool external) { mIsExternal = external; }
-
- inline bool isIgnored() { return mMappers.isEmpty(); }
-
- void dump(String8& dump);
- void addMapper(InputMapper* mapper);
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- void reset(nsecs_t when);
- void process(const RawEvent* rawEvents, size_t count);
- void timeoutExpired(nsecs_t when);
-
- void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
- int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
- bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
- void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
- void cancelVibrate(int32_t token);
-
- int32_t getMetaState();
-
- void fadePointer();
-
- void bumpGeneration();
-
- void notifyReset(nsecs_t when);
-
- inline const PropertyMap& getConfiguration() { return mConfiguration; }
- inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
- bool hasKey(int32_t code) {
- return getEventHub()->hasScanCode(mId, code);
- }
-
- bool hasAbsoluteAxis(int32_t code) {
- RawAbsoluteAxisInfo info;
- getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
- return info.valid;
- }
-
- bool isKeyPressed(int32_t code) {
- return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
- }
-
- int32_t getAbsoluteAxisValue(int32_t code) {
- int32_t value;
- getEventHub()->getAbsoluteAxisValue(mId, code, &value);
- return value;
- }
-
-private:
- InputReaderContext* mContext;
- int32_t mId;
- int32_t mControllerNumber;
- int32_t mGeneration;
- InputDeviceIdentifier mIdentifier;
- String8 mAlias;
- uint32_t mClasses;
-
- Vector<InputMapper*> mMappers;
-
- uint32_t mSources;
- bool mIsExternal;
- bool mDropUntilNextSync;
-
- typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
- int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
-
- PropertyMap mConfiguration;
-};
-
-
-/* Keeps track of the state of mouse or touch pad buttons. */
-class CursorButtonAccumulator {
-public:
- CursorButtonAccumulator();
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
-
- uint32_t getButtonState() const;
-
-private:
- bool mBtnLeft;
- bool mBtnRight;
- bool mBtnMiddle;
- bool mBtnBack;
- bool mBtnSide;
- bool mBtnForward;
- bool mBtnExtra;
- bool mBtnTask;
-
- void clearButtons();
-};
-
-
-/* Keeps track of cursor movements. */
-
-class CursorMotionAccumulator {
-public:
- CursorMotionAccumulator();
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
- void finishSync();
-
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
-
-private:
- int32_t mRelX;
- int32_t mRelY;
-
- void clearRelativeAxes();
-};
-
-
-/* Keeps track of cursor scrolling motions. */
-
-class CursorScrollAccumulator {
-public:
- CursorScrollAccumulator();
- void configure(InputDevice* device);
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
- void finishSync();
-
- inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
- inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
-
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
- inline int32_t getRelativeVWheel() const { return mRelWheel; }
- inline int32_t getRelativeHWheel() const { return mRelHWheel; }
-
-private:
- bool mHaveRelWheel;
- bool mHaveRelHWheel;
-
- int32_t mRelX;
- int32_t mRelY;
- int32_t mRelWheel;
- int32_t mRelHWheel;
-
- void clearRelativeAxes();
-};
-
-
-/* Keeps track of the state of touch, stylus and tool buttons. */
-class TouchButtonAccumulator {
-public:
- TouchButtonAccumulator();
- void configure(InputDevice* device);
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
-
- uint32_t getButtonState() const;
- int32_t getToolType() const;
- bool isToolActive() const;
- bool isHovering() const;
- bool hasStylus() const;
-
-private:
- bool mHaveBtnTouch;
- bool mHaveStylus;
-
- bool mBtnTouch;
- bool mBtnStylus;
- bool mBtnStylus2;
- bool mBtnToolFinger;
- bool mBtnToolPen;
- bool mBtnToolRubber;
- bool mBtnToolBrush;
- bool mBtnToolPencil;
- bool mBtnToolAirbrush;
- bool mBtnToolMouse;
- bool mBtnToolLens;
- bool mBtnToolDoubleTap;
- bool mBtnToolTripleTap;
- bool mBtnToolQuadTap;
-
- void clearButtons();
-};
-
-
-/* Raw axis information from the driver. */
-struct RawPointerAxes {
- RawAbsoluteAxisInfo x;
- RawAbsoluteAxisInfo y;
- RawAbsoluteAxisInfo pressure;
- RawAbsoluteAxisInfo touchMajor;
- RawAbsoluteAxisInfo touchMinor;
- RawAbsoluteAxisInfo toolMajor;
- RawAbsoluteAxisInfo toolMinor;
- RawAbsoluteAxisInfo orientation;
- RawAbsoluteAxisInfo distance;
- RawAbsoluteAxisInfo tiltX;
- RawAbsoluteAxisInfo tiltY;
- RawAbsoluteAxisInfo trackingId;
- RawAbsoluteAxisInfo slot;
-
- RawPointerAxes();
- void clear();
-};
-
-
-/* Raw data for a collection of pointers including a pointer id mapping table. */
-struct RawPointerData {
- struct Pointer {
- uint32_t id;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t touchMajor;
- int32_t touchMinor;
- int32_t toolMajor;
- int32_t toolMinor;
- int32_t orientation;
- int32_t distance;
- int32_t tiltX;
- int32_t tiltY;
- int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
- bool isHovering;
- };
-
- uint32_t pointerCount;
- Pointer pointers[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
-
- RawPointerData();
- void clear();
- void copyFrom(const RawPointerData& other);
- void getCentroidOfTouchingPointers(float* outX, float* outY) const;
-
- inline void markIdBit(uint32_t id, bool isHovering) {
- if (isHovering) {
- hoveringIdBits.markBit(id);
- } else {
- touchingIdBits.markBit(id);
- }
- }
-
- inline void clearIdBits() {
- hoveringIdBits.clear();
- touchingIdBits.clear();
- }
-
- inline const Pointer& pointerForId(uint32_t id) const {
- return pointers[idToIndex[id]];
- }
-
- inline bool isHovering(uint32_t pointerIndex) {
- return pointers[pointerIndex].isHovering;
- }
-};
-
-
-/* Cooked data for a collection of pointers including a pointer id mapping table. */
-struct CookedPointerData {
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
-
- CookedPointerData();
- void clear();
- void copyFrom(const CookedPointerData& other);
-
- inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
- return pointerCoords[idToIndex[id]];
- }
-
- inline bool isHovering(uint32_t pointerIndex) {
- return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
- }
-};
-
-
-/* Keeps track of the state of single-touch protocol. */
-class SingleTouchMotionAccumulator {
-public:
- SingleTouchMotionAccumulator();
-
- void process(const RawEvent* rawEvent);
- void reset(InputDevice* device);
-
- inline int32_t getAbsoluteX() const { return mAbsX; }
- inline int32_t getAbsoluteY() const { return mAbsY; }
- inline int32_t getAbsolutePressure() const { return mAbsPressure; }
- inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; }
- inline int32_t getAbsoluteDistance() const { return mAbsDistance; }
- inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; }
- inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; }
-
-private:
- int32_t mAbsX;
- int32_t mAbsY;
- int32_t mAbsPressure;
- int32_t mAbsToolWidth;
- int32_t mAbsDistance;
- int32_t mAbsTiltX;
- int32_t mAbsTiltY;
-
- void clearAbsoluteAxes();
-};
-
-
-/* Keeps track of the state of multi-touch protocol. */
-class MultiTouchMotionAccumulator {
-public:
- class Slot {
- public:
- inline bool isInUse() const { return mInUse; }
- inline int32_t getX() const { return mAbsMTPositionX; }
- inline int32_t getY() const { return mAbsMTPositionY; }
- inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
- inline int32_t getTouchMinor() const {
- return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; }
- inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
- inline int32_t getToolMinor() const {
- return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; }
- inline int32_t getOrientation() const { return mAbsMTOrientation; }
- inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
- inline int32_t getPressure() const { return mAbsMTPressure; }
- inline int32_t getDistance() const { return mAbsMTDistance; }
- inline int32_t getToolType() const;
-
- private:
- friend class MultiTouchMotionAccumulator;
-
- bool mInUse;
- bool mHaveAbsMTTouchMinor;
- bool mHaveAbsMTWidthMinor;
- bool mHaveAbsMTToolType;
-
- int32_t mAbsMTPositionX;
- int32_t mAbsMTPositionY;
- int32_t mAbsMTTouchMajor;
- int32_t mAbsMTTouchMinor;
- int32_t mAbsMTWidthMajor;
- int32_t mAbsMTWidthMinor;
- int32_t mAbsMTOrientation;
- int32_t mAbsMTTrackingId;
- int32_t mAbsMTPressure;
- int32_t mAbsMTDistance;
- int32_t mAbsMTToolType;
-
- Slot();
- void clear();
- };
-
- MultiTouchMotionAccumulator();
- ~MultiTouchMotionAccumulator();
-
- void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
- void reset(InputDevice* device);
- void process(const RawEvent* rawEvent);
- void finishSync();
- bool hasStylus() const;
-
- inline size_t getSlotCount() const { return mSlotCount; }
- inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
-
-private:
- int32_t mCurrentSlot;
- Slot* mSlots;
- size_t mSlotCount;
- bool mUsingSlotsProtocol;
- bool mHaveStylus;
-
- void clearSlots(int32_t initialSlot);
-};
-
-
-/* An input mapper transforms raw input events into cooked event data.
- * A single input device can have multiple associated input mappers in order to interpret
- * different classes of events.
- *
- * InputMapper lifecycle:
- * - create
- * - configure with 0 changes
- * - reset
- * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
- * - reset
- * - destroy
- */
-class InputMapper {
-public:
- InputMapper(InputDevice* device);
- virtual ~InputMapper();
-
- inline InputDevice* getDevice() { return mDevice; }
- inline int32_t getDeviceId() { return mDevice->getId(); }
- inline const String8 getDeviceName() { return mDevice->getName(); }
- inline InputReaderContext* getContext() { return mContext; }
- inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
- inline InputListenerInterface* getListener() { return mContext->getListener(); }
- inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
- virtual uint32_t getSources() = 0;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(String8& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent) = 0;
- virtual void timeoutExpired(nsecs_t when);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token);
- virtual void cancelVibrate(int32_t token);
-
- virtual int32_t getMetaState();
-
- virtual void fadePointer();
-
-protected:
- InputDevice* mDevice;
- InputReaderContext* mContext;
-
- status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
- void bumpGeneration();
-
- static void dumpRawAbsoluteAxisInfo(String8& dump,
- const RawAbsoluteAxisInfo& axis, const char* name);
-};
-
-
-class SwitchInputMapper : public InputMapper {
-public:
- SwitchInputMapper(InputDevice* device);
- virtual ~SwitchInputMapper();
-
- virtual uint32_t getSources();
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-
-private:
- uint32_t mUpdatedSwitchValues;
- uint32_t mUpdatedSwitchMask;
-
- void processSwitch(int32_t switchCode, int32_t switchValue);
- void sync(nsecs_t when);
-};
-
-
-class VibratorInputMapper : public InputMapper {
-public:
- VibratorInputMapper(InputDevice* device);
- virtual ~VibratorInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void process(const RawEvent* rawEvent);
-
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token);
- virtual void cancelVibrate(int32_t token);
- virtual void timeoutExpired(nsecs_t when);
- virtual void dump(String8& dump);
-
-private:
- bool mVibrating;
- nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
- size_t mPatternSize;
- ssize_t mRepeat;
- int32_t mToken;
- ssize_t mIndex;
- nsecs_t mNextStepTime;
-
- void nextStep();
- void stopVibrating();
-};
-
-
-class KeyboardInputMapper : public InputMapper {
-public:
- KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
- virtual ~KeyboardInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(String8& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual int32_t getMetaState();
-
-private:
- struct KeyDown {
- int32_t keyCode;
- int32_t scanCode;
- };
-
- uint32_t mSource;
- int32_t mKeyboardType;
-
- int32_t mOrientation; // orientation for dpad keys
-
- Vector<KeyDown> mKeyDowns; // keys that are down
- int32_t mMetaState;
- nsecs_t mDownTime; // time of most recent key down
-
- int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
-
- struct LedState {
- bool avail; // led is available
- bool on; // we think the led is currently on
- };
- LedState mCapsLockLedState;
- LedState mNumLockLedState;
- LedState mScrollLockLedState;
-
- // Immutable configuration parameters.
- struct Parameters {
- bool hasAssociatedDisplay;
- bool orientationAware;
- bool handlesKeyRepeat;
- } mParameters;
-
- void configureParameters();
- void dumpParameters(String8& dump);
-
- bool isKeyboardOrGamepadKey(int32_t scanCode);
-
- void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
- uint32_t policyFlags);
-
- ssize_t findKeyDown(int32_t scanCode);
-
- void resetLedState();
- void initializeLedState(LedState& ledState, int32_t led);
- void updateLedState(bool reset);
- void updateLedStateForModifier(LedState& ledState, int32_t led,
- int32_t modifier, bool reset);
-};
-
-
-class CursorInputMapper : public InputMapper {
-public:
- CursorInputMapper(InputDevice* device);
- virtual ~CursorInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(String8& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-
- virtual void fadePointer();
-
-private:
- // Amount that trackball needs to move in order to generate a key event.
- static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
- // Immutable configuration parameters.
- struct Parameters {
- enum Mode {
- MODE_POINTER,
- MODE_NAVIGATION,
- };
-
- Mode mode;
- bool hasAssociatedDisplay;
- bool orientationAware;
- } mParameters;
-
- CursorButtonAccumulator mCursorButtonAccumulator;
- CursorMotionAccumulator mCursorMotionAccumulator;
- CursorScrollAccumulator mCursorScrollAccumulator;
-
- int32_t mSource;
- float mXScale;
- float mYScale;
- float mXPrecision;
- float mYPrecision;
-
- float mVWheelScale;
- float mHWheelScale;
-
- // Velocity controls for mouse pointer and wheel movements.
- // The controls for X and Y wheel movements are separate to keep them decoupled.
- VelocityControl mPointerVelocityControl;
- VelocityControl mWheelXVelocityControl;
- VelocityControl mWheelYVelocityControl;
-
- int32_t mOrientation;
-
- sp<PointerControllerInterface> mPointerController;
-
- int32_t mButtonState;
- nsecs_t mDownTime;
-
- void configureParameters();
- void dumpParameters(String8& dump);
-
- void sync(nsecs_t when);
-};
-
-
-class TouchInputMapper : public InputMapper {
-public:
- TouchInputMapper(InputDevice* device);
- virtual ~TouchInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(String8& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual void fadePointer();
- virtual void timeoutExpired(nsecs_t when);
-
-protected:
- CursorButtonAccumulator mCursorButtonAccumulator;
- CursorScrollAccumulator mCursorScrollAccumulator;
- TouchButtonAccumulator mTouchButtonAccumulator;
-
- struct VirtualKey {
- int32_t keyCode;
- int32_t scanCode;
- uint32_t flags;
-
- // computed hit box, specified in touch screen coords based on known display size
- int32_t hitLeft;
- int32_t hitTop;
- int32_t hitRight;
- int32_t hitBottom;
-
- inline bool isHit(int32_t x, int32_t y) const {
- return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
- }
- };
-
- // Input sources and device mode.
- uint32_t mSource;
-
- enum DeviceMode {
- DEVICE_MODE_DISABLED, // input is disabled
- DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
- DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
- DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- DEVICE_MODE_POINTER, // pointer mapping (pointer)
- };
- DeviceMode mDeviceMode;
-
- // The reader's configuration.
- InputReaderConfiguration mConfig;
-
- // Immutable configuration parameters.
- struct Parameters {
- enum DeviceType {
- DEVICE_TYPE_TOUCH_SCREEN,
- DEVICE_TYPE_TOUCH_PAD,
- DEVICE_TYPE_TOUCH_NAVIGATION,
- DEVICE_TYPE_POINTER,
- };
-
- DeviceType deviceType;
- bool hasAssociatedDisplay;
- bool associatedDisplayIsExternal;
- bool orientationAware;
- bool hasButtonUnderPad;
-
- enum GestureMode {
- GESTURE_MODE_POINTER,
- GESTURE_MODE_SPOTS,
- };
- GestureMode gestureMode;
-
- bool wake;
- } mParameters;
-
- // Immutable calibration parameters in parsed form.
- struct Calibration {
- // Size
- enum SizeCalibration {
- SIZE_CALIBRATION_DEFAULT,
- SIZE_CALIBRATION_NONE,
- SIZE_CALIBRATION_GEOMETRIC,
- SIZE_CALIBRATION_DIAMETER,
- SIZE_CALIBRATION_BOX,
- SIZE_CALIBRATION_AREA,
- };
-
- SizeCalibration sizeCalibration;
-
- bool haveSizeScale;
- float sizeScale;
- bool haveSizeBias;
- float sizeBias;
- bool haveSizeIsSummed;
- bool sizeIsSummed;
-
- // Pressure
- enum PressureCalibration {
- PRESSURE_CALIBRATION_DEFAULT,
- PRESSURE_CALIBRATION_NONE,
- PRESSURE_CALIBRATION_PHYSICAL,
- PRESSURE_CALIBRATION_AMPLITUDE,
- };
-
- PressureCalibration pressureCalibration;
- bool havePressureScale;
- float pressureScale;
-
- // Orientation
- enum OrientationCalibration {
- ORIENTATION_CALIBRATION_DEFAULT,
- ORIENTATION_CALIBRATION_NONE,
- ORIENTATION_CALIBRATION_INTERPOLATED,
- ORIENTATION_CALIBRATION_VECTOR,
- };
-
- OrientationCalibration orientationCalibration;
-
- // Distance
- enum DistanceCalibration {
- DISTANCE_CALIBRATION_DEFAULT,
- DISTANCE_CALIBRATION_NONE,
- DISTANCE_CALIBRATION_SCALED,
- };
-
- DistanceCalibration distanceCalibration;
- bool haveDistanceScale;
- float distanceScale;
-
- enum CoverageCalibration {
- COVERAGE_CALIBRATION_DEFAULT,
- COVERAGE_CALIBRATION_NONE,
- COVERAGE_CALIBRATION_BOX,
- };
-
- CoverageCalibration coverageCalibration;
-
- inline void applySizeScaleAndBias(float* outSize) const {
- if (haveSizeScale) {
- *outSize *= sizeScale;
- }
- if (haveSizeBias) {
- *outSize += sizeBias;
- }
- if (*outSize < 0) {
- *outSize = 0;
- }
- }
- } mCalibration;
-
- // Raw pointer axis information from the driver.
- RawPointerAxes mRawPointerAxes;
-
- // Raw pointer sample data.
- RawPointerData mCurrentRawPointerData;
- RawPointerData mLastRawPointerData;
-
- // Cooked pointer sample data.
- CookedPointerData mCurrentCookedPointerData;
- CookedPointerData mLastCookedPointerData;
-
- // Button state.
- int32_t mCurrentButtonState;
- int32_t mLastButtonState;
-
- // Scroll state.
- int32_t mCurrentRawVScroll;
- int32_t mCurrentRawHScroll;
-
- // Id bits used to differentiate fingers, stylus and mouse tools.
- BitSet32 mCurrentFingerIdBits; // finger or unknown
- BitSet32 mLastFingerIdBits;
- BitSet32 mCurrentStylusIdBits; // stylus or eraser
- BitSet32 mLastStylusIdBits;
- BitSet32 mCurrentMouseIdBits; // mouse or lens
- BitSet32 mLastMouseIdBits;
-
- // True if we sent a HOVER_ENTER event.
- bool mSentHoverEnter;
-
- // The time the primary pointer last went down.
- nsecs_t mDownTime;
-
- // The pointer controller, or null if the device is not a pointer.
- sp<PointerControllerInterface> mPointerController;
-
- Vector<VirtualKey> mVirtualKeys;
-
- virtual void configureParameters();
- virtual void dumpParameters(String8& dump);
- virtual void configureRawPointerAxes();
- virtual void dumpRawPointerAxes(String8& dump);
- virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
- virtual void dumpSurface(String8& dump);
- virtual void configureVirtualKeys();
- virtual void dumpVirtualKeys(String8& dump);
- virtual void parseCalibration();
- virtual void resolveCalibration();
- virtual void dumpCalibration(String8& dump);
- virtual bool hasStylus() const = 0;
-
- virtual void syncTouch(nsecs_t when, bool* outHavePointerIds) = 0;
-
-private:
- // The current viewport.
- // The components of the viewport are specified in the display's rotated orientation.
- DisplayViewport mViewport;
-
- // The surface orientation, width and height set by configureSurface().
- // The width and height are derived from the viewport but are specified
- // in the natural orientation.
- // The surface origin specifies how the surface coordinates should be translated
- // to align with the logical display coordinate space.
- // The orientation may be different from the viewport orientation as it specifies
- // the rotation of the surface coordinates required to produce the viewport's
- // requested orientation, so it will depend on whether the device is orientation aware.
- int32_t mSurfaceWidth;
- int32_t mSurfaceHeight;
- int32_t mSurfaceLeft;
- int32_t mSurfaceTop;
- int32_t mSurfaceOrientation;
-
- // Translation and scaling factors, orientation-independent.
- float mXTranslate;
- float mXScale;
- float mXPrecision;
-
- float mYTranslate;
- float mYScale;
- float mYPrecision;
-
- float mGeometricScale;
-
- float mPressureScale;
-
- float mSizeScale;
-
- float mOrientationScale;
-
- float mDistanceScale;
-
- bool mHaveTilt;
- float mTiltXCenter;
- float mTiltXScale;
- float mTiltYCenter;
- float mTiltYScale;
-
- // Oriented motion ranges for input device info.
- struct OrientedRanges {
- InputDeviceInfo::MotionRange x;
- InputDeviceInfo::MotionRange y;
- InputDeviceInfo::MotionRange pressure;
-
- bool haveSize;
- InputDeviceInfo::MotionRange size;
-
- bool haveTouchSize;
- InputDeviceInfo::MotionRange touchMajor;
- InputDeviceInfo::MotionRange touchMinor;
-
- bool haveToolSize;
- InputDeviceInfo::MotionRange toolMajor;
- InputDeviceInfo::MotionRange toolMinor;
-
- bool haveOrientation;
- InputDeviceInfo::MotionRange orientation;
-
- bool haveDistance;
- InputDeviceInfo::MotionRange distance;
-
- bool haveTilt;
- InputDeviceInfo::MotionRange tilt;
-
- OrientedRanges() {
- clear();
- }
-
- void clear() {
- haveSize = false;
- haveTouchSize = false;
- haveToolSize = false;
- haveOrientation = false;
- haveDistance = false;
- haveTilt = false;
- }
- } mOrientedRanges;
-
- // Oriented dimensions and precision.
- float mOrientedXPrecision;
- float mOrientedYPrecision;
-
- struct CurrentVirtualKeyState {
- bool down;
- bool ignored;
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
- } mCurrentVirtualKey;
-
- // Scale factor for gesture or mouse based pointer movements.
- float mPointerXMovementScale;
- float mPointerYMovementScale;
-
- // Scale factor for gesture based zooming and other freeform motions.
- float mPointerXZoomScale;
- float mPointerYZoomScale;
-
- // The maximum swipe width.
- float mPointerGestureMaxSwipeWidth;
-
- struct PointerDistanceHeapElement {
- uint32_t currentPointerIndex : 8;
- uint32_t lastPointerIndex : 8;
- uint64_t distance : 48; // squared distance
- };
-
- enum PointerUsage {
- POINTER_USAGE_NONE,
- POINTER_USAGE_GESTURES,
- POINTER_USAGE_STYLUS,
- POINTER_USAGE_MOUSE,
- };
- PointerUsage mPointerUsage;
-
- struct PointerGesture {
- enum Mode {
- // No fingers, button is not pressed.
- // Nothing happening.
- NEUTRAL,
-
- // No fingers, button is not pressed.
- // Tap detected.
- // Emits DOWN and UP events at the pointer location.
- TAP,
-
- // Exactly one finger dragging following a tap.
- // Pointer follows the active finger.
- // Emits DOWN, MOVE and UP events at the pointer location.
- //
- // Detect double-taps when the finger goes up while in TAP_DRAG mode.
- TAP_DRAG,
-
- // Button is pressed.
- // Pointer follows the active finger if there is one. Other fingers are ignored.
- // Emits DOWN, MOVE and UP events at the pointer location.
- BUTTON_CLICK_OR_DRAG,
-
- // Exactly one finger, button is not pressed.
- // Pointer follows the active finger.
- // Emits HOVER_MOVE events at the pointer location.
- //
- // Detect taps when the finger goes up while in HOVER mode.
- HOVER,
-
- // Exactly two fingers but neither have moved enough to clearly indicate
- // whether a swipe or freeform gesture was intended. We consider the
- // pointer to be pressed so this enables clicking or long-pressing on buttons.
- // Pointer does not move.
- // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
- PRESS,
-
- // Exactly two fingers moving in the same direction, button is not pressed.
- // Pointer does not move.
- // Emits DOWN, MOVE and UP events with a single pointer coordinate that
- // follows the midpoint between both fingers.
- SWIPE,
-
- // Two or more fingers moving in arbitrary directions, button is not pressed.
- // Pointer does not move.
- // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
- // each finger individually relative to the initial centroid of the finger.
- FREEFORM,
-
- // Waiting for quiet time to end before starting the next gesture.
- QUIET,
- };
-
- // Time the first finger went down.
- nsecs_t firstTouchTime;
-
- // The active pointer id from the raw touch data.
- int32_t activeTouchId; // -1 if none
-
- // The active pointer id from the gesture last delivered to the application.
- int32_t activeGestureId; // -1 if none
-
- // Pointer coords and ids for the current and previous pointer gesture.
- Mode currentGestureMode;
- BitSet32 currentGestureIdBits;
- uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties currentGestureProperties[MAX_POINTERS];
- PointerCoords currentGestureCoords[MAX_POINTERS];
-
- Mode lastGestureMode;
- BitSet32 lastGestureIdBits;
- uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties lastGestureProperties[MAX_POINTERS];
- PointerCoords lastGestureCoords[MAX_POINTERS];
-
- // Time the pointer gesture last went down.
- nsecs_t downTime;
-
- // Time when the pointer went down for a TAP.
- nsecs_t tapDownTime;
-
- // Time when the pointer went up for a TAP.
- nsecs_t tapUpTime;
-
- // Location of initial tap.
- float tapX, tapY;
-
- // Time we started waiting for quiescence.
- nsecs_t quietTime;
-
- // Reference points for multitouch gestures.
- float referenceTouchX; // reference touch X/Y coordinates in surface units
- float referenceTouchY;
- float referenceGestureX; // reference gesture X/Y coordinates in pixels
- float referenceGestureY;
-
- // Distance that each pointer has traveled which has not yet been
- // subsumed into the reference gesture position.
- BitSet32 referenceIdBits;
- struct Delta {
- float dx, dy;
- };
- Delta referenceDeltas[MAX_POINTER_ID + 1];
-
- // Describes how touch ids are mapped to gesture ids for freeform gestures.
- uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
-
- // A velocity tracker for determining whether to switch active pointers during drags.
- VelocityTracker velocityTracker;
-
- void reset() {
- firstTouchTime = LLONG_MIN;
- activeTouchId = -1;
- activeGestureId = -1;
- currentGestureMode = NEUTRAL;
- currentGestureIdBits.clear();
- lastGestureMode = NEUTRAL;
- lastGestureIdBits.clear();
- downTime = 0;
- velocityTracker.clear();
- resetTap();
- resetQuietTime();
- }
-
- void resetTap() {
- tapDownTime = LLONG_MIN;
- tapUpTime = LLONG_MIN;
- }
-
- void resetQuietTime() {
- quietTime = LLONG_MIN;
- }
- } mPointerGesture;
-
- struct PointerSimple {
- PointerCoords currentCoords;
- PointerProperties currentProperties;
- PointerCoords lastCoords;
- PointerProperties lastProperties;
-
- // True if the pointer is down.
- bool down;
-
- // True if the pointer is hovering.
- bool hovering;
-
- // Time the pointer last went down.
- nsecs_t downTime;
-
- void reset() {
- currentCoords.clear();
- currentProperties.clear();
- lastCoords.clear();
- lastProperties.clear();
- down = false;
- hovering = false;
- downTime = 0;
- }
- } mPointerSimple;
-
- // The pointer and scroll velocity controls.
- VelocityControl mPointerVelocityControl;
- VelocityControl mWheelXVelocityControl;
- VelocityControl mWheelYVelocityControl;
-
- void sync(nsecs_t when);
-
- bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags);
-
- void dispatchTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
- void cookPointerData();
-
- void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
- void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
- void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
- bool preparePointerGestures(nsecs_t when,
- bool* outCancelPreviousGesture, bool* outFinishPreviousGesture,
- bool isTimeout);
-
- void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
- void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
- void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
- bool down, bool hovering);
- void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
-
- // Dispatches a motion event.
- // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
- // method will take care of setting the index and transmuting the action to DOWN or UP
- // it is the first / last pointer to go down / up.
- void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags,
- const PointerProperties* properties, const PointerCoords* coords,
- const uint32_t* idToIndex, BitSet32 idBits,
- int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
-
- // Updates pointer coords and properties for pointers with specified ids that have moved.
- // Returns true if any of them changed.
- bool updateMovedPointers(const PointerProperties* inProperties,
- const PointerCoords* inCoords, const uint32_t* inIdToIndex,
- PointerProperties* outProperties, PointerCoords* outCoords,
- const uint32_t* outIdToIndex, BitSet32 idBits) const;
-
- bool isPointInsideSurface(int32_t x, int32_t y);
- const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
-
- void assignPointerIds();
-};
-
-
-class SingleTouchInputMapper : public TouchInputMapper {
-public:
- SingleTouchInputMapper(InputDevice* device);
- virtual ~SingleTouchInputMapper();
-
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-protected:
- virtual void syncTouch(nsecs_t when, bool* outHavePointerIds);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
-
-private:
- SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-};
-
-
-class MultiTouchInputMapper : public TouchInputMapper {
-public:
- MultiTouchInputMapper(InputDevice* device);
- virtual ~MultiTouchInputMapper();
-
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-protected:
- virtual void syncTouch(nsecs_t when, bool* outHavePointerIds);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
-
-private:
- MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
-
- // Specifies the pointer id bits that are in use, and their associated tracking id.
- BitSet32 mPointerIdBits;
- int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
-};
-
-
-class JoystickInputMapper : public InputMapper {
-public:
- JoystickInputMapper(InputDevice* device);
- virtual ~JoystickInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(String8& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-private:
- struct Axis {
- RawAbsoluteAxisInfo rawAxisInfo;
- AxisInfo axisInfo;
-
- bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
-
- float scale; // scale factor from raw to normalized values
- float offset; // offset to add after scaling for normalization
- float highScale; // scale factor from raw to normalized values of high split
- float highOffset; // offset to add after scaling for normalization of high split
-
- float min; // normalized inclusive minimum
- float max; // normalized inclusive maximum
- float flat; // normalized flat region size
- float fuzz; // normalized error tolerance
- float resolution; // normalized resolution in units/mm
-
- float filter; // filter out small variations of this size
- float currentValue; // current value
- float newValue; // most recent value
- float highCurrentValue; // current value of high split
- float highNewValue; // most recent value of high split
-
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
- bool explicitlyMapped, float scale, float offset,
- float highScale, float highOffset,
- float min, float max, float flat, float fuzz, float resolution) {
- this->rawAxisInfo = rawAxisInfo;
- this->axisInfo = axisInfo;
- this->explicitlyMapped = explicitlyMapped;
- this->scale = scale;
- this->offset = offset;
- this->highScale = highScale;
- this->highOffset = highOffset;
- this->min = min;
- this->max = max;
- this->flat = flat;
- this->fuzz = fuzz;
- this->resolution = resolution;
- this->filter = 0;
- resetValue();
- }
-
- void resetValue() {
- this->currentValue = 0;
- this->newValue = 0;
- this->highCurrentValue = 0;
- this->highNewValue = 0;
- }
- };
-
- // Axes indexed by raw ABS_* axis index.
- KeyedVector<int32_t, Axis> mAxes;
-
- void sync(nsecs_t when, bool force);
-
- bool haveAxis(int32_t axisId);
- void pruneAxes(bool ignoreExplicitlyMappedAxes);
- bool filterAxes(bool force);
-
- static bool hasValueChangedSignificantly(float filter,
- float newValue, float currentValue, float min, float max);
- static bool hasMovedNearerToValueWithinFilteredRange(float filter,
- float newValue, float currentValue, float thresholdValue);
-
- static bool isCenteredAxis(int32_t axis);
- static int32_t getCompatAxis(int32_t axis);
-
- static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
- static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
- float value);
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_READER_H
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
deleted file mode 100644
index 18cd8eb..0000000
--- a/libs/input/InputWindow.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 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 "InputWindow"
-
-#include "InputWindow.h"
-
-#include <cutils/log.h>
-
-namespace android {
-
-// --- InputWindowInfo ---
-
-bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
- return touchableRegion.contains(x, y);
-}
-
-bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frameLeft && x <= frameRight
- && y >= frameTop && y <= frameBottom;
-}
-
-bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD
- || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
- || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
- || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
-}
-
-bool InputWindowInfo::supportsSplitTouch() const {
- return layoutParamsFlags & FLAG_SPLIT_TOUCH;
-}
-
-
-// --- InputWindowHandle ---
-
-InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
- inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
-}
-
-InputWindowHandle::~InputWindowHandle() {
- delete mInfo;
-}
-
-void InputWindowHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = NULL;
- }
-}
-
-} // namespace android
diff --git a/libs/input/InputWindow.h b/libs/input/InputWindow.h
deleted file mode 100644
index bd325b5..0000000
--- a/libs/input/InputWindow.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef _UI_INPUT_WINDOW_H
-#define _UI_INPUT_WINDOW_H
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/String8.h>
-
-#include <SkRegion.h>
-
-#include "InputApplication.h"
-
-namespace android {
-
-/*
- * Describes the properties of a window that can receive input.
- */
-struct InputWindowInfo {
- // Window flags from WindowManager.LayoutParams
- enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
- FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
- FLAG_LAYOUT_INSET_DECOR = 0x00010000,
- FLAG_ALT_FOCUSABLE_IM = 0x00020000,
- FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
- FLAG_SHOW_WHEN_LOCKED = 0x00080000,
- FLAG_SHOW_WALLPAPER = 0x00100000,
- FLAG_TURN_SCREEN_ON = 0x00200000,
- FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_SPLIT_TOUCH = 0x00800000,
- FLAG_SLIPPERY = 0x20000000,
- FLAG_NEEDS_MENU_KEY = 0x40000000,
- };
-
- // Private Window flags from WindowManager.LayoutParams
- enum {
- PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100,
- };
-
- // Window types from WindowManager.LayoutParams
- enum {
- FIRST_APPLICATION_WINDOW = 1,
- TYPE_BASE_APPLICATION = 1,
- TYPE_APPLICATION = 2,
- TYPE_APPLICATION_STARTING = 3,
- LAST_APPLICATION_WINDOW = 99,
- FIRST_SUB_WINDOW = 1000,
- TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
- TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
- TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
- TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
- TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
- LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
- TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
- TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
- TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
- TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
- TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
- TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
- TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
- TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
- TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
- TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
- TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
- TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
- TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
- TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
- TYPE_DRAG = FIRST_SYSTEM_WINDOW+16,
- TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17,
- TYPE_POINTER = FIRST_SYSTEM_WINDOW+18,
- TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19,
- TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
- TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
- TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
- LAST_SYSTEM_WINDOW = 2999,
- };
-
- enum {
- INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
- INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
- };
-
- sp<InputChannel> inputChannel;
- String8 name;
- int32_t layoutParamsFlags;
- int32_t layoutParamsPrivateFlags;
- int32_t layoutParamsType;
- nsecs_t dispatchingTimeout;
- int32_t frameLeft;
- int32_t frameTop;
- int32_t frameRight;
- int32_t frameBottom;
- float scaleFactor;
- SkRegion touchableRegion;
- bool visible;
- bool canReceiveKeys;
- bool hasFocus;
- bool hasWallpaper;
- bool paused;
- int32_t layer;
- int32_t ownerPid;
- int32_t ownerUid;
- int32_t inputFeatures;
- int32_t displayId;
-
- bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
- bool frameContainsPoint(int32_t x, int32_t y) const;
-
- /* Returns true if the window is of a trusted type that is allowed to silently
- * overlay other windows for the purpose of implementing the secure views feature.
- * Trusted overlays, such as IME windows, can partly obscure other windows without causing
- * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- */
- bool isTrustedOverlay() const;
-
- bool supportsSplitTouch() const;
-};
-
-
-/*
- * Handle for a window that can receive input.
- *
- * Used by the native input dispatcher to indirectly refer to the window manager objects
- * that describe a window.
- */
-class InputWindowHandle : public RefBase {
-public:
- const sp<InputApplicationHandle> inputApplicationHandle;
-
- inline const InputWindowInfo* getInfo() const {
- return mInfo;
- }
-
- inline sp<InputChannel> getInputChannel() const {
- return mInfo ? mInfo->inputChannel : NULL;
- }
-
- inline String8 getName() const {
- return mInfo ? mInfo->name : String8("<invalid>");
- }
-
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
- }
-
- /**
- * Requests that the state of this object be updated to reflect
- * the most current available information about the application.
- *
- * This method should only be called from within the input dispatcher's
- * critical section.
- *
- * Returns true on success, or false if the handle is no longer valid.
- */
- virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
-protected:
- InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
- virtual ~InputWindowHandle();
-
- InputWindowInfo* mInfo;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_WINDOW_H
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 790c0bb..b9e4ce7 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -21,6 +21,7 @@
#include <ui/DisplayInfo.h>
#include <input/Input.h>
+#include <inputflinger/PointerControllerInterface.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
@@ -30,84 +31,6 @@
namespace android {
-/**
- * Interface for tracking a mouse / touch pad pointer and touch pad spots.
- *
- * The spots are sprites on screen that visually represent the positions of
- * fingers
- *
- * The pointer controller is responsible for providing synchronization and for tracking
- * display orientation changes if needed.
- */
-class PointerControllerInterface : public virtual RefBase {
-protected:
- PointerControllerInterface() { }
- virtual ~PointerControllerInterface() { }
-
-public:
- /* Gets the bounds of the region that the pointer can traverse.
- * Returns true if the bounds are available. */
- virtual bool getBounds(float* outMinX, float* outMinY,
- float* outMaxX, float* outMaxY) const = 0;
-
- /* Move the pointer. */
- virtual void move(float deltaX, float deltaY) = 0;
-
- /* Sets a mask that indicates which buttons are pressed. */
- virtual void setButtonState(int32_t buttonState) = 0;
-
- /* Gets a mask that indicates which buttons are pressed. */
- virtual int32_t getButtonState() const = 0;
-
- /* Sets the absolute location of the pointer. */
- virtual void setPosition(float x, float y) = 0;
-
- /* Gets the absolute location of the pointer. */
- virtual void getPosition(float* outX, float* outY) const = 0;
-
- enum Transition {
- // Fade/unfade immediately.
- TRANSITION_IMMEDIATE,
- // Fade/unfade gradually.
- TRANSITION_GRADUAL,
- };
-
- /* Fades the pointer out now. */
- virtual void fade(Transition transition) = 0;
-
- /* Makes the pointer visible if it has faded out.
- * The pointer never unfades itself automatically. This method must be called
- * by the client whenever the pointer is moved or a button is pressed and it
- * wants to ensure that the pointer becomes visible again. */
- virtual void unfade(Transition transition) = 0;
-
- enum Presentation {
- // Show the mouse pointer.
- PRESENTATION_POINTER,
- // Show spots and a spot anchor in place of the mouse pointer.
- PRESENTATION_SPOT,
- };
-
- /* Sets the mode of the pointer controller. */
- virtual void setPresentation(Presentation presentation) = 0;
-
- /* Sets the spots for the current gesture.
- * The spots are not subject to the inactivity timeout like the pointer
- * itself it since they are expected to remain visible for so long as
- * the fingers are on the touch pad.
- *
- * The values of the AMOTION_EVENT_AXIS_PRESSURE axis is significant.
- * For spotCoords, pressure != 0 indicates that the spot's location is being
- * pressed (not hovering).
- */
- virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits) = 0;
-
- /* Removes all spots. */
- virtual void clearSpots() = 0;
-};
-
-
/*
* Pointer resources.
*/
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index fd9c66b..5391393 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@ void SpriteController::doUpdateSprites() {
} else {
SkBitmap surfaceBitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
- outBuffer.width, outBuffer.height, bpr);
- surfaceBitmap.setPixels(outBuffer.bits);
+ surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+ outBuffer.bits, bpr);
SkCanvas surfaceCanvas(surfaceBitmap);
@@ -216,12 +215,12 @@ void SpriteController::doUpdateSprites() {
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
- if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) {
+ if (outBuffer.width > update.state.icon.bitmap.width()) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
outBuffer.width, update.state.icon.bitmap.height(), paint);
}
- if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) {
+ if (outBuffer.height > update.state.icon.bitmap.height()) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
outBuffer.width, outBuffer.height, paint);
@@ -371,7 +370,8 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eHidden);
+ ISurfaceComposerClient::eHidden |
+ ISurfaceComposerClient::eCursorWindow);
if (surfaceControl == NULL || !surfaceControl->isValid()) {
ALOGE("Error creating sprite surface.");
return NULL;
@@ -402,7 +402,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
uint32_t dirty;
if (icon.isValid()) {
- icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config);
+ icon.bitmap.copyTo(&mLocked.state.icon.bitmap, kN32_SkColorType);
if (!mLocked.state.icon.isValid()
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 75e4843..905833c 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -65,7 +65,7 @@ struct SpriteIcon {
inline SpriteIcon copy() const {
SkBitmap bitmapCopy;
- bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config);
+ bitmap.copyTo(&bitmapCopy, kN32_SkColorType);
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
}
diff --git a/libs/input/tests/Android.mk b/libs/input/tests/Android.mk
deleted file mode 100644
index 9278f41..0000000
--- a/libs/input/tests/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# Build the unit tests.
-test_src_files := \
- InputReader_test.cpp \
- InputDispatcher_test.cpp
-
-shared_libraries := \
- libcutils \
- liblog \
- libandroidfw \
- libutils \
- libhardware \
- libhardware_legacy \
- libui \
- libskia \
- libstlport \
- libinput \
- libinputservice
-
-static_libraries := \
- libgtest \
- libgtest_main
-
-c_includes := \
- bionic \
- bionic/libstdc++/include \
- external/gtest/include \
- external/stlport/stlport \
- external/skia/include/core
-
-module_tags := eng tests
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_C_INCLUDES := $(c_includes)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libs/input/tests/InputDispatcher_test.cpp b/libs/input/tests/InputDispatcher_test.cpp
deleted file mode 100644
index 7aac6ed..0000000
--- a/libs/input/tests/InputDispatcher_test.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.
- */
-
-#include "../InputDispatcher.h"
-
-#include <gtest/gtest.h>
-#include <linux/input.h>
-
-namespace android {
-
-// An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
-
-// An arbitrary device id.
-static const int32_t DEVICE_ID = 1;
-
-// An arbitrary display id.
-static const int32_t DISPLAY_ID = 0;
-
-// An arbitrary injector pid / uid pair that has permission to inject events.
-static const int32_t INJECTOR_PID = 999;
-static const int32_t INJECTOR_UID = 1001;
-
-
-// --- FakeInputDispatcherPolicy ---
-
-class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
- InputDispatcherConfiguration mConfig;
-
-protected:
- virtual ~FakeInputDispatcherPolicy() {
- }
-
-public:
- FakeInputDispatcherPolicy() {
- }
-
-private:
- virtual void notifyConfigurationChanged(nsecs_t when) {
- }
-
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputWindowHandle>& inputWindowHandle,
- const String8& reason) {
- return 0;
- }
-
- virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
- }
-
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
- *outConfig = mConfig;
- }
-
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
- return true;
- }
-
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
- }
-
- virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
- }
-
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
- const KeyEvent* keyEvent, uint32_t policyFlags) {
- return 0;
- }
-
- virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
- const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
- return false;
- }
-
- virtual void notifySwitch(nsecs_t when,
- uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) {
- }
-
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
- }
-
- virtual bool checkInjectEventsPermissionNonReentrant(
- int32_t injectorPid, int32_t injectorUid) {
- return false;
- }
-};
-
-
-// --- InputDispatcherTest ---
-
-class InputDispatcherTest : public testing::Test {
-protected:
- sp<FakeInputDispatcherPolicy> mFakePolicy;
- sp<InputDispatcher> mDispatcher;
-
- virtual void SetUp() {
- mFakePolicy = new FakeInputDispatcherPolicy();
- mDispatcher = new InputDispatcher(mFakePolicy);
- }
-
- virtual void TearDown() {
- mFakePolicy.clear();
- mDispatcher.clear();
- }
-};
-
-
-TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
- KeyEvent event;
-
- // Rejects undefined key actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- /*action*/ -1, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject key events with undefined action.";
-
- // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- AKEY_EVENT_ACTION_MULTIPLE, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject key events with ACTION_MULTIPLE.";
-}
-
-TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
- MotionEvent event;
- PointerProperties pointerProperties[MAX_POINTERS + 1];
- PointerCoords pointerCoords[MAX_POINTERS + 1];
- for (int i = 0; i <= MAX_POINTERS; i++) {
- pointerProperties[i].clear();
- pointerProperties[i].id = i;
- pointerCoords[i].clear();
- }
-
- // Rejects undefined motion actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- /*action*/ -1, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with undefined action.";
-
- // Rejects pointer down with invalid index.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer down index too large.";
-
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer down index too small.";
-
- // Rejects pointer up with invalid index.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer up index too large.";
-
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer up index too small.";
-
- // Rejects motion events with invalid number of pointers.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 0, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with 0 pointers.";
-
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with more than MAX_POINTERS pointers.";
-
- // Rejects motion events with invalid pointer ids.
- pointerProperties[0].id = -1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer ids less than 0.";
-
- pointerProperties[0].id = MAX_POINTER_ID + 1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
-
- // Rejects motion events with duplicate pointer ids.
- pointerProperties[0].id = 1;
- pointerProperties[1].id = 1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 2, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
- << "Should reject motion events with duplicate pointer ids.";
-}
-
-} // namespace android
diff --git a/libs/input/tests/InputReader_test.cpp b/libs/input/tests/InputReader_test.cpp
deleted file mode 100644
index aaa973d..0000000
--- a/libs/input/tests/InputReader_test.cpp
+++ /dev/null
@@ -1,5099 +0,0 @@
-/*
- * 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.
- */
-
-#include "../InputReader.h"
-
-#include <utils/List.h>
-#include <gtest/gtest.h>
-#include <math.h>
-
-namespace android {
-
-// An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
-
-// Arbitrary display properties.
-static const int32_t DISPLAY_ID = 0;
-static const int32_t DISPLAY_WIDTH = 480;
-static const int32_t DISPLAY_HEIGHT = 800;
-
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
-
-template<typename T>
-static inline T min(T a, T b) {
- return a < b ? a : b;
-}
-
-static inline float avg(float x, float y) {
- return (x + y) / 2;
-}
-
-
-// --- FakePointerController ---
-
-class FakePointerController : public PointerControllerInterface {
- bool mHaveBounds;
- float mMinX, mMinY, mMaxX, mMaxY;
- float mX, mY;
- int32_t mButtonState;
-
-protected:
- virtual ~FakePointerController() { }
-
-public:
- FakePointerController() :
- mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
- mButtonState(0) {
- }
-
- void setBounds(float minX, float minY, float maxX, float maxY) {
- mHaveBounds = true;
- mMinX = minX;
- mMinY = minY;
- mMaxX = maxX;
- mMaxY = maxY;
- }
-
- virtual void setPosition(float x, float y) {
- mX = x;
- mY = y;
- }
-
- virtual void setButtonState(int32_t buttonState) {
- mButtonState = buttonState;
- }
-
- virtual int32_t getButtonState() const {
- return mButtonState;
- }
-
- virtual void getPosition(float* outX, float* outY) const {
- *outX = mX;
- *outY = mY;
- }
-
-private:
- virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
- *outMinX = mMinX;
- *outMinY = mMinY;
- *outMaxX = mMaxX;
- *outMaxY = mMaxY;
- return mHaveBounds;
- }
-
- virtual void move(float deltaX, float deltaY) {
- mX += deltaX;
- if (mX < mMinX) mX = mMinX;
- if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
- if (mY < mMinY) mY = mMinY;
- if (mY > mMaxY) mY = mMaxY;
- }
-
- virtual void fade(Transition transition) {
- }
-
- virtual void unfade(Transition transition) {
- }
-
- virtual void setPresentation(Presentation presentation) {
- }
-
- virtual void setSpots(const PointerCoords* spotCoords,
- const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
- }
-
- virtual void clearSpots() {
- }
-};
-
-
-// --- FakeInputReaderPolicy ---
-
-class FakeInputReaderPolicy : public InputReaderPolicyInterface {
- InputReaderConfiguration mConfig;
- KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
- Vector<InputDeviceInfo> mInputDevices;
-
-protected:
- virtual ~FakeInputReaderPolicy() { }
-
-public:
- FakeInputReaderPolicy() {
- }
-
- void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) {
- // Set the size of both the internal and external display at the same time.
- bool isRotated = (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- mConfig.setDisplayInfo(false /*external*/, v);
- mConfig.setDisplayInfo(true /*external*/, v);
- }
-
- void addExcludedDeviceName(const String8& deviceName) {
- mConfig.excludedDeviceNames.push(deviceName);
- }
-
- void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
- mPointerControllers.add(deviceId, controller);
- }
-
- const InputReaderConfiguration* getReaderConfiguration() const {
- return &mConfig;
- }
-
- const Vector<InputDeviceInfo>& getInputDevices() const {
- return mInputDevices;
- }
-
-private:
- virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
- *outConfig = mConfig;
- }
-
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
- return mPointerControllers.valueFor(deviceId);
- }
-
- virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
- mInputDevices = inputDevices;
- }
-
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier) {
- return NULL;
- }
-
- virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) {
- return String8::empty();
- }
-};
-
-
-// --- FakeInputListener ---
-
-class FakeInputListener : public InputListenerInterface {
-private:
- List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
- List<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
- List<NotifyKeyArgs> mNotifyKeyArgsQueue;
- List<NotifyMotionArgs> mNotifyMotionArgsQueue;
- List<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
-protected:
- virtual ~FakeInputListener() { }
-
-public:
- FakeInputListener() {
- }
-
- void assertNotifyConfigurationChangedWasCalled(
- NotifyConfigurationChangedArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
- }
- mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
- }
-
- void assertNotifyDeviceResetWasCalled(
- NotifyDeviceResetArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
- }
- mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
- }
-
- void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyKeyArgsQueue.begin();
- }
- mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
- }
-
- void assertNotifyKeyWasNotCalled() {
- ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to not have been called.";
- }
-
- void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyMotionArgsQueue.begin();
- }
- mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
- }
-
- void assertNotifyMotionWasNotCalled() {
- ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to not have been called.";
- }
-
- void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
- << "Expected notifySwitch() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifySwitchArgsQueue.begin();
- }
- mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
- }
-
-private:
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- mNotifyConfigurationChangedArgsQueue.push_back(*args);
- }
-
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mNotifyDeviceResetArgsQueue.push_back(*args);
- }
-
- virtual void notifyKey(const NotifyKeyArgs* args) {
- mNotifyKeyArgsQueue.push_back(*args);
- }
-
- virtual void notifyMotion(const NotifyMotionArgs* args) {
- mNotifyMotionArgsQueue.push_back(*args);
- }
-
- virtual void notifySwitch(const NotifySwitchArgs* args) {
- mNotifySwitchArgsQueue.push_back(*args);
- }
-};
-
-
-// --- FakeEventHub ---
-
-class FakeEventHub : public EventHubInterface {
- struct KeyInfo {
- int32_t keyCode;
- uint32_t flags;
- };
-
- struct Device {
- InputDeviceIdentifier identifier;
- uint32_t classes;
- PropertyMap configuration;
- KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
- KeyedVector<int, bool> relativeAxes;
- KeyedVector<int32_t, int32_t> keyCodeStates;
- KeyedVector<int32_t, int32_t> scanCodeStates;
- KeyedVector<int32_t, int32_t> switchStates;
- KeyedVector<int32_t, int32_t> absoluteAxisValue;
- KeyedVector<int32_t, KeyInfo> keysByScanCode;
- KeyedVector<int32_t, KeyInfo> keysByUsageCode;
- KeyedVector<int32_t, bool> leds;
- Vector<VirtualKeyDefinition> virtualKeys;
-
- Device(uint32_t classes) :
- classes(classes) {
- }
- };
-
- KeyedVector<int32_t, Device*> mDevices;
- Vector<String8> mExcludedDevices;
- List<RawEvent> mEvents;
-
-protected:
- virtual ~FakeEventHub() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
- }
- }
-
-public:
- FakeEventHub() { }
-
- void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
- Device* device = new Device(classes);
- device->identifier.name = name;
- mDevices.add(deviceId, device);
-
- enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
- }
-
- void removeDevice(int32_t deviceId) {
- delete mDevices.valueFor(deviceId);
- mDevices.removeItem(deviceId);
-
- enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
- }
-
- void finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
- }
-
- void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
- Device* device = getDevice(deviceId);
- device->configuration.addProperty(key, value);
- }
-
- void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
- Device* device = getDevice(deviceId);
- device->configuration.addAll(configuration);
- }
-
- void addAbsoluteAxis(int32_t deviceId, int axis,
- int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) {
- Device* device = getDevice(deviceId);
-
- RawAbsoluteAxisInfo info;
- info.valid = true;
- info.minValue = minValue;
- info.maxValue = maxValue;
- info.flat = flat;
- info.fuzz = fuzz;
- info.resolution = resolution;
- device->absoluteAxes.add(axis, info);
- }
-
- void addRelativeAxis(int32_t deviceId, int32_t axis) {
- Device* device = getDevice(deviceId);
- device->relativeAxes.add(axis, true);
- }
-
- void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->keyCodeStates.replaceValueFor(keyCode, state);
- }
-
- void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->scanCodeStates.replaceValueFor(scanCode, state);
- }
-
- void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->switchStates.replaceValueFor(switchCode, state);
- }
-
- void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
- Device* device = getDevice(deviceId);
- device->absoluteAxisValue.replaceValueFor(axis, value);
- }
-
- void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t keyCode, uint32_t flags) {
- Device* device = getDevice(deviceId);
- KeyInfo info;
- info.keyCode = keyCode;
- info.flags = flags;
- if (scanCode) {
- device->keysByScanCode.add(scanCode, info);
- }
- if (usageCode) {
- device->keysByUsageCode.add(usageCode, info);
- }
- }
-
- void addLed(int32_t deviceId, int32_t led, bool initialState) {
- Device* device = getDevice(deviceId);
- device->leds.add(led, initialState);
- }
-
- bool getLedState(int32_t deviceId, int32_t led) {
- Device* device = getDevice(deviceId);
- return device->leds.valueFor(led);
- }
-
- Vector<String8>& getExcludedDevices() {
- return mExcludedDevices;
- }
-
- void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
- Device* device = getDevice(deviceId);
- device->virtualKeys.push(definition);
- }
-
- void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
- int32_t code, int32_t value) {
- RawEvent event;
- event.when = when;
- event.deviceId = deviceId;
- event.type = type;
- event.code = code;
- event.value = value;
- mEvents.push_back(event);
-
- if (type == EV_ABS) {
- setAbsoluteAxisValue(deviceId, code, value);
- }
- }
-
- void assertQueueIsEmpty() {
- ASSERT_EQ(size_t(0), mEvents.size())
- << "Expected the event queue to be empty (fully consumed).";
- }
-
-private:
- Device* getDevice(int32_t deviceId) const {
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : NULL;
- }
-
- virtual uint32_t getDeviceClasses(int32_t deviceId) const {
- Device* device = getDevice(deviceId);
- return device ? device->classes : 0;
- }
-
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
- Device* device = getDevice(deviceId);
- return device ? device->identifier : InputDeviceIdentifier();
- }
-
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const {
- return 0;
- }
-
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
- Device* device = getDevice(deviceId);
- if (device) {
- *outConfiguration = device->configuration;
- }
- }
-
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->absoluteAxes.indexOfKey(axis);
- if (index >= 0) {
- *outAxisInfo = device->absoluteAxes.valueAt(index);
- return OK;
- }
- }
- outAxisInfo->clear();
- return -1;
- }
-
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
- Device* device = getDevice(deviceId);
- if (device) {
- return device->relativeAxes.indexOfKey(axis) >= 0;
- }
- return false;
- }
-
- virtual bool hasInputProperty(int32_t deviceId, int property) const {
- return false;
- }
-
- virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t* outKeycode, uint32_t* outFlags) const {
- Device* device = getDevice(deviceId);
- if (device) {
- const KeyInfo* key = getKey(device, scanCode, usageCode);
- if (key) {
- if (outKeycode) {
- *outKeycode = key->keyCode;
- }
- if (outFlags) {
- *outFlags = key->flags;
- }
- return OK;
- }
- }
- return NAME_NOT_FOUND;
- }
-
- const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const {
- if (usageCode) {
- ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- return &device->keysByUsageCode.valueAt(index);
- }
- }
- if (scanCode) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- return &device->keysByScanCode.valueAt(index);
- }
- }
- return NULL;
- }
-
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const {
- return NAME_NOT_FOUND;
- }
-
- virtual void setExcludedDevices(const Vector<String8>& devices) {
- mExcludedDevices = devices;
- }
-
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
- if (mEvents.empty()) {
- return 0;
- }
-
- *buffer = *mEvents.begin();
- mEvents.erase(mEvents.begin());
- return 1;
- }
-
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
- if (index >= 0) {
- return device->scanCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
- if (index >= 0) {
- return device->keyCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->switchStates.indexOfKey(sw);
- if (index >= 0) {
- return device->switchStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
- if (index >= 0) {
- *outValue = device->absoluteAxisValue.valueAt(index);
- return OK;
- }
- }
- *outValue = 0;
- return -1;
- }
-
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const {
- bool result = false;
- Device* device = getDevice(deviceId);
- if (device) {
- for (size_t i = 0; i < numCodes; i++) {
- for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
- if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- result = true;
- }
- }
- for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
- if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- result = true;
- }
- }
- }
- }
- return result;
- }
-
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- return index >= 0;
- }
- return false;
- }
-
- virtual bool hasLed(int32_t deviceId, int32_t led) const {
- Device* device = getDevice(deviceId);
- return device && device->leds.indexOfKey(led) >= 0;
- }
-
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->leds.indexOfKey(led);
- if (index >= 0) {
- device->leds.replaceValueAt(led, on);
- } else {
- ADD_FAILURE()
- << "Attempted to set the state of an LED that the EventHub declared "
- "was not present. led=" << led;
- }
- }
- }
-
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const {
- outVirtualKeys.clear();
-
- Device* device = getDevice(deviceId);
- if (device) {
- outVirtualKeys.appendVector(device->virtualKeys);
- }
- }
-
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const {
- return NULL;
- }
-
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
- return false;
- }
-
- virtual void vibrate(int32_t deviceId, nsecs_t duration) {
- }
-
- virtual void cancelVibrate(int32_t deviceId) {
- }
-
- virtual bool isExternal(int32_t deviceId) const {
- return false;
- }
-
- virtual void dump(String8& dump) {
- }
-
- virtual void monitor() {
- }
-
- virtual void requestReopenDevices() {
- }
-
- virtual void wake() {
- }
-};
-
-
-// --- FakeInputReaderContext ---
-
-class FakeInputReaderContext : public InputReaderContext {
- sp<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<InputListenerInterface> mListener;
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
-
-public:
- FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) :
- mEventHub(eventHub), mPolicy(policy), mListener(listener),
- mGlobalMetaState(0) {
- }
-
- virtual ~FakeInputReaderContext() { }
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) {
- mGlobalMetaState = state;
- }
-
-private:
- virtual void updateGlobalMetaState() {
- mUpdateGlobalMetaStateWasCalled = true;
- }
-
- virtual int32_t getGlobalMetaState() {
- return mGlobalMetaState;
- }
-
- virtual EventHubInterface* getEventHub() {
- return mEventHub.get();
- }
-
- virtual InputReaderPolicyInterface* getPolicy() {
- return mPolicy.get();
- }
-
- virtual InputListenerInterface* getListener() {
- return mListener.get();
- }
-
- virtual void disableVirtualKeysUntil(nsecs_t time) {
- }
-
- virtual bool shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) {
- return false;
- }
-
- virtual void fadePointer() {
- }
-
- virtual void requestTimeoutAtTime(nsecs_t when) {
- }
-
- virtual int32_t bumpGeneration() {
- return ++mGeneration;
- }
-};
-
-
-// --- FakeInputMapper ---
-
-class FakeInputMapper : public InputMapper {
- uint32_t mSources;
- int32_t mKeyboardType;
- int32_t mMetaState;
- KeyedVector<int32_t, int32_t> mKeyCodeStates;
- KeyedVector<int32_t, int32_t> mScanCodeStates;
- KeyedVector<int32_t, int32_t> mSwitchStates;
- Vector<int32_t> mSupportedKeyCodes;
- RawEvent mLastEvent;
-
- bool mConfigureWasCalled;
- bool mResetWasCalled;
- bool mProcessWasCalled;
-
-public:
- FakeInputMapper(InputDevice* device, uint32_t sources) :
- InputMapper(device),
- mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
- mMetaState(0),
- mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) {
- }
-
- virtual ~FakeInputMapper() { }
-
- void setKeyboardType(int32_t keyboardType) {
- mKeyboardType = keyboardType;
- }
-
- void setMetaState(int32_t metaState) {
- mMetaState = metaState;
- }
-
- void assertConfigureWasCalled() {
- ASSERT_TRUE(mConfigureWasCalled)
- << "Expected configure() to have been called.";
- mConfigureWasCalled = false;
- }
-
- void assertResetWasCalled() {
- ASSERT_TRUE(mResetWasCalled)
- << "Expected reset() to have been called.";
- mResetWasCalled = false;
- }
-
- void assertProcessWasCalled(RawEvent* outLastEvent = NULL) {
- ASSERT_TRUE(mProcessWasCalled)
- << "Expected process() to have been called.";
- if (outLastEvent) {
- *outLastEvent = mLastEvent;
- }
- mProcessWasCalled = false;
- }
-
- void setKeyCodeState(int32_t keyCode, int32_t state) {
- mKeyCodeStates.replaceValueFor(keyCode, state);
- }
-
- void setScanCodeState(int32_t scanCode, int32_t state) {
- mScanCodeStates.replaceValueFor(scanCode, state);
- }
-
- void setSwitchState(int32_t switchCode, int32_t state) {
- mSwitchStates.replaceValueFor(switchCode, state);
- }
-
- void addSupportedKeyCode(int32_t keyCode) {
- mSupportedKeyCodes.add(keyCode);
- }
-
-private:
- virtual uint32_t getSources() {
- return mSources;
- }
-
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
- InputMapper::populateDeviceInfo(deviceInfo);
-
- if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
- deviceInfo->setKeyboardType(mKeyboardType);
- }
- }
-
- virtual void configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- mConfigureWasCalled = true;
- }
-
- virtual void reset(nsecs_t when) {
- mResetWasCalled = true;
- }
-
- virtual void process(const RawEvent* rawEvent) {
- mLastEvent = *rawEvent;
- mProcessWasCalled = true;
- }
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
- return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
- }
-
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- ssize_t index = mScanCodeStates.indexOfKey(scanCode);
- return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
- }
-
- virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- ssize_t index = mSwitchStates.indexOfKey(switchCode);
- return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
- }
-
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
- for (size_t i = 0; i < numCodes; i++) {
- for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
- if (keyCodes[i] == mSupportedKeyCodes[j]) {
- outFlags[i] = 1;
- result = true;
- }
- }
- }
- return result;
- }
-
- virtual int32_t getMetaState() {
- return mMetaState;
- }
-
- virtual void fadePointer() {
- }
-};
-
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
- InputDevice* mNextDevice;
-
-public:
- InstrumentedInputReader(const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) :
- InputReader(eventHub, policy, listener),
- mNextDevice(NULL) {
- }
-
- virtual ~InstrumentedInputReader() {
- if (mNextDevice) {
- delete mNextDevice;
- }
- }
-
- void setNextDevice(InputDevice* device) {
- mNextDevice = device;
- }
-
- InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const String8& name,
- uint32_t classes) {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- int32_t generation = deviceId + 1;
- return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
- classes);
- }
-
-protected:
- virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes) {
- if (mNextDevice) {
- InputDevice* device = mNextDevice;
- mNextDevice = NULL;
- return device;
- }
- return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes);
- }
-
- friend class InputReaderTest;
-};
-
-
-// --- InputReaderTest ---
-
-class InputReaderTest : public testing::Test {
-protected:
- sp<FakeInputListener> mFakeListener;
- sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeEventHub> mFakeEventHub;
- sp<InstrumentedInputReader> mReader;
-
- virtual void SetUp() {
- mFakeEventHub = new FakeEventHub();
- mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
-
- mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
- }
-
- virtual void TearDown() {
- mReader.clear();
-
- mFakeListener.clear();
- mFakePolicy.clear();
- mFakeEventHub.clear();
- }
-
- void addDevice(int32_t deviceId, const String8& name, uint32_t classes,
- const PropertyMap* configuration) {
- mFakeEventHub->addDevice(deviceId, name, classes);
-
- if (configuration) {
- mFakeEventHub->addConfigurationMap(deviceId, configuration);
- }
- mFakeEventHub->finishDeviceScan();
- mReader->loopOnce();
- mReader->loopOnce();
- mFakeEventHub->assertQueueIsEmpty();
- }
-
- FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
- const String8& name, uint32_t classes, uint32_t sources,
- const PropertyMap* configuration) {
- InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
- FakeInputMapper* mapper = new FakeInputMapper(device, sources);
- device->addMapper(mapper);
- mReader->setNextDevice(device);
- addDevice(deviceId, name, classes, configuration);
- return mapper;
- }
-};
-
-TEST_F(InputReaderTest, GetInputDevices) {
- ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
- INPUT_DEVICE_CLASS_KEYBOARD, NULL));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"),
- 0, NULL)); // no classes so device will be ignored
-
- Vector<InputDeviceInfo> inputDevices;
- mReader->getInputDevices(inputDevices);
-
- ASSERT_EQ(1U, inputDevices.size());
- ASSERT_EQ(1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
- ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-
- // Should also have received a notification describing the new input devices.
- inputDevices = mFakePolicy->getInputDevices();
- ASSERT_EQ(1U, inputDevices.size());
- ASSERT_EQ(1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
- ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-}
-
-TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
- mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
- AINPUT_SOURCE_ANY, AKEYCODE_A))
- << "Should return unknown when the device id is >= 0 but unknown.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1,
- AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return unknown when the device id is valid but the sources are not supported by the device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1,
- AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(-1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
-}
-
-TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
- mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
- AINPUT_SOURCE_ANY, KEY_A))
- << "Should return unknown when the device id is >= 0 but unknown.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1,
- AINPUT_SOURCE_TRACKBALL, KEY_A))
- << "Should return unknown when the device id is valid but the sources are not supported by the device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
- << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1,
- AINPUT_SOURCE_TRACKBALL, KEY_A))
- << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(-1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
- << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
-}
-
-TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
- mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
- AINPUT_SOURCE_ANY, SW_LID))
- << "Should return unknown when the device id is >= 0 but unknown.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1,
- AINPUT_SOURCE_TRACKBALL, SW_LID))
- << "Should return unknown when the device id is valid but the sources are not supported by the device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
- << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1,
- AINPUT_SOURCE_TRACKBALL, SW_LID))
- << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(-1,
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
- << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
-}
-
-TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
- mapper->addSupportedKeyCode(AKEYCODE_A);
- mapper->addSupportedKeyCode(AKEYCODE_B);
-
- const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
- uint8_t flags[4] = { 0, 0, 0, 1 };
-
- ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, 4, keyCodes, flags))
- << "Should return false when device id is >= 0 but unknown.";
- ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
-
- flags[3] = 1;
- ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
- << "Should return false when device id is valid but the sources are not supported by the device.";
- ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
-
- flags[3] = 1;
- ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
- << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
- ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
-
- flags[3] = 1;
- ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
- << "Should return false when the device id is < 0 but the sources are not supported by any device.";
- ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
-
- flags[3] = 1;
- ASSERT_TRUE(mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
- << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
- ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
-}
-
-TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
- addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL);
-
- NotifyConfigurationChangedArgs args;
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
-}
-
-TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
-
- mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
- mReader->loopOnce();
- ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
-
- RawEvent event;
- ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event));
- ASSERT_EQ(0, event.when);
- ASSERT_EQ(1, event.deviceId);
- ASSERT_EQ(EV_KEY, event.type);
- ASSERT_EQ(KEY_A, event.code);
- ASSERT_EQ(1, event.value);
-}
-
-
-// --- InputDeviceTest ---
-
-class InputDeviceTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
-
- sp<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
-
- InputDevice* mDevice;
-
- virtual void SetUp() {
- mFakeEventHub = new FakeEventHub();
- mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-
- mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
- InputDeviceIdentifier identifier;
- identifier.name = DEVICE_NAME;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
- DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
- }
-
- virtual void TearDown() {
- delete mDevice;
-
- delete mFakeContext;
- mFakeListener.clear();
- mFakePolicy.clear();
- mFakeEventHub.clear();
- }
-};
-
-const char* InputDeviceTest::DEVICE_NAME = "device";
-const int32_t InputDeviceTest::DEVICE_ID = 1;
-const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
-const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
- | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
-
-TEST_F(InputDeviceTest, ImmutableProperties) {
- ASSERT_EQ(DEVICE_ID, mDevice->getId());
- ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
- ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
-}
-
-TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
- // Configuration.
- InputReaderConfiguration config;
- mDevice->configure(ARBITRARY_TIME, &config, 0);
-
- // Reset.
- mDevice->reset(ARBITRARY_TIME);
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- // Metadata.
- ASSERT_TRUE(mDevice->isIgnored());
- ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mDevice->getSources());
-
- InputDeviceInfo info;
- mDevice->getDeviceInfo(&info);
- ASSERT_EQ(DEVICE_ID, info.getId());
- ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources());
-
- // State queries.
- ASSERT_EQ(0, mDevice->getMetaState());
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, 0))
- << "Ignored device should return unknown key code state.";
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 0))
- << "Ignored device should return unknown scan code state.";
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 0))
- << "Ignored device should return unknown switch state.";
-
- const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
- uint8_t flags[2] = { 0, 1 };
- ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 2, keyCodes, flags))
- << "Ignored device should never mark any key codes.";
- ASSERT_EQ(0, flags[0]) << "Flag for unsupported key should be unchanged.";
- ASSERT_EQ(1, flags[1]) << "Flag for unsupported key should be unchanged.";
-}
-
-TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
- // Configuration.
- mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
-
- FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
- mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- mapper1->setMetaState(AMETA_ALT_ON);
- mapper1->addSupportedKeyCode(AKEYCODE_A);
- mapper1->addSupportedKeyCode(AKEYCODE_B);
- mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
- mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
- mapper1->setScanCodeState(2, AKEY_STATE_DOWN);
- mapper1->setScanCodeState(3, AKEY_STATE_UP);
- mapper1->setSwitchState(4, AKEY_STATE_DOWN);
- mDevice->addMapper(mapper1);
-
- FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
- mapper2->setMetaState(AMETA_SHIFT_ON);
- mDevice->addMapper(mapper2);
-
- InputReaderConfiguration config;
- mDevice->configure(ARBITRARY_TIME, &config, 0);
-
- String8 propertyValue;
- ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
- << "Device should have read configuration during configuration phase.";
- ASSERT_STREQ("value", propertyValue.string());
-
- ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
- ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled());
-
- // Reset
- mDevice->reset(ARBITRARY_TIME);
- ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled());
- ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled());
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- // Metadata.
- ASSERT_FALSE(mDevice->isIgnored());
- ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), mDevice->getSources());
-
- InputDeviceInfo info;
- mDevice->getDeviceInfo(&info);
- ASSERT_EQ(DEVICE_ID, info.getId());
- ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
- ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources());
-
- // State queries.
- ASSERT_EQ(AMETA_ALT_ON | AMETA_SHIFT_ON, mDevice->getMetaState())
- << "Should query mappers and combine meta states.";
-
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return unknown key code state when source not supported.";
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return unknown scan code state when source not supported.";
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
- << "Should return unknown switch state when source not supported.";
-
- ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, AKEYCODE_A))
- << "Should query mapper when source is supported.";
- ASSERT_EQ(AKEY_STATE_UP, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 3))
- << "Should query mapper when source is supported.";
- ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 4))
- << "Should query mapper when source is supported.";
-
- const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
- uint8_t flags[4] = { 0, 0, 0, 1 };
- ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
- << "Should do nothing when source is unsupported.";
- ASSERT_EQ(0, flags[0]) << "Flag should be unchanged when source is unsupported.";
- ASSERT_EQ(0, flags[1]) << "Flag should be unchanged when source is unsupported.";
- ASSERT_EQ(0, flags[2]) << "Flag should be unchanged when source is unsupported.";
- ASSERT_EQ(1, flags[3]) << "Flag should be unchanged when source is unsupported.";
-
- ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 4, keyCodes, flags))
- << "Should query mapper when source is supported.";
- ASSERT_EQ(1, flags[0]) << "Flag for supported key should be set.";
- ASSERT_EQ(1, flags[1]) << "Flag for supported key should be set.";
- ASSERT_EQ(0, flags[2]) << "Flag for unsupported key should be unchanged.";
- ASSERT_EQ(1, flags[3]) << "Flag for unsupported key should be unchanged.";
-
- // Event handling.
- RawEvent event;
- mDevice->process(&event, 1);
-
- ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
- ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
-}
-
-
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
-
- sp<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
- InputDevice* mDevice;
-
- virtual void SetUp() {
- mFakeEventHub = new FakeEventHub();
- mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
- InputDeviceIdentifier identifier;
- identifier.name = DEVICE_NAME;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
- DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
-
- mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
- }
-
- virtual void TearDown() {
- delete mDevice;
- delete mFakeContext;
- mFakeListener.clear();
- mFakePolicy.clear();
- mFakeEventHub.clear();
- }
-
- void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
- }
-
- void addMapperAndConfigure(InputMapper* mapper) {
- mDevice->addMapper(mapper);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
- mDevice->reset(ARBITRARY_TIME);
- }
-
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation) {
- mFakePolicy->setDisplayInfo(displayId, width, height, orientation);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
- int32_t code, int32_t value) {
- RawEvent event;
- event.when = when;
- event.deviceId = deviceId;
- event.type = type;
- event.code = code;
- event.value = value;
- mapper->process(&event);
- }
-
- static void assertMotionRange(const InputDeviceInfo& info,
- int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
- const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
- ASSERT_TRUE(range != NULL) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
- }
-
- static void assertPointerCoords(const PointerCoords& coords,
- float x, float y, float pressure, float size,
- float touchMajor, float touchMinor, float toolMajor, float toolMinor,
- float orientation, float distance) {
- ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
- ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
- ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
- ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), 1);
- ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), 1);
- ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1);
- ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1);
- ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
- ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
- }
-
- static void assertPosition(const sp<FakePointerController>& controller, float x, float y) {
- float actualX, actualY;
- controller->getPosition(&actualX, &actualY);
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
- }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const int32_t InputMapperTest::DEVICE_ID = 1;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
-
-
-// --- SwitchInputMapperTest ---
-
-class SwitchInputMapperTest : public InputMapperTest {
-protected:
-};
-
-TEST_F(SwitchInputMapperTest, GetSources) {
- SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
-}
-
-TEST_F(SwitchInputMapperTest, GetSwitchState) {
- SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
- addMapperAndConfigure(mapper);
-
- mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
- ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-
- mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
- ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-}
-
-TEST_F(SwitchInputMapperTest, Process) {
- SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
- addMapperAndConfigure(mapper);
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_HEADPHONE_INSERT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
-
- NotifySwitchArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT), args.switchValues);
- ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
- args.switchMask);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-}
-
-
-// --- KeyboardInputMapperTest ---
-
-class KeyboardInputMapperTest : public InputMapperTest {
-protected:
- void testDPadKeyRotation(KeyboardInputMapper* mapper,
- int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
-};
-
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
- int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
- NotifyKeyArgs args;
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
-}
-
-
-TEST_F(KeyboardInputMapperTest, GetSources) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
- const int32_t USAGE_A = 0x070004;
- const int32_t USAGE_UNKNOWN = 0x07ffff;
- mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down by usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, 0, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, 0, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_UNKNOWN, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_UNKNOWN, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
- mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
-
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- // Initial metastate.
- ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
-
- // Metakey down.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_LEFTSHIFT, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
-
- // Key down.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_A, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
-
- // Key up.
- process(mapper, ARBITRARY_TIME + 2, DEVICE_ID,
- EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
-
- // Metakey up.
- process(mapper, ARBITRARY_TIME + 3, DEVICE_ID,
- EV_KEY, KEY_LEFTSHIFT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
- mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
- mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addConfigurationProperty("keyboard.orientationAware", "1");
- addMapperAndConfigure(mapper);
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_180);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_270);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
-
- // Special case: if orientation changes while key is down, we still emit the same keycode
- // in the key up as we did in the key down.
- NotifyKeyArgs args;
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_270);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_180);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-}
-
-TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
- ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-
- mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
- ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-}
-
-TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
- ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-
- mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
- ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-}
-
-TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
-
- const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
- uint8_t flags[2] = { 0, 0 };
- ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
- ASSERT_TRUE(flags[0]);
- ASSERT_FALSE(flags[1]);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
- mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(DEVICE_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
- AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- addMapperAndConfigure(mapper);
-
- // Initialization should have turned all of the lights off.
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-
- // Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
-
- // Toggle num lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
-
- // Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
-
- // Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
-
- // Toggle num lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
-
- // Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
-}
-
-
-// --- CursorInputMapperTest ---
-
-class CursorInputMapperTest : public InputMapperTest {
-protected:
- static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
-
- sp<FakePointerController> mFakePointerController;
-
- virtual void SetUp() {
- InputMapperTest::SetUp();
-
- mFakePointerController = new FakePointerController();
- mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
- }
-
- void testMotionRotation(CursorInputMapper* mapper,
- int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
-};
-
-const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
- int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
- NotifyMotionArgs args;
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, originalX);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, originalY);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
- float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD,
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "pointer");
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "pointer");
- addMapperAndConfigure(mapper);
-
- InputDeviceInfo info;
- mapper->populateDeviceInfo(&info);
-
- // Initially there may not be a valid motion range.
- ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
- ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-
- // When the bounds are set, then there should be a valid motion range.
- mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
-
- InputDeviceInfo info2;
- mapper->populateDeviceInfo(&info2);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
- 1, 800 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE,
- 2, 480 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE,
- 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- InputDeviceInfo info;
- mapper->populateDeviceInfo(&info);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL,
- 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs args;
-
- // Button press.
- // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(0, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs args;
-
- // Motion in X but not Y.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs args;
-
- // Button press.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Button release.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs args;
-
- // Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Release Button.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addMapperAndConfigure(mapper);
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
-}
-
-TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "navigation");
- addConfigurationProperty("cursor.orientationAware", "1");
- addMapperAndConfigure(mapper);
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "pointer");
- addMapperAndConfigure(mapper);
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
- mFakePointerController->setButtonState(0);
-
- NotifyMotionArgs motionArgs;
- NotifyKeyArgs keyArgs;
-
- // press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- mFakePointerController->getButtonState());
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, mFakePointerController->getButtonState());
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-}
-
-TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
- CursorInputMapper* mapper = new CursorInputMapper(mDevice);
- addConfigurationProperty("cursor.mode", "pointer");
- addMapperAndConfigure(mapper);
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
- mFakePointerController->setButtonState(0);
-
- NotifyMotionArgs args;
-
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
-}
-
-
-// --- TouchInputMapperTest ---
-
-class TouchInputMapperTest : public InputMapperTest {
-protected:
- static const int32_t RAW_X_MIN;
- static const int32_t RAW_X_MAX;
- static const int32_t RAW_Y_MIN;
- static const int32_t RAW_Y_MAX;
- static const int32_t RAW_TOUCH_MIN;
- static const int32_t RAW_TOUCH_MAX;
- static const int32_t RAW_TOOL_MIN;
- static const int32_t RAW_TOOL_MAX;
- static const int32_t RAW_PRESSURE_MIN;
- static const int32_t RAW_PRESSURE_MAX;
- static const int32_t RAW_ORIENTATION_MIN;
- static const int32_t RAW_ORIENTATION_MAX;
- static const int32_t RAW_DISTANCE_MIN;
- static const int32_t RAW_DISTANCE_MAX;
- static const int32_t RAW_TILT_MIN;
- static const int32_t RAW_TILT_MAX;
- static const int32_t RAW_ID_MIN;
- static const int32_t RAW_ID_MAX;
- static const int32_t RAW_SLOT_MIN;
- static const int32_t RAW_SLOT_MAX;
- static const float X_PRECISION;
- static const float Y_PRECISION;
-
- static const float GEOMETRIC_SCALE;
-
- static const VirtualKeyDefinition VIRTUAL_KEYS[2];
-
- enum Axes {
- POSITION = 1 << 0,
- TOUCH = 1 << 1,
- TOOL = 1 << 2,
- PRESSURE = 1 << 3,
- ORIENTATION = 1 << 4,
- MINOR = 1 << 5,
- ID = 1 << 6,
- DISTANCE = 1 << 7,
- TILT = 1 << 8,
- SLOT = 1 << 9,
- TOOL_TYPE = 1 << 10,
- };
-
- void prepareDisplay(int32_t orientation);
- void prepareVirtualKeys();
- int32_t toRawX(float displayX);
- int32_t toRawY(float displayY);
- float toDisplayX(int32_t rawX);
- float toDisplayY(int32_t rawY);
-};
-
-const int32_t TouchInputMapperTest::RAW_X_MIN = 25;
-const int32_t TouchInputMapperTest::RAW_X_MAX = 1019;
-const int32_t TouchInputMapperTest::RAW_Y_MIN = 30;
-const int32_t TouchInputMapperTest::RAW_Y_MAX = 1009;
-const int32_t TouchInputMapperTest::RAW_TOUCH_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31;
-const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_TOOL_MAX = 15;
-const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = RAW_TOUCH_MIN;
-const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX;
-const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7;
-const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7;
-const int32_t TouchInputMapperTest::RAW_DISTANCE_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_DISTANCE_MAX = 7;
-const int32_t TouchInputMapperTest::RAW_TILT_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_TILT_MAX = 150;
-const int32_t TouchInputMapperTest::RAW_ID_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_ID_MAX = 9;
-const int32_t TouchInputMapperTest::RAW_SLOT_MIN = 0;
-const int32_t TouchInputMapperTest::RAW_SLOT_MAX = 9;
-const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
-const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
-
-const float TouchInputMapperTest::GEOMETRIC_SCALE =
- avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN + 1),
- float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN + 1));
-
-const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = {
- { KEY_HOME, 60, DISPLAY_HEIGHT + 15, 20, 20 },
- { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
-};
-
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
-}
-
-void TouchInputMapperTest::prepareVirtualKeys() {
- mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
- mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
- mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
-}
-
-int32_t TouchInputMapperTest::toRawX(float displayX) {
- return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH + RAW_X_MIN);
-}
-
-int32_t TouchInputMapperTest::toRawY(float displayY) {
- return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
-}
-
-float TouchInputMapperTest::toDisplayX(int32_t rawX) {
- return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN + 1);
-}
-
-float TouchInputMapperTest::toDisplayY(int32_t rawY) {
- return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN + 1);
-}
-
-
-// --- SingleTouchInputMapperTest ---
-
-class SingleTouchInputMapperTest : public TouchInputMapperTest {
-protected:
- void prepareButtons();
- void prepareAxes(int axes);
-
- void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
- void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
- void processUp(SingleTouchInputMapper* mappery);
- void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
- void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
- void processDistance(SingleTouchInputMapper* mapper, int32_t distance);
- void processTilt(SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY);
- void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value);
- void processSync(SingleTouchInputMapper* mapper);
-};
-
-void SingleTouchInputMapperTest::prepareButtons() {
- mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-}
-
-void SingleTouchInputMapperTest::prepareAxes(int axes) {
- if (axes & POSITION) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X,
- RAW_X_MIN, RAW_X_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y,
- RAW_Y_MIN, RAW_Y_MAX, 0, 0);
- }
- if (axes & PRESSURE) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE,
- RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
- }
- if (axes & TOOL) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH,
- RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
- }
- if (axes & DISTANCE) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE,
- RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
- }
- if (axes & TILT) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_X,
- RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_Y,
- RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
- }
-}
-
-void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
-}
-
-void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
-}
-
-void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0);
-}
-
-void SingleTouchInputMapperTest::processPressure(
- SingleTouchInputMapper* mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, pressure);
-}
-
-void SingleTouchInputMapperTest::processToolMajor(
- SingleTouchInputMapper* mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
-}
-
-void SingleTouchInputMapperTest::processDistance(
- SingleTouchInputMapper* mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_DISTANCE, distance);
-}
-
-void SingleTouchInputMapperTest::processTilt(
- SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_X, tiltX);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_Y, tiltY);
-}
-
-void SingleTouchInputMapperTest::processKey(
- SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
-}
-
-void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
-}
-
-
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- prepareButtons();
- prepareAxes(POSITION);
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
-}
-
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
- mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
- prepareButtons();
- prepareAxes(POSITION);
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
-}
-
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- prepareButtons();
- prepareAxes(POSITION);
- addConfigurationProperty("touch.deviceType", "touchPad");
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
-}
-
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- prepareButtons();
- prepareAxes(POSITION);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- addMapperAndConfigure(mapper);
-
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
-}
-
-TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- // Unknown key.
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-
- // Virtual key is down.
- int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
- int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
- processDown(mapper, x, y);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
-
- ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
-
- // Virtual key is up.
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
-
- ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
-}
-
-TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- // Unknown key.
- ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-
- // Virtual key is down.
- int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
- int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
- processDown(mapper, x, y);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
-
- ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
-
- // Virtual key is up.
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
-
- ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
-}
-
-TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A };
- uint8_t flags[2] = { 0, 0 };
- ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
- ASSERT_TRUE(flags[0]);
- ASSERT_FALSE(flags[1]);
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyKeyArgs args;
-
- // Press virtual key.
- int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
- int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
- processDown(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Release virtual key.
- processUp(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Should not have sent any motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyKeyArgs keyArgs;
-
- // Press virtual key.
- int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
- int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
- processDown(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
- ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, keyArgs.flags);
- ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode);
- ASSERT_EQ(KEY_HOME, keyArgs.scanCode);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState);
- ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime);
-
- // Move out of bounds. This should generate a cancel and a pointer down since we moved
- // into the display area.
- y -= 100;
- processMove(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
- ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
- | AKEY_EVENT_FLAG_CANCELED, keyArgs.flags);
- ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode);
- ASSERT_EQ(KEY_HOME, keyArgs.scanCode);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState);
- ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime);
-
- NotifyMotionArgs motionArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Keep moving out of bounds. Should generate a pointer move.
- y -= 50;
- processMove(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Release out of bounds. Should generate a pointer up.
- processUp(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs motionArgs;
-
- // Initially go down out of bounds.
- int32_t x = -10;
- int32_t y = -10;
- processDown(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // Move into the display area. Should generate a pointer down.
- x = 50;
- y = 75;
- processMove(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Release. Should generate a pointer up.
- processUp(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs motionArgs;
-
- // Down.
- int32_t x = 100;
- int32_t y = 125;
- processDown(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Move.
- x += 50;
- y += 75;
- processMove(mapper, x, y);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Up.
- processUp(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareButtons();
- prepareAxes(POSITION);
- addConfigurationProperty("touch.orientationAware", "0");
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs args;
-
- // Rotation 90.
- prepareDisplay(DISPLAY_ORIENTATION_90);
- processDown(mapper, toRawX(50), toRawY(75));
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareButtons();
- prepareAxes(POSITION);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs args;
-
- // Rotation 0.
- prepareDisplay(DISPLAY_ORIENTATION_0);
- processDown(mapper, toRawX(50), toRawY(75));
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
-
- // Rotation 90.
- prepareDisplay(DISPLAY_ORIENTATION_90);
- processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
-
- // Rotation 180.
- prepareDisplay(DISPLAY_ORIENTATION_180);
- processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
-
- // Rotation 270.
- prepareDisplay(DISPLAY_ORIENTATION_270);
- processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawPressure = 10;
- int32_t rawToolMajor = 12;
- int32_t rawDistance = 2;
- int32_t rawTiltX = 30;
- int32_t rawTiltY = 110;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float pressure = float(rawPressure) / RAW_PRESSURE_MAX;
- float size = float(rawToolMajor) / RAW_TOOL_MAX;
- float tool = float(rawToolMajor) * GEOMETRIC_SCALE;
- float distance = float(rawDistance);
-
- float tiltCenter = (RAW_TILT_MAX + RAW_TILT_MIN) * 0.5f;
- float tiltScale = M_PI / 180;
- float tiltXAngle = (rawTiltX - tiltCenter) * tiltScale;
- float tiltYAngle = (rawTiltY - tiltCenter) * tiltScale;
- float orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
- float tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
-
- processDown(mapper, rawX, rawY);
- processPressure(mapper, rawPressure);
- processToolMajor(mapper, rawToolMajor);
- processDistance(mapper, rawDistance);
- processTilt(mapper, rawTiltX, rawTiltY);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, pressure, size, tool, tool, tool, tool, orientation, distance));
- ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT));
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
- NotifyKeyArgs keyArgs;
-
- processDown(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-
- // press BTN_LEFT, release BTN_LEFT
- processKey(mapper, BTN_LEFT, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_LEFT, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- processKey(mapper, BTN_RIGHT, 1);
- processKey(mapper, BTN_MIDDLE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
-
- processKey(mapper, BTN_RIGHT, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_MIDDLE, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_BACK, release BTN_BACK
- processKey(mapper, BTN_BACK, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_BACK, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_SIDE, release BTN_SIDE
- processKey(mapper, BTN_SIDE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_SIDE, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_FORWARD, release BTN_FORWARD
- processKey(mapper, BTN_FORWARD, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_FORWARD, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_EXTRA, release BTN_EXTRA
- processKey(mapper, BTN_EXTRA, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_EXTRA, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_STYLUS, release BTN_STYLUS
- processKey(mapper, BTN_STYLUS, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_STYLUS, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_STYLUS2, release BTN_STYLUS2
- processKey(mapper, BTN_STYLUS2, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_STYLUS2, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // release touch
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // default tool type is finger
- processDown(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // eraser
- processKey(mapper, BTN_TOOL_RUBBER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
-
- // stylus
- processKey(mapper, BTN_TOOL_RUBBER, 0);
- processKey(mapper, BTN_TOOL_PEN, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // brush
- processKey(mapper, BTN_TOOL_PEN, 0);
- processKey(mapper, BTN_TOOL_BRUSH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // pencil
- processKey(mapper, BTN_TOOL_BRUSH, 0);
- processKey(mapper, BTN_TOOL_PENCIL, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // airbrush
- processKey(mapper, BTN_TOOL_PENCIL, 0);
- processKey(mapper, BTN_TOOL_AIRBRUSH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // mouse
- processKey(mapper, BTN_TOOL_AIRBRUSH, 0);
- processKey(mapper, BTN_TOOL_MOUSE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // lens
- processKey(mapper, BTN_TOOL_MOUSE, 0);
- processKey(mapper, BTN_TOOL_LENS, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // double-tap
- processKey(mapper, BTN_TOOL_LENS, 0);
- processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // triple-tap
- processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
- processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // quad-tap
- processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
- processKey(mapper, BTN_TOOL_QUADTAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // finger
- processKey(mapper, BTN_TOOL_QUADTAP, 0);
- processKey(mapper, BTN_TOOL_FINGER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // stylus trumps finger
- processKey(mapper, BTN_TOOL_PEN, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // eraser trumps stylus
- processKey(mapper, BTN_TOOL_RUBBER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
-
- // mouse trumps eraser
- processKey(mapper, BTN_TOOL_MOUSE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // back to default tool type
- processKey(mapper, BTN_TOOL_MOUSE, 0);
- processKey(mapper, BTN_TOOL_RUBBER, 0);
- processKey(mapper, BTN_TOOL_PEN, 0);
- processKey(mapper, BTN_TOOL_FINGER, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // initially hovering because BTN_TOUCH not sent yet, pressure defaults to 0
- processKey(mapper, BTN_TOOL_FINGER, 1);
- processMove(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // move a little
- processMove(mapper, 150, 250);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // down when BTN_TOUCH is pressed, pressure defaults to 1
- processKey(mapper, BTN_TOUCH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // up when BTN_TOUCH is released, hover restored
- processKey(mapper, BTN_TOUCH, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // exit hover when pointer goes away
- processKey(mapper, BTN_TOOL_FINGER, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
- SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareButtons();
- prepareAxes(POSITION | PRESSURE);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // initially hovering because pressure is 0
- processDown(mapper, 100, 200);
- processPressure(mapper, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // move a little
- processMove(mapper, 150, 250);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // down when pressure is non-zero
- processPressure(mapper, RAW_PRESSURE_MAX);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // up when pressure becomes 0, hover restored
- processPressure(mapper, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // exit hover when pointer goes away
- processUp(mapper);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-}
-
-
-// --- MultiTouchInputMapperTest ---
-
-class MultiTouchInputMapperTest : public TouchInputMapperTest {
-protected:
- void prepareAxes(int axes);
-
- void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y);
- void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor);
- void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor);
- void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor);
- void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
- void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
- void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
- void processDistance(MultiTouchInputMapper* mapper, int32_t distance);
- void processId(MultiTouchInputMapper* mapper, int32_t id);
- void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
- void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
- void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
- void processMTSync(MultiTouchInputMapper* mapper);
- void processSync(MultiTouchInputMapper* mapper);
-};
-
-void MultiTouchInputMapperTest::prepareAxes(int axes) {
- if (axes & POSITION) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X,
- RAW_X_MIN, RAW_X_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y,
- RAW_Y_MIN, RAW_Y_MAX, 0, 0);
- }
- if (axes & TOUCH) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR,
- RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
- if (axes & MINOR) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
- RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
- }
- }
- if (axes & TOOL) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR,
- RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
- if (axes & MINOR) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
- RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0);
- }
- }
- if (axes & ORIENTATION) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION,
- RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0);
- }
- if (axes & PRESSURE) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE,
- RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
- }
- if (axes & DISTANCE) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE,
- RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
- }
- if (axes & ID) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
- RAW_ID_MIN, RAW_ID_MAX, 0, 0);
- }
- if (axes & SLOT) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT,
- RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
- mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0);
- }
- if (axes & TOOL_TYPE) {
- mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE,
- 0, MT_TOOL_MAX, 0, 0);
- }
-}
-
-void MultiTouchInputMapperTest::processPosition(
- MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, y);
-}
-
-void MultiTouchInputMapperTest::processTouchMajor(
- MultiTouchInputMapper* mapper, int32_t touchMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
-}
-
-void MultiTouchInputMapperTest::processTouchMinor(
- MultiTouchInputMapper* mapper, int32_t touchMinor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
-}
-
-void MultiTouchInputMapperTest::processToolMajor(
- MultiTouchInputMapper* mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
-}
-
-void MultiTouchInputMapperTest::processToolMinor(
- MultiTouchInputMapper* mapper, int32_t toolMinor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
-}
-
-void MultiTouchInputMapperTest::processOrientation(
- MultiTouchInputMapper* mapper, int32_t orientation) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, orientation);
-}
-
-void MultiTouchInputMapperTest::processPressure(
- MultiTouchInputMapper* mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, pressure);
-}
-
-void MultiTouchInputMapperTest::processDistance(
- MultiTouchInputMapper* mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_DISTANCE, distance);
-}
-
-void MultiTouchInputMapperTest::processId(
- MultiTouchInputMapper* mapper, int32_t id) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, id);
-}
-
-void MultiTouchInputMapperTest::processSlot(
- MultiTouchInputMapper* mapper, int32_t slot) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_SLOT, slot);
-}
-
-void MultiTouchInputMapperTest::processToolType(
- MultiTouchInputMapper* mapper, int32_t toolType) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
-}
-
-void MultiTouchInputMapperTest::processKey(
- MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
-}
-
-void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0);
-}
-
-void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
-}
-
-
-TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs motionArgs;
-
- // Two fingers down at once.
- int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
- processPosition(mapper, x1, y1);
- processMTSync(mapper);
- processPosition(mapper, x2, y2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Move.
- x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
- processPosition(mapper, x1, y1);
- processMTSync(mapper);
- processPosition(mapper, x2, y2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // First finger up.
- x2 += 15; y2 -= 20;
- processPosition(mapper, x2, y2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Move.
- x2 += 20; y2 -= 25;
- processPosition(mapper, x2, y2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // New finger down.
- int32_t x3 = 700, y3 = 300;
- processPosition(mapper, x2, y2);
- processMTSync(mapper);
- processPosition(mapper, x3, y3);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Second finger up.
- x3 += 30; y3 -= 20;
- processPosition(mapper, x3, y3);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Last finger up.
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
- ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
- ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
- ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs motionArgs;
-
- // Two fingers down at once.
- int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
- processPosition(mapper, x1, y1);
- processId(mapper, 1);
- processMTSync(mapper);
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Move.
- x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
- processPosition(mapper, x1, y1);
- processId(mapper, 1);
- processMTSync(mapper);
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // First finger up.
- x2 += 15; y2 -= 20;
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Move.
- x2 += 20; y2 -= 25;
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // New finger down.
- int32_t x3 = 700, y3 = 300;
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processMTSync(mapper);
- processPosition(mapper, x3, y3);
- processId(mapper, 3);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Second finger up.
- x3 += 30; y3 -= 20;
- processPosition(mapper, x3, y3);
- processId(mapper, 3);
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Last finger up.
- processMTSync(mapper);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID | SLOT);
- prepareVirtualKeys();
- addMapperAndConfigure(mapper);
-
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs motionArgs;
-
- // Two fingers down at once.
- int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
- processPosition(mapper, x1, y1);
- processId(mapper, 1);
- processSlot(mapper, 1);
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Move.
- x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
- processSlot(mapper, 0);
- processPosition(mapper, x1, y1);
- processSlot(mapper, 1);
- processPosition(mapper, x2, y2);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // First finger up.
- x2 += 15; y2 -= 20;
- processSlot(mapper, 0);
- processId(mapper, -1);
- processSlot(mapper, 1);
- processPosition(mapper, x2, y2);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Move.
- x2 += 20; y2 -= 25;
- processPosition(mapper, x2, y2);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // New finger down.
- int32_t x3 = 700, y3 = 300;
- processPosition(mapper, x2, y2);
- processSlot(mapper, 0);
- processId(mapper, 3);
- processPosition(mapper, x3, y3);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Second finger up.
- x3 += 30; y3 -= 20;
- processSlot(mapper, 1);
- processId(mapper, -1);
- processSlot(mapper, 0);
- processPosition(mapper, x3, y3);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Last finger up.
- processId(mapper, -1);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // Should not have sent any more keys or motions.
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawTouchMajor = 7;
- int32_t rawTouchMinor = 6;
- int32_t rawToolMajor = 9;
- int32_t rawToolMinor = 8;
- int32_t rawPressure = 11;
- int32_t rawDistance = 0;
- int32_t rawOrientation = 3;
- int32_t id = 5;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float pressure = float(rawPressure) / RAW_PRESSURE_MAX;
- float size = avg(rawTouchMajor, rawTouchMinor) / RAW_TOUCH_MAX;
- float toolMajor = float(rawToolMajor) * GEOMETRIC_SCALE;
- float toolMinor = float(rawToolMinor) * GEOMETRIC_SCALE;
- float touchMajor = float(rawTouchMajor) * GEOMETRIC_SCALE;
- float touchMinor = float(rawTouchMinor) * GEOMETRIC_SCALE;
- float orientation = float(rawOrientation) / RAW_ORIENTATION_MAX * M_PI_2;
- float distance = float(rawDistance);
-
- processPosition(mapper, rawX, rawY);
- processTouchMajor(mapper, rawTouchMajor);
- processTouchMinor(mapper, rawTouchMinor);
- processToolMajor(mapper, rawToolMajor);
- processToolMinor(mapper, rawToolMinor);
- processPressure(mapper, rawPressure);
- processOrientation(mapper, rawOrientation);
- processDistance(mapper, rawDistance);
- processId(mapper, id);
- processMTSync(mapper);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor,
- orientation, distance));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | TOUCH | TOOL | MINOR);
- addConfigurationProperty("touch.size.calibration", "geometric");
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawTouchMajor = 140;
- int32_t rawTouchMinor = 120;
- int32_t rawToolMajor = 180;
- int32_t rawToolMinor = 160;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float size = avg(rawTouchMajor, rawTouchMinor) / RAW_TOUCH_MAX;
- float toolMajor = float(rawToolMajor) * GEOMETRIC_SCALE;
- float toolMinor = float(rawToolMinor) * GEOMETRIC_SCALE;
- float touchMajor = float(rawTouchMajor) * GEOMETRIC_SCALE;
- float touchMinor = float(rawTouchMinor) * GEOMETRIC_SCALE;
-
- processPosition(mapper, rawX, rawY);
- processTouchMajor(mapper, rawTouchMajor);
- processTouchMinor(mapper, rawTouchMinor);
- processToolMajor(mapper, rawToolMajor);
- processToolMinor(mapper, rawToolMinor);
- processMTSync(mapper);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, 1.0f, size, touchMajor, touchMinor, toolMajor, toolMinor, 0, 0));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | TOUCH | TOOL);
- addConfigurationProperty("touch.size.calibration", "diameter");
- addConfigurationProperty("touch.size.scale", "10");
- addConfigurationProperty("touch.size.bias", "160");
- addConfigurationProperty("touch.size.isSummed", "1");
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- // Note: We only provide a single common touch/tool value because the device is assumed
- // not to emit separate values for each pointer (isSummed = 1).
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawX2 = 150;
- int32_t rawY2 = 250;
- int32_t rawTouchMajor = 5;
- int32_t rawToolMajor = 8;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float x2 = toDisplayX(rawX2);
- float y2 = toDisplayY(rawY2);
- float size = float(rawTouchMajor) / 2 / RAW_TOUCH_MAX;
- float touch = float(rawTouchMajor) / 2 * 10.0f + 160.0f;
- float tool = float(rawToolMajor) / 2 * 10.0f + 160.0f;
-
- processPosition(mapper, rawX, rawY);
- processTouchMajor(mapper, rawTouchMajor);
- processToolMajor(mapper, rawToolMajor);
- processMTSync(mapper);
- processPosition(mapper, rawX2, rawY2);
- processTouchMajor(mapper, rawTouchMajor);
- processToolMajor(mapper, rawToolMajor);
- processMTSync(mapper);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- args.action);
- ASSERT_EQ(size_t(2), args.pointerCount);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, 1.0f, size, touch, touch, tool, tool, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1],
- x2, y2, 1.0f, size, touch, touch, tool, tool, 0, 0));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | TOUCH | TOOL);
- addConfigurationProperty("touch.size.calibration", "area");
- addConfigurationProperty("touch.size.scale", "43");
- addConfigurationProperty("touch.size.bias", "3");
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawTouchMajor = 5;
- int32_t rawToolMajor = 8;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float size = float(rawTouchMajor) / RAW_TOUCH_MAX;
- float touch = sqrtf(rawTouchMajor) * 43.0f + 3.0f;
- float tool = sqrtf(rawToolMajor) * 43.0f + 3.0f;
-
- processPosition(mapper, rawX, rawY);
- processTouchMajor(mapper, rawTouchMajor);
- processToolMajor(mapper, rawToolMajor);
- processMTSync(mapper);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, 1.0f, size, touch, touch, tool, tool, 0, 0));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | PRESSURE);
- addConfigurationProperty("touch.pressure.calibration", "amplitude");
- addConfigurationProperty("touch.pressure.scale", "0.01");
- addMapperAndConfigure(mapper);
-
- // These calculations are based on the input device calibration documentation.
- int32_t rawX = 100;
- int32_t rawY = 200;
- int32_t rawPressure = 60;
-
- float x = toDisplayX(rawX);
- float y = toDisplayY(rawY);
- float pressure = float(rawPressure) * 0.01f;
-
- processPosition(mapper, rawX, rawY);
- processPressure(mapper, rawPressure);
- processMTSync(mapper);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- x, y, pressure, 0, 0, 0, 0, 0, 0, 0));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID | SLOT);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
- NotifyKeyArgs keyArgs;
-
- processId(mapper, 1);
- processPosition(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-
- // press BTN_LEFT, release BTN_LEFT
- processKey(mapper, BTN_LEFT, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_LEFT, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- processKey(mapper, BTN_RIGHT, 1);
- processKey(mapper, BTN_MIDDLE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
-
- processKey(mapper, BTN_RIGHT, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_MIDDLE, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_BACK, release BTN_BACK
- processKey(mapper, BTN_BACK, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_BACK, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_SIDE, release BTN_SIDE
- processKey(mapper, BTN_SIDE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_SIDE, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_FORWARD, release BTN_FORWARD
- processKey(mapper, BTN_FORWARD, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_FORWARD, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_EXTRA, release BTN_EXTRA
- processKey(mapper, BTN_EXTRA, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- processKey(mapper, BTN_EXTRA, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_STYLUS, release BTN_STYLUS
- processKey(mapper, BTN_STYLUS, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_STYLUS, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // press BTN_STYLUS2, release BTN_STYLUS2
- processKey(mapper, BTN_STYLUS2, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
-
- processKey(mapper, BTN_STYLUS2, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-
- // release touch
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // default tool type is finger
- processId(mapper, 1);
- processPosition(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // eraser
- processKey(mapper, BTN_TOOL_RUBBER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
-
- // stylus
- processKey(mapper, BTN_TOOL_RUBBER, 0);
- processKey(mapper, BTN_TOOL_PEN, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // brush
- processKey(mapper, BTN_TOOL_PEN, 0);
- processKey(mapper, BTN_TOOL_BRUSH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // pencil
- processKey(mapper, BTN_TOOL_BRUSH, 0);
- processKey(mapper, BTN_TOOL_PENCIL, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // airbrush
- processKey(mapper, BTN_TOOL_PENCIL, 0);
- processKey(mapper, BTN_TOOL_AIRBRUSH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // mouse
- processKey(mapper, BTN_TOOL_AIRBRUSH, 0);
- processKey(mapper, BTN_TOOL_MOUSE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // lens
- processKey(mapper, BTN_TOOL_MOUSE, 0);
- processKey(mapper, BTN_TOOL_LENS, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // double-tap
- processKey(mapper, BTN_TOOL_LENS, 0);
- processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // triple-tap
- processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
- processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // quad-tap
- processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
- processKey(mapper, BTN_TOOL_QUADTAP, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // finger
- processKey(mapper, BTN_TOOL_QUADTAP, 0);
- processKey(mapper, BTN_TOOL_FINGER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // stylus trumps finger
- processKey(mapper, BTN_TOOL_PEN, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // eraser trumps stylus
- processKey(mapper, BTN_TOOL_RUBBER, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
-
- // mouse trumps eraser
- processKey(mapper, BTN_TOOL_MOUSE, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
-
- // MT tool type trumps BTN tool types: MT_TOOL_FINGER
- processToolType(mapper, MT_TOOL_FINGER); // this is the first time we send MT_TOOL_TYPE
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-
- // MT tool type trumps BTN tool types: MT_TOOL_PEN
- processToolType(mapper, MT_TOOL_PEN);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
-
- // back to default tool type
- processToolType(mapper, -1); // use a deliberately undefined tool type, for testing
- processKey(mapper, BTN_TOOL_MOUSE, 0);
- processKey(mapper, BTN_TOOL_RUBBER, 0);
- processKey(mapper, BTN_TOOL_PEN, 0);
- processKey(mapper, BTN_TOOL_FINGER, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID | SLOT);
- mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // initially hovering because BTN_TOUCH not sent yet, pressure defaults to 0
- processId(mapper, 1);
- processPosition(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // move a little
- processPosition(mapper, 150, 250);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // down when BTN_TOUCH is pressed, pressure defaults to 1
- processKey(mapper, BTN_TOUCH, 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // up when BTN_TOUCH is released, hover restored
- processKey(mapper, BTN_TOUCH, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // exit hover when pointer goes away
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
- MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION | ID | SLOT | PRESSURE);
- addMapperAndConfigure(mapper);
-
- NotifyMotionArgs motionArgs;
-
- // initially hovering because pressure is 0
- processId(mapper, 1);
- processPosition(mapper, 100, 200);
- processPressure(mapper, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // move a little
- processPosition(mapper, 150, 250);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // down when pressure becomes non-zero
- processPressure(mapper, RAW_PRESSURE_MAX);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- // up when pressure becomes 0, hover restored
- processPressure(mapper, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
- // exit hover when pointer goes away
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-}
-
-
-} // namespace android
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
index 300224a..3e07155 100644
--- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
@@ -11,7 +11,6 @@ LOCAL_SRC_FILES := accessorychat.c
LOCAL_MODULE := accessorychat
-LOCAL_C_INCLUDES += bionic/libc/kernel/common
LOCAL_STATIC_LIBRARIES := libusbhost libcutils
LOCAL_LDLIBS += -lpthread
LOCAL_CFLAGS := -g -O0
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/linux/usb/f_accessory.h b/libs/usb/tests/AccessoryChat/accessorychat/linux/usb/f_accessory.h
new file mode 100644
index 0000000..75e017c
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/linux/usb/f_accessory.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+#define ACCESSORY_GET_PROTOCOL 51
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+#define ACCESSORY_REGISTER_HID 54
+#define ACCESSORY_UNREGISTER_HID 55
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+#define ACCESSORY_SEND_HID_EVENT 57
+#define ACCESSORY_SET_AUDIO_MODE 58
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libs/usb/tests/accessorytest/Android.mk b/libs/usb/tests/accessorytest/Android.mk
new file mode 100644
index 0000000..6d9a946
--- /dev/null
+++ b/libs/usb/tests/accessorytest/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build for Linux host only
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := accessory.c \
+ audio.c \
+ hid.c \
+ usb.c
+
+LOCAL_C_INCLUDES += external/tinyalsa/include
+
+LOCAL_MODULE := accessorytest
+
+LOCAL_STATIC_LIBRARIES := libusbhost libcutils libtinyalsa
+LOCAL_LDLIBS += -lpthread
+LOCAL_CFLAGS := -g -O0
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/libs/usb/tests/accessorytest/accessory.c b/libs/usb/tests/accessorytest/accessory.c
new file mode 100644
index 0000000..a3c47af
--- /dev/null
+++ b/libs/usb/tests/accessorytest/accessory.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "accessory.h"
+
+static void usage(char* name) {
+ fprintf(stderr, "Usage: %s [-a] [-h] [-ic input card] [-id input device] "
+ "[-oc output card] [-d output device] [-a] [-h] [-i]\n\n"
+ "\t-ic, -id, -oc and -od specify ALSA card and device numbers\n"
+ "\t-a : enables AccessoryChat mode\n"
+ "\t-i : enables HID pass through (requires running as root\n"
+ "\t-h : prints this usage message\n", name);
+}
+
+int main(int argc, char* argv[])
+{
+ unsigned int input_card = 2;
+ unsigned int input_device = 0;
+ unsigned int output_card = 0;
+ unsigned int output_device = 0;
+ unsigned int enable_accessory = 0;
+ unsigned int enable_hid = 0;
+
+ /* parse command line arguments */
+ argv += 1;
+ while (*argv) {
+ if (strcmp(*argv, "-ic") == 0) {
+ argv++;
+ if (*argv)
+ input_card = atoi(*argv);
+ } else if (strcmp(*argv, "-id") == 0) {
+ argv++;
+ if (*argv)
+ input_device = atoi(*argv);
+ } else if (strcmp(*argv, "-oc") == 0) {
+ argv++;
+ if (*argv)
+ output_card = atoi(*argv);
+ } else if (strcmp(*argv, "-od") == 0) {
+ argv++;
+ if (*argv)
+ output_device = atoi(*argv);
+ } else if (strcmp(*argv, "-a") == 0) {
+ enable_accessory = 1;
+ } else if (strcmp(*argv, "-h") == 0) {
+ usage(argv[0]);
+ return 1;
+ } else if (strcmp(*argv, "-i") == 0) {
+ enable_hid = 1;
+ }
+ if (*argv)
+ argv++;
+ }
+
+ init_audio(input_card, input_device, output_card, output_device);
+ if (enable_hid)
+ init_hid();
+ usb_run(enable_accessory);
+
+ return 0;
+}
diff --git a/libs/usb/tests/accessorytest/accessory.h b/libs/usb/tests/accessorytest/accessory.h
new file mode 100644
index 0000000..55c4550
--- /dev/null
+++ b/libs/usb/tests/accessorytest/accessory.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef __ACCESSORY_H__
+#define __ACCESSORY_H__
+
+int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od);
+void init_hid();
+void usb_run(int enable_accessory);
+
+struct usb_device* usb_wait_for_device();
+
+#endif /* __ACCESSORY_H__ */
diff --git a/libs/usb/tests/accessorytest/audio.c b/libs/usb/tests/accessorytest/audio.c
new file mode 100644
index 0000000..d23d9b3
--- /dev/null
+++ b/libs/usb/tests/accessorytest/audio.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <string.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "accessory.h"
+
+#define BUFFER_COUNT 2
+#define BUFFER_SIZE 16384
+
+#define BUFFER_EMPTY 0
+#define BUFFER_BUSY 1
+#define BUFFER_FULL 2
+
+static char* buffers[BUFFER_COUNT];
+static int buffer_states[BUFFER_COUNT];
+static int empty_index = 0;
+static int full_index = -1;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t empty_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t full_cond = PTHREAD_COND_INITIALIZER;
+
+static unsigned int input_card;
+static unsigned int input_device;
+
+static int get_empty()
+{
+ int index, other;
+
+ pthread_mutex_lock(&mutex);
+
+ while (empty_index == -1)
+ pthread_cond_wait(&empty_cond, &mutex);
+
+ index = empty_index;
+ other = (index == 0 ? 1 : 0);
+ buffer_states[index] = BUFFER_BUSY;
+ if (buffer_states[other] == BUFFER_EMPTY)
+ empty_index = other;
+ else
+ empty_index = -1;
+
+ pthread_mutex_unlock(&mutex);
+ return index;
+}
+
+static void put_empty(int index)
+{
+ pthread_mutex_lock(&mutex);
+
+ buffer_states[index] = BUFFER_EMPTY;
+ if (empty_index == -1) {
+ empty_index = index;
+ pthread_cond_signal(&empty_cond);
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static int get_full()
+{
+ int index, other;
+
+ pthread_mutex_lock(&mutex);
+
+ while (full_index == -1)
+ pthread_cond_wait(&full_cond, &mutex);
+
+ index = full_index;
+ other = (index == 0 ? 1 : 0);
+ buffer_states[index] = BUFFER_BUSY;
+ if (buffer_states[other] == BUFFER_FULL)
+ full_index = other;
+ else
+ full_index = -1;
+
+ pthread_mutex_unlock(&mutex);
+ return index;
+}
+
+static void put_full(int index)
+{
+ pthread_mutex_lock(&mutex);
+
+ buffer_states[index] = BUFFER_FULL;
+ if (full_index == -1) {
+ full_index = index;
+ pthread_cond_signal(&full_cond);
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static void* capture_thread(void* arg)
+{
+ struct pcm_config config;
+ struct pcm *pcm = NULL;
+
+ fprintf(stderr, "capture_thread start\n");
+
+ memset(&config, 0, sizeof(config));
+
+ config.channels = 2;
+ config.rate = 44100;
+ config.period_size = 1024;
+ config.period_count = 4;
+ config.format = PCM_FORMAT_S16_LE;
+
+ while (1) {
+ while (!pcm) {
+ pcm = pcm_open(input_card, input_device, PCM_IN, &config);
+ if (pcm && !pcm_is_ready(pcm)) {
+ pcm_close(pcm);
+ pcm = NULL;
+ }
+ if (!pcm)
+ sleep(1);
+ }
+
+ while (pcm) {
+ int index = get_empty();
+ if (pcm_read(pcm, buffers[index], BUFFER_SIZE)) {
+ put_empty(index);
+ pcm_close(pcm);
+ pcm = NULL;
+ } else {
+ put_full(index);
+ }
+ }
+ }
+
+ fprintf(stderr, "capture_thread done\n");
+ return NULL;
+}
+
+static void* play_thread(void* arg)
+{
+ struct pcm *pcm = arg;
+ char *buffer;
+ int index, err;
+
+ fprintf(stderr, "play_thread start\n");
+
+ while (1) {
+ index = get_full();
+
+ err = pcm_write(pcm, buffers[index], BUFFER_SIZE);
+ if (err)
+ fprintf(stderr, "pcm_write err: %d\n", err);
+
+ put_empty(index);
+ }
+
+ fprintf(stderr, "play_thread done\n");
+ pcm_close(pcm);
+ free(buffer);
+
+ return NULL;
+}
+
+int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od)
+{
+ pthread_t tid;
+ struct pcm_config config;
+ struct pcm *pcm;
+ int i;
+
+ input_card = ic;
+ input_device = id;
+
+ for (i = 0; i < BUFFER_COUNT; i++) {
+ buffers[i] = malloc(BUFFER_SIZE);
+ buffer_states[i] = BUFFER_EMPTY;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.channels = 2;
+ config.rate = 44100;
+ config.period_size = 1024;
+ config.period_count = 4;
+ config.format = PCM_FORMAT_S16_LE;
+
+ pcm = pcm_open(oc, od, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device %d/%d for output (%s)\n",
+ oc, od, pcm_get_error(pcm));
+ return -1;
+ }
+
+ pthread_create(&tid, NULL, capture_thread, NULL);
+ pthread_create(&tid, NULL, play_thread, pcm);
+ return 0;
+}
diff --git a/libs/usb/tests/accessorytest/f_accessory.h b/libs/usb/tests/accessorytest/f_accessory.h
new file mode 100644
index 0000000..312f4ba
--- /dev/null
+++ b/libs/usb/tests/accessorytest/f_accessory.h
@@ -0,0 +1,148 @@
+/*
+ * Gadget Function Driver for Android USB accessories
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LINUX_USB_F_ACCESSORY_H
+#define __LINUX_USB_F_ACCESSORY_H
+
+/* Use Google Vendor ID when in accessory mode */
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+
+
+/* Product ID to use when in accessory mode */
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+
+/* Product ID to use when in accessory mode and adb is enabled */
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+
+/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+
+/* Control request for retrieving device's protocol version
+ *
+ * requestType: USB_DIR_IN | USB_TYPE_VENDOR
+ * request: ACCESSORY_GET_PROTOCOL
+ * value: 0
+ * index: 0
+ * data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds audio and HID support
+ */
+#define ACCESSORY_GET_PROTOCOL 51
+
+/* Control request for host to send a string to the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_STRING
+ * value: 0
+ * index: string ID
+ * data zero terminated UTF8 string
+ *
+ * The device can later retrieve these strings via the
+ * ACCESSORY_GET_STRING_* ioctls
+ */
+#define ACCESSORY_SEND_STRING 52
+
+/* Control request for starting device in accessory mode.
+ * The host sends this after setting all its strings to the device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_START
+ * value: 0
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_START 53
+
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
+/* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SET_AUDIO_MODE 58
+
+
+
+/* ioctls for retrieving strings set by the host */
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+/* returns 1 if there is a start request pending */
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+
+#endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/libs/usb/tests/accessorytest/hid.c b/libs/usb/tests/accessorytest/hid.c
new file mode 100644
index 0000000..b70d678
--- /dev/null
+++ b/libs/usb/tests/accessorytest/hid.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <sys/inotify.h>
+#include <linux/hidraw.h>
+#include <usbhost/usbhost.h>
+
+#include "f_accessory.h"
+#include "accessory.h"
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int next_id = 1;
+
+static void milli_sleep(int millis) {
+ struct timespec tm;
+
+ tm.tv_sec = 0;
+ tm.tv_nsec = millis * 1000000;
+ nanosleep(&tm, NULL);
+}
+
+static void* hid_thread(void* arg) {
+ int fd = (int)arg;
+ char buffer[4096];
+ int id, ret, offset;
+ struct usb_device *device;
+ struct usb_device_descriptor *device_desc;
+ int max_packet;
+ struct hidraw_report_descriptor desc;
+ int desc_length;
+
+ fprintf(stderr, "hid_thread start fd: %d\n", fd);
+
+ if (ioctl(fd, HIDIOCGRDESCSIZE, &desc_length)) {
+ fprintf(stderr, "HIDIOCGRDESCSIZE failed\n");
+ close(fd);
+ goto err;
+ }
+
+ desc.size = HID_MAX_DESCRIPTOR_SIZE - 1;
+ if (ioctl(fd, HIDIOCGRDESC, &desc)) {
+ fprintf(stderr, "HIDIOCGRDESC failed\n");
+ close(fd);
+ goto err;
+ }
+
+wait_for_device:
+ fprintf(stderr, "waiting for device fd: %d\n", fd);
+ device = usb_wait_for_device();
+ max_packet = usb_device_get_device_descriptor(device)->bMaxPacketSize0;
+ // FIXME
+ max_packet--;
+
+ // FIXME
+ milli_sleep(500);
+
+ pthread_mutex_lock(&mutex);
+ id = next_id++;
+
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_REGISTER_HID, id, desc_length, NULL, 0, 1000);
+ fprintf(stderr, "ACCESSORY_REGISTER_HID returned %d\n", ret);
+
+ // FIXME
+ milli_sleep(500);
+
+ for (offset = 0; offset < desc_length; ) {
+ int count = desc_length - offset;
+ if (count > max_packet) count = max_packet;
+
+ fprintf(stderr, "sending ACCESSORY_SET_HID_REPORT_DESC offset: %d count: %d desc_length: %d\n",
+ offset, count, desc_length);
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SET_HID_REPORT_DESC, id, offset, &desc.value[offset], count, 1000);
+ fprintf(stderr, "ACCESSORY_SET_HID_REPORT_DESC returned %d errno %d\n", ret, errno);
+ offset += count;
+ }
+
+ pthread_mutex_unlock(&mutex);
+
+ while (1) {
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret < 0) {
+fprintf(stderr, "read failed, errno: %d, fd: %d\n", errno, fd);
+ break;
+ }
+
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SEND_HID_EVENT, id, 0, buffer, ret, 1000);
+ if (ret < 0 && errno != EPIPE) {
+fprintf(stderr, "ACCESSORY_SEND_HID_EVENT returned %d errno: %d\n", ret, errno);
+ goto wait_for_device;
+ }
+ }
+
+fprintf(stderr, "ACCESSORY_UNREGISTER_HID\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_UNREGISTER_HID, id, 0, NULL, 0, 1000);
+
+fprintf(stderr, "hid thread exiting\n");
+err:
+ return NULL;
+}
+
+static void open_hid(const char* name)
+{
+ char path[100];
+
+ snprintf(path, sizeof(path), "/dev/%s", name);
+ int fd = open(path, O_RDWR);
+ if (fd < 0) return;
+
+ fprintf(stderr, "opened /dev/%s\n", name);
+ pthread_t th;
+ pthread_create(&th, NULL, hid_thread, (void *)fd);
+}
+
+static void* inotify_thread(void* arg)
+{
+ open_hid("hidraw0");
+ open_hid("hidraw1");
+ open_hid("hidraw2");
+ open_hid("hidraw3");
+ open_hid("hidraw4");
+ open_hid("hidraw5");
+ open_hid("hidraw6");
+ open_hid("hidraw7");
+ open_hid("hidraw8");
+ open_hid("hidraw9");
+
+ int inotify_fd = inotify_init();
+ inotify_add_watch(inotify_fd, "/dev", IN_DELETE | IN_CREATE);
+
+ while (1) {
+ char event_buf[512];
+ struct inotify_event *event;
+ int event_pos = 0;
+ int event_size;
+
+ int count = read(inotify_fd, event_buf, sizeof(event_buf));
+ if (count < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ break;
+ }
+ while (count >= (int)sizeof(*event)) {
+ event = (struct inotify_event *)(event_buf + event_pos);
+ //fprintf(stderr, "%d: %08x \"%s\"\n", event->wd, event->mask,
+ // event->len ? event->name : "");
+ if (event->len) {
+ if(event->mask & IN_CREATE) {
+ fprintf(stderr, "created %s\n", event->name);
+ // FIXME
+ milli_sleep(50);
+ open_hid(event->name);
+ } else {
+ fprintf(stderr, "lost %s\n", event->name);
+ }
+ }
+ event_size = sizeof(*event) + event->len;
+ count -= event_size;
+ event_pos += event_size;
+ }
+ }
+
+ close(inotify_fd);
+ return NULL;
+}
+
+void init_hid()
+{
+ pthread_t th;
+ pthread_create(&th, NULL, inotify_thread, NULL);
+}
diff --git a/libs/usb/tests/accessorytest/usb.c b/libs/usb/tests/accessorytest/usb.c
new file mode 100644
index 0000000..ac72b35
--- /dev/null
+++ b/libs/usb/tests/accessorytest/usb.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usbhost/usbhost.h>
+#include "f_accessory.h"
+
+#include "accessory.h"
+
+static struct usb_device *current_device = NULL;
+static uint8_t read_ep;
+static uint8_t write_ep;
+
+static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t device_cond = PTHREAD_COND_INITIALIZER;
+
+static void milli_sleep(int millis) {
+ struct timespec tm;
+
+ tm.tv_sec = 0;
+ tm.tv_nsec = millis * 1000000;
+ nanosleep(&tm, NULL);
+}
+
+static void* read_thread(void* arg) {
+ int ret = 0;
+
+ while (current_device && ret >= 0) {
+ char buffer[16384];
+
+ ret = usb_device_bulk_transfer(current_device, read_ep, buffer, sizeof(buffer), 1000);
+ if (ret < 0 && errno == ETIMEDOUT)
+ ret = 0;
+ if (ret > 0) {
+ fwrite(buffer, 1, ret, stdout);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ }
+ }
+
+ return NULL;
+}
+
+static void* write_thread(void* arg) {
+ int ret = 0;
+
+ while (ret >= 0) {
+ char buffer[16384];
+ char *line = fgets(buffer, sizeof(buffer), stdin);
+ if (!line || !current_device)
+ break;
+ ret = usb_device_bulk_transfer(current_device, write_ep, line, strlen(line), 1000);
+ }
+
+ return NULL;
+}
+
+static void send_string(struct usb_device *device, int index, const char* string) {
+ usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0);
+
+ // some devices can't handle back-to-back requests, so delay a bit
+ milli_sleep(10);
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+ uint16_t vendorId, productId;
+ int ret;
+ int enable_accessory = (int)client_data;
+
+ struct usb_device *device = usb_device_open(devname);
+ if (!device) {
+ fprintf(stderr, "usb_device_open failed\n");
+ return 0;
+ }
+
+ vendorId = usb_device_get_vendor_id(device);
+ productId = usb_device_get_product_id(device);
+
+ if (!current_device && vendorId == 0x18D1 && productId >= 0x2D00 && productId <= 0x2D05) {
+
+ pthread_mutex_lock(&device_mutex);
+ fprintf(stderr, "Found android device in accessory mode\n");
+ current_device = device;
+ pthread_cond_broadcast(&device_cond);
+ pthread_mutex_unlock(&device_mutex);
+
+ if (enable_accessory) {
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+ struct usb_interface_descriptor *intf = NULL;
+ struct usb_endpoint_descriptor *ep1 = NULL;
+ struct usb_endpoint_descriptor *ep2 = NULL;
+ pthread_t th;
+
+ usb_descriptor_iter_init(device, &iter);
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ intf = (struct usb_interface_descriptor *)desc;
+ } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+ if (ep1)
+ ep2 = (struct usb_endpoint_descriptor *)desc;
+ else
+ ep1 = (struct usb_endpoint_descriptor *)desc;
+ }
+ }
+
+ if (!intf) {
+ fprintf(stderr, "interface not found\n");
+ exit(1);
+ }
+ if (!ep1 || !ep2) {
+ fprintf(stderr, "endpoints not found\n");
+ exit(1);
+ }
+
+ if (usb_device_claim_interface(device, intf->bInterfaceNumber)) {
+ fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno);
+ exit(1);
+ }
+
+ if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ read_ep = ep1->bEndpointAddress;
+ write_ep = ep2->bEndpointAddress;
+ } else {
+ read_ep = ep2->bEndpointAddress;
+ write_ep = ep1->bEndpointAddress;
+ }
+
+ pthread_create(&th, NULL, read_thread, NULL);
+ pthread_create(&th, NULL, write_thread, NULL);
+ }
+ } else {
+// fprintf(stderr, "Found new device - attempting to switch to accessory mode\n");
+
+ uint16_t protocol = -1;
+ ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR,
+ ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 1000);
+ if (ret < 0) {
+ // fprintf(stderr, "ACCESSORY_GET_PROTOCOL returned %d errno: %d\n", ret, errno);
+ } else {
+ fprintf(stderr, "device supports protocol version %d\n", protocol);
+ if (protocol >= 2) {
+ if (enable_accessory) {
+ send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");
+ send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat");
+ send_string(device, ACCESSORY_STRING_DESCRIPTION, "Accessory Chat");
+ send_string(device, ACCESSORY_STRING_VERSION, "1.0");
+ send_string(device, ACCESSORY_STRING_URI, "http://www.android.com");
+ send_string(device, ACCESSORY_STRING_SERIAL, "1234567890");
+ }
+
+ fprintf(stderr, "sending ACCESSORY_SET_AUDIO_MODE\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SET_AUDIO_MODE, 1, 0, NULL, 0, 1000);
+ if (ret < 0)
+ fprintf(stderr, "ACCESSORY_SET_AUDIO_MODE returned %d errno: %d\n", ret, errno);
+
+ fprintf(stderr, "sending ACCESSORY_START\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_START, 0, 0, NULL, 0, 1000);
+ fprintf(stderr, "did ACCESSORY_START\n");
+ if (ret < 0)
+ fprintf(stderr, "ACCESSORY_START returned %d errno: %d\n", ret, errno);
+ }
+ }
+
+ return 0;
+ }
+
+ if (device != current_device)
+ usb_device_close(device);
+
+ return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+ pthread_mutex_lock(&device_mutex);
+
+ if (current_device && !strcmp(usb_device_get_name(current_device), devname)) {
+ fprintf(stderr, "current device disconnected\n");
+ usb_device_close(current_device);
+ current_device = NULL;
+ }
+
+ pthread_mutex_unlock(&device_mutex);
+ return 0;
+}
+
+struct usb_device* usb_wait_for_device() {
+ struct usb_device* device = NULL;
+
+ pthread_mutex_lock(&device_mutex);
+ while (!current_device)
+ pthread_cond_wait(&device_cond, &device_mutex);
+ device = current_device;
+ pthread_mutex_unlock(&device_mutex);
+
+ return device;
+}
+
+void usb_run(int enable_accessory) {
+ struct usb_host_context* context = usb_host_init();
+
+ usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)enable_accessory);
+}
+