зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1373478 - Remove the remainder of gonk widget code. r=me
This commit is contained in:
Родитель
4deac3f186
Коммит
7a056a1e03
|
@ -1,360 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
|
||||
/* Copyright 2014 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 "FrameMetrics.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "GeckoTouchDispatcher.h"
|
||||
#include "InputData.h"
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#include "base/basictypes.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "libui/Input.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TouchEvents.h"
|
||||
#include "mozilla/dom/Touch.h"
|
||||
#include "mozilla/layers/APZThreadUtils.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "nsAppShell.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsWindow.h"
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
|
||||
|
||||
// uncomment to print log resample data
|
||||
// #define LOG_RESAMPLE_DATA 1
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Amount of time in MS before an input is considered expired.
|
||||
static const uint64_t kInputExpirationThresholdMs = 1000;
|
||||
|
||||
static StaticRefPtr<GeckoTouchDispatcher> sTouchDispatcher;
|
||||
|
||||
/* static */ GeckoTouchDispatcher*
|
||||
GeckoTouchDispatcher::GetInstance()
|
||||
{
|
||||
if (!sTouchDispatcher) {
|
||||
sTouchDispatcher = new GeckoTouchDispatcher();
|
||||
ClearOnShutdown(&sTouchDispatcher);
|
||||
}
|
||||
return sTouchDispatcher;
|
||||
}
|
||||
|
||||
GeckoTouchDispatcher::GeckoTouchDispatcher()
|
||||
: mTouchQueueLock("GeckoTouchDispatcher::mTouchQueueLock")
|
||||
, mHavePendingTouchMoves(false)
|
||||
, mInflightNonMoveEvents(0)
|
||||
, mTouchEventsFiltered(false)
|
||||
{
|
||||
// Since GeckoTouchDispatcher is initialized when input is initialized
|
||||
// and reads gfxPrefs, it is the first thing to touch gfxPrefs.
|
||||
// The first thing to touch gfxPrefs MUST occur on the main thread and init
|
||||
// the singleton
|
||||
MOZ_ASSERT(sTouchDispatcher == nullptr);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
gfxPrefs::GetSingleton();
|
||||
|
||||
mEnabledUniformityInfo = gfxPrefs::UniformityInfo();
|
||||
mVsyncAdjust = TimeDuration::FromMilliseconds(gfxPrefs::TouchVsyncSampleAdjust());
|
||||
mMaxPredict = TimeDuration::FromMilliseconds(gfxPrefs::TouchResampleMaxPredict());
|
||||
mMinDelta = TimeDuration::FromMilliseconds(gfxPrefs::TouchResampleMinDelta());
|
||||
mOldTouchThreshold = TimeDuration::FromMilliseconds(gfxPrefs::TouchResampleOldTouchThreshold());
|
||||
mDelayedVsyncThreshold = TimeDuration::FromMilliseconds(gfxPrefs::TouchResampleVsyncDelayThreshold());
|
||||
}
|
||||
|
||||
void
|
||||
GeckoTouchDispatcher::SetCompositorVsyncScheduler(mozilla::layers::CompositorVsyncScheduler *aObserver)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// We assume on b2g that there is only 1 CompositorBridgeParent
|
||||
MOZ_ASSERT(mCompositorVsyncScheduler == nullptr);
|
||||
mCompositorVsyncScheduler = aObserver;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoTouchDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp)
|
||||
{
|
||||
layers::APZThreadUtils::AssertOnControllerThread();
|
||||
DispatchTouchMoveEvents(aVsyncTimestamp);
|
||||
}
|
||||
|
||||
// Touch data timestamps are in milliseconds, aEventTime is in nanoseconds
|
||||
void
|
||||
GeckoTouchDispatcher::NotifyTouch(MultiTouchInput& aTouch, TimeStamp aEventTime)
|
||||
{
|
||||
if (mCompositorVsyncScheduler) {
|
||||
mCompositorVsyncScheduler->SetNeedsComposite();
|
||||
}
|
||||
|
||||
if (aTouch.mType == MultiTouchInput::MULTITOUCH_MOVE) {
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
if (mInflightNonMoveEvents > 0) {
|
||||
// If we have any pending non-move events, we shouldn't resample the
|
||||
// move events because we might end up dispatching events out of order.
|
||||
// Instead, fall back to a non-resampling in-order dispatch until we're
|
||||
// done processing the non-move events.
|
||||
layers::APZThreadUtils::RunOnControllerThread(NewRunnableMethod<MultiTouchInput>(
|
||||
this, &GeckoTouchDispatcher::DispatchTouchEvent, aTouch));
|
||||
return;
|
||||
}
|
||||
|
||||
mTouchMoveEvents.push_back(aTouch);
|
||||
mHavePendingTouchMoves = true;
|
||||
} else {
|
||||
{ // scope lock
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
mInflightNonMoveEvents++;
|
||||
}
|
||||
layers::APZThreadUtils::RunOnControllerThread(NewRunnableMethod<MultiTouchInput>(
|
||||
this, &GeckoTouchDispatcher::DispatchTouchNonMoveEvent, aTouch));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoTouchDispatcher::DispatchTouchNonMoveEvent(MultiTouchInput aInput)
|
||||
{
|
||||
layers::APZThreadUtils::AssertOnControllerThread();
|
||||
|
||||
// Flush pending touch move events, if there are any
|
||||
// (DispatchTouchMoveEvents will check the mHavePendingTouchMoves flag and
|
||||
// bail out if there's nothing to be done).
|
||||
NotifyVsync(TimeStamp::Now());
|
||||
DispatchTouchEvent(aInput);
|
||||
|
||||
{ // scope lock
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
mInflightNonMoveEvents--;
|
||||
MOZ_ASSERT(mInflightNonMoveEvents >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoTouchDispatcher::DispatchTouchMoveEvents(TimeStamp aVsyncTime)
|
||||
{
|
||||
MultiTouchInput touchMove;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
if (!mHavePendingTouchMoves) {
|
||||
return;
|
||||
}
|
||||
mHavePendingTouchMoves = false;
|
||||
|
||||
int touchCount = mTouchMoveEvents.size();
|
||||
TimeDuration vsyncTouchDiff = aVsyncTime - mTouchMoveEvents.back().mTimeStamp;
|
||||
// The delay threshold is a positive pref, but we're testing to see if the
|
||||
// vsync time is delayed from the touch, so add a negative sign.
|
||||
bool isDelayedVsyncEvent = vsyncTouchDiff < -mDelayedVsyncThreshold;
|
||||
bool isOldTouch = vsyncTouchDiff > mOldTouchThreshold;
|
||||
bool resample = (touchCount > 1) && !isDelayedVsyncEvent && !isOldTouch;
|
||||
|
||||
if (!resample) {
|
||||
touchMove = mTouchMoveEvents.back();
|
||||
mTouchMoveEvents.clear();
|
||||
if (!isDelayedVsyncEvent && !isOldTouch) {
|
||||
mTouchMoveEvents.push_back(touchMove);
|
||||
}
|
||||
} else {
|
||||
ResampleTouchMoves(touchMove, aVsyncTime);
|
||||
}
|
||||
}
|
||||
|
||||
DispatchTouchEvent(touchMove);
|
||||
}
|
||||
|
||||
static int
|
||||
Interpolate(int start, int end, TimeDuration aFrameDiff, TimeDuration aTouchDiff)
|
||||
{
|
||||
return start + (((end - start) * aFrameDiff.ToMicroseconds()) / aTouchDiff.ToMicroseconds());
|
||||
}
|
||||
|
||||
static const SingleTouchData&
|
||||
GetTouchByID(const SingleTouchData& aCurrentTouch, MultiTouchInput& aOtherTouch)
|
||||
{
|
||||
int32_t index = aOtherTouch.IndexOfTouch(aCurrentTouch.mIdentifier);
|
||||
if (index < 0) {
|
||||
// We can have situations where a previous touch event had 2 fingers
|
||||
// and we lift 1 finger off. In those cases, we won't find the touch event
|
||||
// with given id, so just return the current touch, which will be resampled
|
||||
// without modification and dispatched.
|
||||
return aCurrentTouch;
|
||||
}
|
||||
return aOtherTouch.mTouches[index];
|
||||
}
|
||||
|
||||
|
||||
// aTouchDiff is the duration between the base and current touch times
|
||||
// aFrameDiff is the duration between the base and the time we're resampling to
|
||||
static void
|
||||
ResampleTouch(MultiTouchInput& aOutTouch,
|
||||
MultiTouchInput& aBase, MultiTouchInput& aCurrent,
|
||||
TimeDuration aFrameDiff, TimeDuration aTouchDiff)
|
||||
{
|
||||
aOutTouch = aCurrent;
|
||||
|
||||
// Make sure we only resample the correct finger.
|
||||
for (size_t i = 0; i < aOutTouch.mTouches.Length(); i++) {
|
||||
const SingleTouchData& current = aCurrent.mTouches[i];
|
||||
const SingleTouchData& base = GetTouchByID(current, aBase);
|
||||
|
||||
const ScreenIntPoint& baseTouchPoint = base.mScreenPoint;
|
||||
const ScreenIntPoint& currentTouchPoint = current.mScreenPoint;
|
||||
|
||||
ScreenIntPoint newSamplePoint;
|
||||
newSamplePoint.x = Interpolate(baseTouchPoint.x, currentTouchPoint.x, aFrameDiff, aTouchDiff);
|
||||
newSamplePoint.y = Interpolate(baseTouchPoint.y, currentTouchPoint.y, aFrameDiff, aTouchDiff);
|
||||
|
||||
aOutTouch.mTouches[i].mScreenPoint = newSamplePoint;
|
||||
|
||||
#ifdef LOG_RESAMPLE_DATA
|
||||
const char* type = "extrapolate";
|
||||
if (aFrameDiff < aTouchDiff) {
|
||||
type = "interpolate";
|
||||
}
|
||||
|
||||
float alpha = aFrameDiff / aTouchDiff;
|
||||
LOG("%s base (%d, %d), current (%d, %d) to (%d, %d) alpha %f, touch diff %d, frame diff %d\n",
|
||||
type,
|
||||
baseTouchPoint.x, baseTouchPoint.y,
|
||||
currentTouchPoint.x, currentTouchPoint.y,
|
||||
newSamplePoint.x, newSamplePoint.y,
|
||||
alpha, (int)aTouchDiff.ToMilliseconds(), (int)aFrameDiff.ToMilliseconds());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* +> Base touch (The touch before current touch)
|
||||
* |
|
||||
* | +> Current touch (Latest touch)
|
||||
* | |
|
||||
* | | +> Maximum resample time
|
||||
* | | |
|
||||
* +-----+------+--------------------> Time
|
||||
* ^ ^
|
||||
* | |
|
||||
* +------+--> Potential vsync events which the touches are resampled to
|
||||
* | |
|
||||
* | +> Extrapolation
|
||||
* |
|
||||
* +> Interpolation
|
||||
*/
|
||||
|
||||
void
|
||||
GeckoTouchDispatcher::ResampleTouchMoves(MultiTouchInput& aOutTouch, TimeStamp aVsyncTime)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mTouchMoveEvents.size() >= 2);
|
||||
mTouchQueueLock.AssertCurrentThreadOwns();
|
||||
|
||||
MultiTouchInput currentTouch = mTouchMoveEvents.back();
|
||||
mTouchMoveEvents.pop_back();
|
||||
MultiTouchInput baseTouch = mTouchMoveEvents.back();
|
||||
mTouchMoveEvents.clear();
|
||||
mTouchMoveEvents.push_back(currentTouch);
|
||||
|
||||
TimeStamp sampleTime = aVsyncTime - mVsyncAdjust;
|
||||
TimeDuration touchDiff = currentTouch.mTimeStamp - baseTouch.mTimeStamp;
|
||||
|
||||
if (touchDiff < mMinDelta) {
|
||||
aOutTouch = currentTouch;
|
||||
#ifdef LOG_RESAMPLE_DATA
|
||||
LOG("The touches are too close, skip resampling\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTouch.mTimeStamp < sampleTime) {
|
||||
TimeDuration maxResampleTime = std::min(touchDiff / int64_t(2), mMaxPredict);
|
||||
TimeStamp maxTimestamp = currentTouch.mTimeStamp + maxResampleTime;
|
||||
if (sampleTime > maxTimestamp) {
|
||||
sampleTime = maxTimestamp;
|
||||
#ifdef LOG_RESAMPLE_DATA
|
||||
LOG("Overshot extrapolation time, adjusting sample time\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ResampleTouch(aOutTouch, baseTouch, currentTouch, sampleTime - baseTouch.mTimeStamp, touchDiff);
|
||||
|
||||
// Both mTimeStamp and mTime are being updated to sampleTime here.
|
||||
// mTime needs to be updated using a delta since TimeStamp doesn't
|
||||
// provide a way to obtain a raw value.
|
||||
aOutTouch.mTime += (sampleTime - aOutTouch.mTimeStamp).ToMilliseconds();
|
||||
aOutTouch.mTimeStamp = sampleTime;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsExpired(const MultiTouchInput& aTouch)
|
||||
{
|
||||
// No pending events, the filter state can be updated.
|
||||
uint64_t timeNowMs = systemTime(SYSTEM_TIME_MONOTONIC) / 1000000;
|
||||
return (timeNowMs - aTouch.mTime) > kInputExpirationThresholdMs;
|
||||
}
|
||||
void
|
||||
GeckoTouchDispatcher::DispatchTouchEvent(MultiTouchInput aMultiTouch)
|
||||
{
|
||||
if ((aMultiTouch.mType == MultiTouchInput::MULTITOUCH_END ||
|
||||
aMultiTouch.mType == MultiTouchInput::MULTITOUCH_CANCEL) &&
|
||||
aMultiTouch.mTouches.Length() == 1) {
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
mTouchMoveEvents.clear();
|
||||
} else if (aMultiTouch.mType == MultiTouchInput::MULTITOUCH_START &&
|
||||
aMultiTouch.mTouches.Length() == 1) {
|
||||
mTouchEventsFiltered = IsExpired(aMultiTouch);
|
||||
}
|
||||
|
||||
if (mTouchEventsFiltered) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsWindow::DispatchTouchInput(aMultiTouch);
|
||||
|
||||
if (mEnabledUniformityInfo && profiler_is_active()) {
|
||||
const char* touchAction = "Invalid";
|
||||
switch (aMultiTouch.mType) {
|
||||
case MultiTouchInput::MULTITOUCH_START:
|
||||
touchAction = "Touch_Event_Down";
|
||||
break;
|
||||
case MultiTouchInput::MULTITOUCH_MOVE:
|
||||
touchAction = "Touch_Event_Move";
|
||||
break;
|
||||
case MultiTouchInput::MULTITOUCH_END:
|
||||
case MultiTouchInput::MULTITOUCH_CANCEL:
|
||||
touchAction = "Touch_Event_Up";
|
||||
break;
|
||||
case MultiTouchInput::MULTITOUCH_SENTINEL:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid MultTouchInput.");
|
||||
break;
|
||||
}
|
||||
|
||||
const ScreenIntPoint& touchPoint = aMultiTouch.mTouches[0].mScreenPoint;
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
TouchDataPayload* payload = new TouchDataPayload(touchPoint);
|
||||
PROFILER_MARKER_PAYLOAD(touchAction, payload);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,99 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
|
||||
/* Copyright 2014 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 GECKO_TOUCH_INPUT_DISPATCHER_h
|
||||
#define GECKO_TOUCH_INPUT_DISPATCHER_h
|
||||
|
||||
#include "InputData.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include <vector>
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
class nsIWidget;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
class CompositorVsyncScheduler;
|
||||
}
|
||||
|
||||
// Used to resample touch events whenever a vsync event occurs. It batches
|
||||
// touch moves and on every vsync, resamples the touch position to create smooth
|
||||
// scrolls. We use the Android touch resample algorithm. It uses a combination of
|
||||
// extrapolation and interpolation. The algorithm takes the vsync time and
|
||||
// subtracts mVsyncAdjust time in ms and creates a sample time. All touch events are
|
||||
// relative to this sample time. If the last touch event occurs AFTER this
|
||||
// sample time, interpolate the last two touch events. If the last touch event occurs BEFORE
|
||||
// this sample time, we extrapolate the last two touch events to the sample
|
||||
// time. The magic numbers defined as constants are taken from android
|
||||
// InputTransport.cpp.
|
||||
class GeckoTouchDispatcher final
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoTouchDispatcher)
|
||||
|
||||
public:
|
||||
static GeckoTouchDispatcher* GetInstance();
|
||||
void NotifyTouch(MultiTouchInput& aTouch, TimeStamp aEventTime);
|
||||
void DispatchTouchEvent(MultiTouchInput aMultiTouch);
|
||||
void DispatchTouchNonMoveEvent(MultiTouchInput aInput);
|
||||
void DispatchTouchMoveEvents(TimeStamp aVsyncTime);
|
||||
void NotifyVsync(TimeStamp aVsyncTimestamp);
|
||||
void SetCompositorVsyncScheduler(layers::CompositorVsyncScheduler* aObserver);
|
||||
|
||||
protected:
|
||||
~GeckoTouchDispatcher() {}
|
||||
|
||||
private:
|
||||
GeckoTouchDispatcher();
|
||||
void ResampleTouchMoves(MultiTouchInput& aOutTouch, TimeStamp vsyncTime);
|
||||
void SendTouchEvent(MultiTouchInput& aData);
|
||||
void DispatchMouseEvent(MultiTouchInput& aMultiTouch,
|
||||
bool aForwardToChildren);
|
||||
|
||||
// mTouchQueueLock is used to protect the vector and state below
|
||||
// as it is accessed on multiple threads.
|
||||
Mutex mTouchQueueLock;
|
||||
std::vector<MultiTouchInput> mTouchMoveEvents;
|
||||
bool mHavePendingTouchMoves;
|
||||
int mInflightNonMoveEvents;
|
||||
// end stuff protected by mTouchQueueLock
|
||||
|
||||
bool mResamplingEnabled;
|
||||
bool mTouchEventsFiltered;
|
||||
bool mEnabledUniformityInfo;
|
||||
|
||||
// All times below are in nanoseconds
|
||||
TimeDuration mVsyncAdjust; // Time from vsync we create sample times from
|
||||
TimeDuration mMaxPredict; // How far into the future we're allowed to extrapolate
|
||||
TimeDuration mMinDelta; // Minimal time difference between touches for resampling
|
||||
|
||||
// Amount of time between vsync and the last event that is required before we
|
||||
// resample
|
||||
TimeDuration mMinResampleTime;
|
||||
|
||||
// Threshold if a vsync event runs too far behind touch events
|
||||
TimeDuration mDelayedVsyncThreshold;
|
||||
|
||||
// How far ahead can vsync events get ahead of touch events.
|
||||
TimeDuration mOldTouchThreshold;
|
||||
|
||||
RefPtr<layers::CompositorVsyncScheduler> mCompositorVsyncScheduler;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GECKO_TOUCH_INPUT_DISPATCHER_h
|
|
@ -1,194 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GfxInfo.h"
|
||||
|
||||
using namespace mozilla::widget;
|
||||
|
||||
/* GetD2DEnabled and GetDwriteEnabled shouldn't be called until after gfxPlatform initialization
|
||||
* has occurred because they depend on it for information. (See bug 591561) */
|
||||
nsresult
|
||||
GfxInfo::GetD2DEnabled(bool *aEnabled)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GfxInfo::GetDWriteEnabled(bool *aEnabled)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription)
|
||||
{
|
||||
aAdapterDescription.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM)
|
||||
{
|
||||
aAdapterRAM.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver)
|
||||
{
|
||||
aAdapterDriver.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion)
|
||||
{
|
||||
aAdapterDriverVersion.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate)
|
||||
{
|
||||
aAdapterDriverDate.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID)
|
||||
{
|
||||
aAdapterVendorID.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID)
|
||||
{
|
||||
aAdapterDeviceID.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterSubsysID(nsAString & aAdapterSubsysID)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetAdapterSubsysID2(nsAString & aAdapterSubsysID)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const nsTArray<GfxDriverInfo>&
|
||||
GfxInfo::GetGfxDriverInfo()
|
||||
{
|
||||
return *mDriverInfo;
|
||||
}
|
||||
|
||||
uint32_t GfxInfo::OperatingSystemVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GfxInfo::GetFeatureStatusImpl(int32_t /*aFeature*/,
|
||||
int32_t *aStatus,
|
||||
nsAString & /*aSuggestedDriverVersion*/,
|
||||
const nsTArray<GfxDriverInfo>& /*aDriverInfo*/,
|
||||
nsACString& aFailureId,
|
||||
OperatingSystem* /*aOS*/ /* = nullptr */)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStatus);
|
||||
*aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Implement nsIGfxInfoDebug
|
||||
|
||||
NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString &)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString &)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString &)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||
/* vim: se cin sw=2 ts=2 et : */
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __mozilla_widget_GfxInfo_h__
|
||||
#define __mozilla_widget_GfxInfo_h__
|
||||
|
||||
#include "GfxInfoBase.h"
|
||||
#include "GfxDriverInfo.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
class GfxInfo : public GfxInfoBase
|
||||
{
|
||||
public:
|
||||
// We only declare the subset of nsIGfxInfo that we actually implement. The
|
||||
// rest is brought forward from GfxInfoBase.
|
||||
NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled);
|
||||
NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled);
|
||||
NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion);
|
||||
NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams);
|
||||
NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription);
|
||||
NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver);
|
||||
NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID);
|
||||
NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID);
|
||||
NS_IMETHOD GetAdapterSubsysID(nsAString & aAdapterSubsysID);
|
||||
NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM);
|
||||
NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion);
|
||||
NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate);
|
||||
NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription);
|
||||
NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver);
|
||||
NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID);
|
||||
NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID);
|
||||
NS_IMETHOD GetAdapterSubsysID2(nsAString & aAdapterSubsysID);
|
||||
NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM);
|
||||
NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion);
|
||||
NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate);
|
||||
NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active);
|
||||
using GfxInfoBase::GetFeatureStatus;
|
||||
using GfxInfoBase::GetFeatureSuggestedDriverVersion;
|
||||
using GfxInfoBase::GetWebGLParameter;
|
||||
|
||||
virtual uint32_t OperatingSystemVersion() override;
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_DECL_NSIGFXINFODEBUG
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
|
||||
int32_t *aStatus,
|
||||
nsAString & aSuggestedDriverVersion,
|
||||
const nsTArray<GfxDriverInfo>& aDriverInfo,
|
||||
nsACString& aFailureId,
|
||||
OperatingSystem* aOS = nullptr);
|
||||
virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo();
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __mozilla_widget_GfxInfo_h__ */
|
|
@ -1,75 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GonkClipboardData.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
GonkClipboardData::SetText(const nsAString &aText)
|
||||
{
|
||||
mPlain = aText;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkClipboardData::HasText() const
|
||||
{
|
||||
return !mPlain.IsEmpty();
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
GonkClipboardData::GetText() const
|
||||
{
|
||||
return mPlain;
|
||||
}
|
||||
|
||||
void
|
||||
GonkClipboardData::SetHTML(const nsAString &aHTML)
|
||||
{
|
||||
mHTML = aHTML;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkClipboardData::HasHTML() const
|
||||
{
|
||||
return !mHTML.IsEmpty();
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
GonkClipboardData::GetHTML() const
|
||||
{
|
||||
return mHTML;
|
||||
}
|
||||
|
||||
void
|
||||
GonkClipboardData::SetImage(gfx::DataSourceSurface* aDataSource)
|
||||
{
|
||||
// Clone a new DataSourceSurface and store it.
|
||||
mImage = gfx::CreateDataSourceSurfaceByCloning(aDataSource);
|
||||
}
|
||||
|
||||
bool
|
||||
GonkClipboardData::HasImage() const
|
||||
{
|
||||
return static_cast<bool>(mImage);
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
GonkClipboardData::GetImage() const
|
||||
{
|
||||
// Return cloned DataSourceSurface.
|
||||
RefPtr<gfx::DataSourceSurface> cloned = gfx::CreateDataSourceSurfaceByCloning(mImage);
|
||||
return cloned.forget();
|
||||
}
|
||||
|
||||
void
|
||||
GonkClipboardData::Clear()
|
||||
{
|
||||
mPlain.Truncate(0);
|
||||
mHTML.Truncate(0);
|
||||
mImage = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,49 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_GonkClipboardData
|
||||
#define mozilla_GonkClipboardData
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
}
|
||||
|
||||
class GonkClipboardData final
|
||||
{
|
||||
public:
|
||||
explicit GonkClipboardData() = default;
|
||||
~GonkClipboardData() = default;
|
||||
|
||||
// For text/plain
|
||||
void SetText(const nsAString &aText);
|
||||
bool HasText() const;
|
||||
const nsAString& GetText() const;
|
||||
|
||||
// For text/html
|
||||
void SetHTML(const nsAString &aHTML);
|
||||
bool HasHTML() const;
|
||||
const nsAString& GetHTML() const;
|
||||
|
||||
// For images
|
||||
void SetImage(gfx::DataSourceSurface* aDataSource);
|
||||
bool HasImage() const;
|
||||
already_AddRefed<gfx::DataSourceSurface> GetImage() const;
|
||||
|
||||
// For other APIs
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
nsAutoString mPlain;
|
||||
nsAutoString mHTML;
|
||||
RefPtr<gfx::DataSourceSurface> mImage;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_GonkClipboardData
|
|
@ -1,301 +0,0 @@
|
|||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 GONKKEYMAPPING_H
|
||||
#define GONKKEYMAPPING_H
|
||||
|
||||
#include "libui/android_keycodes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
/* See libui/KeycodeLabels.h for the mapping */
|
||||
static const unsigned long kKeyMapping[] = {
|
||||
0,
|
||||
0, // SOFT_LEFT
|
||||
0, // SOFT_RIGHT
|
||||
NS_VK_HOME, // HOME
|
||||
NS_VK_ESCAPE, // BACK
|
||||
0, // CALL
|
||||
NS_VK_SLEEP, // ENDCALL
|
||||
NS_VK_0,
|
||||
NS_VK_1,
|
||||
NS_VK_2,
|
||||
NS_VK_3,
|
||||
NS_VK_4,
|
||||
NS_VK_5,
|
||||
NS_VK_6,
|
||||
NS_VK_7,
|
||||
NS_VK_8,
|
||||
NS_VK_9,
|
||||
NS_VK_ASTERISK,
|
||||
NS_VK_HASH,
|
||||
NS_VK_UP,
|
||||
NS_VK_DOWN,
|
||||
NS_VK_LEFT,
|
||||
NS_VK_RIGHT,
|
||||
NS_VK_RETURN,
|
||||
NS_VK_VOLUME_UP,
|
||||
NS_VK_VOLUME_DOWN,
|
||||
NS_VK_SLEEP, // POWER
|
||||
NS_VK_PRINTSCREEN, // CAMERA
|
||||
NS_VK_CLEAR,
|
||||
NS_VK_A,
|
||||
NS_VK_B,
|
||||
NS_VK_C,
|
||||
NS_VK_D,
|
||||
NS_VK_E,
|
||||
NS_VK_F,
|
||||
NS_VK_G,
|
||||
NS_VK_H,
|
||||
NS_VK_I,
|
||||
NS_VK_J,
|
||||
NS_VK_K,
|
||||
NS_VK_L,
|
||||
NS_VK_M,
|
||||
NS_VK_N,
|
||||
NS_VK_O,
|
||||
NS_VK_P,
|
||||
NS_VK_Q,
|
||||
NS_VK_R,
|
||||
NS_VK_S,
|
||||
NS_VK_T,
|
||||
NS_VK_U,
|
||||
NS_VK_V,
|
||||
NS_VK_W,
|
||||
NS_VK_X,
|
||||
NS_VK_Y,
|
||||
NS_VK_Z,
|
||||
NS_VK_COMMA,
|
||||
NS_VK_PERIOD,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NS_VK_TAB,
|
||||
NS_VK_SPACE,
|
||||
NS_VK_META, // SYM
|
||||
0, // EXPLORER
|
||||
0, // ENVELOPE
|
||||
NS_VK_RETURN, // ENTER
|
||||
NS_VK_BACK,
|
||||
NS_VK_BACK_QUOTE, // GRAVE
|
||||
NS_VK_HYPHEN_MINUS,
|
||||
NS_VK_EQUALS,
|
||||
NS_VK_OPEN_BRACKET,
|
||||
NS_VK_CLOSE_BRACKET,
|
||||
NS_VK_BACK_SLASH,
|
||||
NS_VK_SEMICOLON,
|
||||
NS_VK_QUOTE,
|
||||
NS_VK_SLASH,
|
||||
NS_VK_AT,
|
||||
0, // NUM
|
||||
NS_VK_F1, // HEADSETHOOK
|
||||
0, // FOCUS
|
||||
NS_VK_PLUS,
|
||||
NS_VK_CONTEXT_MENU,
|
||||
0, // NOTIFICATION
|
||||
NS_VK_F5, // SEARCH
|
||||
0, // MEDIA_PLAY_PAUSE
|
||||
0, // MEDIA_STOP
|
||||
0, // MEDIA_NEXT
|
||||
0, // MEDIA_PREVIOUS
|
||||
0, // MEDIA_REWIND
|
||||
0, // MEDIA_FAST_FORWARD
|
||||
0, // MUTE
|
||||
NS_VK_PAGE_UP,
|
||||
NS_VK_PAGE_DOWN,
|
||||
0, // PICTSYMBOLS
|
||||
0, // SWITCH_CHARSET
|
||||
0, // BUTTON_A
|
||||
0, // BUTTON_B
|
||||
0, // BUTTON_C
|
||||
0, // BUTTON_X
|
||||
0, // BUTTON_Y
|
||||
0, // BUTTON_Z
|
||||
0, // BUTTON_L1
|
||||
0, // BUTTON_R1
|
||||
0, // BUTTON_L2
|
||||
0, // BUTTON_R2
|
||||
0, // BUTTON_THUMBL
|
||||
0, // BUTTON_THUMBR
|
||||
0, // BUTTON_START
|
||||
0, // BUTTON_SELECT
|
||||
0, // BUTTON_MODE
|
||||
NS_VK_ESCAPE,
|
||||
NS_VK_DELETE,
|
||||
0, // CTRL_LEFT
|
||||
0, // CTRL_RIGHT
|
||||
NS_VK_CAPS_LOCK,
|
||||
NS_VK_SCROLL_LOCK,
|
||||
0, // META_LEFT
|
||||
0, // META_RIGHT
|
||||
0, // FUNCTION
|
||||
0, // SYSRQ
|
||||
0, // BREAK
|
||||
NS_VK_HOME, // MOVE_HOME
|
||||
NS_VK_END,
|
||||
NS_VK_INSERT,
|
||||
0, // FORWARD
|
||||
0, // MEDIA_PLAY
|
||||
0, // MEDIA_PAUSE
|
||||
0, // MEDIA_CLOSE
|
||||
0, // MEDIA_EJECT
|
||||
0, // MEDIA_RECORD
|
||||
NS_VK_F1,
|
||||
NS_VK_F2,
|
||||
NS_VK_F3,
|
||||
NS_VK_F4,
|
||||
NS_VK_F5,
|
||||
NS_VK_F6,
|
||||
NS_VK_F7,
|
||||
NS_VK_F8,
|
||||
NS_VK_F9,
|
||||
NS_VK_F10,
|
||||
NS_VK_F11,
|
||||
NS_VK_F12,
|
||||
NS_VK_NUM_LOCK,
|
||||
NS_VK_NUMPAD0,
|
||||
NS_VK_NUMPAD1,
|
||||
NS_VK_NUMPAD2,
|
||||
NS_VK_NUMPAD3,
|
||||
NS_VK_NUMPAD4,
|
||||
NS_VK_NUMPAD5,
|
||||
NS_VK_NUMPAD6,
|
||||
NS_VK_NUMPAD7,
|
||||
NS_VK_NUMPAD8,
|
||||
NS_VK_NUMPAD9,
|
||||
NS_VK_DIVIDE,
|
||||
NS_VK_MULTIPLY,
|
||||
NS_VK_SUBTRACT,
|
||||
NS_VK_ADD,
|
||||
NS_VK_PERIOD,
|
||||
NS_VK_COMMA,
|
||||
NS_VK_RETURN,
|
||||
NS_VK_EQUALS,
|
||||
0, // NUMPAD_LEFT_PAREN
|
||||
0, // NUMPAD_RIGHT_PAREN
|
||||
NS_VK_VOLUME_MUTE,
|
||||
// There are more but we don't map them
|
||||
};
|
||||
|
||||
static KeyNameIndex GetKeyNameIndex(int aKeyCode)
|
||||
{
|
||||
switch (aKeyCode) {
|
||||
#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
|
||||
case aNativeKey: return aKeyNameIndex;
|
||||
|
||||
#include "NativeKeyToDOMKeyName.h"
|
||||
|
||||
#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
|
||||
|
||||
case AKEYCODE_0:
|
||||
case AKEYCODE_1:
|
||||
case AKEYCODE_2:
|
||||
case AKEYCODE_3:
|
||||
case AKEYCODE_4:
|
||||
case AKEYCODE_5:
|
||||
case AKEYCODE_6:
|
||||
case AKEYCODE_7:
|
||||
case AKEYCODE_8:
|
||||
case AKEYCODE_9:
|
||||
case AKEYCODE_STAR:
|
||||
case AKEYCODE_POUND:
|
||||
case AKEYCODE_A:
|
||||
case AKEYCODE_B:
|
||||
case AKEYCODE_C:
|
||||
case AKEYCODE_D:
|
||||
case AKEYCODE_E:
|
||||
case AKEYCODE_F:
|
||||
case AKEYCODE_G:
|
||||
case AKEYCODE_H:
|
||||
case AKEYCODE_I:
|
||||
case AKEYCODE_J:
|
||||
case AKEYCODE_K:
|
||||
case AKEYCODE_L:
|
||||
case AKEYCODE_M:
|
||||
case AKEYCODE_N:
|
||||
case AKEYCODE_O:
|
||||
case AKEYCODE_P:
|
||||
case AKEYCODE_Q:
|
||||
case AKEYCODE_R:
|
||||
case AKEYCODE_S:
|
||||
case AKEYCODE_T:
|
||||
case AKEYCODE_U:
|
||||
case AKEYCODE_V:
|
||||
case AKEYCODE_W:
|
||||
case AKEYCODE_X:
|
||||
case AKEYCODE_Y:
|
||||
case AKEYCODE_Z:
|
||||
case AKEYCODE_COMMA:
|
||||
case AKEYCODE_PERIOD:
|
||||
case AKEYCODE_SPACE:
|
||||
case AKEYCODE_GRAVE:
|
||||
case AKEYCODE_MINUS:
|
||||
case AKEYCODE_EQUALS:
|
||||
case AKEYCODE_LEFT_BRACKET:
|
||||
case AKEYCODE_RIGHT_BRACKET:
|
||||
case AKEYCODE_BACKSLASH:
|
||||
case AKEYCODE_SEMICOLON:
|
||||
case AKEYCODE_APOSTROPHE:
|
||||
case AKEYCODE_SLASH:
|
||||
case AKEYCODE_AT:
|
||||
case AKEYCODE_PLUS:
|
||||
case AKEYCODE_NUMPAD_0:
|
||||
case AKEYCODE_NUMPAD_1:
|
||||
case AKEYCODE_NUMPAD_2:
|
||||
case AKEYCODE_NUMPAD_3:
|
||||
case AKEYCODE_NUMPAD_4:
|
||||
case AKEYCODE_NUMPAD_5:
|
||||
case AKEYCODE_NUMPAD_6:
|
||||
case AKEYCODE_NUMPAD_7:
|
||||
case AKEYCODE_NUMPAD_8:
|
||||
case AKEYCODE_NUMPAD_9:
|
||||
case AKEYCODE_NUMPAD_DIVIDE:
|
||||
case AKEYCODE_NUMPAD_MULTIPLY:
|
||||
case AKEYCODE_NUMPAD_SUBTRACT:
|
||||
case AKEYCODE_NUMPAD_ADD:
|
||||
case AKEYCODE_NUMPAD_DOT:
|
||||
case AKEYCODE_NUMPAD_COMMA:
|
||||
case AKEYCODE_NUMPAD_EQUALS:
|
||||
case AKEYCODE_NUMPAD_LEFT_PAREN:
|
||||
case AKEYCODE_NUMPAD_RIGHT_PAREN:
|
||||
return KEY_NAME_INDEX_USE_STRING;
|
||||
|
||||
default:
|
||||
return KEY_NAME_INDEX_Unidentified;
|
||||
}
|
||||
}
|
||||
|
||||
static CodeNameIndex GetCodeNameIndex(int aScanCode)
|
||||
{
|
||||
switch (aScanCode) {
|
||||
#define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
|
||||
case aNativeKey: return aCodeNameIndex;
|
||||
|
||||
#include "NativeKeyToDOMCodeName.h"
|
||||
|
||||
#undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
|
||||
|
||||
default:
|
||||
return CODE_NAME_INDEX_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* GONKKEYMAPPING_H */
|
|
@ -1,355 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <android/log.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include "GonkMemoryPressureMonitoring.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ProcessPriorityManager.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsMemoryPressure.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#define LOG(args...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "GonkMemoryPressure" , ## args)
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* MemoryPressureWatcher watches sysfs from its own thread to notice when the
|
||||
* system is under memory pressure. When we observe memory pressure, we use
|
||||
* MemoryPressureRunnable to notify observers that they should release memory.
|
||||
*
|
||||
* When the system is under memory pressure, we don't want to constantly fire
|
||||
* memory-pressure events. So instead, we try to detect when sysfs indicates
|
||||
* that we're no longer under memory pressure, and only then start firing events
|
||||
* again.
|
||||
*
|
||||
* (This is a bit problematic because we can't poll() to detect when we're no
|
||||
* longer under memory pressure; instead we have to periodically read the sysfs
|
||||
* node. If we remain under memory pressure for a long time, this means we'll
|
||||
* continue waking up to read from the node for a long time, potentially wasting
|
||||
* battery life. Hopefully we don't hit this case in practice! We write to
|
||||
* logcat each time we go around this loop so it's at least noticable.)
|
||||
*
|
||||
* Shutting down safely is a bit of a chore. XPCOM won't shut down until all
|
||||
* threads exit, so we need to exit the Run() method below on shutdown. But our
|
||||
* thread might be blocked in one of two situations: We might be poll()'ing the
|
||||
* sysfs node waiting for memory pressure to occur, or we might be asleep
|
||||
* waiting to read() the sysfs node to see if we're no longer under memory
|
||||
* pressure.
|
||||
*
|
||||
* To let us wake up from the poll(), we poll() not just the sysfs node but also
|
||||
* a pipe, which we write to on shutdown. To let us wake up from sleeping
|
||||
* between read()s, we sleep by Wait()'ing on a monitor, which we notify on
|
||||
* shutdown.
|
||||
*/
|
||||
class MemoryPressureWatcher final
|
||||
: public nsIRunnable
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
MemoryPressureWatcher()
|
||||
: mMonitor("MemoryPressureWatcher")
|
||||
, mLowMemTriggerKB(0)
|
||||
, mPageSize(0)
|
||||
, mShuttingDown(false)
|
||||
, mTriggerFd(-1)
|
||||
, mShutdownPipeRead(-1)
|
||||
, mShutdownPipeWrite(-1)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
nsresult Init()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
NS_ENSURE_STATE(os);
|
||||
|
||||
// The observer service holds us alive.
|
||||
os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, /* ownsWeak */ false);
|
||||
|
||||
// Initialize the internal state
|
||||
mPageSize = sysconf(_SC_PAGESIZE);
|
||||
ReadPrefs();
|
||||
nsresult rv = OpenFiles();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
SetLowMemTrigger(mSoftLowMemTriggerKB);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
|
||||
LOG("Observed XPCOM shutdown.");
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mShuttingDown = true;
|
||||
mMonitor.Notify();
|
||||
|
||||
int rv;
|
||||
do {
|
||||
// Write something to the pipe; doesn't matter what.
|
||||
uint32_t dummy = 0;
|
||||
rv = write(mShutdownPipeWrite, &dummy, sizeof(dummy));
|
||||
} while(rv == -1 && errno == EINTR);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
int triggerResetTimeout = -1;
|
||||
bool memoryPressure;
|
||||
nsresult rv = CheckForMemoryPressure(&memoryPressure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (true) {
|
||||
// Wait for a notification on mTriggerFd or for data to be written to
|
||||
// mShutdownPipeWrite. (poll(mTriggerFd, POLLPRI) blocks until we're
|
||||
// under memory pressure or until we time out, the time out is used
|
||||
// to adjust the trigger level after a memory pressure event.)
|
||||
struct pollfd pollfds[2];
|
||||
pollfds[0].fd = mTriggerFd;
|
||||
pollfds[0].events = POLLPRI;
|
||||
pollfds[1].fd = mShutdownPipeRead;
|
||||
pollfds[1].events = POLLIN;
|
||||
|
||||
int pollRv = MOZ_TEMP_FAILURE_RETRY(
|
||||
poll(pollfds, ArrayLength(pollfds), triggerResetTimeout)
|
||||
);
|
||||
|
||||
if (pollRv == 0) {
|
||||
// Timed out, adjust the trigger and update the timeout.
|
||||
triggerResetTimeout = AdjustTrigger(triggerResetTimeout);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pollfds[1].revents) {
|
||||
// Something was written to our shutdown pipe; we're outta here.
|
||||
LOG("shutting down (1)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If pollfds[1] isn't happening, pollfds[0] ought to be!
|
||||
if (!(pollfds[0].revents & POLLPRI)) {
|
||||
LOG("Unexpected revents value after poll(): %d. "
|
||||
"Shutting down GonkMemoryPressureMonitoring.", pollfds[0].revents);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// POLLPRI on mTriggerFd indicates that we're in a low-memory situation.
|
||||
// We could read lowMemFd to double-check, but we've observed that the
|
||||
// read sometimes completes after the memory-pressure event is over, so
|
||||
// let's just believe the result of poll().
|
||||
rv = DispatchMemoryPressure(MemPressure_New);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Move to the hard level if we're on the soft one.
|
||||
if (mLowMemTriggerKB > mHardLowMemTriggerKB) {
|
||||
SetLowMemTrigger(mHardLowMemTriggerKB);
|
||||
}
|
||||
|
||||
// Manually check mTriggerFd until we observe that memory pressure is
|
||||
// over. We won't fire any more low-memory events until we observe that
|
||||
// we're no longer under pressure. Instead, we fire low-memory-ongoing
|
||||
// events, which cause processes to keep flushing caches but will not
|
||||
// trigger expensive GCs and other attempts to save memory that are
|
||||
// likely futile at this point.
|
||||
do {
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
// We need to check mShuttingDown before we wait here, in order to
|
||||
// catch a shutdown signal sent after we poll()'ed mShutdownPipeRead
|
||||
// above but before we started waiting on the monitor. But we don't
|
||||
// need to check after we wait, because we'll either do another
|
||||
// iteration of this inner loop, in which case we'll check
|
||||
// mShuttingDown, or we'll exit this loop and do another iteration
|
||||
// of the outer loop, in which case we'll check the shutdown pipe.
|
||||
if (mShuttingDown) {
|
||||
LOG("shutting down (2)");
|
||||
return NS_OK;
|
||||
}
|
||||
mMonitor.Wait(PR_MillisecondsToInterval(mPollMS));
|
||||
}
|
||||
|
||||
LOG("Checking to see if memory pressure is over.");
|
||||
rv = CheckForMemoryPressure(&memoryPressure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (memoryPressure) {
|
||||
rv = DispatchMemoryPressure(MemPressure_Ongoing);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
continue;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// The main process will try to adjust the trigger.
|
||||
triggerResetTimeout = mPollMS * 2;
|
||||
}
|
||||
|
||||
LOG("Memory pressure is over.");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~MemoryPressureWatcher() {}
|
||||
|
||||
private:
|
||||
void ReadPrefs() {
|
||||
// While we're under memory pressure, we periodically read()
|
||||
// notify_trigger_active to try and see when we're no longer under memory
|
||||
// pressure. mPollMS indicates how many milliseconds we wait between those
|
||||
// read()s.
|
||||
Preferences::AddUintVarCache(&mPollMS,
|
||||
"gonk.systemMemoryPressureRecoveryPollMS", /* default */ 5000);
|
||||
|
||||
// We have two values for the notify trigger, a soft one which is triggered
|
||||
// before we start killing background applications and an hard one which is
|
||||
// after we've killed background applications but before we start killing
|
||||
// foreground ones.
|
||||
Preferences::AddUintVarCache(&mSoftLowMemTriggerKB,
|
||||
"gonk.notifySoftLowMemUnderKB", /* default */ 43008);
|
||||
Preferences::AddUintVarCache(&mHardLowMemTriggerKB,
|
||||
"gonk.notifyHardLowMemUnderKB", /* default */ 14336);
|
||||
}
|
||||
|
||||
nsresult OpenFiles() {
|
||||
mTriggerFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
NS_ENSURE_STATE(mTriggerFd != -1);
|
||||
|
||||
int pipes[2];
|
||||
NS_ENSURE_STATE(!pipe(pipes));
|
||||
mShutdownPipeRead = pipes[0];
|
||||
mShutdownPipeWrite = pipes[1];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the low memory trigger to the specified value, this can be done by
|
||||
* the main process alone.
|
||||
*/
|
||||
void SetLowMemTrigger(uint32_t aValue) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsPrintfCString str("%ld", (aValue * 1024) / mPageSize);
|
||||
if (WriteSysFile("/sys/module/lowmemorykiller/parameters/notify_trigger",
|
||||
str.get())) {
|
||||
mLowMemTriggerKB = aValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from the trigger file descriptor and determine whether we're
|
||||
* currently under memory pressure.
|
||||
*
|
||||
* We don't expect this method to block.
|
||||
*/
|
||||
nsresult CheckForMemoryPressure(bool* aOut)
|
||||
{
|
||||
*aOut = false;
|
||||
|
||||
lseek(mTriggerFd, 0, SEEK_SET);
|
||||
|
||||
char buf[2];
|
||||
int nread = MOZ_TEMP_FAILURE_RETRY(read(mTriggerFd, buf, sizeof(buf)));
|
||||
NS_ENSURE_STATE(nread == 2);
|
||||
|
||||
// The notify_trigger_active sysfs node should contain either "0\n" or
|
||||
// "1\n". The latter indicates memory pressure.
|
||||
*aOut = (buf[0] == '1');
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int AdjustTrigger(int timeout)
|
||||
{
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return -1; // Only the main process can adjust the trigger.
|
||||
}
|
||||
|
||||
struct sysinfo info;
|
||||
int rv = sysinfo(&info);
|
||||
if (rv < 0) {
|
||||
return -1; // Without system information we're blind, bail out.
|
||||
}
|
||||
|
||||
size_t freeMemory = (info.freeram * info.mem_unit) / 1024;
|
||||
|
||||
if (freeMemory > mSoftLowMemTriggerKB) {
|
||||
SetLowMemTrigger(mSoftLowMemTriggerKB);
|
||||
return -1; // Trigger adjusted, wait indefinitely.
|
||||
}
|
||||
|
||||
// Wait again but double the duration, max once per day.
|
||||
return std::min(86400000, timeout * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the specified memory pressure event. If a high-priority process is
|
||||
* present then it's likely responding to an urgent event (an incoming call or
|
||||
* message for example) so avoid wasting CPU time responding to low-memory
|
||||
* events.
|
||||
*/
|
||||
nsresult DispatchMemoryPressure(MemoryPressureState state)
|
||||
{
|
||||
return NS_DispatchMemoryPressure(state);
|
||||
}
|
||||
|
||||
Monitor mMonitor;
|
||||
uint32_t mPollMS; // Ongoing pressure poll delay
|
||||
uint32_t mSoftLowMemTriggerKB; // Soft memory pressure level
|
||||
uint32_t mHardLowMemTriggerKB; // Hard memory pressure level
|
||||
uint32_t mLowMemTriggerKB; // Current value of the trigger
|
||||
size_t mPageSize;
|
||||
bool mShuttingDown;
|
||||
|
||||
ScopedClose mTriggerFd;
|
||||
ScopedClose mShutdownPipeRead;
|
||||
ScopedClose mShutdownPipeWrite;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MemoryPressureWatcher, nsIRunnable, nsIObserver);
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
InitGonkMemoryPressureMonitoring()
|
||||
{
|
||||
// memoryPressureWatcher is held alive by the observer service.
|
||||
RefPtr<MemoryPressureWatcher> memoryPressureWatcher =
|
||||
new MemoryPressureWatcher();
|
||||
NS_ENSURE_SUCCESS_VOID(memoryPressureWatcher->Init());
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
NS_NewNamedThread("MemoryPressure", getter_AddRefs(thread),
|
||||
memoryPressureWatcher);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,14 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_GonkMemoryPressureMonitoring_h_
|
||||
#define mozilla_GonkMemoryPressureMonitoring_h_
|
||||
|
||||
namespace mozilla {
|
||||
void InitGonkMemoryPressureMonitoring();
|
||||
}
|
||||
|
||||
#endif /* mozilla_GonkMemoryPressureMonitoring_h_ */
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Mozilla Foundation
|
||||
*
|
||||
* 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 "GonkPermission.h"
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/IPermissionController.h>
|
||||
|
||||
#ifndef HAVE_ANDROID_OS
|
||||
#define HAVE_ANDROID_OS 1
|
||||
#endif
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#undef LOG
|
||||
#include <android/log.h>
|
||||
#undef ALOGE
|
||||
#define ALOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "gonkperm" , ## args)
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla;
|
||||
|
||||
bool
|
||||
GonkPermissionService::checkPermission(const String16& permission, int32_t pid,
|
||||
int32_t uid)
|
||||
{
|
||||
// root can do anything.
|
||||
if (0 == uid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String8 perm8(permission);
|
||||
|
||||
// Some ril implementations need android.permission.MODIFY_AUDIO_SETTINGS
|
||||
if ((uid == AID_SYSTEM || uid == AID_RADIO || uid == AID_BLUETOOTH) &&
|
||||
perm8 == "android.permission.MODIFY_AUDIO_SETTINGS") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other permissions apply to non-app processes.
|
||||
if (uid < AID_APP) {
|
||||
ALOGE("%s for pid=%d,uid=%d denied: not an app",
|
||||
String8(permission).string(), pid, uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only these permissions can be granted to apps through this service.
|
||||
if (perm8 != "android.permission.CAMERA" &&
|
||||
perm8 != "android.permission.RECORD_AUDIO") {
|
||||
ALOGE("%s for pid=%d,uid=%d denied: unsupported permission",
|
||||
String8(permission).string(), pid, uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Users granted the permission through a prompt dialog.
|
||||
// Before permission managment of gUM is done, app cannot remember the
|
||||
// permission.
|
||||
PermissionGrant permGrant(perm8.string(), pid);
|
||||
if (nsTArray<PermissionGrant>::NoIndex != mGrantArray.IndexOf(permGrant)) {
|
||||
mGrantArray.RemoveElement(permGrant);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static GonkPermissionService* gGonkPermissionService = NULL;
|
||||
|
||||
/* static */
|
||||
void
|
||||
GonkPermissionService::instantiate()
|
||||
{
|
||||
defaultServiceManager()->addService(String16(getServiceName()),
|
||||
GetInstance());
|
||||
}
|
||||
|
||||
/* static */
|
||||
GonkPermissionService*
|
||||
GonkPermissionService::GetInstance()
|
||||
{
|
||||
if (!gGonkPermissionService) {
|
||||
gGonkPermissionService = new GonkPermissionService();
|
||||
}
|
||||
return gGonkPermissionService;
|
||||
}
|
||||
|
||||
void
|
||||
GonkPermissionService::addGrantInfo(const char* permission, int32_t pid)
|
||||
{
|
||||
mGrantArray.AppendElement(PermissionGrant(permission, pid));
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Mozilla Foundation
|
||||
*
|
||||
* 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 GONKPERMISSION_H
|
||||
#define GONKPERMISSION_H
|
||||
|
||||
#include <binder/BinderService.h>
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
class PermissionGrant
|
||||
{
|
||||
public:
|
||||
PermissionGrant(const char* perm, int32_t p) : mPid(p)
|
||||
{
|
||||
mPermission.Assign(perm);
|
||||
}
|
||||
|
||||
PermissionGrant(const nsACString& permission, int32_t pid) : mPid(pid),
|
||||
mPermission(permission)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const PermissionGrant& other) const
|
||||
{
|
||||
return (mPid == other.pid() && mPermission.Equals(other.permission()));
|
||||
}
|
||||
|
||||
int32_t pid() const
|
||||
{
|
||||
return mPid;
|
||||
}
|
||||
|
||||
const nsACString& permission() const
|
||||
{
|
||||
return mPermission;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mPid;
|
||||
nsCString mPermission;
|
||||
};
|
||||
|
||||
class PermissionGrant;
|
||||
|
||||
class GonkPermissionService :
|
||||
public android::BinderService<GonkPermissionService>,
|
||||
public android::BnPermissionController
|
||||
{
|
||||
public:
|
||||
virtual ~GonkPermissionService() {}
|
||||
static GonkPermissionService* GetInstance();
|
||||
static const char *getServiceName() {
|
||||
return "permission";
|
||||
}
|
||||
|
||||
static void instantiate();
|
||||
|
||||
virtual android::status_t dump(int fd, const android::Vector<android::String16>& args) {
|
||||
return android::NO_ERROR;
|
||||
}
|
||||
virtual bool checkPermission(const android::String16& permission, int32_t pid,
|
||||
int32_t uid);
|
||||
|
||||
void addGrantInfo(const char* permission, int32_t pid);
|
||||
private:
|
||||
GonkPermissionService(): android::BnPermissionController() {}
|
||||
nsTArray<PermissionGrant> mGrantArray;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GONKPERMISSION_H
|
|
@ -1,971 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/*
|
||||
* Copyright (c) 2012, 2013 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 <android/log.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gfxPrefs.h"
|
||||
#include "ImageLayers.h"
|
||||
#include "libdisplay/GonkDisplay.h"
|
||||
#include "HwcComposer2D.h"
|
||||
#include "LayerScope.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/LayerManagerComposite.h"
|
||||
#include "mozilla/layers/PLayerTransaction.h"
|
||||
#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
|
||||
#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "VsyncSource.h"
|
||||
#include "nsScreenManagerGonk.h"
|
||||
#include "nsWindow.h"
|
||||
|
||||
#if ANDROID_VERSION >= 17
|
||||
#include "libdisplay/DisplaySurface.h"
|
||||
#endif
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
#define LOG_TAG "HWComposer"
|
||||
|
||||
/*
|
||||
* By default the debug message of hwcomposer (LOG_DEBUG level) are undefined,
|
||||
* but can be enabled by uncommenting HWC_DEBUG below.
|
||||
*/
|
||||
//#define HWC_DEBUG
|
||||
|
||||
#ifdef HWC_DEBUG
|
||||
#define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, ## args)
|
||||
#else
|
||||
#define LOGD(args...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define LOGI(args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, ## args)
|
||||
#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, ## args)
|
||||
|
||||
#define LAYER_COUNT_INCREMENTS 5
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static void
|
||||
HookInvalidate(const struct hwc_procs* aProcs)
|
||||
{
|
||||
HwcComposer2D::GetInstance()->Invalidate();
|
||||
}
|
||||
|
||||
static void
|
||||
HookVsync(const struct hwc_procs* aProcs, int aDisplay,
|
||||
int64_t aTimestamp)
|
||||
{
|
||||
HwcComposer2D::GetInstance()->Vsync(aDisplay, aTimestamp);
|
||||
}
|
||||
|
||||
static void
|
||||
HookHotplug(const struct hwc_procs* aProcs, int aDisplay,
|
||||
int aConnected)
|
||||
{
|
||||
HwcComposer2D::GetInstance()->Hotplug(aDisplay, aConnected);
|
||||
}
|
||||
|
||||
static StaticRefPtr<HwcComposer2D> sInstance;
|
||||
|
||||
HwcComposer2D::HwcComposer2D()
|
||||
: mList(nullptr)
|
||||
, mMaxLayerCount(0)
|
||||
, mColorFill(false)
|
||||
, mRBSwapSupport(false)
|
||||
, mPrepared(false)
|
||||
, mHasHWVsync(false)
|
||||
, mLock("mozilla.HwcComposer2D.mLock")
|
||||
{
|
||||
mHal = HwcHALBase::CreateHwcHAL();
|
||||
if (!mHal->HasHwc()) {
|
||||
LOGD("no hwc support");
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterHwcEventCallback();
|
||||
|
||||
nsIntSize screenSize;
|
||||
|
||||
GonkDisplay::NativeData data = GetGonkDisplay()->GetNativeData(GonkDisplay::DISPLAY_PRIMARY);
|
||||
ANativeWindow *win = data.mNativeWindow.get();
|
||||
win->query(win, NATIVE_WINDOW_WIDTH, &screenSize.width);
|
||||
win->query(win, NATIVE_WINDOW_HEIGHT, &screenSize.height);
|
||||
mScreenRect = gfx::IntRect(gfx::IntPoint(0, 0), screenSize);
|
||||
|
||||
mColorFill = mHal->Query(HwcHALBase::QueryType::COLOR_FILL);
|
||||
mRBSwapSupport = mHal->Query(HwcHALBase::QueryType::RB_SWAP);
|
||||
}
|
||||
|
||||
HwcComposer2D::~HwcComposer2D()
|
||||
{
|
||||
free(mList);
|
||||
}
|
||||
|
||||
HwcComposer2D*
|
||||
HwcComposer2D::GetInstance()
|
||||
{
|
||||
if (!sInstance) {
|
||||
#ifdef HWC_DEBUG
|
||||
// Make sure only create once
|
||||
static int timesCreated = 0;
|
||||
++timesCreated;
|
||||
MOZ_ASSERT(timesCreated == 1);
|
||||
#endif
|
||||
LOGI("Creating new instance");
|
||||
sInstance = new HwcComposer2D();
|
||||
|
||||
// If anyone uses the compositor thread to create HwcComposer2D,
|
||||
// we just skip this function.
|
||||
// If ClearOnShutdown() can handle objects in other threads
|
||||
// in the future, we can remove this check.
|
||||
if (NS_IsMainThread()) {
|
||||
// If we create HwcComposer2D by the main thread, we can use
|
||||
// ClearOnShutdown() to make sure it will be nullified properly.
|
||||
ClearOnShutdown(&sInstance);
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::EnableVsync(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mHasHWVsync) {
|
||||
return false;
|
||||
}
|
||||
return mHal->EnableVsync(aEnable) && aEnable;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::RegisterHwcEventCallback()
|
||||
{
|
||||
const HwcHALProcs_t cHWCProcs = {
|
||||
&HookInvalidate, // 1st: void (*invalidate)(...)
|
||||
&HookVsync, // 2nd: void (*vsync)(...)
|
||||
&HookHotplug // 3rd: void (*hotplug)(...)
|
||||
};
|
||||
mHasHWVsync = mHal->RegisterHwcEventCallback(cHWCProcs);
|
||||
return mHasHWVsync;
|
||||
}
|
||||
|
||||
void
|
||||
HwcComposer2D::Vsync(int aDisplay, nsecs_t aVsyncTimestamp)
|
||||
{
|
||||
// Only support hardware vsync on kitkat, L and up due to inaccurate timings
|
||||
// with JellyBean.
|
||||
#if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
|
||||
TimeStamp vsyncTime = mozilla::TimeStamp::FromSystemTime(aVsyncTimestamp);
|
||||
gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().NotifyVsync(vsyncTime);
|
||||
#else
|
||||
// If this device doesn't support vsync, this function should not be used.
|
||||
MOZ_ASSERT(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Called on the "invalidator" thread (run from HAL).
|
||||
void
|
||||
HwcComposer2D::Invalidate()
|
||||
{
|
||||
if (!mHal->HasHwc()) {
|
||||
LOGE("HwcComposer2D::Invalidate failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
if (mCompositorBridgeParent) {
|
||||
mCompositorBridgeParent->ScheduleRenderOnCompositorThread();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class HotplugEvent : public Runnable {
|
||||
public:
|
||||
HotplugEvent(GonkDisplay::DisplayType aType, bool aConnected)
|
||||
: mType(aType)
|
||||
, mConnected(aConnected)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
RefPtr<nsScreenManagerGonk> screenManager =
|
||||
nsScreenManagerGonk::GetInstance();
|
||||
if (mConnected) {
|
||||
screenManager->AddScreen(mType);
|
||||
} else {
|
||||
screenManager->RemoveScreen(mType);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
GonkDisplay::DisplayType mType;
|
||||
bool mConnected;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void
|
||||
HwcComposer2D::Hotplug(int aDisplay, int aConnected)
|
||||
{
|
||||
NS_DispatchToMainThread(new HotplugEvent(GonkDisplay::DISPLAY_EXTERNAL,
|
||||
aConnected));
|
||||
}
|
||||
|
||||
void
|
||||
HwcComposer2D::SetCompositorBridgeParent(CompositorBridgeParent* aCompositorBridgeParent)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mCompositorBridgeParent = aCompositorBridgeParent;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::ReallocLayerList()
|
||||
{
|
||||
int size = sizeof(HwcList) +
|
||||
((mMaxLayerCount + LAYER_COUNT_INCREMENTS) * sizeof(HwcLayer));
|
||||
|
||||
HwcList* listrealloc = (HwcList*)realloc(mList, size);
|
||||
|
||||
if (!listrealloc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mList) {
|
||||
//first alloc, initialize
|
||||
listrealloc->numHwLayers = 0;
|
||||
listrealloc->flags = 0;
|
||||
}
|
||||
|
||||
mList = listrealloc;
|
||||
mMaxLayerCount += LAYER_COUNT_INCREMENTS;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::PrepareLayerList(Layer* aLayer,
|
||||
const nsIntRect& aClip,
|
||||
const Matrix& aParentTransform,
|
||||
bool aFindSidebandStreams)
|
||||
{
|
||||
// NB: we fall off this path whenever there are container layers
|
||||
// that require intermediate surfaces. That means all the
|
||||
// GetEffective*() coordinates are relative to the framebuffer.
|
||||
|
||||
bool fillColor = false;
|
||||
|
||||
const nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
|
||||
if (visibleRegion.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t opacity = std::min(0xFF, (int)(aLayer->GetEffectiveOpacity() * 256.0));
|
||||
if (opacity == 0) {
|
||||
LOGD("%s Layer has zero opacity; skipping", aLayer->Name());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mHal->SupportTransparency() && opacity < 0xFF && !aFindSidebandStreams) {
|
||||
LOGD("%s Layer has planar semitransparency which is unsupported by hwcomposer", aLayer->Name());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aLayer->GetMaskLayer() && !aFindSidebandStreams) {
|
||||
LOGD("%s Layer has MaskLayer which is unsupported by hwcomposer", aLayer->Name());
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIntRect clip;
|
||||
nsIntRect layerClip = aLayer->GetLocalClipRect().valueOr(ParentLayerIntRect()).ToUnknownRect();
|
||||
nsIntRect* layerClipPtr = aLayer->GetLocalClipRect() ? &layerClip : nullptr;
|
||||
if (!HwcUtils::CalculateClipRect(aParentTransform,
|
||||
layerClipPtr,
|
||||
aClip,
|
||||
&clip))
|
||||
{
|
||||
LOGD("%s Clip rect is empty. Skip layer", aLayer->Name());
|
||||
return true;
|
||||
}
|
||||
|
||||
// HWC supports only the following 2D transformations:
|
||||
//
|
||||
// Scaling via the sourceCrop and displayFrame in HwcLayer
|
||||
// Translation via the sourceCrop and displayFrame in HwcLayer
|
||||
// Rotation (in square angles only) via the HWC_TRANSFORM_ROT_* flags
|
||||
// Reflection (horizontal and vertical) via the HWC_TRANSFORM_FLIP_* flags
|
||||
//
|
||||
// A 2D transform with PreservesAxisAlignedRectangles() has all the attributes
|
||||
// above
|
||||
Matrix layerTransform;
|
||||
if (!aLayer->GetEffectiveTransform().Is2D(&layerTransform) ||
|
||||
!layerTransform.PreservesAxisAlignedRectangles()) {
|
||||
LOGD("Layer EffectiveTransform has a 3D transform or a non-square angle rotation");
|
||||
return false;
|
||||
}
|
||||
|
||||
Matrix layerBufferTransform;
|
||||
if (!aLayer->GetEffectiveTransformForBuffer().Is2D(&layerBufferTransform) ||
|
||||
!layerBufferTransform.PreservesAxisAlignedRectangles()) {
|
||||
LOGD("Layer EffectiveTransformForBuffer has a 3D transform or a non-square angle rotation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ContainerLayer* container = aLayer->AsContainerLayer()) {
|
||||
if (container->UseIntermediateSurface() && !aFindSidebandStreams) {
|
||||
LOGD("Container layer needs intermediate surface");
|
||||
return false;
|
||||
}
|
||||
AutoTArray<Layer*, 12> children;
|
||||
container->SortChildrenBy3DZOrder(children);
|
||||
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
if (!PrepareLayerList(children[i], clip, layerTransform, aFindSidebandStreams) &&
|
||||
!aFindSidebandStreams) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LayerRenderState state = aLayer->GetRenderState();
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (!state.GetGrallocBuffer() && !state.GetSidebandStream().IsValid()) {
|
||||
#else
|
||||
if (!state.GetGrallocBuffer()) {
|
||||
#endif
|
||||
if (aLayer->AsColorLayer() && mColorFill) {
|
||||
fillColor = true;
|
||||
} else {
|
||||
LOGD("%s Layer doesn't have a gralloc buffer", aLayer->Name());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsIntRect visibleRect = visibleRegion.GetBounds();
|
||||
|
||||
nsIntRect bufferRect;
|
||||
if (fillColor) {
|
||||
bufferRect = nsIntRect(visibleRect);
|
||||
} else {
|
||||
nsIntRect layerRect;
|
||||
if (state.mHasOwnOffset) {
|
||||
bufferRect = nsIntRect(state.mOffset.x, state.mOffset.y,
|
||||
state.mSize.width, state.mSize.height);
|
||||
layerRect = bufferRect;
|
||||
} else {
|
||||
//Since the buffer doesn't have its own offset, assign the whole
|
||||
//surface size as its buffer bounds
|
||||
bufferRect = nsIntRect(0, 0, state.mSize.width, state.mSize.height);
|
||||
layerRect = bufferRect;
|
||||
if (aLayer->GetType() == Layer::TYPE_IMAGE) {
|
||||
ImageLayer* imageLayer = static_cast<ImageLayer*>(aLayer);
|
||||
if(imageLayer->GetScaleMode() != ScaleMode::SCALE_NONE) {
|
||||
layerRect = nsIntRect(0, 0, imageLayer->GetScaleToSize().width, imageLayer->GetScaleToSize().height);
|
||||
}
|
||||
}
|
||||
}
|
||||
// In some cases the visible rect assigned to the layer can be larger
|
||||
// than the layer's surface, e.g., an ImageLayer with a small Image
|
||||
// in it.
|
||||
visibleRect.IntersectRect(visibleRect, layerRect);
|
||||
}
|
||||
|
||||
// Buffer rotation is not to be confused with the angled rotation done by a transform matrix
|
||||
// It's a fancy PaintedLayer feature used for scrolling
|
||||
if (state.BufferRotated()) {
|
||||
LOGD("%s Layer has a rotated buffer", aLayer->Name());
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool needsYFlip = state.OriginBottomLeft() ? true
|
||||
: false;
|
||||
|
||||
hwc_rect_t sourceCrop, displayFrame;
|
||||
if(!HwcUtils::PrepareLayerRects(visibleRect,
|
||||
layerTransform,
|
||||
layerBufferTransform,
|
||||
clip,
|
||||
bufferRect,
|
||||
needsYFlip,
|
||||
&(sourceCrop),
|
||||
&(displayFrame)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// OK! We can compose this layer with hwc.
|
||||
int current = mList ? mList->numHwLayers : 0;
|
||||
|
||||
// Do not compose any layer below full-screen Opaque layer
|
||||
// Note: It can be generalized to non-fullscreen Opaque layers.
|
||||
bool isOpaque = opacity == 0xFF &&
|
||||
(state.mFlags & LayerRenderStateFlags::OPAQUE);
|
||||
// Currently we perform opacity calculation using the *bounds* of the layer.
|
||||
// We can only make this assumption if we're not dealing with a complex visible region.
|
||||
bool isSimpleVisibleRegion = visibleRegion.Contains(visibleRect);
|
||||
if (current && isOpaque && isSimpleVisibleRegion) {
|
||||
nsIntRect displayRect = nsIntRect(displayFrame.left, displayFrame.top,
|
||||
displayFrame.right - displayFrame.left, displayFrame.bottom - displayFrame.top);
|
||||
if (displayRect.Contains(mScreenRect)) {
|
||||
// In z-order, all previous layers are below
|
||||
// the current layer. We can ignore them now.
|
||||
mList->numHwLayers = current = 0;
|
||||
mHwcLayerMap.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mList || current >= mMaxLayerCount) {
|
||||
if (!ReallocLayerList() || current >= mMaxLayerCount) {
|
||||
LOGE("PrepareLayerList failed! Could not increase the maximum layer count");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HwcLayer& hwcLayer = mList->hwLayers[current];
|
||||
hwcLayer.displayFrame = displayFrame;
|
||||
mHal->SetCrop(hwcLayer, sourceCrop);
|
||||
buffer_handle_t handle = nullptr;
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (state.GetSidebandStream().IsValid()) {
|
||||
handle = state.GetSidebandStream().GetRawNativeHandle();
|
||||
} else if (state.GetGrallocBuffer()) {
|
||||
handle = state.GetGrallocBuffer()->getNativeBuffer()->handle;
|
||||
}
|
||||
#else
|
||||
if (state.GetGrallocBuffer()) {
|
||||
handle = state.GetGrallocBuffer()->getNativeBuffer()->handle;
|
||||
}
|
||||
#endif
|
||||
hwcLayer.handle = handle;
|
||||
|
||||
hwcLayer.flags = 0;
|
||||
hwcLayer.hints = 0;
|
||||
hwcLayer.blending = isOpaque ? HWC_BLENDING_NONE : HWC_BLENDING_PREMULT;
|
||||
#if ANDROID_VERSION >= 17
|
||||
hwcLayer.compositionType = HWC_FRAMEBUFFER;
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (state.GetSidebandStream().IsValid()) {
|
||||
hwcLayer.compositionType = HWC_SIDEBAND;
|
||||
}
|
||||
#endif
|
||||
hwcLayer.acquireFenceFd = -1;
|
||||
hwcLayer.releaseFenceFd = -1;
|
||||
#if ANDROID_VERSION >= 18
|
||||
hwcLayer.planeAlpha = opacity;
|
||||
#endif
|
||||
#else
|
||||
hwcLayer.compositionType = HwcUtils::HWC_USE_COPYBIT;
|
||||
#endif
|
||||
|
||||
if (!fillColor) {
|
||||
if (state.FormatRBSwapped()) {
|
||||
if (!mRBSwapSupport) {
|
||||
LOGD("No R/B swap support in H/W Composer");
|
||||
return false;
|
||||
}
|
||||
hwcLayer.flags |= HwcUtils::HWC_FORMAT_RB_SWAP;
|
||||
}
|
||||
|
||||
// Translation and scaling have been addressed in PrepareLayerRects().
|
||||
// Given the above and that we checked for PreservesAxisAlignedRectangles()
|
||||
// the only possible transformations left to address are
|
||||
// square angle rotation and horizontal/vertical reflection.
|
||||
//
|
||||
// The rotation and reflection permutations total 16 but can be
|
||||
// reduced to 8 transformations after eliminating redundancies.
|
||||
//
|
||||
// All matrices represented here are in the form
|
||||
//
|
||||
// | xx xy |
|
||||
// | yx yy |
|
||||
//
|
||||
// And ignore scaling.
|
||||
//
|
||||
// Reflection is applied before rotation
|
||||
gfx::Matrix rotation = layerTransform;
|
||||
// Compute fuzzy zero like PreservesAxisAlignedRectangles()
|
||||
if (fabs(rotation._11) < 1e-6) {
|
||||
if (rotation._21 < 0) {
|
||||
if (rotation._12 > 0) {
|
||||
// 90 degree rotation
|
||||
//
|
||||
// | 0 -1 |
|
||||
// | 1 0 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_ROT_90;
|
||||
LOGD("Layer rotated 90 degrees");
|
||||
}
|
||||
else {
|
||||
// Horizontal reflection then 90 degree rotation
|
||||
//
|
||||
// | 0 -1 | | -1 0 | = | 0 -1 |
|
||||
// | 1 0 | | 0 1 | | -1 0 |
|
||||
//
|
||||
// same as vertical reflection then 270 degree rotation
|
||||
//
|
||||
// | 0 1 | | 1 0 | = | 0 -1 |
|
||||
// | -1 0 | | 0 -1 | | -1 0 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_H;
|
||||
LOGD("Layer vertically reflected then rotated 270 degrees");
|
||||
}
|
||||
} else {
|
||||
if (rotation._12 < 0) {
|
||||
// 270 degree rotation
|
||||
//
|
||||
// | 0 1 |
|
||||
// | -1 0 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_ROT_270;
|
||||
LOGD("Layer rotated 270 degrees");
|
||||
}
|
||||
else {
|
||||
// Vertical reflection then 90 degree rotation
|
||||
//
|
||||
// | 0 1 | | -1 0 | = | 0 1 |
|
||||
// | -1 0 | | 0 1 | | 1 0 |
|
||||
//
|
||||
// Same as horizontal reflection then 270 degree rotation
|
||||
//
|
||||
// | 0 -1 | | 1 0 | = | 0 1 |
|
||||
// | 1 0 | | 0 -1 | | 1 0 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_V;
|
||||
LOGD("Layer horizontally reflected then rotated 270 degrees");
|
||||
}
|
||||
}
|
||||
} else if (rotation._11 < 0) {
|
||||
if (rotation._22 > 0) {
|
||||
// Horizontal reflection
|
||||
//
|
||||
// | -1 0 |
|
||||
// | 0 1 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_FLIP_H;
|
||||
LOGD("Layer rotated 180 degrees");
|
||||
}
|
||||
else {
|
||||
// 180 degree rotation
|
||||
//
|
||||
// | -1 0 |
|
||||
// | 0 -1 |
|
||||
//
|
||||
// Same as horizontal and vertical reflection
|
||||
//
|
||||
// | -1 0 | | 1 0 | = | -1 0 |
|
||||
// | 0 1 | | 0 -1 | | 0 -1 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_ROT_180;
|
||||
LOGD("Layer rotated 180 degrees");
|
||||
}
|
||||
} else {
|
||||
if (rotation._22 < 0) {
|
||||
// Vertical reflection
|
||||
//
|
||||
// | 1 0 |
|
||||
// | 0 -1 |
|
||||
//
|
||||
hwcLayer.transform = HWC_TRANSFORM_FLIP_V;
|
||||
LOGD("Layer rotated 180 degrees");
|
||||
}
|
||||
else {
|
||||
// No rotation or reflection
|
||||
//
|
||||
// | 1 0 |
|
||||
// | 0 1 |
|
||||
//
|
||||
hwcLayer.transform = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const bool needsYFlip = state.OriginBottomLeft() ? true
|
||||
: false;
|
||||
|
||||
if (needsYFlip) {
|
||||
// Invert vertical reflection flag if it was already set
|
||||
hwcLayer.transform ^= HWC_TRANSFORM_FLIP_V;
|
||||
}
|
||||
hwc_region_t region;
|
||||
if (visibleRegion.GetNumRects() > 1) {
|
||||
mVisibleRegions.push_back(HwcUtils::RectVector());
|
||||
HwcUtils::RectVector* visibleRects = &(mVisibleRegions.back());
|
||||
bool isVisible = false;
|
||||
if(!HwcUtils::PrepareVisibleRegion(visibleRegion,
|
||||
layerTransform,
|
||||
layerBufferTransform,
|
||||
clip,
|
||||
bufferRect,
|
||||
visibleRects,
|
||||
isVisible)) {
|
||||
LOGD("A region of layer is too small to be rendered by HWC");
|
||||
return false;
|
||||
}
|
||||
if (!isVisible) {
|
||||
// Layer is not visible, no need to render it
|
||||
return true;
|
||||
}
|
||||
region.numRects = visibleRects->size();
|
||||
region.rects = &((*visibleRects)[0]);
|
||||
} else {
|
||||
region.numRects = 1;
|
||||
region.rects = &(hwcLayer.displayFrame);
|
||||
}
|
||||
hwcLayer.visibleRegionScreen = region;
|
||||
} else {
|
||||
hwcLayer.flags |= HwcUtils::HWC_COLOR_FILL;
|
||||
ColorLayer* colorLayer = aLayer->AsColorLayer();
|
||||
if (colorLayer->GetColor().a < 1.0) {
|
||||
LOGD("Color layer has semitransparency which is unsupported");
|
||||
return false;
|
||||
}
|
||||
hwcLayer.transform = colorLayer->GetColor().ToABGR();
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (aFindSidebandStreams && hwcLayer.compositionType == HWC_SIDEBAND) {
|
||||
mCachedSidebandLayers.AppendElement(hwcLayer);
|
||||
}
|
||||
#endif
|
||||
|
||||
mHwcLayerMap.AppendElement(static_cast<LayerComposite*>(aLayer->ImplData()));
|
||||
mList->numHwLayers++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if ANDROID_VERSION >= 17
|
||||
bool
|
||||
HwcComposer2D::TryHwComposition(nsScreenGonk* aScreen)
|
||||
{
|
||||
DisplaySurface* dispSurface = aScreen->GetDisplaySurface();
|
||||
|
||||
if (!(dispSurface && dispSurface->lastHandle)) {
|
||||
LOGD("H/W Composition failed. DispSurface not initialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add FB layer
|
||||
int idx = mList->numHwLayers++;
|
||||
if (idx >= mMaxLayerCount) {
|
||||
if (!ReallocLayerList() || idx >= mMaxLayerCount) {
|
||||
LOGE("TryHwComposition failed! Could not add FB layer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Prepare(dispSurface->lastHandle, -1, aScreen);
|
||||
|
||||
/* Possible composition paths, after hwc prepare:
|
||||
1. GPU Composition
|
||||
2. BLIT Composition
|
||||
3. Full OVERLAY Composition
|
||||
4. Partial OVERLAY Composition (GPU + OVERLAY) */
|
||||
|
||||
bool gpuComposite = false;
|
||||
bool blitComposite = false;
|
||||
bool overlayComposite = true;
|
||||
|
||||
for (int j=0; j < idx; j++) {
|
||||
if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER ||
|
||||
mList->hwLayers[j].compositionType == HWC_BLIT) {
|
||||
// Full OVERLAY composition is not possible on this frame
|
||||
// It is either GPU / BLIT / partial OVERLAY composition.
|
||||
overlayComposite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!overlayComposite) {
|
||||
for (int k=0; k < idx; k++) {
|
||||
switch (mList->hwLayers[k].compositionType) {
|
||||
case HWC_FRAMEBUFFER:
|
||||
gpuComposite = true;
|
||||
break;
|
||||
case HWC_BLIT:
|
||||
blitComposite = true;
|
||||
break;
|
||||
#if ANDROID_VERSION >= 21
|
||||
case HWC_SIDEBAND:
|
||||
#endif
|
||||
case HWC_OVERLAY: {
|
||||
// HWC will compose HWC_OVERLAY layers in partial
|
||||
// Overlay Composition, set layer composition flag
|
||||
// on mapped LayerComposite to skip GPU composition
|
||||
mHwcLayerMap[k]->SetLayerComposited(true);
|
||||
|
||||
uint8_t opacity = std::min(0xFF, (int)(mHwcLayerMap[k]->GetLayer()->GetEffectiveOpacity() * 256.0));
|
||||
if ((mList->hwLayers[k].hints & HWC_HINT_CLEAR_FB) &&
|
||||
(opacity == 0xFF)) {
|
||||
// Clear visible rect on FB with transparent pixels.
|
||||
hwc_rect_t r = mList->hwLayers[k].displayFrame;
|
||||
mHwcLayerMap[k]->SetClearRect(nsIntRect(r.left, r.top,
|
||||
r.right - r.left,
|
||||
r.bottom - r.top));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpuComposite) {
|
||||
// GPU or partial OVERLAY Composition
|
||||
return false;
|
||||
} else if (blitComposite) {
|
||||
// BLIT Composition, flip DispSurface target
|
||||
GetGonkDisplay()->UpdateDispSurface(aScreen->GetEGLDisplay(), aScreen->GetEGLSurface());
|
||||
DisplaySurface* dispSurface = aScreen->GetDisplaySurface();
|
||||
if (!dispSurface) {
|
||||
LOGE("H/W Composition failed. NULL DispSurface.");
|
||||
return false;
|
||||
}
|
||||
mList->hwLayers[idx].handle = dispSurface->lastHandle;
|
||||
mList->hwLayers[idx].acquireFenceFd = dispSurface->GetPrevDispAcquireFd();
|
||||
}
|
||||
}
|
||||
|
||||
// BLIT or full OVERLAY Composition
|
||||
return Commit(aScreen);
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::Render(nsIWidget* aWidget)
|
||||
{
|
||||
nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen();
|
||||
|
||||
// HWC module does not exist or mList is not created yet.
|
||||
if (!mHal->HasHwc() || !mList) {
|
||||
return GetGonkDisplay()->SwapBuffers(screen->GetEGLDisplay(), screen->GetEGLSurface());
|
||||
} else if (!mList && !ReallocLayerList()) {
|
||||
LOGE("Cannot realloc layer list");
|
||||
return false;
|
||||
}
|
||||
|
||||
DisplaySurface* dispSurface = screen->GetDisplaySurface();
|
||||
if (!dispSurface) {
|
||||
LOGE("H/W Composition failed. DispSurface not initialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mPrepared) {
|
||||
// No mHwc prepare, if already prepared in current draw cycle
|
||||
mList->hwLayers[mList->numHwLayers - 1].handle = dispSurface->lastHandle;
|
||||
mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = dispSurface->GetPrevDispAcquireFd();
|
||||
} else {
|
||||
// Update screen rect to handle a case that TryRenderWithHwc() is not called.
|
||||
mScreenRect = screen->GetNaturalBounds().ToUnknownRect();
|
||||
|
||||
mList->flags = HWC_GEOMETRY_CHANGED;
|
||||
mList->numHwLayers = 2;
|
||||
mList->hwLayers[0].hints = 0;
|
||||
mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
|
||||
mList->hwLayers[0].flags = HWC_SKIP_LAYER;
|
||||
mList->hwLayers[0].backgroundColor = {0};
|
||||
mList->hwLayers[0].acquireFenceFd = -1;
|
||||
mList->hwLayers[0].releaseFenceFd = -1;
|
||||
mList->hwLayers[0].displayFrame = {0, 0, mScreenRect.width, mScreenRect.height};
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
// Prepare layers for sideband streams
|
||||
const uint32_t len = mCachedSidebandLayers.Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
++mList->numHwLayers;
|
||||
mList->hwLayers[i+1] = mCachedSidebandLayers[i];
|
||||
}
|
||||
#endif
|
||||
Prepare(dispSurface->lastHandle, dispSurface->GetPrevDispAcquireFd(), screen);
|
||||
}
|
||||
|
||||
// GPU or partial HWC Composition
|
||||
return Commit(screen);
|
||||
}
|
||||
|
||||
void
|
||||
HwcComposer2D::Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* screen)
|
||||
{
|
||||
if (mPrepared) {
|
||||
LOGE("Multiple hwc prepare calls!");
|
||||
}
|
||||
hwc_rect_t dispRect = {0, 0, mScreenRect.width, mScreenRect.height};
|
||||
mHal->Prepare(mList, screen->GetDisplayType(), dispRect, dispHandle, fence);
|
||||
mPrepared = true;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::Commit(nsScreenGonk* aScreen)
|
||||
{
|
||||
for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) {
|
||||
mList->hwLayers[j].acquireFenceFd = -1;
|
||||
if (mHwcLayerMap.IsEmpty() ||
|
||||
(mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER)) {
|
||||
continue;
|
||||
}
|
||||
LayerRenderState state = mHwcLayerMap[j]->GetLayer()->GetRenderState();
|
||||
if (!state.mTexture) {
|
||||
continue;
|
||||
}
|
||||
FenceHandle fence = state.mTexture->GetAndResetAcquireFenceHandle();
|
||||
if (fence.IsValid()) {
|
||||
RefPtr<FenceHandle::FdObj> fdObj = fence.GetAndResetFdObj();
|
||||
mList->hwLayers[j].acquireFenceFd = fdObj->GetAndResetFd();
|
||||
}
|
||||
}
|
||||
|
||||
int err = mHal->Set(mList, aScreen->GetDisplayType());
|
||||
|
||||
mPrevRetireFence.TransferToAnotherFenceHandle(mPrevDisplayFence);
|
||||
|
||||
for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) {
|
||||
if (mList->hwLayers[j].releaseFenceFd >= 0) {
|
||||
int fd = mList->hwLayers[j].releaseFenceFd;
|
||||
mList->hwLayers[j].releaseFenceFd = -1;
|
||||
RefPtr<FenceHandle::FdObj> fdObj = new FenceHandle::FdObj(fd);
|
||||
FenceHandle fence(fdObj);
|
||||
|
||||
LayerRenderState state = mHwcLayerMap[j]->GetLayer()->GetRenderState();
|
||||
if (!state.mTexture) {
|
||||
continue;
|
||||
}
|
||||
state.mTexture->SetReleaseFenceHandle(fence);
|
||||
}
|
||||
}
|
||||
|
||||
if (mList->retireFenceFd >= 0) {
|
||||
mPrevRetireFence = FenceHandle(new FenceHandle::FdObj(mList->retireFenceFd));
|
||||
}
|
||||
|
||||
// Set DisplaySurface layer fence
|
||||
DisplaySurface* displaySurface = aScreen->GetDisplaySurface();
|
||||
displaySurface->setReleaseFenceFd(mList->hwLayers[mList->numHwLayers - 1].releaseFenceFd);
|
||||
mList->hwLayers[mList->numHwLayers - 1].releaseFenceFd = -1;
|
||||
|
||||
mPrepared = false;
|
||||
return !err;
|
||||
}
|
||||
#else
|
||||
bool
|
||||
HwcComposer2D::TryHwComposition(nsScreenGonk* aScreen)
|
||||
{
|
||||
mHal->SetEGLInfo(aScreen->GetEGLDisplay(), aScreen->GetEGLSurface());
|
||||
return !mHal->Set(mList, aScreen->GetDisplayType());
|
||||
}
|
||||
|
||||
bool
|
||||
HwcComposer2D::Render(nsIWidget* aWidget)
|
||||
{
|
||||
nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen();
|
||||
return GetGonkDisplay()->SwapBuffers(screen->GetEGLDisplay(), screen->GetEGLSurface());
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
HwcComposer2D::TryRenderWithHwc(Layer* aRoot,
|
||||
nsIWidget* aWidget,
|
||||
bool aGeometryChanged,
|
||||
bool aHasImageHostOverlays)
|
||||
{
|
||||
if (!mHal->HasHwc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen();
|
||||
|
||||
if (mList) {
|
||||
mList->flags = mHal->GetGeometryChangedFlag(aGeometryChanged);
|
||||
mList->numHwLayers = 0;
|
||||
mHwcLayerMap.Clear();
|
||||
}
|
||||
|
||||
if (mPrepared) {
|
||||
mHal->ResetHwc();
|
||||
mPrepared = false;
|
||||
}
|
||||
|
||||
// XXX: The clear() below means all rect vectors will be have to be
|
||||
// reallocated. We may want to avoid this if possible
|
||||
mVisibleRegions.clear();
|
||||
|
||||
mScreenRect = screen->GetNaturalBounds().ToUnknownRect();
|
||||
MOZ_ASSERT(mHwcLayerMap.IsEmpty());
|
||||
mCachedSidebandLayers.Clear();
|
||||
if (!PrepareLayerList(aRoot,
|
||||
mScreenRect,
|
||||
gfx::Matrix(),
|
||||
/* aFindSidebandStreams */ false))
|
||||
{
|
||||
mHwcLayerMap.Clear();
|
||||
LOGD("Render aborted. Fallback to GPU Composition");
|
||||
if (aHasImageHostOverlays) {
|
||||
LOGD("Prepare layers of SidebandStreams");
|
||||
// Failed to create a layer list for hwc. But we need the list
|
||||
// only for handling sideband streams. Traverse layer tree without
|
||||
// some early returns to make sure we can find all the layers.
|
||||
// It is the best wrong thing that we can do.
|
||||
PrepareLayerList(aRoot,
|
||||
mScreenRect,
|
||||
gfx::Matrix(),
|
||||
/* aFindSidebandStreams */ true);
|
||||
// Reset mPrepared to false, since we already fell back to
|
||||
// gpu composition.
|
||||
mPrepared = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send data to LayerScope for debugging
|
||||
SendtoLayerScope();
|
||||
|
||||
if (!TryHwComposition(screen)) {
|
||||
LOGD("Full HWC Composition failed. Fallback to GPU Composition or partial OVERLAY Composition");
|
||||
LayerScope::CleanLayer();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("Frame rendered");
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HwcComposer2D::SendtoLayerScope()
|
||||
{
|
||||
if (!LayerScope::CheckSendable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int len = mList->numHwLayers;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
LayerComposite* layer = mHwcLayerMap[i];
|
||||
const hwc_rect_t r = mList->hwLayers[i].displayFrame;
|
||||
LayerScope::SendLayer(layer, r.right - r.left, r.bottom - r.top);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,123 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/*
|
||||
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 mozilla_HwcComposer2D
|
||||
#define mozilla_HwcComposer2D
|
||||
|
||||
#include "Composer2D.h"
|
||||
#include "hwchal/HwcHALBase.h" // for HwcHAL
|
||||
#include "HwcUtils.h" // for RectVector
|
||||
#include "Layers.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/layers/FenceUtils.h" // for FenceHandle
|
||||
#include "mozilla/UniquePtr.h" // for HwcHAL
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include <utils/Timers.h>
|
||||
|
||||
class nsScreenGonk;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gl {
|
||||
class GLContext;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
class CompositorBridgeParent;
|
||||
class Layer;
|
||||
}
|
||||
|
||||
/*
|
||||
* HwcComposer2D provides a way for gecko to render frames
|
||||
* using hwcomposer.h in the AOSP HAL.
|
||||
*
|
||||
* hwcomposer.h defines an interface for display composition
|
||||
* using dedicated hardware. This hardware is usually faster
|
||||
* or more power efficient than the GPU. However, in exchange
|
||||
* for better performance, generality has to be sacrificed:
|
||||
* no 3d transforms, no intermediate surfaces, no special shader effects,
|
||||
* and loss of other goodies depending on the platform.
|
||||
*
|
||||
* In general, when hwc is enabled gecko tries to compose
|
||||
* its frames using HwcComposer2D first. Then if HwcComposer2D is
|
||||
* unable to compose a frame then it falls back to compose it
|
||||
* using the GPU with OpenGL.
|
||||
*
|
||||
*/
|
||||
class HwcComposer2D : public mozilla::layers::Composer2D {
|
||||
public:
|
||||
HwcComposer2D();
|
||||
virtual ~HwcComposer2D();
|
||||
|
||||
static HwcComposer2D* GetInstance();
|
||||
|
||||
// Returns TRUE if the container has been succesfully rendered
|
||||
// Returns FALSE if the container cannot be fully rendered
|
||||
// by this composer so nothing was rendered at all
|
||||
virtual bool TryRenderWithHwc(layers::Layer* aRoot,
|
||||
nsIWidget* aWidget,
|
||||
bool aGeometryChanged,
|
||||
bool aHasImageHostOverlays) override;
|
||||
|
||||
virtual bool Render(nsIWidget* aWidget) override;
|
||||
|
||||
virtual bool HasHwc() override { return mHal->HasHwc(); }
|
||||
|
||||
bool EnableVsync(bool aEnable);
|
||||
bool RegisterHwcEventCallback();
|
||||
void Vsync(int aDisplay, int64_t aTimestamp);
|
||||
void Invalidate();
|
||||
void Hotplug(int aDisplay, int aConnected);
|
||||
void SetCompositorBridgeParent(layers::CompositorBridgeParent* aCompositorBridgeParent);
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
void Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* screen);
|
||||
bool Commit(nsScreenGonk* aScreen);
|
||||
bool TryHwComposition(nsScreenGonk* aScreen);
|
||||
bool ReallocLayerList();
|
||||
bool PrepareLayerList(layers::Layer* aContainer, const nsIntRect& aClip,
|
||||
const gfx::Matrix& aParentTransform,
|
||||
bool aFindSidebandStreams);
|
||||
void SendtoLayerScope();
|
||||
|
||||
UniquePtr<HwcHALBase> mHal;
|
||||
HwcList* mList;
|
||||
nsIntRect mScreenRect;
|
||||
int mMaxLayerCount;
|
||||
bool mColorFill;
|
||||
bool mRBSwapSupport;
|
||||
//Holds all the dynamically allocated RectVectors needed
|
||||
//to render the current frame
|
||||
std::list<HwcUtils::RectVector> mVisibleRegions;
|
||||
layers::FenceHandle mPrevRetireFence;
|
||||
layers::FenceHandle mPrevDisplayFence;
|
||||
nsTArray<HwcLayer> mCachedSidebandLayers;
|
||||
nsTArray<layers::LayerComposite*> mHwcLayerMap;
|
||||
bool mPrepared;
|
||||
bool mHasHWVsync;
|
||||
layers::CompositorBridgeParent* mCompositorBridgeParent;
|
||||
Mutex mLock;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_HwcComposer2D
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 <android/log.h>
|
||||
#include "HwcUtils.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
#define LOG_TAG "HwcUtils"
|
||||
|
||||
#if (LOG_NDEBUG == 0)
|
||||
#define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, ## args)
|
||||
#else
|
||||
#define LOGD(args...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, ## args)
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* Utility functions for HwcComposer */
|
||||
|
||||
|
||||
|
||||
/* static */ bool
|
||||
HwcUtils::PrepareLayerRects(nsIntRect aVisible,
|
||||
const gfx::Matrix& aLayerTransform,
|
||||
const gfx::Matrix& aLayerBufferTransform,
|
||||
nsIntRect aClip, nsIntRect aBufferRect,
|
||||
bool aYFlipped,
|
||||
hwc_rect_t* aSourceCrop, hwc_rect_t* aVisibleRegionScreen) {
|
||||
|
||||
gfxMatrix aTransform = gfx::ThebesMatrix(aLayerTransform);
|
||||
gfxRect visibleRect(ThebesRect(aVisible));
|
||||
gfxRect clip(ThebesRect(aClip));
|
||||
gfxRect visibleRectScreen = aTransform.TransformBounds(visibleRect);
|
||||
// |clip| is guaranteed to be integer
|
||||
visibleRectScreen.IntersectRect(visibleRectScreen, clip);
|
||||
|
||||
if (visibleRectScreen.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfxMatrix inverse = gfx::ThebesMatrix(aLayerBufferTransform);
|
||||
inverse.Invert();
|
||||
gfxRect crop = inverse.TransformBounds(visibleRectScreen);
|
||||
|
||||
//clip to buffer size
|
||||
crop.IntersectRect(crop, ThebesRect(aBufferRect));
|
||||
crop.Round();
|
||||
|
||||
if (crop.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//propagate buffer clipping back to visible rect
|
||||
gfxMatrix layerBufferTransform = gfx::ThebesMatrix(aLayerBufferTransform);
|
||||
visibleRectScreen = layerBufferTransform.TransformBounds(crop);
|
||||
visibleRectScreen.Round();
|
||||
|
||||
// Map from layer space to buffer space
|
||||
crop -= aBufferRect.TopLeft();
|
||||
if (aYFlipped) {
|
||||
crop.y = aBufferRect.height - (crop.y + crop.height);
|
||||
}
|
||||
|
||||
aSourceCrop->left = crop.x;
|
||||
aSourceCrop->top = crop.y;
|
||||
aSourceCrop->right = crop.x + crop.width;
|
||||
aSourceCrop->bottom = crop.y + crop.height;
|
||||
|
||||
aVisibleRegionScreen->left = visibleRectScreen.x;
|
||||
aVisibleRegionScreen->top = visibleRectScreen.y;
|
||||
aVisibleRegionScreen->right = visibleRectScreen.x + visibleRectScreen.width;
|
||||
aVisibleRegionScreen->bottom = visibleRectScreen.y + visibleRectScreen.height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
HwcUtils::PrepareVisibleRegion(const nsIntRegion& aVisible,
|
||||
const gfx::Matrix& aLayerTransform,
|
||||
const gfx::Matrix& aLayerBufferTransform,
|
||||
nsIntRect aClip, nsIntRect aBufferRect,
|
||||
RectVector* aVisibleRegionScreen,
|
||||
bool& aIsVisible) {
|
||||
const float MIN_SRC_WIDTH = 2.f;
|
||||
const float MIN_SRC_HEIGHT = 2.f;
|
||||
|
||||
gfxMatrix layerTransform = gfx::ThebesMatrix(aLayerTransform);
|
||||
gfxMatrix layerBufferTransform = gfx::ThebesMatrix(aLayerBufferTransform);
|
||||
gfxRect bufferRect =
|
||||
layerBufferTransform.TransformBounds(ThebesRect(aBufferRect));
|
||||
gfxMatrix inverse = gfx::ThebesMatrix(aLayerBufferTransform);
|
||||
inverse.Invert();
|
||||
aIsVisible = false;
|
||||
|
||||
for (auto iter = aVisible.RectIter(); !iter.Done(); iter.Next()) {
|
||||
gfxRect screenRect =
|
||||
layerTransform.TransformBounds(ThebesRect(iter.Get()));
|
||||
screenRect.IntersectRect(screenRect, bufferRect);
|
||||
screenRect.IntersectRect(screenRect, ThebesRect(aClip));
|
||||
screenRect.Round();
|
||||
if (screenRect.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hwc_rect_t visibleRectScreen;
|
||||
visibleRectScreen.left = screenRect.x;
|
||||
visibleRectScreen.top = screenRect.y;
|
||||
visibleRectScreen.right = screenRect.XMost();
|
||||
visibleRectScreen.bottom = screenRect.YMost();
|
||||
|
||||
gfxRect srcCrop = inverse.TransformBounds(screenRect);
|
||||
// When src crop is very small, HWC could not render correctly in some cases.
|
||||
// See Bug 1169093
|
||||
if(srcCrop.Width() < MIN_SRC_WIDTH || srcCrop.Height() < MIN_SRC_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aVisibleRegionScreen->push_back(visibleRectScreen);
|
||||
aIsVisible = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
HwcUtils::CalculateClipRect(const gfx::Matrix& transform,
|
||||
const nsIntRect* aLayerClip,
|
||||
nsIntRect aParentClip, nsIntRect* aRenderClip) {
|
||||
|
||||
gfxMatrix aTransform = gfx::ThebesMatrix(transform);
|
||||
*aRenderClip = aParentClip;
|
||||
|
||||
if (!aLayerClip) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aLayerClip->IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIntRect clip = *aLayerClip;
|
||||
|
||||
gfxRect r = ThebesRect(clip);
|
||||
gfxRect trClip = aTransform.TransformBounds(r);
|
||||
trClip.Round();
|
||||
gfxUtils::GfxRectToIntRect(trClip, &clip);
|
||||
|
||||
aRenderClip->IntersectRect(*aRenderClip, clip);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 mozilla_HwcUtils
|
||||
#define mozilla_HwcUtils
|
||||
|
||||
#include "Layers.h"
|
||||
#include <vector>
|
||||
#include "hardware/hwcomposer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class Matrix;
|
||||
}
|
||||
|
||||
class HwcUtils {
|
||||
public:
|
||||
|
||||
enum {
|
||||
HWC_USE_GPU = HWC_FRAMEBUFFER,
|
||||
HWC_USE_OVERLAY = HWC_OVERLAY,
|
||||
HWC_USE_COPYBIT
|
||||
};
|
||||
|
||||
// HWC layer flags
|
||||
enum {
|
||||
// Draw a solid color rectangle
|
||||
// The color should be set on the transform member of the hwc_layer_t struct
|
||||
// The expected format is a 32 bit ABGR with 8 bits per component
|
||||
HWC_COLOR_FILL = 0x8,
|
||||
// Swap the RB pixels of gralloc buffer, like RGBA<->BGRA or RGBX<->BGRX
|
||||
// The flag will be set inside LayerRenderState
|
||||
HWC_FORMAT_RB_SWAP = 0x40
|
||||
};
|
||||
|
||||
typedef std::vector<hwc_rect_t> RectVector;
|
||||
|
||||
/* Utility functions - implemented in HwcUtils.cpp */
|
||||
|
||||
/**
|
||||
* Calculates the layer's clipping rectangle
|
||||
*
|
||||
* @param aTransform Input. A transformation matrix
|
||||
* It transforms the clip rect to screen space
|
||||
* @param aLayerClip Input. The layer's internal clipping rectangle.
|
||||
* This may be NULL which means the layer has no internal clipping
|
||||
* The origin is the top-left corner of the layer
|
||||
* @param aParentClip Input. The parent layer's rendering clipping rectangle
|
||||
* The origin is the top-left corner of the screen
|
||||
* @param aRenderClip Output. The layer's rendering clipping rectangle
|
||||
* The origin is the top-left corner of the screen
|
||||
* @return true if the layer should be rendered.
|
||||
* false if the layer can be skipped
|
||||
*/
|
||||
static bool CalculateClipRect(const gfx::Matrix& aTransform,
|
||||
const nsIntRect* aLayerClip,
|
||||
nsIntRect aParentClip, nsIntRect* aRenderClip);
|
||||
|
||||
|
||||
/**
|
||||
* Prepares hwc layer visible region required for hwc composition
|
||||
*
|
||||
* @param aVisible Input. Layer's unclipped visible region
|
||||
* The origin is the top-left corner of the layer
|
||||
* @param aLayerTransform Input. Layer's transformation matrix
|
||||
* It transforms from layer space to screen space
|
||||
* @param aLayerBufferTransform Input. Layer buffer's transformation matrix
|
||||
* It transforms from layer buffer's space to screen space
|
||||
* @param aClip Input. A clipping rectangle.
|
||||
* The origin is the top-left corner of the screen
|
||||
* @param aBufferRect Input. The layer's buffer bounds
|
||||
* The origin is the top-left corner of the layer
|
||||
* @param aVisibleRegionScreen Output. Visible region in screen space.
|
||||
* The origin is the top-left corner of the screen
|
||||
* @param aIsVisible Output. true if region is visible
|
||||
* false if region is not visible
|
||||
* @return true if region can be rendered by HWC.
|
||||
* false if region should not be rendered by HWC
|
||||
*/
|
||||
static bool PrepareVisibleRegion(const nsIntRegion& aVisible,
|
||||
const gfx::Matrix& aLayerTransform,
|
||||
const gfx::Matrix& aLayerBufferTransform,
|
||||
nsIntRect aClip, nsIntRect aBufferRect,
|
||||
RectVector* aVisibleRegionScreen,
|
||||
bool& aIsVisible);
|
||||
|
||||
|
||||
/**
|
||||
* Sets hwc layer rectangles required for hwc composition
|
||||
*
|
||||
* @param aVisible Input. Layer's unclipped visible rectangle
|
||||
* The origin is the top-left corner of the layer
|
||||
* @param aLayerTransform Input. Layer's transformation matrix
|
||||
* It transforms from layer space to screen space
|
||||
* @param aLayerBufferTransform Input. Layer buffer's transformation matrix
|
||||
* It transforms from layer buffer's space to screen space
|
||||
* @param aClip Input. A clipping rectangle.
|
||||
* The origin is the top-left corner of the screen
|
||||
* @param aBufferRect Input. The layer's buffer bounds
|
||||
* The origin is the top-left corner of the layer
|
||||
* @param aYFlipped Input. true if the buffer is rendered as Y flipped
|
||||
* @param aSurceCrop Output. Area of the source to consider,
|
||||
* the origin is the top-left corner of the buffer
|
||||
* @param aVisibleRegionScreen Output. Visible region in screen space.
|
||||
* The origin is the top-left corner of the screen
|
||||
* @return true if the layer should be rendered.
|
||||
* false if the layer can be skipped
|
||||
*/
|
||||
static bool PrepareLayerRects(nsIntRect aVisible,
|
||||
const gfx::Matrix& aLayerTransform,
|
||||
const gfx::Matrix& aLayerBufferTransform,
|
||||
nsIntRect aClip, nsIntRect aBufferRect,
|
||||
bool aYFlipped,
|
||||
hwc_rect_t* aSourceCrop,
|
||||
hwc_rect_t* aVisibleRegionScreen);
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_HwcUtils
|
|
@ -1,518 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Linux Foundation. All rights reserved
|
||||
*
|
||||
* Copyright (C) 2008 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 "base/basictypes.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIScreen.h"
|
||||
#include "nsIScreenManager.h"
|
||||
#include "ProcessOrientation.h"
|
||||
#include "mozilla/HalSensor.h"
|
||||
#include "math.h"
|
||||
#include "limits.h"
|
||||
#include "android/log.h"
|
||||
|
||||
#if 0
|
||||
#define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, "ProcessOrientation" , ## args)
|
||||
#else
|
||||
#define LOGD(args...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// We work with all angles in degrees in this class.
|
||||
#define RADIANS_TO_DEGREES (180/M_PI)
|
||||
|
||||
// Number of nanoseconds per millisecond.
|
||||
#define NANOS_PER_MS 1000000
|
||||
|
||||
// Indices into SensorEvent.values for the accelerometer sensor.
|
||||
#define ACCELEROMETER_DATA_X 0
|
||||
#define ACCELEROMETER_DATA_Y 1
|
||||
#define ACCELEROMETER_DATA_Z 2
|
||||
|
||||
// The minimum amount of time that a predicted rotation must be stable before
|
||||
// it is accepted as a valid rotation proposal. This value can be quite small
|
||||
// because the low-pass filter already suppresses most of the noise so we're
|
||||
// really just looking for quick confirmation that the last few samples are in
|
||||
// agreement as to the desired orientation.
|
||||
#define PROPOSAL_SETTLE_TIME_NANOS (40*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device last
|
||||
// exited the flat state (time since it was picked up) before the proposed
|
||||
// rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS (500*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device stopped
|
||||
// swinging (time since device appeared to be in the process of being put down
|
||||
// or put away into a pocket) before the proposed rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS (300*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device stopped
|
||||
// undergoing external acceleration before the proposed rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS (500*NANOS_PER_MS)
|
||||
|
||||
// If the tilt angle remains greater than the specified angle for a minimum of
|
||||
// the specified time, then the device is deemed to be lying flat
|
||||
// (just chillin' on a table).
|
||||
#define FLAT_ANGLE 75
|
||||
#define FLAT_TIME_NANOS (1000*NANOS_PER_MS)
|
||||
|
||||
// If the tilt angle has increased by at least delta degrees within the
|
||||
// specified amount of time, then the device is deemed to be swinging away
|
||||
// from the user down towards flat (tilt = 90).
|
||||
#define SWING_AWAY_ANGLE_DELTA 20
|
||||
#define SWING_TIME_NANOS (300*NANOS_PER_MS)
|
||||
|
||||
// The maximum sample inter-arrival time in milliseconds. If the acceleration
|
||||
// samples are further apart than this amount in time, we reset the state of
|
||||
// the low-pass filter and orientation properties. This helps to handle
|
||||
// boundary conditions when the device is turned on, wakes from suspend or
|
||||
// there is a significant gap in samples.
|
||||
#define MAX_FILTER_DELTA_TIME_NANOS (1000*NANOS_PER_MS)
|
||||
|
||||
// The acceleration filter time constant.
|
||||
//
|
||||
// This time constant is used to tune the acceleration filter such that
|
||||
// impulses and vibrational noise (think car dock) is suppressed before we try
|
||||
// to calculate the tilt and orientation angles.
|
||||
//
|
||||
// The filter time constant is related to the filter cutoff frequency, which
|
||||
// is the frequency at which signals are attenuated by 3dB (half the passband
|
||||
// power). Each successive octave beyond this frequency is attenuated by an
|
||||
// additional 6dB.
|
||||
//
|
||||
// Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
|
||||
// is given by Fc = 1 / (2pi * t).
|
||||
//
|
||||
// The higher the time constant, the lower the cutoff frequency, so more noise
|
||||
// will be suppressed.
|
||||
//
|
||||
// Filtering adds latency proportional the time constant (inversely
|
||||
// proportional to the cutoff frequency) so we don't want to make the time
|
||||
// constant too large or we can lose responsiveness. Likewise we don't want
|
||||
// to make it too small or we do a poor job suppressing acceleration spikes.
|
||||
// Empirically, 100ms seems to be too small and 500ms is too large. Android
|
||||
// default is 200.
|
||||
#define FILTER_TIME_CONSTANT_MS 200.0f
|
||||
|
||||
// State for orientation detection. Thresholds for minimum and maximum
|
||||
// allowable deviation from gravity.
|
||||
//
|
||||
// If the device is undergoing external acceleration (being bumped, in a car
|
||||
// that is turning around a corner or a plane taking off) then the magnitude
|
||||
// may be substantially more or less than gravity. This can skew our
|
||||
// orientation detection by making us think that up is pointed in a different
|
||||
// direction.
|
||||
//
|
||||
// Conversely, if the device is in freefall, then there will be no gravity to
|
||||
// measure at all. This is problematic because we cannot detect the orientation
|
||||
// without gravity to tell us which way is up. A magnitude near 0 produces
|
||||
// singularities in the tilt and orientation calculations.
|
||||
//
|
||||
// In both cases, we postpone choosing an orientation.
|
||||
//
|
||||
// However, we need to tolerate some acceleration because the angular momentum
|
||||
// of turning the device can skew the observed acceleration for a short period
|
||||
// of time.
|
||||
#define NEAR_ZERO_MAGNITUDE 1 // m/s^2
|
||||
#define ACCELERATION_TOLERANCE 4 // m/s^2
|
||||
#define STANDARD_GRAVITY 9.80665f
|
||||
#define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY-ACCELERATION_TOLERANCE)
|
||||
#define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY+ACCELERATION_TOLERANCE)
|
||||
|
||||
// Maximum absolute tilt angle at which to consider orientation data. Beyond
|
||||
// this (i.e. when screen is facing the sky or ground), we completely ignore
|
||||
// orientation data.
|
||||
#define MAX_TILT 75
|
||||
|
||||
// The gap angle in degrees between adjacent orientation angles for
|
||||
// hysteresis.This creates a "dead zone" between the current orientation and a
|
||||
// proposed adjacent orientation. No orientation proposal is made when the
|
||||
// orientation angle is within the gap between the current orientation and the
|
||||
// adjacent orientation.
|
||||
#define ADJACENT_ORIENTATION_ANGLE_GAP 45
|
||||
|
||||
const int
|
||||
ProcessOrientation::tiltTolerance[][4] = {
|
||||
{-25, 70}, // ROTATION_0
|
||||
{-25, 65}, // ROTATION_90
|
||||
{-25, 60}, // ROTATION_180
|
||||
{-25, 65} // ROTATION_270
|
||||
};
|
||||
|
||||
int
|
||||
ProcessOrientation::GetProposedRotation()
|
||||
{
|
||||
return mProposedRotation;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::OnSensorChanged(const SensorData& event,
|
||||
int deviceCurrentRotation)
|
||||
{
|
||||
// The vector given in the SensorEvent points straight up (towards the sky)
|
||||
// under ideal conditions (the phone is not accelerating). I'll call this up
|
||||
// vector elsewhere.
|
||||
const InfallibleTArray<float>& values = event.values();
|
||||
float x = values[ACCELEROMETER_DATA_X];
|
||||
float y = values[ACCELEROMETER_DATA_Y];
|
||||
float z = values[ACCELEROMETER_DATA_Z];
|
||||
|
||||
LOGD
|
||||
("ProcessOrientation: Raw acceleration vector: x = %f, y = %f, z = %f,"
|
||||
"magnitude = %f\n", x, y, z, sqrt(x * x + y * y + z * z));
|
||||
// Apply a low-pass filter to the acceleration up vector in cartesian space.
|
||||
// Reset the orientation listener state if the samples are too far apart in
|
||||
// time or when we see values of (0, 0, 0) which indicates that we polled the
|
||||
// accelerometer too soon after turning it on and we don't have any data yet.
|
||||
const int64_t now = (int64_t) event.timestamp();
|
||||
const int64_t then = mLastFilteredTimestampNanos;
|
||||
const float timeDeltaMS = (now - then) * 0.000001f;
|
||||
bool skipSample = false;
|
||||
if (now < then
|
||||
|| now > then + MAX_FILTER_DELTA_TIME_NANOS
|
||||
|| (x == 0 && y == 0 && z == 0)) {
|
||||
LOGD
|
||||
("ProcessOrientation: Resetting orientation listener.");
|
||||
Reset();
|
||||
skipSample = true;
|
||||
} else {
|
||||
const float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
|
||||
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
|
||||
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
|
||||
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
|
||||
LOGD
|
||||
("ProcessOrientation: Filtered acceleration vector: x=%f, y=%f, z=%f,"
|
||||
"magnitude=%f", z, y, z, sqrt(x * x + y * y + z * z));
|
||||
skipSample = false;
|
||||
}
|
||||
mLastFilteredTimestampNanos = now;
|
||||
mLastFilteredX = x;
|
||||
mLastFilteredY = y;
|
||||
mLastFilteredZ = z;
|
||||
|
||||
bool isAccelerating = false;
|
||||
bool isFlat = false;
|
||||
bool isSwinging = false;
|
||||
if (skipSample) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calculate the magnitude of the acceleration vector.
|
||||
const float magnitude = sqrt(x * x + y * y + z * z);
|
||||
if (magnitude < NEAR_ZERO_MAGNITUDE) {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, magnitude too close to"
|
||||
" zero.");
|
||||
ClearPredictedRotation();
|
||||
} else {
|
||||
// Determine whether the device appears to be undergoing external
|
||||
// acceleration.
|
||||
if (this->IsAccelerating(magnitude)) {
|
||||
isAccelerating = true;
|
||||
mAccelerationTimestampNanos = now;
|
||||
}
|
||||
// Calculate the tilt angle. This is the angle between the up vector and
|
||||
// the x-y plane (the plane of the screen) in a range of [-90, 90]
|
||||
// degrees.
|
||||
// -90 degrees: screen horizontal and facing the ground (overhead)
|
||||
// 0 degrees: screen vertical
|
||||
// 90 degrees: screen horizontal and facing the sky (on table)
|
||||
const int tiltAngle =
|
||||
static_cast<int>(roundf(asin(z / magnitude) * RADIANS_TO_DEGREES));
|
||||
AddTiltHistoryEntry(now, tiltAngle);
|
||||
|
||||
// Determine whether the device appears to be flat or swinging.
|
||||
if (this->IsFlat(now)) {
|
||||
isFlat = true;
|
||||
mFlatTimestampNanos = now;
|
||||
}
|
||||
if (this->IsSwinging(now, tiltAngle)) {
|
||||
isSwinging = true;
|
||||
mSwingTimestampNanos = now;
|
||||
}
|
||||
// If the tilt angle is too close to horizontal then we cannot determine
|
||||
// the orientation angle of the screen.
|
||||
if (abs(tiltAngle) > MAX_TILT) {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, tilt angle too high:"
|
||||
" tiltAngle=%d", tiltAngle);
|
||||
ClearPredictedRotation();
|
||||
} else {
|
||||
// Calculate the orientation angle.
|
||||
// This is the angle between the x-y projection of the up vector onto
|
||||
// the +y-axis, increasing clockwise in a range of [0, 360] degrees.
|
||||
int orientationAngle =
|
||||
static_cast<int>(roundf(-atan2f(-x, y) * RADIANS_TO_DEGREES));
|
||||
if (orientationAngle < 0) {
|
||||
// atan2 returns [-180, 180]; normalize to [0, 360]
|
||||
orientationAngle += 360;
|
||||
}
|
||||
// Find the nearest rotation.
|
||||
int nearestRotation = (orientationAngle + 45) / 90;
|
||||
if (nearestRotation == 4) {
|
||||
nearestRotation = 0;
|
||||
}
|
||||
// Determine the predicted orientation.
|
||||
if (IsTiltAngleAcceptable(nearestRotation, tiltAngle)
|
||||
&&
|
||||
IsOrientationAngleAcceptable
|
||||
(nearestRotation, orientationAngle, deviceCurrentRotation)) {
|
||||
UpdatePredictedRotation(now, nearestRotation);
|
||||
LOGD
|
||||
("ProcessOrientation: Predicted: tiltAngle=%d, orientationAngle=%d,"
|
||||
" predictedRotation=%d, predictedRotationAgeMS=%f",
|
||||
tiltAngle,
|
||||
orientationAngle,
|
||||
mPredictedRotation,
|
||||
((now - mPredictedRotationTimestampNanos) * 0.000001f));
|
||||
} else {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, no predicted rotation:"
|
||||
" tiltAngle=%d, orientationAngle=%d",
|
||||
tiltAngle,
|
||||
orientationAngle);
|
||||
ClearPredictedRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine new proposed rotation.
|
||||
const int oldProposedRotation = mProposedRotation;
|
||||
if (mPredictedRotation < 0 || IsPredictedRotationAcceptable(now)) {
|
||||
mProposedRotation = mPredictedRotation;
|
||||
}
|
||||
// Write final statistics about where we are in the orientation detection
|
||||
// process.
|
||||
LOGD
|
||||
("ProcessOrientation: Result: oldProposedRotation=%d,currentRotation=%d, "
|
||||
"proposedRotation=%d, predictedRotation=%d, timeDeltaMS=%f, "
|
||||
"isAccelerating=%d, isFlat=%d, isSwinging=%d, timeUntilSettledMS=%f, "
|
||||
"timeUntilAccelerationDelayExpiredMS=%f, timeUntilFlatDelayExpiredMS=%f, "
|
||||
"timeUntilSwingDelayExpiredMS=%f",
|
||||
oldProposedRotation,
|
||||
deviceCurrentRotation, mProposedRotation,
|
||||
mPredictedRotation, timeDeltaMS, isAccelerating, isFlat,
|
||||
isSwinging, RemainingMS(now,
|
||||
mPredictedRotationTimestampNanos +
|
||||
PROPOSAL_SETTLE_TIME_NANOS),
|
||||
RemainingMS(now,
|
||||
mAccelerationTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS),
|
||||
RemainingMS(now,
|
||||
mFlatTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS),
|
||||
RemainingMS(now,
|
||||
mSwingTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
|
||||
|
||||
// Avoid unused-but-set compile warnings for these variables, when LOGD is
|
||||
// a no-op, as it is by default:
|
||||
Unused << isAccelerating;
|
||||
Unused << isFlat;
|
||||
Unused << isSwinging;
|
||||
|
||||
// Tell the listener.
|
||||
if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
|
||||
LOGD
|
||||
("ProcessOrientation: Proposed rotation changed! proposedRotation=%d, "
|
||||
"oldProposedRotation=%d",
|
||||
mProposedRotation,
|
||||
oldProposedRotation);
|
||||
return mProposedRotation;
|
||||
}
|
||||
// Don't rotate screen
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsTiltAngleAcceptable(int rotation, int tiltAngle)
|
||||
{
|
||||
return (tiltAngle >= tiltTolerance[rotation][0]
|
||||
&& tiltAngle <= tiltTolerance[rotation][1]);
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsOrientationAngleAcceptable(int rotation,
|
||||
int orientationAngle,
|
||||
int currentRotation)
|
||||
{
|
||||
// If there is no current rotation, then there is no gap.
|
||||
// The gap is used only to introduce hysteresis among advertised orientation
|
||||
// changes to avoid flapping.
|
||||
if (currentRotation < 0) {
|
||||
return true;
|
||||
}
|
||||
// If the specified rotation is the same or is counter-clockwise adjacent
|
||||
// to the current rotation, then we set a lower bound on the orientation
|
||||
// angle. For example, if currentRotation is ROTATION_0 and proposed is
|
||||
// ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
|
||||
if (rotation == currentRotation || rotation == (currentRotation + 1) % 4) {
|
||||
int lowerBound = rotation * 90 - 45 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
|
||||
if (rotation == 0) {
|
||||
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (orientationAngle < lowerBound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the specified rotation is the same or is clockwise adjacent, then we
|
||||
// set an upper bound on the orientation angle. For example, if
|
||||
// currentRotation is ROTATION_0 and rotation is ROTATION_270, then we want
|
||||
// to check orientationAngle < 315 - GAP / 2.
|
||||
if (rotation == currentRotation || rotation == (currentRotation + 3) % 4) {
|
||||
int upperBound = rotation * 90 + 45 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
|
||||
if (rotation == 0) {
|
||||
if (orientationAngle <= 45 && orientationAngle > upperBound) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (orientationAngle > upperBound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsPredictedRotationAcceptable(int64_t now)
|
||||
{
|
||||
// The predicted rotation must have settled long enough.
|
||||
if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last flat state (time since picked up) must have been sufficiently long
|
||||
// ago.
|
||||
if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last swing state (time since last movement to put down) must have been
|
||||
// sufficiently long ago.
|
||||
if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last acceleration state must have been sufficiently long ago.
|
||||
if (now < mAccelerationTimestampNanos
|
||||
+ PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// Looks good!
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::Reset()
|
||||
{
|
||||
mLastFilteredTimestampNanos = std::numeric_limits<int64_t>::min();
|
||||
mProposedRotation = -1;
|
||||
mFlatTimestampNanos = std::numeric_limits<int64_t>::min();
|
||||
mSwingTimestampNanos = std::numeric_limits<int64_t>::min();
|
||||
mAccelerationTimestampNanos = std::numeric_limits<int64_t>::min();
|
||||
ClearPredictedRotation();
|
||||
ClearTiltHistory();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::ClearPredictedRotation()
|
||||
{
|
||||
mPredictedRotation = -1;
|
||||
mPredictedRotationTimestampNanos = std::numeric_limits<int64_t>::min();
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::UpdatePredictedRotation(int64_t now, int rotation)
|
||||
{
|
||||
if (mPredictedRotation != rotation) {
|
||||
mPredictedRotation = rotation;
|
||||
mPredictedRotationTimestampNanos = now;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsAccelerating(float magnitude)
|
||||
{
|
||||
return magnitude < MIN_ACCELERATION_MAGNITUDE
|
||||
|| magnitude > MAX_ACCELERATION_MAGNITUDE;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::ClearTiltHistory()
|
||||
{
|
||||
mTiltHistory.history[0].timestampNanos = std::numeric_limits<int64_t>::min();
|
||||
mTiltHistory.index = 1;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::AddTiltHistoryEntry(int64_t now, float tilt)
|
||||
{
|
||||
mTiltHistory.history[mTiltHistory.index].tiltAngle = tilt;
|
||||
mTiltHistory.history[mTiltHistory.index].timestampNanos = now;
|
||||
mTiltHistory.index = (mTiltHistory.index + 1) % TILT_HISTORY_SIZE;
|
||||
mTiltHistory.history[mTiltHistory.index].timestampNanos = std::numeric_limits<int64_t>::min();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsFlat(int64_t now)
|
||||
{
|
||||
for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
|
||||
if (mTiltHistory.history[i].tiltAngle < FLAT_ANGLE) {
|
||||
break;
|
||||
}
|
||||
if (mTiltHistory.history[i].timestampNanos + FLAT_TIME_NANOS <= now) {
|
||||
// Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsSwinging(int64_t now, float tilt)
|
||||
{
|
||||
for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
|
||||
if (mTiltHistory.history[i].timestampNanos + SWING_TIME_NANOS < now) {
|
||||
break;
|
||||
}
|
||||
if (mTiltHistory.history[i].tiltAngle + SWING_AWAY_ANGLE_DELTA <= tilt) {
|
||||
// Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::NextTiltHistoryIndex(int index)
|
||||
{
|
||||
index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
|
||||
return mTiltHistory.history[index].timestampNanos != std::numeric_limits<int64_t>::min() ? index : -1;
|
||||
}
|
||||
|
||||
float
|
||||
ProcessOrientation::RemainingMS(int64_t now, int64_t until)
|
||||
{
|
||||
return now >= until ? 0 : (until - now) * 0.000001f;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Linux Foundation. All rights reserved
|
||||
*
|
||||
* Copyright (C) 2008 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 ProcessOrientation_h
|
||||
#define ProcessOrientation_h
|
||||
|
||||
#include "mozilla/Hal.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// History of observed tilt angles.
|
||||
#define TILT_HISTORY_SIZE 40
|
||||
|
||||
class ProcessOrientation {
|
||||
public:
|
||||
ProcessOrientation() {};
|
||||
~ProcessOrientation() {};
|
||||
|
||||
int OnSensorChanged(const mozilla::hal::SensorData& event, int deviceCurrentRotation);
|
||||
int Reset();
|
||||
|
||||
private:
|
||||
int GetProposedRotation();
|
||||
|
||||
// Returns true if the tilt angle is acceptable for a given predicted
|
||||
// rotation.
|
||||
bool IsTiltAngleAcceptable(int rotation, int tiltAngle);
|
||||
|
||||
// Returns true if the orientation angle is acceptable for a given predicted
|
||||
// rotation. This function takes into account the gap between adjacent
|
||||
// orientations for hysteresis.
|
||||
bool IsOrientationAngleAcceptable(int rotation, int orientationAngle,
|
||||
int currentRotation);
|
||||
|
||||
// Returns true if the predicted rotation is ready to be advertised as a
|
||||
// proposed rotation.
|
||||
bool IsPredictedRotationAcceptable(int64_t now);
|
||||
|
||||
void ClearPredictedRotation();
|
||||
void UpdatePredictedRotation(int64_t now, int rotation);
|
||||
bool IsAccelerating(float magnitude);
|
||||
void ClearTiltHistory();
|
||||
void AddTiltHistoryEntry(int64_t now, float tilt);
|
||||
bool IsFlat(int64_t now);
|
||||
bool IsSwinging(int64_t now, float tilt);
|
||||
int NextTiltHistoryIndex(int index);
|
||||
float RemainingMS(int64_t now, int64_t until);
|
||||
|
||||
// The tilt angle range in degrees for each orientation. Beyond these tilt
|
||||
// angles, we don't even consider transitioning into the specified orientation.
|
||||
// We place more stringent requirements on unnatural orientations than natural
|
||||
// ones to make it less likely to accidentally transition into those states.
|
||||
// The first value of each pair is negative so it applies a limit when the
|
||||
// device is facing down (overhead reading in bed). The second value of each
|
||||
// pair is positive so it applies a limit when the device is facing up
|
||||
// (resting on a table). The ideal tilt angle is 0 (when the device is vertical)
|
||||
// so the limits establish how close to vertical the device must be in order
|
||||
// to change orientation.
|
||||
static const int tiltTolerance[][4];
|
||||
|
||||
// Timestamp and value of the last accelerometer sample.
|
||||
int64_t mLastFilteredTimestampNanos;
|
||||
float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
|
||||
|
||||
// The last proposed rotation, -1 if unknown.
|
||||
int mProposedRotation;
|
||||
|
||||
// Value of the current predicted rotation, -1 if unknown.
|
||||
int mPredictedRotation;
|
||||
|
||||
// Timestamp of when the predicted rotation most recently changed.
|
||||
int64_t mPredictedRotationTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be flat for sure (the flat delay
|
||||
// elapsed).
|
||||
int64_t mFlatTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be swinging.
|
||||
int64_t mSwingTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be undergoing external
|
||||
// acceleration.
|
||||
int64_t mAccelerationTimestampNanos;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
float tiltAngle;
|
||||
int64_t timestampNanos;
|
||||
} history[TILT_HISTORY_SIZE];
|
||||
int index;
|
||||
} mTiltHistory;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -1,96 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/WidgetTraceEvent.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <mozilla/CondVar.h>
|
||||
#include <mozilla/Mutex.h>
|
||||
|
||||
using mozilla::CondVar;
|
||||
using mozilla::Mutex;
|
||||
using mozilla::MutexAutoLock;
|
||||
|
||||
namespace mozilla {
|
||||
class TracerRunnable : public Runnable {
|
||||
public:
|
||||
TracerRunnable() {
|
||||
mTracerLock = new Mutex("TracerRunnable");
|
||||
mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable");
|
||||
mMainThread = do_GetMainThread();
|
||||
}
|
||||
|
||||
~TracerRunnable() {
|
||||
delete mTracerCondVar;
|
||||
delete mTracerLock;
|
||||
mTracerLock = nullptr;
|
||||
mTracerCondVar = nullptr;
|
||||
}
|
||||
|
||||
virtual nsresult Run() {
|
||||
MutexAutoLock lock(*mTracerLock);
|
||||
mHasRun = true;
|
||||
mTracerCondVar->Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool Fire() {
|
||||
if (!mTracerLock || !mTracerCondVar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(*mTracerLock);
|
||||
mHasRun = false;
|
||||
mMainThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
while (!mHasRun) {
|
||||
mTracerCondVar->Wait();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Signal() {
|
||||
MutexAutoLock lock(*mTracerLock);
|
||||
mHasRun = true;
|
||||
mTracerCondVar->Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex* mTracerLock;
|
||||
CondVar* mTracerCondVar;
|
||||
bool mHasRun;
|
||||
nsCOMPtr<nsIThread> mMainThread;
|
||||
};
|
||||
|
||||
StaticRefPtr<TracerRunnable> sTracerRunnable;
|
||||
|
||||
bool InitWidgetTracing()
|
||||
{
|
||||
if (!sTracerRunnable) {
|
||||
sTracerRunnable = new TracerRunnable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanUpWidgetTracing()
|
||||
{
|
||||
sTracerRunnable = nullptr;
|
||||
}
|
||||
|
||||
bool FireAndWaitForTracerEvent()
|
||||
{
|
||||
if (sTracerRunnable) {
|
||||
return sTracerRunnable->Fire();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SignalTracerThread()
|
||||
{
|
||||
if (sTracerRunnable) {
|
||||
return sTracerRunnable->Signal();
|
||||
}
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/*
|
||||
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 "HwcHAL.h"
|
||||
#include "libdisplay/GonkDisplay.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
HwcHAL::HwcHAL()
|
||||
: HwcHALBase()
|
||||
{
|
||||
// Some HALs don't want to open hwc twice.
|
||||
// If GetDisplay already load hwc module, we don't need to load again
|
||||
mHwc = (HwcDevice*)GetGonkDisplay()->GetHWCDevice();
|
||||
if (!mHwc) {
|
||||
printf_stderr("HwcHAL Error: Cannot load hwcomposer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HwcHAL::~HwcHAL()
|
||||
{
|
||||
mHwc = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
HwcHAL::Query(QueryType aType)
|
||||
{
|
||||
if (!mHwc || !mHwc->query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool value = false;
|
||||
int supported = 0;
|
||||
if (mHwc->query(mHwc, static_cast<int>(aType), &supported) == 0/*android::NO_ERROR*/) {
|
||||
value = !!supported;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int
|
||||
HwcHAL::Set(HwcList *aList,
|
||||
uint32_t aDisp)
|
||||
{
|
||||
MOZ_ASSERT(mHwc);
|
||||
if (!mHwc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
HwcList *displays[HWC_NUM_DISPLAY_TYPES] = { nullptr };
|
||||
displays[aDisp] = aList;
|
||||
return mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
|
||||
}
|
||||
|
||||
int
|
||||
HwcHAL::ResetHwc()
|
||||
{
|
||||
return Set(nullptr, HWC_DISPLAY_PRIMARY);
|
||||
}
|
||||
|
||||
int
|
||||
HwcHAL::Prepare(HwcList *aList,
|
||||
uint32_t aDisp,
|
||||
hwc_rect_t aDispRect,
|
||||
buffer_handle_t aHandle,
|
||||
int aFenceFd)
|
||||
{
|
||||
MOZ_ASSERT(mHwc);
|
||||
if (!mHwc) {
|
||||
printf_stderr("HwcHAL Error: HwcDevice doesn't exist. A fence might be leaked.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
HwcList *displays[HWC_NUM_DISPLAY_TYPES] = { nullptr };
|
||||
displays[aDisp] = aList;
|
||||
#if ANDROID_VERSION >= 18
|
||||
aList->outbufAcquireFenceFd = -1;
|
||||
aList->outbuf = nullptr;
|
||||
#endif
|
||||
aList->retireFenceFd = -1;
|
||||
|
||||
const auto idx = aList->numHwLayers - 1;
|
||||
aList->hwLayers[idx].hints = 0;
|
||||
aList->hwLayers[idx].flags = 0;
|
||||
aList->hwLayers[idx].transform = 0;
|
||||
aList->hwLayers[idx].handle = aHandle;
|
||||
aList->hwLayers[idx].blending = HWC_BLENDING_PREMULT;
|
||||
aList->hwLayers[idx].compositionType = HWC_FRAMEBUFFER_TARGET;
|
||||
SetCrop(aList->hwLayers[idx], aDispRect);
|
||||
aList->hwLayers[idx].displayFrame = aDispRect;
|
||||
aList->hwLayers[idx].visibleRegionScreen.numRects = 1;
|
||||
aList->hwLayers[idx].visibleRegionScreen.rects = &aList->hwLayers[idx].displayFrame;
|
||||
aList->hwLayers[idx].acquireFenceFd = aFenceFd;
|
||||
aList->hwLayers[idx].releaseFenceFd = -1;
|
||||
#if ANDROID_VERSION >= 18
|
||||
aList->hwLayers[idx].planeAlpha = 0xFF;
|
||||
#endif
|
||||
return mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
|
||||
}
|
||||
|
||||
bool
|
||||
HwcHAL::SupportTransparency() const
|
||||
{
|
||||
#if ANDROID_VERSION >= 18
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HwcHAL::GetGeometryChangedFlag(bool aGeometryChanged) const
|
||||
{
|
||||
#if ANDROID_VERSION >= 19
|
||||
return aGeometryChanged ? HWC_GEOMETRY_CHANGED : 0;
|
||||
#else
|
||||
return HWC_GEOMETRY_CHANGED;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
HwcHAL::SetCrop(HwcLayer &aLayer,
|
||||
const hwc_rect_t &aSrcCrop) const
|
||||
{
|
||||
if (GetAPIVersion() >= HwcAPIVersion(1, 3)) {
|
||||
#if ANDROID_VERSION >= 19
|
||||
aLayer.sourceCropf.left = aSrcCrop.left;
|
||||
aLayer.sourceCropf.top = aSrcCrop.top;
|
||||
aLayer.sourceCropf.right = aSrcCrop.right;
|
||||
aLayer.sourceCropf.bottom = aSrcCrop.bottom;
|
||||
#endif
|
||||
} else {
|
||||
aLayer.sourceCrop = aSrcCrop;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HwcHAL::EnableVsync(bool aEnable)
|
||||
{
|
||||
// Only support hardware vsync on kitkat, L and up due to inaccurate timings
|
||||
// with JellyBean.
|
||||
#if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
|
||||
if (!mHwc) {
|
||||
return false;
|
||||
}
|
||||
return !mHwc->eventControl(mHwc,
|
||||
HWC_DISPLAY_PRIMARY,
|
||||
HWC_EVENT_VSYNC,
|
||||
aEnable);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
HwcHAL::RegisterHwcEventCallback(const HwcHALProcs_t &aProcs)
|
||||
{
|
||||
if (!mHwc || !mHwc->registerProcs) {
|
||||
printf_stderr("Failed to get hwc\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable Vsync first, and then register callback functions.
|
||||
mHwc->eventControl(mHwc,
|
||||
HWC_DISPLAY_PRIMARY,
|
||||
HWC_EVENT_VSYNC,
|
||||
false);
|
||||
static const hwc_procs_t sHwcJBProcs = {aProcs.invalidate,
|
||||
aProcs.vsync,
|
||||
aProcs.hotplug};
|
||||
mHwc->registerProcs(mHwc, &sHwcJBProcs);
|
||||
|
||||
// Only support hardware vsync on kitkat, L and up due to inaccurate timings
|
||||
// with JellyBean.
|
||||
#if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HwcHAL::GetAPIVersion() const
|
||||
{
|
||||
if (!mHwc) {
|
||||
// default value: HWC_MODULE_API_VERSION_0_1
|
||||
return 1;
|
||||
}
|
||||
return mHwc->common.version;
|
||||
}
|
||||
|
||||
// Create HwcHAL
|
||||
UniquePtr<HwcHALBase>
|
||||
HwcHALBase::CreateHwcHAL()
|
||||
{
|
||||
return Move(MakeUnique<HwcHAL>());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,70 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/*
|
||||
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 mozilla_HwcHAL
|
||||
#define mozilla_HwcHAL
|
||||
|
||||
#include "HwcHALBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class HwcHAL final : public HwcHALBase {
|
||||
public:
|
||||
explicit HwcHAL();
|
||||
|
||||
virtual ~HwcHAL();
|
||||
|
||||
virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
|
||||
|
||||
virtual void SetEGLInfo(hwc_display_t aDpy,
|
||||
hwc_surface_t aSur) override { }
|
||||
|
||||
virtual bool Query(QueryType aType) override;
|
||||
|
||||
virtual int Set(HwcList *aList,
|
||||
uint32_t aDisp) override;
|
||||
|
||||
virtual int ResetHwc() override;
|
||||
|
||||
virtual int Prepare(HwcList *aList,
|
||||
uint32_t aDisp,
|
||||
hwc_rect_t aDispRect,
|
||||
buffer_handle_t aHandle,
|
||||
int aFenceFd) override;
|
||||
|
||||
virtual bool SupportTransparency() const override;
|
||||
|
||||
virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const override;
|
||||
|
||||
virtual void SetCrop(HwcLayer &aLayer,
|
||||
const hwc_rect_t &aSrcCrop) const override;
|
||||
|
||||
virtual bool EnableVsync(bool aEnable) override;
|
||||
|
||||
virtual bool RegisterHwcEventCallback(const HwcHALProcs_t &aProcs) override;
|
||||
|
||||
private:
|
||||
uint32_t GetAPIVersion() const;
|
||||
|
||||
private:
|
||||
HwcDevice *mHwc = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_HwcHAL
|
|
@ -1,134 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et: */
|
||||
/*
|
||||
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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 mozilla_HwcHALBase
|
||||
#define mozilla_HwcHALBase
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
#include <hardware/hwcomposer.h>
|
||||
|
||||
#ifndef HWC_BLIT
|
||||
#if ANDROID_VERSION >= 21
|
||||
#define HWC_BLIT 0xFF
|
||||
#elif ANDROID_VERSION >= 17
|
||||
#define HWC_BLIT (HWC_FRAMEBUFFER_TARGET + 1)
|
||||
#else
|
||||
// ICS didn't support this. However, we define this
|
||||
// for passing compilation
|
||||
#define HWC_BLIT 0xFF
|
||||
#endif // #if ANDROID_VERSION
|
||||
#endif // #ifndef HWC_BLIT
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if ANDROID_VERSION >= 17
|
||||
using HwcDevice = hwc_composer_device_1_t;
|
||||
using HwcList = hwc_display_contents_1_t;
|
||||
using HwcLayer = hwc_layer_1_t;
|
||||
#else
|
||||
using HwcDevice = hwc_composer_device_t;
|
||||
using HwcList = hwc_layer_list_t;
|
||||
using HwcLayer = hwc_layer_t;
|
||||
#endif
|
||||
|
||||
// HwcHAL definition for HwcEvent callback types
|
||||
// Note: hwc_procs is different between ICS and later,
|
||||
// and the signature of invalidate is also different.
|
||||
// Use this wrap struct to hide the detail. BTW,
|
||||
// we don't have to register callback functions on ICS, so
|
||||
// there is no callbacks for ICS in HwcHALProcs.
|
||||
typedef struct HwcHALProcs {
|
||||
void (*invalidate)(const struct hwc_procs* procs);
|
||||
void (*vsync)(const struct hwc_procs* procs, int disp, int64_t timestamp);
|
||||
void (*hotplug)(const struct hwc_procs* procs, int disp, int connected);
|
||||
} HwcHALProcs_t;
|
||||
|
||||
// HwcHAL class
|
||||
// This class handle all the HAL related work
|
||||
// The purpose of HwcHAL is to make HwcComposer2D simpler.
|
||||
class HwcHALBase {
|
||||
|
||||
public:
|
||||
// Query Types. We can add more types easily in the future
|
||||
enum class QueryType {
|
||||
COLOR_FILL = 0x8,
|
||||
RB_SWAP = 0x40
|
||||
};
|
||||
|
||||
public:
|
||||
explicit HwcHALBase() = default;
|
||||
|
||||
virtual ~HwcHALBase() {}
|
||||
|
||||
// Create HwcHAL module, Only HwcComposer2D calls this.
|
||||
// If other modules want to use HwcHAL, please use APIs in
|
||||
// HwcComposer2D
|
||||
static UniquePtr<HwcHALBase> CreateHwcHAL();
|
||||
|
||||
// Check if mHwc exists
|
||||
virtual bool HasHwc() const = 0;
|
||||
|
||||
// Set EGL info (only ICS need this info)
|
||||
virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
|
||||
hwc_surface_t aEGLSurface) = 0;
|
||||
|
||||
// HwcDevice query properties
|
||||
virtual bool Query(QueryType aType) = 0;
|
||||
|
||||
// HwcDevice set
|
||||
virtual int Set(HwcList *aList,
|
||||
uint32_t aDisp) = 0;
|
||||
|
||||
// Reset HwcDevice
|
||||
virtual int ResetHwc() = 0;
|
||||
|
||||
// HwcDevice prepare
|
||||
virtual int Prepare(HwcList *aList,
|
||||
uint32_t aDisp,
|
||||
hwc_rect_t aDispRect,
|
||||
buffer_handle_t aHandle,
|
||||
int aFenceFd) = 0;
|
||||
|
||||
// Check transparency support
|
||||
virtual bool SupportTransparency() const = 0;
|
||||
|
||||
// Get a geometry change flag
|
||||
virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const = 0;
|
||||
|
||||
// Set crop help
|
||||
virtual void SetCrop(HwcLayer &aLayer,
|
||||
const hwc_rect_t &aSrcCrop) const = 0;
|
||||
|
||||
// Enable HW Vsync
|
||||
virtual bool EnableVsync(bool aEnable) = 0;
|
||||
|
||||
// Register HW event callback functions
|
||||
virtual bool RegisterHwcEventCallback(const HwcHALProcs_t &aProcs) = 0;
|
||||
|
||||
protected:
|
||||
constexpr static uint32_t HwcAPIVersion(uint32_t aMaj, uint32_t aMin) {
|
||||
// HARDWARE_MAKE_API_VERSION_2, from Android hardware.h
|
||||
return (((aMaj & 0xff) << 24) | ((aMin & 0xff) << 16) | (1 & 0xffff));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_HwcHALBase
|
|
@ -1,726 +0,0 @@
|
|||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "android/log.h"
|
||||
#include "GonkDisplay.h"
|
||||
#include "hardware/gralloc.h"
|
||||
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
|
||||
#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
|
||||
#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace std;
|
||||
|
||||
static pthread_t sAnimationThread;
|
||||
static bool sRunAnimation;
|
||||
|
||||
/* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
|
||||
struct local_file_header {
|
||||
uint32_t signature;
|
||||
uint16_t min_version;
|
||||
uint16_t general_flag;
|
||||
uint16_t compression;
|
||||
uint16_t lastmod_time;
|
||||
uint16_t lastmod_date;
|
||||
uint32_t crc32;
|
||||
uint32_t compressed_size;
|
||||
uint32_t uncompressed_size;
|
||||
uint16_t filename_size;
|
||||
uint16_t extra_field_size;
|
||||
char data[0];
|
||||
|
||||
uint32_t GetDataSize() const
|
||||
{
|
||||
return letoh32(uncompressed_size);
|
||||
}
|
||||
|
||||
uint32_t GetSize() const
|
||||
{
|
||||
/* XXX account for data descriptor */
|
||||
return sizeof(local_file_header) + letoh16(filename_size) +
|
||||
letoh16(extra_field_size) + GetDataSize();
|
||||
}
|
||||
|
||||
const char * GetData() const
|
||||
{
|
||||
return data + letoh16(filename_size) + letoh16(extra_field_size);
|
||||
}
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct data_descriptor {
|
||||
uint32_t crc32;
|
||||
uint32_t compressed_size;
|
||||
uint32_t uncompressed_size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct cdir_entry {
|
||||
uint32_t signature;
|
||||
uint16_t creator_version;
|
||||
uint16_t min_version;
|
||||
uint16_t general_flag;
|
||||
uint16_t compression;
|
||||
uint16_t lastmod_time;
|
||||
uint16_t lastmod_date;
|
||||
uint32_t crc32;
|
||||
uint32_t compressed_size;
|
||||
uint32_t uncompressed_size;
|
||||
uint16_t filename_size;
|
||||
uint16_t extra_field_size;
|
||||
uint16_t file_comment_size;
|
||||
uint16_t disk_num;
|
||||
uint16_t internal_attr;
|
||||
uint32_t external_attr;
|
||||
uint32_t offset;
|
||||
char data[0];
|
||||
|
||||
uint32_t GetDataSize() const
|
||||
{
|
||||
return letoh32(compressed_size);
|
||||
}
|
||||
|
||||
uint32_t GetSize() const
|
||||
{
|
||||
return sizeof(cdir_entry) + letoh16(filename_size) +
|
||||
letoh16(extra_field_size) + letoh16(file_comment_size);
|
||||
}
|
||||
|
||||
bool Valid() const
|
||||
{
|
||||
return signature == htole32(0x02014b50);
|
||||
}
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct cdir_end {
|
||||
uint32_t signature;
|
||||
uint16_t disk_num;
|
||||
uint16_t cdir_disk;
|
||||
uint16_t disk_entries;
|
||||
uint16_t cdir_entries;
|
||||
uint32_t cdir_size;
|
||||
uint32_t cdir_offset;
|
||||
uint16_t comment_size;
|
||||
char comment[0];
|
||||
|
||||
bool Valid() const
|
||||
{
|
||||
return signature == htole32(0x06054b50);
|
||||
}
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* We don't have access to libjar and the zip reader in android
|
||||
* doesn't quite fit what we want to do. */
|
||||
class ZipReader {
|
||||
const char *mBuf;
|
||||
const cdir_end *mEnd;
|
||||
const char *mCdir_limit;
|
||||
uint32_t mBuflen;
|
||||
|
||||
public:
|
||||
ZipReader() : mBuf(nullptr) {}
|
||||
~ZipReader() {
|
||||
if (mBuf)
|
||||
munmap((void *)mBuf, mBuflen);
|
||||
}
|
||||
|
||||
bool OpenArchive(const char *path)
|
||||
{
|
||||
int fd;
|
||||
do {
|
||||
fd = open(path, O_RDONLY);
|
||||
} while (fd == -1 && errno == EINTR);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
mBuflen = sb.st_size;
|
||||
mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
|
||||
if (!mBuf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
madvise(mBuf, sb.st_size, MADV_SEQUENTIAL);
|
||||
|
||||
mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end));
|
||||
while (!mEnd->Valid() &&
|
||||
(char *)mEnd > mBuf) {
|
||||
mEnd = (cdir_end *)((char *)mEnd - 1);
|
||||
}
|
||||
|
||||
mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size);
|
||||
|
||||
if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) {
|
||||
munmap((void *)mBuf, mBuflen);
|
||||
mBuf = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Pass null to get the first cdir entry */
|
||||
const cdir_entry * GetNextEntry(const cdir_entry *prev)
|
||||
{
|
||||
const cdir_entry *entry;
|
||||
if (prev)
|
||||
entry = (cdir_entry *)((char *)prev + prev->GetSize());
|
||||
else
|
||||
entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset));
|
||||
|
||||
if (((char *)entry + entry->GetSize()) > mCdir_limit ||
|
||||
!entry->Valid())
|
||||
return nullptr;
|
||||
return entry;
|
||||
}
|
||||
|
||||
string GetEntryName(const cdir_entry *entry)
|
||||
{
|
||||
uint16_t len = letoh16(entry->filename_size);
|
||||
|
||||
string name;
|
||||
name.append(entry->data, len);
|
||||
return name;
|
||||
}
|
||||
|
||||
const local_file_header * GetLocalEntry(const cdir_entry *entry)
|
||||
{
|
||||
const local_file_header * data =
|
||||
(local_file_header *)(mBuf + letoh32(entry->offset));
|
||||
if (((char *)data + data->GetSize()) > (char *)mEnd)
|
||||
return nullptr;
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationFrame {
|
||||
char path[256];
|
||||
png_color_16 bgcolor;
|
||||
char *buf;
|
||||
const local_file_header *file;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint16_t bytepp;
|
||||
bool has_bgcolor;
|
||||
|
||||
AnimationFrame() : buf(nullptr) {}
|
||||
AnimationFrame(const AnimationFrame &frame) : buf(nullptr) {
|
||||
strncpy(path, frame.path, sizeof(path));
|
||||
file = frame.file;
|
||||
}
|
||||
~AnimationFrame()
|
||||
{
|
||||
if (buf)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
bool operator<(const AnimationFrame &other) const
|
||||
{
|
||||
return strcmp(path, other.path) < 0;
|
||||
}
|
||||
|
||||
void ReadPngFrame(int outputFormat);
|
||||
};
|
||||
|
||||
struct AnimationPart {
|
||||
int32_t count;
|
||||
int32_t pause;
|
||||
// If you alter the size of the path, change ReadFromString() as well.
|
||||
char path[256];
|
||||
vector<AnimationFrame> frames;
|
||||
|
||||
bool
|
||||
ReadFromString(const char* aLine)
|
||||
{
|
||||
MOZ_ASSERT(aLine);
|
||||
// this 255 value must be in sync with AnimationPart::path.
|
||||
return sscanf(aLine, "p %d %d %255s", &count, &pause, path) == 3;
|
||||
}
|
||||
};
|
||||
|
||||
struct RawReadState {
|
||||
const char *start;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
static void
|
||||
RawReader(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr);
|
||||
if (length > (state->length - state->offset))
|
||||
png_error(png_ptr, "PNG read overrun");
|
||||
|
||||
memcpy(data, state->start + state->offset, length);
|
||||
state->offset += length;
|
||||
}
|
||||
|
||||
static void
|
||||
TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data)
|
||||
{
|
||||
uint16_t *outbuf = (uint16_t *)data;
|
||||
uint8_t *inbuf = (uint8_t *)data;
|
||||
for (uint32_t i = 0; i < row_info->rowbytes; i += 3) {
|
||||
*outbuf++ = ((inbuf[i] & 0xF8) << 8) |
|
||||
((inbuf[i + 1] & 0xFC) << 3) |
|
||||
((inbuf[i + 2] ) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
GetFormatBPP(int aFormat)
|
||||
{
|
||||
uint16_t bpp = 0;
|
||||
|
||||
switch (aFormat) {
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
bpp = 4;
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGB_888:
|
||||
bpp = 3;
|
||||
break;
|
||||
default:
|
||||
LOGW("Unknown pixel format %d. Assuming RGB 565.", aFormat);
|
||||
// FALL THROUGH
|
||||
case HAL_PIXEL_FORMAT_RGB_565:
|
||||
bpp = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationFrame::ReadPngFrame(int outputFormat)
|
||||
{
|
||||
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
||||
static const png_byte unused_chunks[] =
|
||||
{ 99, 72, 82, 77, '\0', /* cHRM */
|
||||
104, 73, 83, 84, '\0', /* hIST */
|
||||
105, 67, 67, 80, '\0', /* iCCP */
|
||||
105, 84, 88, 116, '\0', /* iTXt */
|
||||
111, 70, 70, 115, '\0', /* oFFs */
|
||||
112, 67, 65, 76, '\0', /* pCAL */
|
||||
115, 67, 65, 76, '\0', /* sCAL */
|
||||
112, 72, 89, 115, '\0', /* pHYs */
|
||||
115, 66, 73, 84, '\0', /* sBIT */
|
||||
115, 80, 76, 84, '\0', /* sPLT */
|
||||
116, 69, 88, 116, '\0', /* tEXt */
|
||||
116, 73, 77, 69, '\0', /* tIME */
|
||||
122, 84, 88, 116, '\0'}; /* zTXt */
|
||||
static const png_byte tRNS_chunk[] =
|
||||
{116, 82, 78, 83, '\0'}; /* tRNS */
|
||||
#endif
|
||||
|
||||
png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||
nullptr, nullptr, nullptr);
|
||||
|
||||
if (!pngread)
|
||||
return;
|
||||
|
||||
png_infop pnginfo = png_create_info_struct(pngread);
|
||||
|
||||
if (!pnginfo) {
|
||||
png_destroy_read_struct(&pngread, &pnginfo, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(pngread))) {
|
||||
// libpng reported an error and longjumped here. Clean up and return.
|
||||
png_destroy_read_struct(&pngread, &pnginfo, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
RawReadState state;
|
||||
state.start = file->GetData();
|
||||
state.length = file->GetDataSize();
|
||||
state.offset = 0;
|
||||
|
||||
png_set_read_fn(pngread, &state, RawReader);
|
||||
|
||||
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
|
||||
/* Ignore unused chunks */
|
||||
png_set_keep_unknown_chunks(pngread, 1, unused_chunks,
|
||||
(int)sizeof(unused_chunks)/5);
|
||||
|
||||
/* Ignore the tRNS chunk if we only want opaque output */
|
||||
if (outputFormat == HAL_PIXEL_FORMAT_RGB_888 ||
|
||||
outputFormat == HAL_PIXEL_FORMAT_RGB_565) {
|
||||
png_set_keep_unknown_chunks(pngread, 1, tRNS_chunk, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
png_read_info(pngread, pnginfo);
|
||||
|
||||
png_color_16p colorp;
|
||||
has_bgcolor = (PNG_INFO_bKGD == png_get_bKGD(pngread, pnginfo, &colorp));
|
||||
bgcolor = has_bgcolor ? *colorp : png_color_16();
|
||||
width = png_get_image_width(pngread, pnginfo);
|
||||
height = png_get_image_height(pngread, pnginfo);
|
||||
|
||||
LOG("Decoded %s: %d x %d frame with bgcolor? %s (%#x, %#x, %#x; gray:%#x)",
|
||||
path, width, height, has_bgcolor ? "yes" : "no",
|
||||
bgcolor.red, bgcolor.green, bgcolor.blue, bgcolor.gray);
|
||||
|
||||
bytepp = GetFormatBPP(outputFormat);
|
||||
|
||||
switch (outputFormat) {
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
png_set_bgr(pngread);
|
||||
// FALL THROUGH
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGB_888:
|
||||
png_set_strip_alpha(pngread);
|
||||
break;
|
||||
default:
|
||||
LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat);
|
||||
// FALL THROUGH
|
||||
case HAL_PIXEL_FORMAT_RGB_565:
|
||||
png_set_strip_alpha(pngread);
|
||||
png_set_read_user_transform_fn(pngread, TransformTo565);
|
||||
break;
|
||||
}
|
||||
|
||||
// An extra row is added to give libpng enough space when
|
||||
// decoding 3/4 bytepp inputs for 2 bytepp output surfaces
|
||||
buf = (char *)malloc(width * (height + 1) * bytepp);
|
||||
|
||||
vector<char *> rows(height + 1);
|
||||
uint32_t stride = width * bytepp;
|
||||
for (uint32_t i = 0; i < height; i++) {
|
||||
rows[i] = buf + (stride * i);
|
||||
}
|
||||
rows[height] = nullptr;
|
||||
png_set_strip_16(pngread);
|
||||
png_set_palette_to_rgb(pngread);
|
||||
png_set_gray_to_rgb(pngread);
|
||||
png_read_image(pngread, (png_bytepp)&rows.front());
|
||||
png_destroy_read_struct(&pngread, &pnginfo, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a wchar_t that when used to |wmemset()| an image buffer will
|
||||
* fill it with the color defined by |color16|. The packed wchar_t
|
||||
* may comprise one or two pixels depending on |outputFormat|.
|
||||
*/
|
||||
static wchar_t
|
||||
AsBackgroundFill(const png_color_16& color16, int outputFormat)
|
||||
{
|
||||
static_assert(sizeof(wchar_t) == sizeof(uint32_t),
|
||||
"TODO: support 2-byte wchar_t");
|
||||
union {
|
||||
uint32_t r8g8b8;
|
||||
struct {
|
||||
uint8_t b8;
|
||||
uint8_t g8;
|
||||
uint8_t r8;
|
||||
uint8_t x8;
|
||||
};
|
||||
} color;
|
||||
color.b8 = color16.blue;
|
||||
color.g8 = color16.green;
|
||||
color.r8 = color16.red;
|
||||
color.x8 = 0xFF;
|
||||
|
||||
switch (outputFormat) {
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
return color.r8g8b8;
|
||||
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
swap(color.r8, color.b8);
|
||||
return color.r8g8b8;
|
||||
|
||||
case HAL_PIXEL_FORMAT_RGB_565: {
|
||||
// NB: we could do a higher-quality downsample here, but we
|
||||
// want the results to be a pixel-perfect match with the fast
|
||||
// downsample in TransformTo565().
|
||||
uint16_t color565 = ((color.r8 & 0xF8) << 8) |
|
||||
((color.g8 & 0xFC) << 3) |
|
||||
((color.b8 ) >> 3);
|
||||
return (color565 << 16) | color565;
|
||||
}
|
||||
default:
|
||||
LOGW("Unhandled pixel format %d; falling back on black", outputFormat);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ShowSolidColorFrame(GonkDisplay *aDisplay,
|
||||
const gralloc_module_t *grallocModule,
|
||||
int32_t aFormat)
|
||||
{
|
||||
LOGW("Show solid color frame for bootAnim");
|
||||
|
||||
ANativeWindowBuffer *buffer = aDisplay->DequeueBuffer();
|
||||
void *mappedAddress = nullptr;
|
||||
|
||||
if (!buffer) {
|
||||
LOGW("Failed to get an ANativeWindowBuffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!grallocModule->lock(grallocModule, buffer->handle,
|
||||
GRALLOC_USAGE_SW_READ_NEVER |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN |
|
||||
GRALLOC_USAGE_HW_FB,
|
||||
0, 0, buffer->width, buffer->height, &mappedAddress)) {
|
||||
// Just show a black solid color frame.
|
||||
memset(mappedAddress, 0, buffer->height * buffer->stride * GetFormatBPP(aFormat));
|
||||
grallocModule->unlock(grallocModule, buffer->handle);
|
||||
}
|
||||
|
||||
aDisplay->QueueBuffer(buffer);
|
||||
}
|
||||
|
||||
static void *
|
||||
AnimationThread(void *)
|
||||
{
|
||||
GonkDisplay *display = GetGonkDisplay();
|
||||
int32_t format = display->surfaceformat;
|
||||
|
||||
const hw_module_t *module = nullptr;
|
||||
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
|
||||
LOGW("Could not get gralloc module");
|
||||
return nullptr;
|
||||
}
|
||||
const gralloc_module_t *grmodule =
|
||||
reinterpret_cast<gralloc_module_t const*>(module);
|
||||
|
||||
ZipReader reader;
|
||||
if (!reader.OpenArchive("/system/media/bootanimation.zip")) {
|
||||
LOGW("Could not open boot animation");
|
||||
ShowSolidColorFrame(display, grmodule, format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const cdir_entry *entry = nullptr;
|
||||
const local_file_header *file = nullptr;
|
||||
while ((entry = reader.GetNextEntry(entry))) {
|
||||
string name = reader.GetEntryName(entry);
|
||||
if (!name.compare("desc.txt")) {
|
||||
file = reader.GetLocalEntry(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
LOGW("Could not find desc.txt in boot animation");
|
||||
ShowSolidColorFrame(display, grmodule, format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string descCopy;
|
||||
descCopy.append(file->GetData(), entry->GetDataSize());
|
||||
int32_t width, height, fps;
|
||||
const char *line = descCopy.c_str();
|
||||
const char *end;
|
||||
bool headerRead = true;
|
||||
vector<AnimationPart> parts;
|
||||
bool animPlayed = false;
|
||||
|
||||
/*
|
||||
* bootanimation.zip
|
||||
*
|
||||
* This is the boot animation file format that Android uses.
|
||||
* It's a zip file with a directories containing png frames
|
||||
* and a desc.txt that describes how they should be played.
|
||||
*
|
||||
* desc.txt contains two types of lines
|
||||
* 1. [width] [height] [fps]
|
||||
* There is one of these lines per bootanimation.
|
||||
* If the width and height are smaller than the screen,
|
||||
* the frames are centered on a black background.
|
||||
* XXX: Currently we stretch instead of centering the frame.
|
||||
* 2. p [count] [pause] [path]
|
||||
* This describes one animation part.
|
||||
* Each animation part is played in sequence.
|
||||
* An animation part contains all the files/frames in the
|
||||
* directory specified in [path]
|
||||
* [count] indicates the number of times this part repeats.
|
||||
* [pause] indicates the number of frames that this part
|
||||
* should pause for after playing the full sequence but
|
||||
* before repeating.
|
||||
*/
|
||||
|
||||
do {
|
||||
end = strstr(line, "\n");
|
||||
|
||||
AnimationPart part;
|
||||
if (headerRead &&
|
||||
sscanf(line, "%d %d %d", &width, &height, &fps) == 3) {
|
||||
headerRead = false;
|
||||
} else if (part.ReadFromString(line)) {
|
||||
parts.push_back(part);
|
||||
}
|
||||
} while (end && *(line = end + 1));
|
||||
|
||||
for (uint32_t i = 0; i < parts.size(); i++) {
|
||||
AnimationPart &part = parts[i];
|
||||
entry = nullptr;
|
||||
char search[256];
|
||||
snprintf(search, sizeof(search), "%s/", part.path);
|
||||
while ((entry = reader.GetNextEntry(entry))) {
|
||||
string name = reader.GetEntryName(entry);
|
||||
if (name.find(search) ||
|
||||
!entry->GetDataSize() ||
|
||||
name.length() >= 256)
|
||||
continue;
|
||||
|
||||
part.frames.resize(part.frames.size() + 1);
|
||||
AnimationFrame &frame = part.frames.back();
|
||||
strcpy(frame.path, name.c_str());
|
||||
frame.file = reader.GetLocalEntry(entry);
|
||||
}
|
||||
|
||||
sort(part.frames.begin(), part.frames.end());
|
||||
}
|
||||
|
||||
long int frameDelayUs = 1000000 / fps;
|
||||
|
||||
for (uint32_t i = 0; i < parts.size(); i++) {
|
||||
AnimationPart &part = parts[i];
|
||||
|
||||
int32_t j = 0;
|
||||
while (sRunAnimation && (!part.count || j++ < part.count)) {
|
||||
for (uint32_t k = 0; k < part.frames.size(); k++) {
|
||||
struct timeval tv1, tv2;
|
||||
gettimeofday(&tv1, nullptr);
|
||||
AnimationFrame &frame = part.frames[k];
|
||||
if (!frame.buf) {
|
||||
frame.ReadPngFrame(format);
|
||||
}
|
||||
|
||||
ANativeWindowBuffer *buf = display->DequeueBuffer();
|
||||
if (!buf) {
|
||||
LOGW("Failed to get an ANativeWindowBuffer");
|
||||
break;
|
||||
}
|
||||
|
||||
void *vaddr;
|
||||
if (grmodule->lock(grmodule, buf->handle,
|
||||
GRALLOC_USAGE_SW_READ_NEVER |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN |
|
||||
GRALLOC_USAGE_HW_FB,
|
||||
0, 0, width, height, &vaddr)) {
|
||||
LOGW("Failed to lock buffer_handle_t");
|
||||
display->QueueBuffer(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame.has_bgcolor) {
|
||||
wchar_t bgfill = AsBackgroundFill(frame.bgcolor, format);
|
||||
wmemset((wchar_t*)vaddr, bgfill,
|
||||
(buf->height * buf->stride * frame.bytepp) / sizeof(wchar_t));
|
||||
}
|
||||
|
||||
if ((uint32_t)buf->height == frame.height && (uint32_t)buf->stride == frame.width) {
|
||||
memcpy(vaddr, frame.buf,
|
||||
frame.width * frame.height * frame.bytepp);
|
||||
} else if ((uint32_t)buf->height >= frame.height &&
|
||||
(uint32_t)buf->width >= frame.width) {
|
||||
int startx = (buf->width - frame.width) / 2;
|
||||
int starty = (buf->height - frame.height) / 2;
|
||||
|
||||
int src_stride = frame.width * frame.bytepp;
|
||||
int dst_stride = buf->stride * frame.bytepp;
|
||||
|
||||
char *src = frame.buf;
|
||||
char *dst = (char *) vaddr + starty * dst_stride + startx * frame.bytepp;
|
||||
|
||||
for (uint32_t i = 0; i < frame.height; i++) {
|
||||
memcpy(dst, src, src_stride);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
grmodule->unlock(grmodule, buf->handle);
|
||||
|
||||
gettimeofday(&tv2, nullptr);
|
||||
|
||||
timersub(&tv2, &tv1, &tv2);
|
||||
|
||||
if (tv2.tv_usec < frameDelayUs) {
|
||||
usleep(frameDelayUs - tv2.tv_usec);
|
||||
} else {
|
||||
LOGW("Frame delay is %ld us but decoding took %ld us",
|
||||
frameDelayUs, tv2.tv_usec);
|
||||
}
|
||||
|
||||
animPlayed = true;
|
||||
display->QueueBuffer(buf);
|
||||
|
||||
if (part.count && j >= part.count) {
|
||||
free(frame.buf);
|
||||
frame.buf = nullptr;
|
||||
}
|
||||
}
|
||||
usleep(frameDelayUs * part.pause);
|
||||
}
|
||||
}
|
||||
|
||||
if (!animPlayed) {
|
||||
ShowSolidColorFrame(display, grmodule, format);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
void
|
||||
StartBootAnimation()
|
||||
{
|
||||
GetGonkDisplay(); // Ensure GonkDisplay exist
|
||||
sRunAnimation = true;
|
||||
pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
void
|
||||
StopBootAnimation()
|
||||
{
|
||||
if (sRunAnimation) {
|
||||
sRunAnimation = false;
|
||||
pthread_join(sAnimationThread, nullptr);
|
||||
GetGonkDisplay()->NotifyBootAnimationStopped();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,30 +0,0 @@
|
|||
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 BOOTANIMATION_H
|
||||
#define BOOTANIMATION_H
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_EXPORT __attribute__ ((weak))
|
||||
void StartBootAnimation();
|
||||
|
||||
/* Stop the boot animation if it's still running. */
|
||||
MOZ_EXPORT __attribute__ ((weak))
|
||||
void StopBootAnimation();
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* BOOTANIMATION_H */
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright 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_SF_DISPLAY_SURFACE_H
|
||||
#define ANDROID_SF_DISPLAY_SURFACE_H
|
||||
|
||||
#include <gui/ConsumerBase.h>
|
||||
#include <system/window.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class IGraphicBufferProducer;
|
||||
class String8;
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
typedef IGraphicBufferConsumer StreamConsumer;
|
||||
#else
|
||||
typedef BufferQueue StreamConsumer;
|
||||
#endif
|
||||
|
||||
class DisplaySurface : public ConsumerBase {
|
||||
public:
|
||||
// beginFrame is called at the beginning of the composition loop, before
|
||||
// the configuration is known. The DisplaySurface should do anything it
|
||||
// needs to do to enable HWComposer to decide how to compose the frame.
|
||||
// We pass in mustRecompose so we can keep VirtualDisplaySurface's state
|
||||
// machine happy without actually queueing a buffer if nothing has changed.
|
||||
virtual status_t beginFrame(bool mustRecompose) = 0;
|
||||
|
||||
// prepareFrame is called after the composition configuration is known but
|
||||
// before composition takes place. The DisplaySurface can use the
|
||||
// composition type to decide how to manage the flow of buffers between
|
||||
// GLES and HWC for this frame.
|
||||
enum CompositionType {
|
||||
COMPOSITION_UNKNOWN = 0,
|
||||
COMPOSITION_GLES = 1,
|
||||
COMPOSITION_HWC = 2,
|
||||
COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
|
||||
};
|
||||
virtual status_t prepareFrame(CompositionType compositionType) = 0;
|
||||
|
||||
// Should be called when composition rendering is complete for a frame (but
|
||||
// eglSwapBuffers hasn't necessarily been called). Required by certain
|
||||
// older drivers for synchronization.
|
||||
// TODO: Remove this when we drop support for HWC 1.0.
|
||||
virtual status_t compositionComplete() = 0;
|
||||
|
||||
// Inform the surface that GLES composition is complete for this frame, and
|
||||
// the surface should make sure that HWComposer has the correct buffer for
|
||||
// this frame. Some implementations may only push a new buffer to
|
||||
// HWComposer if GLES composition took place, others need to push a new
|
||||
// buffer on every frame.
|
||||
//
|
||||
// advanceFrame must be followed by a call to onFrameCommitted before
|
||||
// advanceFrame may be called again.
|
||||
virtual status_t advanceFrame() = 0;
|
||||
|
||||
// onFrameCommitted is called after the frame has been committed to the
|
||||
// hardware composer. The surface collects the release fence for this
|
||||
// frame's buffer.
|
||||
virtual void onFrameCommitted() = 0;
|
||||
|
||||
virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
|
||||
|
||||
// setReleaseFenceFd stores a fence file descriptor that will signal when the
|
||||
// current buffer is no longer being read. This fence will be returned to
|
||||
// the producer when the current buffer is released by updateTexImage().
|
||||
// Multiple fences can be set for a given buffer; they will be merged into
|
||||
// a single union fence. The SurfaceTexture will close the file descriptor
|
||||
// when finished with it.
|
||||
virtual status_t setReleaseFenceFd(int fenceFd) = 0;
|
||||
|
||||
virtual int GetPrevDispAcquireFd() = 0;
|
||||
|
||||
buffer_handle_t lastHandle;
|
||||
|
||||
protected:
|
||||
DisplaySurface(const sp<StreamConsumer>& sc)
|
||||
#if ANDROID_VERSION >= 19
|
||||
: ConsumerBase(sc, true)
|
||||
#else
|
||||
: ConsumerBase(sc)
|
||||
#endif
|
||||
, lastHandle(0)
|
||||
{ }
|
||||
virtual ~DisplaySurface() {}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
} // namespace android
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_SF_DISPLAY_SURFACE_H
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
**
|
||||
** Copyright 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <ui/Rect.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
#if ANDROID_VERSION == 17
|
||||
#include <gui/SurfaceTextureClient.h>
|
||||
#endif
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include "FramebufferSurface.h"
|
||||
#include "GraphicBufferAlloc.h"
|
||||
|
||||
#ifndef NUM_FRAMEBUFFER_SURFACE_BUFFERS
|
||||
#define NUM_FRAMEBUFFER_SURFACE_BUFFERS (2)
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* This implements the (main) framebuffer management. This class
|
||||
* was adapted from the version in SurfaceFlinger
|
||||
*/
|
||||
FramebufferSurface::FramebufferSurface(int disp,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t format,
|
||||
const sp<StreamConsumer>& sc)
|
||||
: DisplaySurface(sc)
|
||||
, mDisplayType(disp)
|
||||
, mCurrentBufferSlot(-1)
|
||||
, mCurrentBuffer(0)
|
||||
{
|
||||
mName = "FramebufferSurface";
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
sp<IGraphicBufferConsumer> consumer = mConsumer;
|
||||
#else
|
||||
sp<BufferQueue> consumer = mBufferQueue;
|
||||
consumer->setSynchronousMode(true);
|
||||
#endif
|
||||
consumer->setConsumerName(mName);
|
||||
consumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
|
||||
GRALLOC_USAGE_HW_RENDER |
|
||||
GRALLOC_USAGE_HW_COMPOSER);
|
||||
consumer->setDefaultBufferFormat(format);
|
||||
consumer->setDefaultBufferSize(width, height);
|
||||
consumer->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS);
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::prepareFrame(CompositionType /*compositionType*/) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::advanceFrame() {
|
||||
// Once we remove FB HAL support, we can call nextBuffer() from here
|
||||
// instead of using onFrameAvailable(). No real benefit, except it'll be
|
||||
// more like VirtualDisplaySurface.
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
BufferQueue::BufferItem item;
|
||||
#if ANDROID_VERSION >= 19
|
||||
status_t err = acquireBufferLocked(&item, 0);
|
||||
#else
|
||||
status_t err = acquireBufferLocked(&item);
|
||||
#endif
|
||||
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
|
||||
outBuffer = mCurrentBuffer;
|
||||
return NO_ERROR;
|
||||
} else if (err != NO_ERROR) {
|
||||
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// If the BufferQueue has freed and reallocated a buffer in mCurrentSlot
|
||||
// then we may have acquired the slot we already own. If we had released
|
||||
// our current buffer before we call acquireBuffer then that release call
|
||||
// would have returned STALE_BUFFER_SLOT, and we would have called
|
||||
// freeBufferLocked on that slot. Because the buffer slot has already
|
||||
// been overwritten with the new buffer all we have to do is skip the
|
||||
// releaseBuffer call and we should be in the same state we'd be in if we
|
||||
// had released the old buffer first.
|
||||
if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
|
||||
item.mBuf != mCurrentBufferSlot) {
|
||||
// Release the previous buffer.
|
||||
#if ANDROID_VERSION >= 19
|
||||
err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
|
||||
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
|
||||
#else
|
||||
err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY,
|
||||
EGL_NO_SYNC_KHR);
|
||||
#endif
|
||||
if (err != NO_ERROR && err != StreamConsumer::STALE_BUFFER_SLOT) {
|
||||
ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
mCurrentBufferSlot = item.mBuf;
|
||||
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
|
||||
outFence = item.mFence;
|
||||
outBuffer = mCurrentBuffer;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
|
||||
#if ANDROID_VERSION >= 22
|
||||
void FramebufferSurface::onFrameAvailable(const ::android::BufferItem &item) {
|
||||
#else
|
||||
void FramebufferSurface::onFrameAvailable() {
|
||||
#endif
|
||||
sp<GraphicBuffer> buf;
|
||||
sp<Fence> acquireFence;
|
||||
status_t err = nextBuffer(buf, acquireFence);
|
||||
if (err != NO_ERROR) {
|
||||
ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
|
||||
strerror(-err), err);
|
||||
return;
|
||||
}
|
||||
if (acquireFence.get() && acquireFence->isValid())
|
||||
mPrevFBAcquireFence = new Fence(acquireFence->dup());
|
||||
else
|
||||
mPrevFBAcquireFence = Fence::NO_FENCE;
|
||||
|
||||
lastHandle = buf->handle;
|
||||
}
|
||||
|
||||
void FramebufferSurface::freeBufferLocked(int slotIndex) {
|
||||
ConsumerBase::freeBufferLocked(slotIndex);
|
||||
if (slotIndex == mCurrentBufferSlot) {
|
||||
mCurrentBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
|
||||
}
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::setReleaseFenceFd(int fenceFd) {
|
||||
status_t err = NO_ERROR;
|
||||
if (fenceFd >= 0) {
|
||||
sp<Fence> fence(new Fence(fenceFd));
|
||||
if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||
#if ANDROID_VERSION >= 19
|
||||
status_t err = addReleaseFence(mCurrentBufferSlot, mCurrentBuffer, fence);
|
||||
#else
|
||||
status_t err = addReleaseFence(mCurrentBufferSlot, fence);
|
||||
#endif
|
||||
ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)",
|
||||
strerror(-err), err);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int FramebufferSurface::GetPrevDispAcquireFd() {
|
||||
if (mPrevFBAcquireFence.get() && mPrevFBAcquireFence->isValid()) {
|
||||
return mPrevFBAcquireFence->dup();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FramebufferSurface::onFrameCommitted() {
|
||||
// XXX This role is almost same to setReleaseFenceFd().
|
||||
}
|
||||
|
||||
status_t FramebufferSurface::compositionComplete()
|
||||
{
|
||||
// Actual implementaiton is in GonkDisplay::SwapBuffers()
|
||||
// XXX need to move that to here.
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
// ----------------------------------------------------------------------------
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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_SF_FRAMEBUFFER_SURFACE_H
|
||||
#define ANDROID_SF_FRAMEBUFFER_SURFACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "DisplaySurface.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class Rect;
|
||||
class String8;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class FramebufferSurface : public DisplaySurface {
|
||||
public:
|
||||
FramebufferSurface(int disp, uint32_t width, uint32_t height, uint32_t format, const sp<StreamConsumer>& sc);
|
||||
|
||||
// From DisplaySurface
|
||||
virtual status_t beginFrame(bool mustRecompose);
|
||||
virtual status_t prepareFrame(CompositionType compositionType);
|
||||
virtual status_t compositionComplete();
|
||||
virtual status_t advanceFrame();
|
||||
virtual void onFrameCommitted();
|
||||
// Cannot resize a buffers in a FramebufferSurface. Only works with virtual
|
||||
// displays.
|
||||
virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { };
|
||||
|
||||
// setReleaseFenceFd stores a fence file descriptor that will signal when the
|
||||
// current buffer is no longer being read. This fence will be returned to
|
||||
// the producer when the current buffer is released by updateTexImage().
|
||||
// Multiple fences can be set for a given buffer; they will be merged into
|
||||
// a single union fence. The SurfaceTexture will close the file descriptor
|
||||
// when finished with it.
|
||||
status_t setReleaseFenceFd(int fenceFd);
|
||||
|
||||
virtual int GetPrevDispAcquireFd();
|
||||
|
||||
private:
|
||||
virtual ~FramebufferSurface() { }; // this class cannot be overloaded
|
||||
|
||||
#if ANDROID_VERSION >= 22
|
||||
virtual void onFrameAvailable(const ::android::BufferItem &item);
|
||||
#else
|
||||
virtual void onFrameAvailable();
|
||||
#endif
|
||||
virtual void freeBufferLocked(int slotIndex);
|
||||
|
||||
// nextBuffer waits for and then latches the next buffer from the
|
||||
// BufferQueue and releases the previously latched buffer to the
|
||||
// BufferQueue. The new buffer is returned in the 'buffer' argument.
|
||||
status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
|
||||
|
||||
// mDisplayType must match one of the HWC display types
|
||||
int mDisplayType;
|
||||
|
||||
// mCurrentBufferIndex is the slot index of the current buffer or
|
||||
// INVALID_BUFFER_SLOT to indicate that either there is no current buffer
|
||||
// or the buffer is not associated with a slot.
|
||||
int mCurrentBufferSlot;
|
||||
|
||||
// mCurrentBuffer is the current buffer or NULL to indicate that there is
|
||||
// no current buffer.
|
||||
sp<GraphicBuffer> mCurrentBuffer;
|
||||
|
||||
android::sp<android::Fence> mPrevFBAcquireFence;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_SF_FRAMEBUFFER_SURFACE_H
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/* Copyright 2013 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 GONKDISPLAY_H
|
||||
#define GONKDISPLAY_H
|
||||
|
||||
#include <system/window.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
namespace android {
|
||||
class DisplaySurface;
|
||||
class IGraphicBufferProducer;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef void * EGLDisplay;
|
||||
typedef void * EGLSurface;
|
||||
|
||||
class MOZ_EXPORT GonkDisplay {
|
||||
public:
|
||||
/**
|
||||
* This enum is for types of display. DISPLAY_PRIMARY refers to the default
|
||||
* built-in display, DISPLAY_EXTERNAL refers to displays connected with
|
||||
* HDMI, and DISPLAY_VIRTUAL are displays which makes composited output
|
||||
* available within the system. Currently, displays of external are detected
|
||||
* via the hotplug detection in HWC, and displays of virtual are connected
|
||||
* via Wifi Display.
|
||||
*/
|
||||
enum DisplayType {
|
||||
DISPLAY_PRIMARY,
|
||||
DISPLAY_EXTERNAL,
|
||||
DISPLAY_VIRTUAL,
|
||||
NUM_DISPLAY_TYPES
|
||||
};
|
||||
|
||||
struct NativeData {
|
||||
android::sp<ANativeWindow> mNativeWindow;
|
||||
#if ANDROID_VERSION >= 17
|
||||
android::sp<android::DisplaySurface> mDisplaySurface;
|
||||
#endif
|
||||
float mXdpi;
|
||||
};
|
||||
|
||||
virtual void SetEnabled(bool enabled) = 0;
|
||||
|
||||
typedef void (*OnEnabledCallbackType)(bool enabled);
|
||||
|
||||
virtual void OnEnabled(OnEnabledCallbackType callback) = 0;
|
||||
|
||||
virtual void* GetHWCDevice() = 0;
|
||||
|
||||
/**
|
||||
* Only GonkDisplayICS uses arguments.
|
||||
*/
|
||||
virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur) = 0;
|
||||
|
||||
virtual ANativeWindowBuffer* DequeueBuffer() = 0;
|
||||
|
||||
virtual bool QueueBuffer(ANativeWindowBuffer* buf) = 0;
|
||||
|
||||
virtual void UpdateDispSurface(EGLDisplay dpy, EGLSurface sur) = 0;
|
||||
|
||||
virtual NativeData GetNativeData(
|
||||
GonkDisplay::DisplayType aDisplayType,
|
||||
android::IGraphicBufferProducer* aSink = nullptr) = 0;
|
||||
|
||||
virtual void NotifyBootAnimationStopped() = 0;
|
||||
|
||||
float xdpi;
|
||||
int32_t surfaceformat;
|
||||
};
|
||||
|
||||
MOZ_EXPORT __attribute__ ((weak))
|
||||
GonkDisplay* GetGonkDisplay();
|
||||
|
||||
}
|
||||
#endif /* GONKDISPLAY_H */
|
|
@ -1,461 +0,0 @@
|
|||
/* Copyright 2013 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 "GonkDisplayJB.h"
|
||||
#if ANDROID_VERSION == 17
|
||||
#include <gui/SurfaceTextureClient.h>
|
||||
#else
|
||||
#include <gui/Surface.h>
|
||||
#include <gui/GraphicBufferAlloc.h>
|
||||
#endif
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/hwcomposer.h>
|
||||
#include <hardware/power.h>
|
||||
#include <suspend/autosuspend.h>
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
#include "VirtualDisplaySurface.h"
|
||||
#endif
|
||||
#include "FramebufferSurface.h"
|
||||
#if ANDROID_VERSION == 17
|
||||
#include "GraphicBufferAlloc.h"
|
||||
#endif
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#define DEFAULT_XDPI 75.0
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static GonkDisplayJB* sGonkDisplay = nullptr;
|
||||
|
||||
GonkDisplayJB::GonkDisplayJB()
|
||||
: mModule(nullptr)
|
||||
, mFBModule(nullptr)
|
||||
, mHwc(nullptr)
|
||||
, mFBDevice(nullptr)
|
||||
, mPowerModule(nullptr)
|
||||
, mList(nullptr)
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mEnabledCallback(nullptr)
|
||||
{
|
||||
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mFBModule);
|
||||
ALOGW_IF(err, "%s module not found", GRALLOC_HARDWARE_MODULE_ID);
|
||||
if (!err) {
|
||||
err = framebuffer_open(mFBModule, &mFBDevice);
|
||||
ALOGW_IF(err, "could not open framebuffer");
|
||||
}
|
||||
|
||||
if (!err && mFBDevice) {
|
||||
mWidth = mFBDevice->width;
|
||||
mHeight = mFBDevice->height;
|
||||
xdpi = mFBDevice->xdpi;
|
||||
/* The emulator actually reports RGBA_8888, but EGL doesn't return
|
||||
* any matching configuration. We force RGBX here to fix it. */
|
||||
surfaceformat = HAL_PIXEL_FORMAT_RGBX_8888;
|
||||
}
|
||||
|
||||
err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
|
||||
ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
|
||||
if (!err) {
|
||||
err = hwc_open_1(mModule, &mHwc);
|
||||
ALOGE_IF(err, "%s device failed to initialize (%s)",
|
||||
HWC_HARDWARE_COMPOSER, strerror(-err));
|
||||
}
|
||||
|
||||
/* Fallback on the FB rendering path instead of trying to support HWC 1.0 */
|
||||
if (!err && mHwc->common.version == HWC_DEVICE_API_VERSION_1_0) {
|
||||
hwc_close_1(mHwc);
|
||||
mHwc = nullptr;
|
||||
}
|
||||
|
||||
if (!err && mHwc) {
|
||||
if (mFBDevice) {
|
||||
framebuffer_close(mFBDevice);
|
||||
mFBDevice = nullptr;
|
||||
}
|
||||
|
||||
int32_t values[3];
|
||||
const uint32_t attrs[] = {
|
||||
HWC_DISPLAY_WIDTH,
|
||||
HWC_DISPLAY_HEIGHT,
|
||||
HWC_DISPLAY_DPI_X,
|
||||
HWC_DISPLAY_NO_ATTRIBUTE
|
||||
};
|
||||
mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
|
||||
|
||||
mWidth = values[0];
|
||||
mHeight = values[1];
|
||||
xdpi = values[2] / 1000.0f;
|
||||
surfaceformat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
}
|
||||
|
||||
err = hw_get_module(POWER_HARDWARE_MODULE_ID,
|
||||
(hw_module_t const**)&mPowerModule);
|
||||
if (!err)
|
||||
mPowerModule->init(mPowerModule);
|
||||
ALOGW_IF(err, "Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
|
||||
|
||||
mAlloc = new GraphicBufferAlloc();
|
||||
|
||||
CreateFramebufferSurface(mSTClient, mDispSurface, mWidth, mHeight);
|
||||
|
||||
mList = (hwc_display_contents_1_t *)calloc(1, sizeof(*mList) + (sizeof(hwc_layer_1_t)*2));
|
||||
|
||||
uint32_t usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
if (mFBDevice) {
|
||||
// If device uses fb, they can not use single buffer for boot animation
|
||||
mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_BUFFER_COUNT, 2);
|
||||
mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_USAGE, usage);
|
||||
} else if (mHwc) {
|
||||
PowerOnDisplay(HWC_DISPLAY_PRIMARY);
|
||||
// For devices w/ hwc v1.0 or no hwc, this buffer can not be created,
|
||||
// only create this buffer for devices w/ hwc version > 1.0.
|
||||
CreateFramebufferSurface(mBootAnimSTClient, mBootAnimDispSurface, mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
|
||||
GonkDisplayJB::~GonkDisplayJB()
|
||||
{
|
||||
if (mHwc)
|
||||
hwc_close_1(mHwc);
|
||||
if (mFBDevice)
|
||||
framebuffer_close(mFBDevice);
|
||||
free(mList);
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::CreateFramebufferSurface(android::sp<ANativeWindow>& aNativeWindow,
|
||||
android::sp<android::DisplaySurface>& aDisplaySurface,
|
||||
uint32_t aWidth,
|
||||
uint32_t aHeight)
|
||||
{
|
||||
#if ANDROID_VERSION >= 21
|
||||
sp<IGraphicBufferProducer> producer;
|
||||
sp<IGraphicBufferConsumer> consumer;
|
||||
BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
|
||||
#elif ANDROID_VERSION >= 19
|
||||
sp<BufferQueue> consumer = new BufferQueue(mAlloc);
|
||||
sp<IGraphicBufferProducer> producer = consumer;
|
||||
#elif ANDROID_VERSION >= 18
|
||||
sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
|
||||
sp<IGraphicBufferProducer> producer = consumer;
|
||||
#else
|
||||
sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
|
||||
#endif
|
||||
|
||||
aDisplaySurface = new FramebufferSurface(0, aWidth, aHeight, surfaceformat, consumer);
|
||||
|
||||
#if ANDROID_VERSION == 17
|
||||
aNativeWindow = new SurfaceTextureClient(
|
||||
static_cast<sp<ISurfaceTexture>>(aDisplaySurface->getBufferQueue()));
|
||||
#else
|
||||
aNativeWindow = new Surface(producer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::CreateVirtualDisplaySurface(android::IGraphicBufferProducer* aSink,
|
||||
android::sp<ANativeWindow>& aNativeWindow,
|
||||
android::sp<android::DisplaySurface>& aDisplaySurface)
|
||||
{
|
||||
#if ANDROID_VERSION >= 21
|
||||
sp<IGraphicBufferProducer> producer;
|
||||
sp<IGraphicBufferConsumer> consumer;
|
||||
BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
|
||||
#elif ANDROID_VERSION >= 19
|
||||
sp<BufferQueue> consumer = new BufferQueue(mAlloc);
|
||||
sp<IGraphicBufferProducer> producer = consumer;
|
||||
#endif
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
sp<VirtualDisplaySurface> virtualDisplay;
|
||||
virtualDisplay = new VirtualDisplaySurface(-1, aSink, producer, consumer, String8("VirtualDisplaySurface"));
|
||||
aDisplaySurface = virtualDisplay;
|
||||
aNativeWindow = new Surface(virtualDisplay);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::SetEnabled(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
autosuspend_disable();
|
||||
mPowerModule->setInteractive(mPowerModule, true);
|
||||
}
|
||||
|
||||
if (!enabled && mEnabledCallback) {
|
||||
mEnabledCallback(enabled);
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (mHwc) {
|
||||
if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
|
||||
mHwc->setPowerMode(mHwc, HWC_DISPLAY_PRIMARY,
|
||||
(enabled ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF));
|
||||
} else {
|
||||
mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
|
||||
}
|
||||
} else if (mFBDevice && mFBDevice->enableScreen) {
|
||||
mFBDevice->enableScreen(mFBDevice, enabled);
|
||||
}
|
||||
#else
|
||||
if (mHwc && mHwc->blank) {
|
||||
mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
|
||||
} else if (mFBDevice && mFBDevice->enableScreen) {
|
||||
mFBDevice->enableScreen(mFBDevice, enabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (enabled && mEnabledCallback) {
|
||||
mEnabledCallback(enabled);
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
autosuspend_enable();
|
||||
mPowerModule->setInteractive(mPowerModule, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::OnEnabled(OnEnabledCallbackType callback)
|
||||
{
|
||||
mEnabledCallback = callback;
|
||||
}
|
||||
|
||||
void*
|
||||
GonkDisplayJB::GetHWCDevice()
|
||||
{
|
||||
return mHwc;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur)
|
||||
{
|
||||
// Should be called when composition rendering is complete for a frame.
|
||||
// Only HWC v1.0 needs this call.
|
||||
// HWC > v1.0 case, do not call compositionComplete().
|
||||
// mFBDevice is present only when HWC is v1.0.
|
||||
if (mFBDevice && mFBDevice->compositionComplete) {
|
||||
mFBDevice->compositionComplete(mFBDevice);
|
||||
}
|
||||
return Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
|
||||
}
|
||||
|
||||
bool
|
||||
GonkDisplayJB::Post(buffer_handle_t buf, int fence)
|
||||
{
|
||||
if (!mHwc) {
|
||||
if (fence >= 0)
|
||||
close(fence);
|
||||
return !mFBDevice->post(mFBDevice, buf);
|
||||
}
|
||||
|
||||
hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = {NULL};
|
||||
const hwc_rect_t r = { 0, 0, static_cast<int>(mWidth), static_cast<int>(mHeight) };
|
||||
displays[HWC_DISPLAY_PRIMARY] = mList;
|
||||
mList->retireFenceFd = -1;
|
||||
mList->numHwLayers = 2;
|
||||
mList->flags = HWC_GEOMETRY_CHANGED;
|
||||
#if ANDROID_VERSION >= 18
|
||||
mList->outbuf = nullptr;
|
||||
mList->outbufAcquireFenceFd = -1;
|
||||
#endif
|
||||
mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
|
||||
mList->hwLayers[0].hints = 0;
|
||||
/* Skip this layer so the hwc module doesn't complain about null handles */
|
||||
mList->hwLayers[0].flags = HWC_SKIP_LAYER;
|
||||
mList->hwLayers[0].backgroundColor = {0};
|
||||
mList->hwLayers[0].acquireFenceFd = -1;
|
||||
mList->hwLayers[0].releaseFenceFd = -1;
|
||||
/* hwc module checks displayFrame even though it shouldn't */
|
||||
mList->hwLayers[0].displayFrame = r;
|
||||
mList->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET;
|
||||
mList->hwLayers[1].hints = 0;
|
||||
mList->hwLayers[1].flags = 0;
|
||||
mList->hwLayers[1].handle = buf;
|
||||
mList->hwLayers[1].transform = 0;
|
||||
mList->hwLayers[1].blending = HWC_BLENDING_NONE;
|
||||
#if ANDROID_VERSION >= 19
|
||||
if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_3) {
|
||||
mList->hwLayers[1].sourceCropf.left = 0;
|
||||
mList->hwLayers[1].sourceCropf.top = 0;
|
||||
mList->hwLayers[1].sourceCropf.right = mWidth;
|
||||
mList->hwLayers[1].sourceCropf.bottom = mHeight;
|
||||
} else {
|
||||
mList->hwLayers[1].sourceCrop = r;
|
||||
}
|
||||
#else
|
||||
mList->hwLayers[1].sourceCrop = r;
|
||||
#endif
|
||||
mList->hwLayers[1].displayFrame = r;
|
||||
mList->hwLayers[1].visibleRegionScreen.numRects = 1;
|
||||
mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].displayFrame;
|
||||
mList->hwLayers[1].acquireFenceFd = fence;
|
||||
mList->hwLayers[1].releaseFenceFd = -1;
|
||||
#if ANDROID_VERSION >= 18
|
||||
mList->hwLayers[1].planeAlpha = 0xFF;
|
||||
#endif
|
||||
mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
|
||||
int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
|
||||
|
||||
if (!mBootAnimDispSurface.get()) {
|
||||
mDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
|
||||
} else {
|
||||
mBootAnimDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
|
||||
}
|
||||
|
||||
if (mList->retireFenceFd >= 0)
|
||||
close(mList->retireFenceFd);
|
||||
return !err;
|
||||
}
|
||||
|
||||
ANativeWindowBuffer*
|
||||
GonkDisplayJB::DequeueBuffer()
|
||||
{
|
||||
// Check for bootAnim or normal display flow.
|
||||
sp<ANativeWindow> nativeWindow =
|
||||
!mBootAnimSTClient.get() ? mSTClient : mBootAnimSTClient;
|
||||
|
||||
ANativeWindowBuffer *buf;
|
||||
int fenceFd = -1;
|
||||
nativeWindow->dequeueBuffer(nativeWindow.get(), &buf, &fenceFd);
|
||||
sp<Fence> fence(new Fence(fenceFd));
|
||||
#if ANDROID_VERSION == 17
|
||||
fence->waitForever(1000, "GonkDisplayJB_DequeueBuffer");
|
||||
// 1000 is what Android uses. It is a warning timeout in ms.
|
||||
// This timeout was removed in ANDROID_VERSION 18.
|
||||
#else
|
||||
fence->waitForever("GonkDisplayJB_DequeueBuffer");
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkDisplayJB::QueueBuffer(ANativeWindowBuffer* buf)
|
||||
{
|
||||
bool success = false;
|
||||
int error = DoQueueBuffer(buf);
|
||||
// Check for bootAnim or normal display flow.
|
||||
if (!mBootAnimSTClient.get()) {
|
||||
success = Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
|
||||
} else {
|
||||
success = Post(mBootAnimDispSurface->lastHandle, mBootAnimDispSurface->GetPrevDispAcquireFd());
|
||||
}
|
||||
return error == 0 && success;
|
||||
}
|
||||
|
||||
int
|
||||
GonkDisplayJB::DoQueueBuffer(ANativeWindowBuffer* buf)
|
||||
{
|
||||
int error = 0;
|
||||
// Check for bootAnim or normal display flow.
|
||||
if (!mBootAnimSTClient.get()) {
|
||||
error = mSTClient->queueBuffer(mSTClient.get(), buf, -1);
|
||||
} else {
|
||||
error = mBootAnimSTClient->queueBuffer(mBootAnimSTClient.get(), buf, -1);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::UpdateDispSurface(EGLDisplay dpy, EGLSurface sur)
|
||||
{
|
||||
if (sur != EGL_NO_SURFACE) {
|
||||
eglSwapBuffers(dpy, sur);
|
||||
} else {
|
||||
// When BasicCompositor is used as Compositor,
|
||||
// EGLSurface does not exit.
|
||||
ANativeWindowBuffer* buf = DequeueBuffer();
|
||||
DoQueueBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::NotifyBootAnimationStopped()
|
||||
{
|
||||
if (mBootAnimSTClient.get()) {
|
||||
mBootAnimSTClient = nullptr;
|
||||
mBootAnimDispSurface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDisplayJB::PowerOnDisplay(int aDpy)
|
||||
{
|
||||
MOZ_ASSERT(mHwc);
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
|
||||
mHwc->setPowerMode(mHwc, aDpy, HWC_POWER_MODE_NORMAL);
|
||||
} else {
|
||||
mHwc->blank(mHwc, aDpy, 0);
|
||||
}
|
||||
#else
|
||||
mHwc->blank(mHwc, aDpy, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
GonkDisplay::NativeData
|
||||
GonkDisplayJB::GetNativeData(GonkDisplay::DisplayType aDisplayType,
|
||||
android::IGraphicBufferProducer* aSink)
|
||||
{
|
||||
NativeData data;
|
||||
|
||||
if (aDisplayType == DISPLAY_PRIMARY) {
|
||||
data.mNativeWindow = mSTClient;
|
||||
data.mDisplaySurface = mDispSurface;
|
||||
data.mXdpi = xdpi;
|
||||
} else if (aDisplayType == DISPLAY_EXTERNAL) {
|
||||
int32_t values[3];
|
||||
const uint32_t attrs[] = {
|
||||
HWC_DISPLAY_WIDTH,
|
||||
HWC_DISPLAY_HEIGHT,
|
||||
HWC_DISPLAY_DPI_X,
|
||||
HWC_DISPLAY_NO_ATTRIBUTE
|
||||
};
|
||||
mHwc->getDisplayAttributes(mHwc, aDisplayType, 0, attrs, values);
|
||||
int width = values[0];
|
||||
int height = values[1];
|
||||
// FIXME!! values[2] returns 0 for external display, which doesn't
|
||||
// sound right, Bug 1169176 is the follow-up bug for this issue.
|
||||
data.mXdpi = values[2] ? values[2] / 1000.f : DEFAULT_XDPI;
|
||||
PowerOnDisplay(HWC_DISPLAY_EXTERNAL);
|
||||
CreateFramebufferSurface(data.mNativeWindow,
|
||||
data.mDisplaySurface,
|
||||
width,
|
||||
height);
|
||||
} else if (aDisplayType == DISPLAY_VIRTUAL) {
|
||||
data.mXdpi = xdpi;
|
||||
CreateVirtualDisplaySurface(aSink,
|
||||
data.mNativeWindow,
|
||||
data.mDisplaySurface);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
GonkDisplay*
|
||||
GetGonkDisplay()
|
||||
{
|
||||
if (!sGonkDisplay)
|
||||
sGonkDisplay = new GonkDisplayJB();
|
||||
return sGonkDisplay;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,85 +0,0 @@
|
|||
/* Copyright 2013 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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 GONKDISPLAYJB_H
|
||||
#define GONKDISPLAYJB_H
|
||||
|
||||
#include "DisplaySurface.h"
|
||||
#include "GonkDisplay.h"
|
||||
#include "hardware/hwcomposer.h"
|
||||
#include "hardware/power.h"
|
||||
#include "ui/Fence.h"
|
||||
#include "utils/RefBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MOZ_EXPORT GonkDisplayJB : public GonkDisplay {
|
||||
public:
|
||||
GonkDisplayJB();
|
||||
~GonkDisplayJB();
|
||||
|
||||
virtual void SetEnabled(bool enabled);
|
||||
|
||||
virtual void OnEnabled(OnEnabledCallbackType callback);
|
||||
|
||||
virtual void* GetHWCDevice();
|
||||
|
||||
virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur);
|
||||
|
||||
virtual ANativeWindowBuffer* DequeueBuffer();
|
||||
|
||||
virtual bool QueueBuffer(ANativeWindowBuffer* buf);
|
||||
|
||||
virtual void UpdateDispSurface(EGLDisplay dpy, EGLSurface sur);
|
||||
|
||||
bool Post(buffer_handle_t buf, int fence);
|
||||
|
||||
virtual NativeData GetNativeData(
|
||||
GonkDisplay::DisplayType aDisplayType,
|
||||
android::IGraphicBufferProducer* aSink = nullptr);
|
||||
|
||||
virtual void NotifyBootAnimationStopped();
|
||||
|
||||
private:
|
||||
void CreateFramebufferSurface(android::sp<ANativeWindow>& aNativeWindow,
|
||||
android::sp<android::DisplaySurface>& aDisplaySurface,
|
||||
uint32_t aWidth, uint32_t aHeight);
|
||||
void CreateVirtualDisplaySurface(android::IGraphicBufferProducer* aSink,
|
||||
android::sp<ANativeWindow>& aNativeWindow,
|
||||
android::sp<android::DisplaySurface>& aDisplaySurface);
|
||||
|
||||
void PowerOnDisplay(int aDpy);
|
||||
|
||||
int DoQueueBuffer(ANativeWindowBuffer* buf);
|
||||
|
||||
hw_module_t const* mModule;
|
||||
hw_module_t const* mFBModule;
|
||||
hwc_composer_device_1_t* mHwc;
|
||||
framebuffer_device_t* mFBDevice;
|
||||
power_module_t* mPowerModule;
|
||||
android::sp<android::DisplaySurface> mDispSurface;
|
||||
android::sp<ANativeWindow> mSTClient;
|
||||
android::sp<android::DisplaySurface> mBootAnimDispSurface;
|
||||
android::sp<ANativeWindow> mBootAnimSTClient;
|
||||
android::sp<android::IGraphicBufferAlloc> mAlloc;
|
||||
hwc_display_contents_1_t* mList;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
OnEnabledCallbackType mEnabledCallback;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* GONKDISPLAYJB_H */
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
**
|
||||
** Copyright 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 <cutils/log.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include "GraphicBufferAlloc.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
GraphicBufferAlloc::GraphicBufferAlloc() {
|
||||
}
|
||||
|
||||
GraphicBufferAlloc::~GraphicBufferAlloc() {
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
|
||||
PixelFormat format, uint32_t usage, status_t* error) {
|
||||
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
|
||||
status_t err = graphicBuffer->initCheck();
|
||||
*error = err;
|
||||
if (err != 0 || graphicBuffer->handle == 0) {
|
||||
if (err == NO_MEMORY) {
|
||||
GraphicBuffer::dumpAllocationsToSystemLog();
|
||||
}
|
||||
ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
|
||||
"failed (%s), handle=%p",
|
||||
w, h, strerror(-err), graphicBuffer->handle);
|
||||
return 0;
|
||||
}
|
||||
return graphicBuffer;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
// ----------------------------------------------------------------------------
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* 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_SF_GRAPHIC_BUFFER_ALLOC_H
|
||||
#define ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <ui/PixelFormat.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
class GraphicBufferAlloc : public BnGraphicBufferAlloc {
|
||||
public:
|
||||
GraphicBufferAlloc();
|
||||
virtual ~GraphicBufferAlloc();
|
||||
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
|
||||
PixelFormat format, uint32_t usage, status_t* error);
|
||||
};
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
|
|
@ -1,635 +0,0 @@
|
|||
/*
|
||||
* Copyright 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_NDEBUG 0
|
||||
#include "VirtualDisplaySurface.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#if defined(FORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS)
|
||||
static const bool sForceHwcCopy = true;
|
||||
#else
|
||||
static const bool sForceHwcCopy = false;
|
||||
#endif
|
||||
|
||||
#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
|
||||
mDisplayName.string(), ##__VA_ARGS__)
|
||||
#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
|
||||
mDisplayName.string(), ##__VA_ARGS__)
|
||||
#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
|
||||
mDisplayName.string(), ##__VA_ARGS__)
|
||||
|
||||
__attribute__((unused))
|
||||
static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
|
||||
switch (type) {
|
||||
case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
|
||||
case DisplaySurface::COMPOSITION_GLES: return "GLES";
|
||||
case DisplaySurface::COMPOSITION_HWC: return "HWC";
|
||||
case DisplaySurface::COMPOSITION_MIXED: return "MIXED";
|
||||
default: return "<INVALID>";
|
||||
}
|
||||
}
|
||||
|
||||
VirtualDisplaySurface::VirtualDisplaySurface(int32_t dispId,
|
||||
const sp<IGraphicBufferProducer>& sink,
|
||||
const sp<IGraphicBufferProducer>& bqProducer,
|
||||
const sp<StreamConsumer>& bqConsumer,
|
||||
const String8& name)
|
||||
: DisplaySurface(bqConsumer),
|
||||
mDisplayId(dispId),
|
||||
mDisplayName(name),
|
||||
mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
|
||||
mProducerSlotSource(0),
|
||||
mDbgState(DBG_STATE_IDLE),
|
||||
mDbgLastCompositionType(COMPOSITION_UNKNOWN),
|
||||
mMustRecompose(false)
|
||||
{
|
||||
mSource[SOURCE_SINK] = sink;
|
||||
mSource[SOURCE_SCRATCH] = bqProducer;
|
||||
|
||||
resetPerFrameState();
|
||||
|
||||
int sinkWidth, sinkHeight;
|
||||
sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth);
|
||||
sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight);
|
||||
mSinkBufferWidth = sinkWidth;
|
||||
mSinkBufferHeight = sinkHeight;
|
||||
|
||||
// Pick the buffer format to request from the sink when not rendering to it
|
||||
// with GLES. If the consumer needs CPU access, use the default format
|
||||
// set by the consumer. Otherwise allow gralloc to decide the format based
|
||||
// on usage bits.
|
||||
int sinkUsage;
|
||||
sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage);
|
||||
if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
|
||||
int sinkFormat;
|
||||
sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat);
|
||||
mDefaultOutputFormat = sinkFormat;
|
||||
} else {
|
||||
mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
|
||||
}
|
||||
mOutputFormat = mDefaultOutputFormat;
|
||||
|
||||
ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
|
||||
mConsumer->setConsumerName(ConsumerBase::mName);
|
||||
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
|
||||
mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
|
||||
mConsumer->setDefaultMaxBufferCount(2);
|
||||
}
|
||||
|
||||
VirtualDisplaySurface::~VirtualDisplaySurface() {
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
|
||||
if (mDisplayId < 0)
|
||||
return NO_ERROR;
|
||||
|
||||
mMustRecompose = mustRecompose;
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
|
||||
"Unexpected beginFrame() in %s state", dbgStateStr());
|
||||
mDbgState = DBG_STATE_BEGUN;
|
||||
|
||||
return refreshOutputBuffer();
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
|
||||
if (mDisplayId < 0)
|
||||
return NO_ERROR;
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
|
||||
"Unexpected prepareFrame() in %s state", dbgStateStr());
|
||||
mDbgState = DBG_STATE_PREPARED;
|
||||
|
||||
mCompositionType = compositionType;
|
||||
if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
|
||||
// Some hardware can do RGB->YUV conversion more efficiently in hardware
|
||||
// controlled by HWC than in hardware controlled by the video encoder.
|
||||
// Forcing GLES-composed frames to go through an extra copy by the HWC
|
||||
// allows the format conversion to happen there, rather than passing RGB
|
||||
// directly to the consumer.
|
||||
//
|
||||
// On the other hand, when the consumer prefers RGB or can consume RGB
|
||||
// inexpensively, this forces an unnecessary copy.
|
||||
mCompositionType = COMPOSITION_MIXED;
|
||||
}
|
||||
|
||||
if (mCompositionType != mDbgLastCompositionType) {
|
||||
VDS_LOGV("prepareFrame: composition type changed to %s",
|
||||
dbgCompositionTypeStr(mCompositionType));
|
||||
mDbgLastCompositionType = mCompositionType;
|
||||
}
|
||||
|
||||
if (mCompositionType != COMPOSITION_GLES &&
|
||||
(mOutputFormat != mDefaultOutputFormat ||
|
||||
mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
|
||||
// We must have just switched from GLES-only to MIXED or HWC
|
||||
// composition. Stop using the format and usage requested by the GLES
|
||||
// driver; they may be suboptimal when HWC is writing to the output
|
||||
// buffer. For example, if the output is going to a video encoder, and
|
||||
// HWC can write directly to YUV, some hardware can skip a
|
||||
// memory-to-memory RGB-to-YUV conversion step.
|
||||
//
|
||||
// If we just switched *to* GLES-only mode, we'll change the
|
||||
// format/usage and get a new buffer when the GLES driver calls
|
||||
// dequeueBuffer().
|
||||
mOutputFormat = mDefaultOutputFormat;
|
||||
mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
|
||||
refreshOutputBuffer();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::compositionComplete() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::advanceFrame() {
|
||||
return NO_ERROR;
|
||||
|
||||
// XXX Add HWC support
|
||||
|
||||
#if 0
|
||||
if (mDisplayId < 0)
|
||||
return NO_ERROR;
|
||||
|
||||
if (mCompositionType == COMPOSITION_HWC) {
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
|
||||
"Unexpected advanceFrame() in %s state on HWC frame",
|
||||
dbgStateStr());
|
||||
} else {
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
|
||||
"Unexpected advanceFrame() in %s state on GLES/MIXED frame",
|
||||
dbgStateStr());
|
||||
}
|
||||
mDbgState = DBG_STATE_HWC;
|
||||
|
||||
if (mOutputProducerSlot < 0 ||
|
||||
(mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
|
||||
// Last chance bailout if something bad happened earlier. For example,
|
||||
// in a GLES configuration, if the sink disappears then dequeueBuffer
|
||||
// will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
|
||||
// will soldier on. So we end up here without a buffer. There should
|
||||
// be lots of scary messages in the log just before this.
|
||||
VDS_LOGE("advanceFrame: no buffer, bailing out");
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
|
||||
mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL);
|
||||
sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
|
||||
VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
|
||||
mFbProducerSlot, fbBuffer.get(),
|
||||
mOutputProducerSlot, outBuffer.get());
|
||||
|
||||
// At this point we know the output buffer acquire fence,
|
||||
// so update HWC state with it.
|
||||
mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
|
||||
|
||||
status_t result = NO_ERROR;
|
||||
if (fbBuffer != NULL) {
|
||||
result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::onFrameCommitted() {
|
||||
return;
|
||||
|
||||
// XXX Add HWC support
|
||||
|
||||
#if 0
|
||||
if (mDisplayId < 0)
|
||||
return;
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
|
||||
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
|
||||
mDbgState = DBG_STATE_IDLE;
|
||||
|
||||
sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
|
||||
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
|
||||
// release the scratch buffer back to the pool
|
||||
Mutex::Autolock lock(mMutex);
|
||||
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
|
||||
VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
|
||||
addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
|
||||
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
|
||||
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
|
||||
}
|
||||
|
||||
if (mOutputProducerSlot >= 0) {
|
||||
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
|
||||
QueueBufferOutput qbo;
|
||||
sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
|
||||
VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
|
||||
if (mMustRecompose) {
|
||||
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
|
||||
QueueBufferInput(
|
||||
systemTime(), false /* isAutoTimestamp */,
|
||||
Rect(mSinkBufferWidth, mSinkBufferHeight),
|
||||
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
|
||||
true /* async*/,
|
||||
outFence),
|
||||
&qbo);
|
||||
if (result == NO_ERROR) {
|
||||
updateQueueBufferOutput(qbo);
|
||||
}
|
||||
} else {
|
||||
// If the surface hadn't actually been updated, then we only went
|
||||
// through the motions of updating the display to keep our state
|
||||
// machine happy. We cancel the buffer to avoid triggering another
|
||||
// re-composition and causing an infinite loop.
|
||||
mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence);
|
||||
}
|
||||
}
|
||||
|
||||
resetPerFrameState();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
|
||||
uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
|
||||
mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers);
|
||||
mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers);
|
||||
|
||||
mSinkBufferWidth = w;
|
||||
mSinkBufferHeight = h;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::requestBuffer(int pslot,
|
||||
sp<GraphicBuffer>* outBuf) {
|
||||
if (mDisplayId < 0)
|
||||
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
|
||||
"Unexpected requestBuffer pslot=%d in %s state",
|
||||
pslot, dbgStateStr());
|
||||
|
||||
*outBuf = mProducerBuffers[pslot];
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::setBufferCount(int bufferCount) {
|
||||
return mSource[SOURCE_SINK]->setBufferCount(bufferCount);
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
|
||||
uint32_t format, uint32_t usage, int* sslot, sp<Fence>* fence) {
|
||||
LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
|
||||
// Don't let a slow consumer block us
|
||||
bool async = (source == SOURCE_SINK);
|
||||
|
||||
status_t result = mSource[source]->dequeueBuffer(sslot, fence, async,
|
||||
mSinkBufferWidth, mSinkBufferHeight, format, usage);
|
||||
if (result < 0)
|
||||
return result;
|
||||
int pslot = mapSource2ProducerSlot(source, *sslot);
|
||||
VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
|
||||
dbgSourceStr(source), *sslot, pslot, result);
|
||||
uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
|
||||
|
||||
if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) {
|
||||
// This slot was previously dequeued from the other source; must
|
||||
// re-request the buffer.
|
||||
result |= BUFFER_NEEDS_REALLOCATION;
|
||||
mProducerSlotSource &= ~(1ULL << pslot);
|
||||
mProducerSlotSource |= sourceBit;
|
||||
}
|
||||
|
||||
if (result & RELEASE_ALL_BUFFERS) {
|
||||
for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||
if ((mProducerSlotSource & (1ULL << i)) == sourceBit)
|
||||
mProducerBuffers[i].clear();
|
||||
}
|
||||
}
|
||||
if (result & BUFFER_NEEDS_REALLOCATION) {
|
||||
result = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
|
||||
if (result < 0) {
|
||||
mProducerBuffers[pslot].clear();
|
||||
mSource[source]->cancelBuffer(*sslot, *fence);
|
||||
return result;
|
||||
}
|
||||
VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#x",
|
||||
dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
|
||||
mProducerBuffers[pslot]->getPixelFormat(),
|
||||
mProducerBuffers[pslot]->getUsage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
|
||||
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
|
||||
if (mDisplayId < 0)
|
||||
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, async, w, h, format, usage);
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
|
||||
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
|
||||
mDbgState = DBG_STATE_GLES;
|
||||
|
||||
VDS_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)");
|
||||
VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage);
|
||||
|
||||
status_t result = NO_ERROR;
|
||||
Source source = fbSourceForCompositionType(mCompositionType);
|
||||
|
||||
if (source == SOURCE_SINK) {
|
||||
|
||||
if (mOutputProducerSlot < 0) {
|
||||
// Last chance bailout if something bad happened earlier. For example,
|
||||
// in a GLES configuration, if the sink disappears then dequeueBuffer
|
||||
// will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
|
||||
// will soldier on. So we end up here without a buffer. There should
|
||||
// be lots of scary messages in the log just before this.
|
||||
VDS_LOGE("dequeueBuffer: no buffer, bailing out");
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// We already dequeued the output buffer. If the GLES driver wants
|
||||
// something incompatible, we have to cancel and get a new one. This
|
||||
// will mean that HWC will see a different output buffer between
|
||||
// prepare and set, but since we're in GLES-only mode already it
|
||||
// shouldn't matter.
|
||||
|
||||
usage |= GRALLOC_USAGE_HW_COMPOSER;
|
||||
const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot];
|
||||
if ((usage & ~buf->getUsage()) != 0 ||
|
||||
(format != 0 && format != (uint32_t)buf->getPixelFormat()) ||
|
||||
(w != 0 && w != mSinkBufferWidth) ||
|
||||
(h != 0 && h != mSinkBufferHeight)) {
|
||||
VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
|
||||
"want %dx%d fmt=%d use=%#x, "
|
||||
"have %dx%d fmt=%d use=%#x",
|
||||
w, h, format, usage,
|
||||
mSinkBufferWidth, mSinkBufferHeight,
|
||||
buf->getPixelFormat(), buf->getUsage());
|
||||
mOutputFormat = format;
|
||||
mOutputUsage = usage;
|
||||
result = refreshOutputBuffer();
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (source == SOURCE_SINK) {
|
||||
*pslot = mOutputProducerSlot;
|
||||
*fence = mOutputFence;
|
||||
} else {
|
||||
int sslot;
|
||||
result = dequeueBuffer(source, format, usage, &sslot, fence);
|
||||
if (result >= 0) {
|
||||
*pslot = mapSource2ProducerSlot(source, sslot);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
|
||||
VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::detachNextBuffer(
|
||||
sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
|
||||
VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
|
||||
const sp<GraphicBuffer>& /* buffer */) {
|
||||
VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::queueBuffer(int pslot,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output) {
|
||||
if (mDisplayId < 0)
|
||||
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
|
||||
"Unexpected queueBuffer(pslot=%d) in %s state", pslot,
|
||||
dbgStateStr());
|
||||
mDbgState = DBG_STATE_GLES_DONE;
|
||||
|
||||
VDS_LOGV("queueBuffer pslot=%d", pslot);
|
||||
|
||||
status_t result;
|
||||
if (mCompositionType == COMPOSITION_MIXED) {
|
||||
// Queue the buffer back into the scratch pool
|
||||
QueueBufferOutput scratchQBO;
|
||||
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
|
||||
result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO);
|
||||
if (result != NO_ERROR)
|
||||
return result;
|
||||
|
||||
// Now acquire the buffer from the scratch pool -- should be the same
|
||||
// slot and fence as we just queued.
|
||||
Mutex::Autolock lock(mMutex);
|
||||
BufferQueue::BufferItem item;
|
||||
result = acquireBufferLocked(&item, 0);
|
||||
if (result != NO_ERROR)
|
||||
return result;
|
||||
VDS_LOGW_IF(item.mBuf != sslot,
|
||||
"queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
|
||||
item.mBuf, sslot);
|
||||
mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf);
|
||||
mFbFence = mSlots[item.mBuf].mFence;
|
||||
|
||||
} else {
|
||||
LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
|
||||
"Unexpected queueBuffer in state %s for compositionType %s",
|
||||
dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
|
||||
|
||||
// Extract the GLES release fence for HWC to acquire
|
||||
int64_t timestamp;
|
||||
bool isAutoTimestamp;
|
||||
Rect crop;
|
||||
int scalingMode;
|
||||
uint32_t transform;
|
||||
bool async;
|
||||
input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode,
|
||||
&transform, &async, &mFbFence);
|
||||
|
||||
mFbProducerSlot = pslot;
|
||||
mOutputFence = mFbFence;
|
||||
}
|
||||
|
||||
*output = mQueueBufferOutput;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) {
|
||||
if (mDisplayId < 0)
|
||||
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
|
||||
|
||||
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
|
||||
"Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
|
||||
dbgStateStr());
|
||||
VDS_LOGV("cancelBuffer pslot=%d", pslot);
|
||||
Source source = fbSourceForCompositionType(mCompositionType);
|
||||
return mSource[source]->cancelBuffer(
|
||||
mapProducer2SourceSlot(source, pslot), fence);
|
||||
}
|
||||
|
||||
int VirtualDisplaySurface::query(int what, int* value) {
|
||||
switch (what) {
|
||||
case NATIVE_WINDOW_WIDTH:
|
||||
*value = mSinkBufferWidth;
|
||||
break;
|
||||
case NATIVE_WINDOW_HEIGHT:
|
||||
*value = mSinkBufferHeight;
|
||||
break;
|
||||
default:
|
||||
return mSource[SOURCE_SINK]->query(what, value);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp,
|
||||
QueueBufferOutput* output) {
|
||||
QueueBufferOutput qbo;
|
||||
status_t result = mSource[SOURCE_SINK]->connect(listener, api,
|
||||
producerControlledByApp, &qbo);
|
||||
if (result == NO_ERROR) {
|
||||
updateQueueBufferOutput(qbo);
|
||||
*output = mQueueBufferOutput;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
|
||||
int api, bool producerControlledByApp,
|
||||
QueueBufferOutput* output) {
|
||||
QueueBufferOutput qbo;
|
||||
status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
|
||||
if (result == NO_ERROR) {
|
||||
updateQueueBufferOutput(qbo);
|
||||
*output = mQueueBufferOutput;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
status_t VirtualDisplaySurface::disconnect(int api) {
|
||||
return mSource[SOURCE_SINK]->disconnect(api);
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
#endif
|
||||
|
||||
void VirtualDisplaySurface::allocateBuffers(bool /* async */,
|
||||
uint32_t /* width */, uint32_t /* height */, uint32_t /* format */,
|
||||
uint32_t /* usage */) {
|
||||
// TODO: Should we actually allocate buffers for a virtual display?
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::updateQueueBufferOutput(
|
||||
const QueueBufferOutput& qbo) {
|
||||
uint32_t w, h, transformHint, numPendingBuffers;
|
||||
qbo.deflate(&w, &h, &transformHint, &numPendingBuffers);
|
||||
mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers);
|
||||
}
|
||||
|
||||
void VirtualDisplaySurface::resetPerFrameState() {
|
||||
mCompositionType = COMPOSITION_UNKNOWN;
|
||||
mFbFence = Fence::NO_FENCE;
|
||||
mOutputFence = Fence::NO_FENCE;
|
||||
mOutputProducerSlot = -1;
|
||||
mFbProducerSlot = -1;
|
||||
}
|
||||
|
||||
status_t VirtualDisplaySurface::refreshOutputBuffer() {
|
||||
|
||||
return INVALID_OPERATION;
|
||||
|
||||
// XXX Add HWC support
|
||||
|
||||
#if 0
|
||||
if (mOutputProducerSlot >= 0) {
|
||||
mSource[SOURCE_SINK]->cancelBuffer(
|
||||
mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
|
||||
mOutputFence);
|
||||
}
|
||||
|
||||
int sslot;
|
||||
status_t result = dequeueBuffer(SOURCE_SINK, mOutputFormat, mOutputUsage,
|
||||
&sslot, &mOutputFence);
|
||||
if (result < 0)
|
||||
return result;
|
||||
mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
|
||||
|
||||
// On GLES-only frames, we don't have the right output buffer acquire fence
|
||||
// until after GLES calls queueBuffer(). So here we just set the buffer
|
||||
// (for use in HWC prepare) but not the fence; we'll call this again with
|
||||
// the proper fence once we have it.
|
||||
result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
|
||||
mProducerBuffers[mOutputProducerSlot]);
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This slot mapping function is its own inverse, so two copies are unnecessary.
|
||||
// Both are kept to make the intent clear where the function is called, and for
|
||||
// the (unlikely) chance that we switch to a different mapping function.
|
||||
int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) {
|
||||
if (source == SOURCE_SCRATCH) {
|
||||
return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1;
|
||||
} else {
|
||||
return sslot;
|
||||
}
|
||||
}
|
||||
int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) {
|
||||
return mapSource2ProducerSlot(source, pslot);
|
||||
}
|
||||
|
||||
VirtualDisplaySurface::Source
|
||||
VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
|
||||
return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
|
||||
}
|
||||
|
||||
const char* VirtualDisplaySurface::dbgStateStr() const {
|
||||
switch (mDbgState) {
|
||||
case DBG_STATE_IDLE: return "IDLE";
|
||||
case DBG_STATE_PREPARED: return "PREPARED";
|
||||
case DBG_STATE_GLES: return "GLES";
|
||||
case DBG_STATE_GLES_DONE: return "GLES_DONE";
|
||||
case DBG_STATE_HWC: return "HWC";
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
|
||||
switch (s) {
|
||||
case SOURCE_SINK: return "SINK";
|
||||
case SOURCE_SCRATCH: return "SCRATCH";
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
} // namespace android
|
||||
// ---------------------------------------------------------------------------
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Copyright 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_SF_VIRTUAL_DISPLAY_SURFACE_H
|
||||
#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
|
||||
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
|
||||
#include "DisplaySurface.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class HWComposer;
|
||||
class IProducerListener;
|
||||
|
||||
/* This DisplaySurface implementation supports virtual displays, where GLES
|
||||
* and/or HWC compose into a buffer that is then passed to an arbitrary
|
||||
* consumer (the sink) running in another process.
|
||||
*
|
||||
* The simplest case is when the virtual display will never use the h/w
|
||||
* composer -- either the h/w composer doesn't support writing to buffers, or
|
||||
* there are more virtual displays than it supports simultaneously. In this
|
||||
* case, the GLES driver works directly with the output buffer queue, and
|
||||
* calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
|
||||
* nothing.
|
||||
*
|
||||
* If h/w composer might be used, then each frame will fall into one of three
|
||||
* configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
|
||||
* we must provide a FB target buffer and output buffer for the HWC set() call.
|
||||
*
|
||||
* In GLES-only composition, the GLES driver is given a buffer from the sink to
|
||||
* render into. When the GLES driver queues the buffer to the
|
||||
* VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
|
||||
* immediately queueing it to the sink. The buffer is used as both the FB
|
||||
* target and output buffer for HWC, though on these frames the HWC doesn't
|
||||
* do any work for this display and doesn't write to the output buffer. After
|
||||
* composition is complete, the buffer is queued to the sink.
|
||||
*
|
||||
* In HWC-only composition, the VirtualDisplaySurface dequeues a buffer from
|
||||
* the sink and passes it to HWC as both the FB target buffer and output
|
||||
* buffer. The HWC doesn't need to read from the FB target buffer, but does
|
||||
* write to the output buffer. After composition is complete, the buffer is
|
||||
* queued to the sink.
|
||||
*
|
||||
* On MIXED frames, things become more complicated, since some h/w composer
|
||||
* implementations can't read from and write to the same buffer. This class has
|
||||
* an internal BufferQueue that it uses as a scratch buffer pool. The GLES
|
||||
* driver is given a scratch buffer to render into. When it finishes rendering,
|
||||
* the buffer is queued and then immediately acquired by the
|
||||
* VirtualDisplaySurface. The scratch buffer is then used as the FB target
|
||||
* buffer for HWC, and a separate buffer is dequeued from the sink and used as
|
||||
* the HWC output buffer. When HWC composition is complete, the scratch buffer
|
||||
* is released and the output buffer is queued to the sink.
|
||||
*/
|
||||
class VirtualDisplaySurface : public DisplaySurface,
|
||||
public BnGraphicBufferProducer {
|
||||
public:
|
||||
VirtualDisplaySurface(int32_t dispId,
|
||||
const sp<IGraphicBufferProducer>& sink,
|
||||
const sp<IGraphicBufferProducer>& bqProducer,
|
||||
const sp<StreamConsumer>& bqConsumer,
|
||||
const String8& name);
|
||||
|
||||
//
|
||||
// DisplaySurface interface
|
||||
//
|
||||
virtual status_t beginFrame(bool mustRecompose);
|
||||
virtual status_t prepareFrame(CompositionType compositionType);
|
||||
virtual status_t compositionComplete();
|
||||
virtual status_t advanceFrame();
|
||||
virtual void onFrameCommitted();
|
||||
virtual void resizeBuffers(const uint32_t w, const uint32_t h);
|
||||
|
||||
virtual status_t setReleaseFenceFd(int fenceFd) { return INVALID_OPERATION; }
|
||||
virtual int GetPrevDispAcquireFd() { return -1; };
|
||||
|
||||
private:
|
||||
enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1};
|
||||
|
||||
virtual ~VirtualDisplaySurface();
|
||||
|
||||
//
|
||||
// IGraphicBufferProducer interface, used by the GLES driver.
|
||||
//
|
||||
virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
|
||||
uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
|
||||
virtual status_t detachBuffer(int slot);
|
||||
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||
sp<Fence>* outFence);
|
||||
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
|
||||
virtual status_t queueBuffer(int pslot,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
|
||||
virtual int query(int what, int* value);
|
||||
#if ANDROID_VERSION >= 21
|
||||
virtual status_t connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput* output);
|
||||
#else
|
||||
virtual status_t connect(const sp<IBinder>& token,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput* output);
|
||||
#endif
|
||||
virtual status_t disconnect(int api);
|
||||
#if ANDROID_VERSION >= 21
|
||||
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
|
||||
#endif
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t usage);
|
||||
|
||||
//
|
||||
// Utility methods
|
||||
//
|
||||
static Source fbSourceForCompositionType(CompositionType type);
|
||||
status_t dequeueBuffer(Source source, uint32_t format, uint32_t usage,
|
||||
int* sslot, sp<Fence>* fence);
|
||||
void updateQueueBufferOutput(const QueueBufferOutput& qbo);
|
||||
void resetPerFrameState();
|
||||
status_t refreshOutputBuffer();
|
||||
|
||||
// Both the sink and scratch buffer pools have their own set of slots
|
||||
// ("source slots", or "sslot"). We have to merge these into the single
|
||||
// set of slots used by the GLES producer ("producer slots" or "pslot") and
|
||||
// internally in the VirtualDisplaySurface. To minimize the number of times
|
||||
// a producer slot switches which source it comes from, we map source slot
|
||||
// numbers to producer slot numbers differently for each source.
|
||||
static int mapSource2ProducerSlot(Source source, int sslot);
|
||||
static int mapProducer2SourceSlot(Source source, int pslot);
|
||||
|
||||
//
|
||||
// Immutable after construction
|
||||
//
|
||||
const int32_t mDisplayId;
|
||||
const String8 mDisplayName;
|
||||
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
|
||||
uint32_t mDefaultOutputFormat;
|
||||
|
||||
//
|
||||
// Inter-frame state
|
||||
//
|
||||
|
||||
// To avoid buffer reallocations, we track the buffer usage and format
|
||||
// we used on the previous frame and use it again on the new frame. If
|
||||
// the composition type changes or the GLES driver starts requesting
|
||||
// different usage/format, we'll get a new buffer.
|
||||
uint32_t mOutputFormat;
|
||||
uint32_t mOutputUsage;
|
||||
|
||||
// Since we present a single producer interface to the GLES driver, but
|
||||
// are internally muxing between the sink and scratch producers, we have
|
||||
// to keep track of which source last returned each producer slot from
|
||||
// dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
|
||||
// slot. Both mProducerSlotSource and mProducerBuffers are indexed by a
|
||||
// "producer slot"; see the mapSlot*() functions.
|
||||
uint64_t mProducerSlotSource;
|
||||
sp<GraphicBuffer> mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS];
|
||||
|
||||
// The QueueBufferOutput with the latest info from the sink, and with the
|
||||
// transform hint cleared. Since we defer queueBuffer from the GLES driver
|
||||
// to the sink, we have to return the previous version.
|
||||
QueueBufferOutput mQueueBufferOutput;
|
||||
|
||||
// Details of the current sink buffer. These become valid when a buffer is
|
||||
// dequeued from the sink, and are used when queueing the buffer.
|
||||
uint32_t mSinkBufferWidth, mSinkBufferHeight;
|
||||
|
||||
//
|
||||
// Intra-frame state
|
||||
//
|
||||
|
||||
// Composition type and GLES buffer source for the current frame.
|
||||
// Valid after prepareFrame(), cleared in onFrameCommitted.
|
||||
CompositionType mCompositionType;
|
||||
|
||||
// mFbFence is the fence HWC should wait for before reading the framebuffer
|
||||
// target buffer.
|
||||
sp<Fence> mFbFence;
|
||||
|
||||
// mOutputFence is the fence HWC should wait for before writing to the
|
||||
// output buffer.
|
||||
sp<Fence> mOutputFence;
|
||||
|
||||
// Producer slot numbers for the buffers to use for HWC framebuffer target
|
||||
// and output.
|
||||
int mFbProducerSlot;
|
||||
int mOutputProducerSlot;
|
||||
|
||||
// Debug only -- track the sequence of events in each frame so we can make
|
||||
// sure they happen in the order we expect. This class implicitly models
|
||||
// a state machine; this enum/variable makes it explicit.
|
||||
//
|
||||
// +-----------+-------------------+-------------+
|
||||
// | State | Event || Next State |
|
||||
// +-----------+-------------------+-------------+
|
||||
// | IDLE | beginFrame || BEGUN |
|
||||
// | BEGUN | prepareFrame || PREPARED |
|
||||
// | PREPARED | dequeueBuffer [1] || GLES |
|
||||
// | PREPARED | advanceFrame [2] || HWC |
|
||||
// | GLES | queueBuffer || GLES_DONE |
|
||||
// | GLES_DONE | advanceFrame || HWC |
|
||||
// | HWC | onFrameCommitted || IDLE |
|
||||
// +-----------+-------------------++------------+
|
||||
// [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
|
||||
// [2] COMPOSITION_HWC frames.
|
||||
//
|
||||
enum DbgState {
|
||||
// no buffer dequeued, don't know anything about the next frame
|
||||
DBG_STATE_IDLE,
|
||||
// output buffer dequeued, framebuffer source not yet known
|
||||
DBG_STATE_BEGUN,
|
||||
// output buffer dequeued, framebuffer source known but not provided
|
||||
// to GLES yet.
|
||||
DBG_STATE_PREPARED,
|
||||
// GLES driver has a buffer dequeued
|
||||
DBG_STATE_GLES,
|
||||
// GLES driver has queued the buffer, we haven't sent it to HWC yet
|
||||
DBG_STATE_GLES_DONE,
|
||||
// HWC has the buffer for this frame
|
||||
DBG_STATE_HWC,
|
||||
};
|
||||
DbgState mDbgState;
|
||||
CompositionType mDbgLastCompositionType;
|
||||
|
||||
const char* dbgStateStr() const;
|
||||
static const char* dbgSourceStr(Source s);
|
||||
|
||||
bool mMustRecompose;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
} // namespace android
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# Copyright 2013 Mozilla Foundation and Mozilla contributors
|
||||
#
|
||||
# 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.
|
||||
|
||||
SOURCES += [
|
||||
'BootAnimation.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] >= '19':
|
||||
SOURCES += [
|
||||
'FramebufferSurface.cpp',
|
||||
'GonkDisplayJB.cpp',
|
||||
'VirtualDisplaySurface.cpp',
|
||||
]
|
||||
elif CONFIG['ANDROID_VERSION'] == '18':
|
||||
SOURCES += [
|
||||
'FramebufferSurface.cpp',
|
||||
'GonkDisplayJB.cpp',
|
||||
]
|
||||
elif CONFIG['ANDROID_VERSION'] == '17':
|
||||
SOURCES += [
|
||||
'FramebufferSurface.cpp',
|
||||
'GonkDisplayJB.cpp',
|
||||
'GraphicBufferAlloc.cpp',
|
||||
]
|
||||
elif CONFIG['ANDROID_VERSION'] and CONFIG['COMPILE_ENVIRONMENT']:
|
||||
error('Unsupported platform version: %s' % (CONFIG['ANDROID_VERSION']))
|
||||
|
||||
Library('display')
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FORCE_STATIC_LIB = True
|
||||
|
||||
DEFINES['XPCOM_GLUE'] = True
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/native/include/gui',
|
||||
'frameworks/native/opengl/include',
|
||||
'hardware/libhardware/include',
|
||||
'hardware/libhardware_legacy/include',
|
||||
'system/core/libsuspend/include',
|
||||
]
|
||||
]
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,435 +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.h"
|
||||
#include "InputDevice.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
#include "VirtualKeyMap.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/threads.h>
|
||||
#include "cutils_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 "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 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;
|
||||
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 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 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);
|
||||
|
||||
status_t closeDeviceByPathLocked(const char *devicePath);
|
||||
void closeDeviceLocked(Device* device);
|
||||
void closeAllDevicesLocked();
|
||||
|
||||
status_t scanDirLocked(const char *dirname);
|
||||
void scanDevicesLocked();
|
||||
status_t readNotifyLocked();
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
|
@ -1,635 +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 "Input"
|
||||
//#define LOG_NDEBUG 0
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "Input.h"
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <binder/Parcel.h>
|
||||
|
||||
#include "SkPoint.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkScalar.h"
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- InputEvent ---
|
||||
|
||||
void InputEvent::initialize(int32_t deviceId, int32_t source) {
|
||||
mDeviceId = deviceId;
|
||||
mSource = source;
|
||||
}
|
||||
|
||||
void InputEvent::initialize(const InputEvent& from) {
|
||||
mDeviceId = from.mDeviceId;
|
||||
mSource = from.mSource;
|
||||
}
|
||||
|
||||
// --- KeyEvent ---
|
||||
|
||||
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
||||
switch (keyCode) {
|
||||
case AKEYCODE_HOME:
|
||||
case AKEYCODE_BACK:
|
||||
case AKEYCODE_CALL:
|
||||
case AKEYCODE_ENDCALL:
|
||||
case AKEYCODE_VOLUME_UP:
|
||||
case AKEYCODE_VOLUME_DOWN:
|
||||
case AKEYCODE_VOLUME_MUTE:
|
||||
case AKEYCODE_POWER:
|
||||
case AKEYCODE_CAMERA:
|
||||
case AKEYCODE_HEADSETHOOK:
|
||||
case AKEYCODE_MENU:
|
||||
case AKEYCODE_NOTIFICATION:
|
||||
case AKEYCODE_FOCUS:
|
||||
case AKEYCODE_SEARCH:
|
||||
case AKEYCODE_MEDIA_PLAY:
|
||||
case AKEYCODE_MEDIA_PAUSE:
|
||||
case AKEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case AKEYCODE_MEDIA_STOP:
|
||||
case AKEYCODE_MEDIA_NEXT:
|
||||
case AKEYCODE_MEDIA_PREVIOUS:
|
||||
case AKEYCODE_MEDIA_REWIND:
|
||||
case AKEYCODE_MEDIA_RECORD:
|
||||
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||
case AKEYCODE_MUTE:
|
||||
case AKEYCODE_BRIGHTNESS_DOWN:
|
||||
case AKEYCODE_BRIGHTNESS_UP:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyEvent::hasDefaultAction() const {
|
||||
return hasDefaultAction(getKeyCode());
|
||||
}
|
||||
|
||||
bool KeyEvent::isSystemKey(int32_t keyCode) {
|
||||
switch (keyCode) {
|
||||
case AKEYCODE_MENU:
|
||||
case AKEYCODE_SOFT_RIGHT:
|
||||
case AKEYCODE_HOME:
|
||||
case AKEYCODE_BACK:
|
||||
case AKEYCODE_CALL:
|
||||
case AKEYCODE_ENDCALL:
|
||||
case AKEYCODE_VOLUME_UP:
|
||||
case AKEYCODE_VOLUME_DOWN:
|
||||
case AKEYCODE_VOLUME_MUTE:
|
||||
case AKEYCODE_MUTE:
|
||||
case AKEYCODE_POWER:
|
||||
case AKEYCODE_HEADSETHOOK:
|
||||
case AKEYCODE_MEDIA_PLAY:
|
||||
case AKEYCODE_MEDIA_PAUSE:
|
||||
case AKEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case AKEYCODE_MEDIA_STOP:
|
||||
case AKEYCODE_MEDIA_NEXT:
|
||||
case AKEYCODE_MEDIA_PREVIOUS:
|
||||
case AKEYCODE_MEDIA_REWIND:
|
||||
case AKEYCODE_MEDIA_RECORD:
|
||||
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||
case AKEYCODE_CAMERA:
|
||||
case AKEYCODE_FOCUS:
|
||||
case AKEYCODE_SEARCH:
|
||||
case AKEYCODE_BRIGHTNESS_DOWN:
|
||||
case AKEYCODE_BRIGHTNESS_UP:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyEvent::isSystemKey() const {
|
||||
return isSystemKey(getKeyCode());
|
||||
}
|
||||
|
||||
void KeyEvent::initialize(
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t keyCode,
|
||||
int32_t scanCode,
|
||||
int32_t metaState,
|
||||
int32_t repeatCount,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime) {
|
||||
InputEvent::initialize(deviceId, source);
|
||||
mAction = action;
|
||||
mFlags = flags;
|
||||
mKeyCode = keyCode;
|
||||
mScanCode = scanCode;
|
||||
mMetaState = metaState;
|
||||
mRepeatCount = repeatCount;
|
||||
mDownTime = downTime;
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
void KeyEvent::initialize(const KeyEvent& from) {
|
||||
InputEvent::initialize(from);
|
||||
mAction = from.mAction;
|
||||
mFlags = from.mFlags;
|
||||
mKeyCode = from.mKeyCode;
|
||||
mScanCode = from.mScanCode;
|
||||
mMetaState = from.mMetaState;
|
||||
mRepeatCount = from.mRepeatCount;
|
||||
mDownTime = from.mDownTime;
|
||||
mEventTime = from.mEventTime;
|
||||
}
|
||||
|
||||
|
||||
// --- PointerCoords ---
|
||||
|
||||
float PointerCoords::getAxisValue(int32_t axis) const {
|
||||
if (axis < 0 || axis > 63) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t axisBit = 1LL << axis;
|
||||
if (!(bits & axisBit)) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
|
||||
return values[index];
|
||||
}
|
||||
|
||||
status_t PointerCoords::setAxisValue(int32_t axis, float value) {
|
||||
if (axis < 0 || axis > 63) {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint64_t axisBit = 1LL << axis;
|
||||
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
|
||||
if (!(bits & axisBit)) {
|
||||
if (value == 0) {
|
||||
return OK; // axes with value 0 do not need to be stored
|
||||
}
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
if (count >= MAX_AXES) {
|
||||
tooManyAxes(axis);
|
||||
return NO_MEMORY;
|
||||
}
|
||||
bits |= axisBit;
|
||||
for (uint32_t i = count; i > index; i--) {
|
||||
values[i] = values[i - 1];
|
||||
}
|
||||
}
|
||||
values[index] = value;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
|
||||
float value = c.getAxisValue(axis);
|
||||
if (value != 0) {
|
||||
c.setAxisValue(axis, value * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerCoords::scale(float scaleFactor) {
|
||||
// No need to scale pressure or size since they are normalized.
|
||||
// No need to scale orientation since it is meaningless to do so.
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
|
||||
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
status_t PointerCoords::readFromParcel(Parcel* parcel) {
|
||||
bits = parcel->readInt64();
|
||||
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
if (count > MAX_AXES) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
values[i] = parcel->readFloat();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t PointerCoords::writeToParcel(Parcel* parcel) const {
|
||||
parcel->writeInt64(bits);
|
||||
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
parcel->writeFloat(values[i]);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PointerCoords::tooManyAxes(int axis) {
|
||||
ALOGW("Could not set value for axis %d because the PointerCoords structure is full and "
|
||||
"cannot contain more than %d axis values.", axis, int(MAX_AXES));
|
||||
}
|
||||
|
||||
bool PointerCoords::operator==(const PointerCoords& other) const {
|
||||
if (bits != other.bits) {
|
||||
return false;
|
||||
}
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (values[i] != other.values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointerCoords::copyFrom(const PointerCoords& other) {
|
||||
bits = other.bits;
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
values[i] = other.values[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- PointerProperties ---
|
||||
|
||||
bool PointerProperties::operator==(const PointerProperties& other) const {
|
||||
return id == other.id
|
||||
&& toolType == other.toolType;
|
||||
}
|
||||
|
||||
void PointerProperties::copyFrom(const PointerProperties& other) {
|
||||
id = other.id;
|
||||
toolType = other.toolType;
|
||||
}
|
||||
|
||||
|
||||
// --- MotionEvent ---
|
||||
|
||||
void MotionEvent::initialize(
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t edgeFlags,
|
||||
int32_t metaState,
|
||||
int32_t buttonState,
|
||||
float xOffset,
|
||||
float yOffset,
|
||||
float xPrecision,
|
||||
float yPrecision,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime,
|
||||
size_t pointerCount,
|
||||
const PointerProperties* pointerProperties,
|
||||
const PointerCoords* pointerCoords) {
|
||||
InputEvent::initialize(deviceId, source);
|
||||
mAction = action;
|
||||
mFlags = flags;
|
||||
mEdgeFlags = edgeFlags;
|
||||
mMetaState = metaState;
|
||||
mButtonState = buttonState;
|
||||
mXOffset = xOffset;
|
||||
mYOffset = yOffset;
|
||||
mXPrecision = xPrecision;
|
||||
mYPrecision = yPrecision;
|
||||
mDownTime = downTime;
|
||||
mPointerProperties.clear();
|
||||
mPointerProperties.appendArray(pointerProperties, pointerCount);
|
||||
mSampleEventTimes.clear();
|
||||
mSamplePointerCoords.clear();
|
||||
addSample(eventTime, pointerCoords);
|
||||
}
|
||||
|
||||
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
|
||||
InputEvent::initialize(other->mDeviceId, other->mSource);
|
||||
mAction = other->mAction;
|
||||
mFlags = other->mFlags;
|
||||
mEdgeFlags = other->mEdgeFlags;
|
||||
mMetaState = other->mMetaState;
|
||||
mButtonState = other->mButtonState;
|
||||
mXOffset = other->mXOffset;
|
||||
mYOffset = other->mYOffset;
|
||||
mXPrecision = other->mXPrecision;
|
||||
mYPrecision = other->mYPrecision;
|
||||
mDownTime = other->mDownTime;
|
||||
mPointerProperties = other->mPointerProperties;
|
||||
|
||||
if (keepHistory) {
|
||||
mSampleEventTimes = other->mSampleEventTimes;
|
||||
mSamplePointerCoords = other->mSamplePointerCoords;
|
||||
} else {
|
||||
mSampleEventTimes.clear();
|
||||
mSampleEventTimes.push(other->getEventTime());
|
||||
mSamplePointerCoords.clear();
|
||||
size_t pointerCount = other->getPointerCount();
|
||||
size_t historySize = other->getHistorySize();
|
||||
mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
|
||||
+ (historySize * pointerCount), pointerCount);
|
||||
}
|
||||
}
|
||||
|
||||
void MotionEvent::addSample(
|
||||
int64_t eventTime,
|
||||
const PointerCoords* pointerCoords) {
|
||||
mSampleEventTimes.push(eventTime);
|
||||
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
|
||||
}
|
||||
|
||||
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
|
||||
return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
|
||||
}
|
||||
|
||||
float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
|
||||
return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
|
||||
}
|
||||
|
||||
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
|
||||
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
|
||||
switch (axis) {
|
||||
case AMOTION_EVENT_AXIS_X:
|
||||
return value + mXOffset;
|
||||
case AMOTION_EVENT_AXIS_Y:
|
||||
return value + mYOffset;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
|
||||
size_t pointerIndex, size_t historicalIndex) const {
|
||||
return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
|
||||
}
|
||||
|
||||
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
|
||||
size_t historicalIndex) const {
|
||||
return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
|
||||
}
|
||||
|
||||
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
|
||||
size_t historicalIndex) const {
|
||||
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
|
||||
switch (axis) {
|
||||
case AMOTION_EVENT_AXIS_X:
|
||||
return value + mXOffset;
|
||||
case AMOTION_EVENT_AXIS_Y:
|
||||
return value + mYOffset;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
|
||||
size_t pointerCount = mPointerProperties.size();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
if (mPointerProperties.itemAt(i).id == pointerId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
|
||||
mXOffset += xOffset;
|
||||
mYOffset += yOffset;
|
||||
}
|
||||
|
||||
void MotionEvent::scale(float scaleFactor) {
|
||||
mXOffset *= scaleFactor;
|
||||
mYOffset *= scaleFactor;
|
||||
mXPrecision *= scaleFactor;
|
||||
mYPrecision *= scaleFactor;
|
||||
|
||||
size_t numSamples = mSamplePointerCoords.size();
|
||||
for (size_t i = 0; i < numSamples; i++) {
|
||||
mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
|
||||
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
|
||||
// Coordinate system: down is increasing Y, right is increasing X.
|
||||
SkPoint vector;
|
||||
vector.fX = SkFloatToScalar(sinf(angleRadians));
|
||||
vector.fY = SkFloatToScalar(-cosf(angleRadians));
|
||||
matrix->mapVectors(& vector, 1);
|
||||
|
||||
// Derive the transformed vector's clockwise angle from vertical.
|
||||
float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY));
|
||||
if (result < - M_PI_2) {
|
||||
result += M_PI;
|
||||
} else if (result > M_PI_2) {
|
||||
result -= M_PI;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MotionEvent::transform(const SkMatrix* matrix) {
|
||||
float oldXOffset = mXOffset;
|
||||
float oldYOffset = mYOffset;
|
||||
|
||||
// The tricky part of this implementation is to preserve the value of
|
||||
// rawX and rawY. So we apply the transformation to the first point
|
||||
// then derive an appropriate new X/Y offset that will preserve rawX and rawY.
|
||||
SkPoint point;
|
||||
float rawX = getRawX(0);
|
||||
float rawY = getRawY(0);
|
||||
matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
|
||||
& point);
|
||||
float newX = SkScalarToFloat(point.fX);
|
||||
float newY = SkScalarToFloat(point.fY);
|
||||
float newXOffset = newX - rawX;
|
||||
float newYOffset = newY - rawY;
|
||||
|
||||
mXOffset = newXOffset;
|
||||
mYOffset = newYOffset;
|
||||
|
||||
// Apply the transformation to all samples.
|
||||
size_t numSamples = mSamplePointerCoords.size();
|
||||
for (size_t i = 0; i < numSamples; i++) {
|
||||
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
|
||||
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
|
||||
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
|
||||
matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point);
|
||||
c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset);
|
||||
c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset);
|
||||
|
||||
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
|
||||
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation));
|
||||
}
|
||||
}
|
||||
|
||||
status_t MotionEvent::readFromParcel(Parcel* parcel) {
|
||||
size_t pointerCount = parcel->readInt32();
|
||||
size_t sampleCount = parcel->readInt32();
|
||||
if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mDeviceId = parcel->readInt32();
|
||||
mSource = parcel->readInt32();
|
||||
mAction = parcel->readInt32();
|
||||
mFlags = parcel->readInt32();
|
||||
mEdgeFlags = parcel->readInt32();
|
||||
mMetaState = parcel->readInt32();
|
||||
mButtonState = parcel->readInt32();
|
||||
mXOffset = parcel->readFloat();
|
||||
mYOffset = parcel->readFloat();
|
||||
mXPrecision = parcel->readFloat();
|
||||
mYPrecision = parcel->readFloat();
|
||||
mDownTime = parcel->readInt64();
|
||||
|
||||
mPointerProperties.clear();
|
||||
mPointerProperties.setCapacity(pointerCount);
|
||||
mSampleEventTimes.clear();
|
||||
mSampleEventTimes.setCapacity(sampleCount);
|
||||
mSamplePointerCoords.clear();
|
||||
mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
|
||||
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
mPointerProperties.push();
|
||||
PointerProperties& properties = mPointerProperties.editTop();
|
||||
properties.id = parcel->readInt32();
|
||||
properties.toolType = parcel->readInt32();
|
||||
}
|
||||
|
||||
while (sampleCount-- > 0) {
|
||||
mSampleEventTimes.push(parcel->readInt64());
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
mSamplePointerCoords.push();
|
||||
status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MotionEvent::writeToParcel(Parcel* parcel) const {
|
||||
size_t pointerCount = mPointerProperties.size();
|
||||
size_t sampleCount = mSampleEventTimes.size();
|
||||
|
||||
parcel->writeInt32(pointerCount);
|
||||
parcel->writeInt32(sampleCount);
|
||||
|
||||
parcel->writeInt32(mDeviceId);
|
||||
parcel->writeInt32(mSource);
|
||||
parcel->writeInt32(mAction);
|
||||
parcel->writeInt32(mFlags);
|
||||
parcel->writeInt32(mEdgeFlags);
|
||||
parcel->writeInt32(mMetaState);
|
||||
parcel->writeInt32(mButtonState);
|
||||
parcel->writeFloat(mXOffset);
|
||||
parcel->writeFloat(mYOffset);
|
||||
parcel->writeFloat(mXPrecision);
|
||||
parcel->writeFloat(mYPrecision);
|
||||
parcel->writeInt64(mDownTime);
|
||||
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
const PointerProperties& properties = mPointerProperties.itemAt(i);
|
||||
parcel->writeInt32(properties.id);
|
||||
parcel->writeInt32(properties.toolType);
|
||||
}
|
||||
|
||||
const PointerCoords* pc = mSamplePointerCoords.array();
|
||||
for (size_t h = 0; h < sampleCount; h++) {
|
||||
parcel->writeInt64(mSampleEventTimes.itemAt(h));
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
status_t status = (pc++)->writeToParcel(parcel);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
|
||||
if (source & AINPUT_SOURCE_CLASS_POINTER) {
|
||||
// Specifically excludes HOVER_MOVE and SCROLL.
|
||||
switch (action & AMOTION_EVENT_ACTION_MASK) {
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_MOVE:
|
||||
case AMOTION_EVENT_ACTION_UP:
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN:
|
||||
case AMOTION_EVENT_ACTION_POINTER_UP:
|
||||
case AMOTION_EVENT_ACTION_CANCEL:
|
||||
case AMOTION_EVENT_ACTION_OUTSIDE:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// --- PooledInputEventFactory ---
|
||||
|
||||
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
|
||||
mMaxPoolSize(maxPoolSize) {
|
||||
}
|
||||
|
||||
PooledInputEventFactory::~PooledInputEventFactory() {
|
||||
for (size_t i = 0; i < mKeyEventPool.size(); i++) {
|
||||
delete mKeyEventPool.itemAt(i);
|
||||
}
|
||||
for (size_t i = 0; i < mMotionEventPool.size(); i++) {
|
||||
delete mMotionEventPool.itemAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent* PooledInputEventFactory::createKeyEvent() {
|
||||
if (!mKeyEventPool.isEmpty()) {
|
||||
KeyEvent* event = mKeyEventPool.top();
|
||||
mKeyEventPool.pop();
|
||||
return event;
|
||||
}
|
||||
return new KeyEvent();
|
||||
}
|
||||
|
||||
MotionEvent* PooledInputEventFactory::createMotionEvent() {
|
||||
if (!mMotionEventPool.isEmpty()) {
|
||||
MotionEvent* event = mMotionEventPool.top();
|
||||
mMotionEventPool.pop();
|
||||
return event;
|
||||
}
|
||||
return new MotionEvent();
|
||||
}
|
||||
|
||||
void PooledInputEventFactory::recycle(InputEvent* event) {
|
||||
switch (event->getType()) {
|
||||
case AINPUT_EVENT_TYPE_KEY:
|
||||
if (mKeyEventPool.size() < mMaxPoolSize) {
|
||||
mKeyEventPool.push(static_cast<KeyEvent*>(event));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case AINPUT_EVENT_TYPE_MOTION:
|
||||
if (mMotionEventPool.size() < mMaxPoolSize) {
|
||||
mMotionEventPool.push(static_cast<MotionEvent*>(event));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
delete event;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,622 +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 _ANDROIDFW_INPUT_H
|
||||
#define _ANDROIDFW_INPUT_H
|
||||
|
||||
/**
|
||||
* Native input event structures.
|
||||
*/
|
||||
|
||||
#include "android_input.h"
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class SkMatrix;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Additional private constants not defined in ndk/ui/input.h.
|
||||
*/
|
||||
enum {
|
||||
/* Signifies that the key is being predispatched */
|
||||
AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
|
||||
|
||||
/* Private control to determine when an app is tracking a key sequence. */
|
||||
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
|
||||
|
||||
/* Key event is inconsistent with previously sent key events. */
|
||||
AKEY_EVENT_FLAG_TAINTED = 0x80000000,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Motion event is inconsistent with previously sent motion events. */
|
||||
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Used when a motion event is not associated with any display.
|
||||
* Typically used for non-pointer events. */
|
||||
ADISPLAY_ID_NONE = -1,
|
||||
|
||||
/* The default display id. */
|
||||
ADISPLAY_ID_DEFAULT = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Indicates that an input device has switches.
|
||||
* This input source flag is hidden from the API because switches are only used by the system
|
||||
* and applications have no way to interact with them.
|
||||
*/
|
||||
AINPUT_SOURCE_SWITCH = 0x80000000,
|
||||
};
|
||||
|
||||
/*
|
||||
* SystemUiVisibility constants from View.
|
||||
*/
|
||||
enum {
|
||||
ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
|
||||
ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
|
||||
};
|
||||
|
||||
/*
|
||||
* Maximum number of pointers supported per motion event.
|
||||
* Smallest number of pointers is 1.
|
||||
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
|
||||
* will occasionally emit 11. There is not much harm making this constant bigger.)
|
||||
*/
|
||||
#define MAX_POINTERS 16
|
||||
|
||||
/*
|
||||
* Maximum pointer id value supported in a motion event.
|
||||
* Smallest pointer id is 0.
|
||||
* (This is limited by our use of BitSet32 to track pointer assignments.)
|
||||
*/
|
||||
#define MAX_POINTER_ID 31
|
||||
|
||||
/*
|
||||
* Declare a concrete type for the NDK's input event forward declaration.
|
||||
*/
|
||||
struct AInputEvent {
|
||||
virtual ~AInputEvent() { }
|
||||
};
|
||||
|
||||
/*
|
||||
* Declare a concrete type for the NDK's input device forward declaration.
|
||||
*/
|
||||
struct AInputDevice {
|
||||
virtual ~AInputDevice() { }
|
||||
};
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class Parcel;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Flags that flow alongside events in the input dispatch system to help with certain
|
||||
* policy decisions such as waking from device sleep.
|
||||
*
|
||||
* These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java.
|
||||
*/
|
||||
enum {
|
||||
/* These flags originate in RawEvents and are generally set in the key map.
|
||||
* NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
|
||||
|
||||
POLICY_FLAG_WAKE = 0x00000001,
|
||||
POLICY_FLAG_WAKE_DROPPED = 0x00000002,
|
||||
POLICY_FLAG_SHIFT = 0x00000004,
|
||||
POLICY_FLAG_CAPS_LOCK = 0x00000008,
|
||||
POLICY_FLAG_ALT = 0x00000010,
|
||||
POLICY_FLAG_ALT_GR = 0x00000020,
|
||||
POLICY_FLAG_MENU = 0x00000040,
|
||||
POLICY_FLAG_LAUNCHER = 0x00000080,
|
||||
POLICY_FLAG_VIRTUAL = 0x00000100,
|
||||
POLICY_FLAG_FUNCTION = 0x00000200,
|
||||
|
||||
POLICY_FLAG_RAW_MASK = 0x0000ffff,
|
||||
|
||||
/* These flags are set by the input dispatcher. */
|
||||
|
||||
// Indicates that the input event was injected.
|
||||
POLICY_FLAG_INJECTED = 0x01000000,
|
||||
|
||||
// Indicates that the input event is from a trusted source such as a directly attached
|
||||
// input device or an application with system-wide event injection permission.
|
||||
POLICY_FLAG_TRUSTED = 0x02000000,
|
||||
|
||||
// Indicates that the input event has passed through an input filter.
|
||||
POLICY_FLAG_FILTERED = 0x04000000,
|
||||
|
||||
// Disables automatic key repeating behavior.
|
||||
POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,
|
||||
|
||||
/* These flags are set by the input reader policy as it intercepts each event. */
|
||||
|
||||
// Indicates that the screen was off when the event was received and the event
|
||||
// should wake the device.
|
||||
POLICY_FLAG_WOKE_HERE = 0x10000000,
|
||||
|
||||
// Indicates that the screen was dim when the event was received and the event
|
||||
// should brighten the device.
|
||||
POLICY_FLAG_BRIGHT_HERE = 0x20000000,
|
||||
|
||||
// Indicates that the event should be dispatched to applications.
|
||||
// The input event should still be sent to the InputDispatcher so that it can see all
|
||||
// input events received include those that it will not deliver.
|
||||
POLICY_FLAG_PASS_TO_USER = 0x40000000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Pointer coordinate data.
|
||||
*/
|
||||
struct PointerCoords {
|
||||
enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
|
||||
|
||||
// Bitfield of axes that are present in this structure.
|
||||
uint64_t bits;
|
||||
|
||||
// Values of axes that are stored in this structure packed in order by axis id
|
||||
// for each axis that is present in the structure according to 'bits'.
|
||||
float values[MAX_AXES];
|
||||
|
||||
inline void clear() {
|
||||
bits = 0;
|
||||
}
|
||||
|
||||
float getAxisValue(int32_t axis) const;
|
||||
status_t setAxisValue(int32_t axis, float value);
|
||||
|
||||
void scale(float scale);
|
||||
|
||||
inline float getX() const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_X);
|
||||
}
|
||||
|
||||
inline float getY() const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_Y);
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
status_t readFromParcel(Parcel* parcel);
|
||||
status_t writeToParcel(Parcel* parcel) const;
|
||||
#endif
|
||||
|
||||
bool operator==(const PointerCoords& other) const;
|
||||
inline bool operator!=(const PointerCoords& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void copyFrom(const PointerCoords& other);
|
||||
|
||||
private:
|
||||
void tooManyAxes(int axis);
|
||||
};
|
||||
|
||||
/*
|
||||
* Pointer property data.
|
||||
*/
|
||||
struct PointerProperties {
|
||||
// The id of the pointer.
|
||||
int32_t id;
|
||||
|
||||
// The pointer tool type.
|
||||
int32_t toolType;
|
||||
|
||||
inline void clear() {
|
||||
id = -1;
|
||||
toolType = 0;
|
||||
}
|
||||
|
||||
bool operator==(const PointerProperties& other) const;
|
||||
inline bool operator!=(const PointerProperties& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void copyFrom(const PointerProperties& other);
|
||||
};
|
||||
|
||||
/*
|
||||
* Input events.
|
||||
*/
|
||||
class InputEvent : public AInputEvent {
|
||||
public:
|
||||
virtual ~InputEvent() { }
|
||||
|
||||
virtual int32_t getType() const = 0;
|
||||
|
||||
inline int32_t getDeviceId() const { return mDeviceId; }
|
||||
|
||||
inline int32_t getSource() const { return mSource; }
|
||||
|
||||
inline void setSource(int32_t source) { mSource = source; }
|
||||
|
||||
protected:
|
||||
void initialize(int32_t deviceId, int32_t source);
|
||||
void initialize(const InputEvent& from);
|
||||
|
||||
int32_t mDeviceId;
|
||||
int32_t mSource;
|
||||
};
|
||||
|
||||
/*
|
||||
* Key events.
|
||||
*/
|
||||
class KeyEvent : public InputEvent {
|
||||
public:
|
||||
virtual ~KeyEvent() { }
|
||||
|
||||
virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; }
|
||||
|
||||
inline int32_t getAction() const { return mAction; }
|
||||
|
||||
inline int32_t getFlags() const { return mFlags; }
|
||||
|
||||
inline void setFlags(int32_t flags) { mFlags = flags; }
|
||||
|
||||
inline int32_t getKeyCode() const { return mKeyCode; }
|
||||
|
||||
inline int32_t getScanCode() const { return mScanCode; }
|
||||
|
||||
inline int32_t getMetaState() const { return mMetaState; }
|
||||
|
||||
inline int32_t getRepeatCount() const { return mRepeatCount; }
|
||||
|
||||
inline nsecs_t getDownTime() const { return mDownTime; }
|
||||
|
||||
inline nsecs_t getEventTime() const { return mEventTime; }
|
||||
|
||||
// Return true if this event may have a default action implementation.
|
||||
static bool hasDefaultAction(int32_t keyCode);
|
||||
bool hasDefaultAction() const;
|
||||
|
||||
// Return true if this event represents a system key.
|
||||
static bool isSystemKey(int32_t keyCode);
|
||||
bool isSystemKey() const;
|
||||
|
||||
void initialize(
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t keyCode,
|
||||
int32_t scanCode,
|
||||
int32_t metaState,
|
||||
int32_t repeatCount,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime);
|
||||
void initialize(const KeyEvent& from);
|
||||
|
||||
protected:
|
||||
int32_t mAction;
|
||||
int32_t mFlags;
|
||||
int32_t mKeyCode;
|
||||
int32_t mScanCode;
|
||||
int32_t mMetaState;
|
||||
int32_t mRepeatCount;
|
||||
nsecs_t mDownTime;
|
||||
nsecs_t mEventTime;
|
||||
};
|
||||
|
||||
/*
|
||||
* Motion events.
|
||||
*/
|
||||
class MotionEvent : public InputEvent {
|
||||
public:
|
||||
virtual ~MotionEvent() { }
|
||||
|
||||
virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; }
|
||||
|
||||
inline int32_t getAction() const { return mAction; }
|
||||
|
||||
inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
|
||||
|
||||
inline int32_t getActionIndex() const {
|
||||
return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
|
||||
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
||||
}
|
||||
|
||||
inline void setAction(int32_t action) { mAction = action; }
|
||||
|
||||
inline int32_t getFlags() const { return mFlags; }
|
||||
|
||||
inline void setFlags(int32_t flags) { mFlags = flags; }
|
||||
|
||||
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
|
||||
|
||||
inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
|
||||
|
||||
inline int32_t getMetaState() const { return mMetaState; }
|
||||
|
||||
inline void setMetaState(int32_t metaState) { mMetaState = metaState; }
|
||||
|
||||
inline int32_t getButtonState() const { return mButtonState; }
|
||||
|
||||
inline float getXOffset() const { return mXOffset; }
|
||||
|
||||
inline float getYOffset() const { return mYOffset; }
|
||||
|
||||
inline float getXPrecision() const { return mXPrecision; }
|
||||
|
||||
inline float getYPrecision() const { return mYPrecision; }
|
||||
|
||||
inline nsecs_t getDownTime() const { return mDownTime; }
|
||||
|
||||
inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; }
|
||||
|
||||
inline size_t getPointerCount() const { return mPointerProperties.size(); }
|
||||
|
||||
inline const PointerProperties* getPointerProperties(size_t pointerIndex) const {
|
||||
return &mPointerProperties[pointerIndex];
|
||||
}
|
||||
|
||||
inline int32_t getPointerId(size_t pointerIndex) const {
|
||||
return mPointerProperties[pointerIndex].id;
|
||||
}
|
||||
|
||||
inline int32_t getToolType(size_t pointerIndex) const {
|
||||
return mPointerProperties[pointerIndex].toolType;
|
||||
}
|
||||
|
||||
inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
|
||||
|
||||
const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
|
||||
|
||||
float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
|
||||
|
||||
inline float getRawX(size_t pointerIndex) const {
|
||||
return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getRawY(size_t pointerIndex) const {
|
||||
return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
|
||||
}
|
||||
|
||||
float getAxisValue(int32_t axis, size_t pointerIndex) const;
|
||||
|
||||
inline float getX(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getY(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getPressure(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getSize(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getTouchMajor(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getTouchMinor(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getToolMajor(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getToolMinor(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex);
|
||||
}
|
||||
|
||||
inline float getOrientation(size_t pointerIndex) const {
|
||||
return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex);
|
||||
}
|
||||
|
||||
inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
|
||||
|
||||
inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const {
|
||||
return mSampleEventTimes[historicalIndex];
|
||||
}
|
||||
|
||||
const PointerCoords* getHistoricalRawPointerCoords(
|
||||
size_t pointerIndex, size_t historicalIndex) const;
|
||||
|
||||
float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
|
||||
size_t historicalIndex) const;
|
||||
|
||||
inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalRawAxisValue(
|
||||
AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalRawAxisValue(
|
||||
AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const;
|
||||
|
||||
inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const {
|
||||
return getHistoricalAxisValue(
|
||||
AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
|
||||
}
|
||||
|
||||
ssize_t findPointerIndex(int32_t pointerId) const;
|
||||
|
||||
void initialize(
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t edgeFlags,
|
||||
int32_t metaState,
|
||||
int32_t buttonState,
|
||||
float xOffset,
|
||||
float yOffset,
|
||||
float xPrecision,
|
||||
float yPrecision,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime,
|
||||
size_t pointerCount,
|
||||
const PointerProperties* pointerProperties,
|
||||
const PointerCoords* pointerCoords);
|
||||
|
||||
void copyFrom(const MotionEvent* other, bool keepHistory);
|
||||
|
||||
void addSample(
|
||||
nsecs_t eventTime,
|
||||
const PointerCoords* pointerCoords);
|
||||
|
||||
void offsetLocation(float xOffset, float yOffset);
|
||||
|
||||
void scale(float scaleFactor);
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
void transform(const SkMatrix* matrix);
|
||||
|
||||
status_t readFromParcel(Parcel* parcel);
|
||||
status_t writeToParcel(Parcel* parcel) const;
|
||||
#endif
|
||||
|
||||
static bool isTouchEvent(int32_t source, int32_t action);
|
||||
inline bool isTouchEvent() const {
|
||||
return isTouchEvent(mSource, mAction);
|
||||
}
|
||||
|
||||
// Low-level accessors.
|
||||
inline const PointerProperties* getPointerProperties() const {
|
||||
return mPointerProperties.array();
|
||||
}
|
||||
inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
|
||||
inline const PointerCoords* getSamplePointerCoords() const {
|
||||
return mSamplePointerCoords.array();
|
||||
}
|
||||
|
||||
protected:
|
||||
int32_t mAction;
|
||||
int32_t mFlags;
|
||||
int32_t mEdgeFlags;
|
||||
int32_t mMetaState;
|
||||
int32_t mButtonState;
|
||||
float mXOffset;
|
||||
float mYOffset;
|
||||
float mXPrecision;
|
||||
float mYPrecision;
|
||||
nsecs_t mDownTime;
|
||||
Vector<PointerProperties> mPointerProperties;
|
||||
Vector<nsecs_t> mSampleEventTimes;
|
||||
Vector<PointerCoords> mSamplePointerCoords;
|
||||
};
|
||||
|
||||
/*
|
||||
* Input event factory.
|
||||
*/
|
||||
class InputEventFactoryInterface {
|
||||
protected:
|
||||
virtual ~InputEventFactoryInterface() { }
|
||||
|
||||
public:
|
||||
InputEventFactoryInterface() { }
|
||||
|
||||
virtual KeyEvent* createKeyEvent() = 0;
|
||||
virtual MotionEvent* createMotionEvent() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* A simple input event factory implementation that uses a single preallocated instance
|
||||
* of each type of input event that are reused for each request.
|
||||
*/
|
||||
class PreallocatedInputEventFactory : public InputEventFactoryInterface {
|
||||
public:
|
||||
PreallocatedInputEventFactory() { }
|
||||
virtual ~PreallocatedInputEventFactory() { }
|
||||
|
||||
virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
|
||||
virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
|
||||
|
||||
private:
|
||||
KeyEvent mKeyEvent;
|
||||
MotionEvent mMotionEvent;
|
||||
};
|
||||
|
||||
/*
|
||||
* An input event factory implementation that maintains a pool of input events.
|
||||
*/
|
||||
class PooledInputEventFactory : public InputEventFactoryInterface {
|
||||
public:
|
||||
PooledInputEventFactory(size_t maxPoolSize = 20);
|
||||
virtual ~PooledInputEventFactory();
|
||||
|
||||
virtual KeyEvent* createKeyEvent();
|
||||
virtual MotionEvent* createMotionEvent();
|
||||
|
||||
void recycle(InputEvent* event);
|
||||
|
||||
private:
|
||||
const size_t mMaxPoolSize;
|
||||
|
||||
Vector<KeyEvent*> mKeyEventPool;
|
||||
Vector<MotionEvent*> mMotionEventPool;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_INPUT_H
|
|
@ -1,42 +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 "InputApplication"
|
||||
|
||||
#include "InputApplication.h"
|
||||
|
||||
#include "cutils_log.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- InputApplicationHandle ---
|
||||
|
||||
InputApplicationHandle::InputApplicationHandle() :
|
||||
mInfo(NULL) {
|
||||
}
|
||||
|
||||
InputApplicationHandle::~InputApplicationHandle() {
|
||||
delete mInfo;
|
||||
}
|
||||
|
||||
void InputApplicationHandle::releaseInfo() {
|
||||
if (mInfo) {
|
||||
delete mInfo;
|
||||
mInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -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.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
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "InputDevice"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "InputDevice.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* CONFIGURATION_FILE_DIR[] = {
|
||||
"idc/",
|
||||
"keylayout/",
|
||||
"keychars/",
|
||||
};
|
||||
|
||||
static const char* CONFIGURATION_FILE_EXTENSION[] = {
|
||||
".idc",
|
||||
".kl",
|
||||
".kcm",
|
||||
};
|
||||
|
||||
static bool isValidNameChar(char ch) {
|
||||
return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
|
||||
}
|
||||
|
||||
static void appendInputDeviceConfigurationFileRelativePath(String8& path,
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
path.append(CONFIGURATION_FILE_DIR[type]);
|
||||
for (size_t i = 0; i < name.length(); i++) {
|
||||
char ch = name[i];
|
||||
if (!isValidNameChar(ch)) {
|
||||
ch = '_';
|
||||
}
|
||||
path.append(&ch, 1);
|
||||
}
|
||||
path.append(CONFIGURATION_FILE_EXTENSION[type]);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type) {
|
||||
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
|
||||
if (deviceIdentifier.version != 0) {
|
||||
// Try vendor product version.
|
||||
String8 versionPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product,
|
||||
deviceIdentifier.version),
|
||||
type));
|
||||
if (!versionPath.isEmpty()) {
|
||||
return versionPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try vendor product.
|
||||
String8 productPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product),
|
||||
type));
|
||||
if (!productPath.isEmpty()) {
|
||||
return productPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try device name.
|
||||
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
// Search system repository.
|
||||
String8 path;
|
||||
path.setTo(getenv("ANDROID_ROOT"));
|
||||
path.append("/usr/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Search user repository.
|
||||
// TODO Should only look here if not in safe mode.
|
||||
path.setTo(getenv("ANDROID_DATA"));
|
||||
path.append("/system/devices/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Not found.
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
|
||||
name.string(), type);
|
||||
#endif
|
||||
return String8();
|
||||
}
|
||||
|
||||
|
||||
// --- InputDeviceInfo ---
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo() {
|
||||
initialize(-1, -1, InputDeviceIdentifier(), String8(), false);
|
||||
}
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
|
||||
mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier),
|
||||
mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources),
|
||||
mKeyboardType(other.mKeyboardType),
|
||||
mKeyCharacterMap(other.mKeyCharacterMap),
|
||||
mHasVibrator(other.mHasVibrator),
|
||||
mMotionRanges(other.mMotionRanges) {
|
||||
}
|
||||
|
||||
InputDeviceInfo::~InputDeviceInfo() {
|
||||
}
|
||||
|
||||
void InputDeviceInfo::initialize(int32_t id, int32_t generation,
|
||||
const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) {
|
||||
mId = id;
|
||||
mGeneration = generation;
|
||||
mIdentifier = identifier;
|
||||
mAlias = alias;
|
||||
mIsExternal = isExternal;
|
||||
mSources = 0;
|
||||
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
|
||||
mHasVibrator = false;
|
||||
mMotionRanges.clear();
|
||||
}
|
||||
|
||||
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
|
||||
int32_t axis, uint32_t source) const {
|
||||
size_t numRanges = mMotionRanges.size();
|
||||
for (size_t i = 0; i < numRanges; i++) {
|
||||
const MotionRange& range = mMotionRanges.itemAt(i);
|
||||
if (range.axis == axis && range.source == source) {
|
||||
return ⦥
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addSource(uint32_t source) {
|
||||
mSources |= source;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
|
||||
float flat, float fuzz, float resolution) {
|
||||
MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
|
||||
mMotionRanges.add(range);
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(const MotionRange& range) {
|
||||
mMotionRanges.add(range);
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* 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 _ANDROIDFW_INPUT_DEVICE_H
|
||||
#define _ANDROIDFW_INPUT_DEVICE_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Identifies a device.
|
||||
*/
|
||||
struct InputDeviceIdentifier {
|
||||
inline InputDeviceIdentifier() :
|
||||
bus(0), vendor(0), product(0), version(0) {
|
||||
}
|
||||
|
||||
// Information provided by the kernel.
|
||||
String8 name;
|
||||
String8 location;
|
||||
String8 uniqueId;
|
||||
uint16_t bus;
|
||||
uint16_t vendor;
|
||||
uint16_t product;
|
||||
uint16_t version;
|
||||
|
||||
// A composite input device descriptor string that uniquely identifies the device
|
||||
// even across reboots or reconnections. The value of this field is used by
|
||||
// upper layers of the input system to associate settings with individual devices.
|
||||
// It is hashed from whatever kernel provided information is available.
|
||||
// Ideally, the way this value is computed should not change between Android releases
|
||||
// because that would invalidate persistent settings that rely on it.
|
||||
String8 descriptor;
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes the characteristics and capabilities of an input device.
|
||||
*/
|
||||
class InputDeviceInfo {
|
||||
public:
|
||||
InputDeviceInfo();
|
||||
InputDeviceInfo(const InputDeviceInfo& other);
|
||||
~InputDeviceInfo();
|
||||
|
||||
struct MotionRange {
|
||||
int32_t axis;
|
||||
uint32_t source;
|
||||
float min;
|
||||
float max;
|
||||
float flat;
|
||||
float fuzz;
|
||||
float resolution;
|
||||
};
|
||||
|
||||
void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
|
||||
const String8& alias, bool isExternal);
|
||||
|
||||
inline int32_t getId() const { return mId; }
|
||||
inline int32_t getGeneration() const { return mGeneration; }
|
||||
inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
|
||||
inline const String8& getAlias() const { return mAlias; }
|
||||
inline const String8& getDisplayName() const {
|
||||
return mAlias.isEmpty() ? mIdentifier.name : mAlias;
|
||||
}
|
||||
inline bool isExternal() const { return mIsExternal; }
|
||||
inline uint32_t getSources() const { return mSources; }
|
||||
|
||||
const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
|
||||
|
||||
void addSource(uint32_t source);
|
||||
void addMotionRange(int32_t axis, uint32_t source,
|
||||
float min, float max, float flat, float fuzz, float resolution);
|
||||
void addMotionRange(const MotionRange& range);
|
||||
|
||||
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
|
||||
inline int32_t getKeyboardType() const { return mKeyboardType; }
|
||||
|
||||
inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
|
||||
mKeyCharacterMap = value;
|
||||
}
|
||||
|
||||
inline sp<KeyCharacterMap> getKeyCharacterMap() const {
|
||||
return mKeyCharacterMap;
|
||||
}
|
||||
|
||||
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
|
||||
inline bool hasVibrator() const { return mHasVibrator; }
|
||||
|
||||
inline const Vector<MotionRange>& getMotionRanges() const {
|
||||
return mMotionRanges;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mId;
|
||||
int32_t mGeneration;
|
||||
InputDeviceIdentifier mIdentifier;
|
||||
String8 mAlias;
|
||||
bool mIsExternal;
|
||||
uint32_t mSources;
|
||||
int32_t mKeyboardType;
|
||||
sp<KeyCharacterMap> mKeyCharacterMap;
|
||||
bool mHasVibrator;
|
||||
|
||||
Vector<MotionRange> mMotionRanges;
|
||||
};
|
||||
|
||||
/* Types of input device configuration files. */
|
||||
enum InputDeviceConfigurationFileType {
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The device identifier is used to construct several default configuration file
|
||||
* names to try based on the device name, vendor, product, and version.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type);
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The name is case-sensitive and is used to construct the filename to resolve.
|
||||
* All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_INPUT_DEVICE_H
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
|
@ -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.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
|
|
@ -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
|
|
@ -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.h"
|
||||
#include "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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,957 +0,0 @@
|
|||
//
|
||||
// Copyright 2010 The Android Open Source Project
|
||||
//
|
||||
// Provides a shared memory transport for input events.
|
||||
//
|
||||
#define LOG_TAG "InputTransport"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about channel messages (send message, receive message)
|
||||
#define DEBUG_CHANNEL_MESSAGES 0
|
||||
|
||||
// Log debug messages whenever InputChannel objects are created/destroyed
|
||||
#define DEBUG_CHANNEL_LIFECYCLE 0
|
||||
|
||||
// Log debug messages about transport actions
|
||||
#define DEBUG_TRANSPORT_ACTIONS 0
|
||||
|
||||
// Log debug messages about touch event resampling
|
||||
#define DEBUG_RESAMPLING 0
|
||||
|
||||
|
||||
#include "cutils_log.h"
|
||||
#include <cutils/properties.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include "InputTransport.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
// Socket buffer size. The default is typically about 128KB, which is much larger than
|
||||
// we really need. So we make it smaller. It just needs to be big enough to hold
|
||||
// a few dozen large multi-finger motion events in the case where an application gets
|
||||
// behind processing touches.
|
||||
static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
// Nanoseconds per milliseconds.
|
||||
static const nsecs_t NANOS_PER_MS = 1000000;
|
||||
|
||||
// Latency added during resampling. A few milliseconds doesn't hurt much but
|
||||
// reduces the impact of mispredicted touch positions.
|
||||
static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS;
|
||||
|
||||
// Minimum time difference between consecutive samples before attempting to resample.
|
||||
static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS;
|
||||
|
||||
// Maximum time to predict forward from the last known state, to avoid predicting too
|
||||
// far into the future. This time is further bounded by 50% of the last time delta.
|
||||
static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
|
||||
|
||||
template<typename T>
|
||||
inline static T min(const T& a, const T& b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
inline static float lerp(float a, float b, float alpha) {
|
||||
return a + alpha * (b - a);
|
||||
}
|
||||
|
||||
// --- InputMessage ---
|
||||
|
||||
bool InputMessage::isValid(size_t actualSize) const {
|
||||
if (size() == actualSize) {
|
||||
switch (header.type) {
|
||||
case TYPE_KEY:
|
||||
return true;
|
||||
case TYPE_MOTION:
|
||||
return body.motion.pointerCount > 0
|
||||
&& body.motion.pointerCount <= MAX_POINTERS;
|
||||
case TYPE_FINISHED:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t InputMessage::size() const {
|
||||
switch (header.type) {
|
||||
case TYPE_KEY:
|
||||
return sizeof(Header) + body.key.size();
|
||||
case TYPE_MOTION:
|
||||
return sizeof(Header) + body.motion.size();
|
||||
case TYPE_FINISHED:
|
||||
return sizeof(Header) + body.finished.size();
|
||||
}
|
||||
return sizeof(Header);
|
||||
}
|
||||
|
||||
|
||||
// --- InputChannel ---
|
||||
|
||||
InputChannel::InputChannel(const String8& name, int fd) :
|
||||
mName(name), mFd(fd) {
|
||||
#if DEBUG_CHANNEL_LIFECYCLE
|
||||
ALOGD("Input channel constructed: name='%s', fd=%d",
|
||||
mName.string(), fd);
|
||||
#endif
|
||||
|
||||
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
|
||||
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
|
||||
"non-blocking. errno=%d", mName.string(), errno);
|
||||
}
|
||||
|
||||
InputChannel::~InputChannel() {
|
||||
#if DEBUG_CHANNEL_LIFECYCLE
|
||||
ALOGD("Input channel destroyed: name='%s', fd=%d",
|
||||
mName.string(), mFd);
|
||||
#endif
|
||||
|
||||
::close(mFd);
|
||||
}
|
||||
|
||||
status_t InputChannel::openInputChannelPair(const String8& name,
|
||||
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
|
||||
status_t result = -errno;
|
||||
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
|
||||
name.string(), errno);
|
||||
outServerChannel.clear();
|
||||
outClientChannel.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
int bufferSize = SOCKET_BUFFER_SIZE;
|
||||
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
|
||||
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
|
||||
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
|
||||
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
|
||||
|
||||
String8 serverChannelName = name;
|
||||
serverChannelName.append(" (server)");
|
||||
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
|
||||
|
||||
String8 clientChannelName = name;
|
||||
clientChannelName.append(" (client)");
|
||||
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputChannel::sendMessage(const InputMessage* msg) {
|
||||
size_t msgLength = msg->size();
|
||||
ssize_t nWrite;
|
||||
do {
|
||||
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||
} while (nWrite == -1 && errno == EINTR);
|
||||
|
||||
if (nWrite < 0) {
|
||||
int error = errno;
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
|
||||
msg->header.type, error);
|
||||
#endif
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
if (error == EPIPE || error == ENOTCONN) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return -error;
|
||||
}
|
||||
|
||||
if (size_t(nWrite) != msgLength) {
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
|
||||
mName.string(), msg->header.type);
|
||||
#endif
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputChannel::receiveMessage(InputMessage* msg) {
|
||||
ssize_t nRead;
|
||||
do {
|
||||
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
|
||||
} while (nRead == -1 && errno == EINTR);
|
||||
|
||||
if (nRead < 0) {
|
||||
int error = errno;
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
|
||||
#endif
|
||||
if (error == EAGAIN || error == EWOULDBLOCK) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
if (error == EPIPE || error == ENOTCONN) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return -error;
|
||||
}
|
||||
|
||||
if (nRead == 0) { // check for EOF
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
|
||||
#endif
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
if (!msg->isValid(nRead)) {
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ received invalid message", mName.string());
|
||||
#endif
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if DEBUG_CHANNEL_MESSAGES
|
||||
ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
sp<InputChannel> InputChannel::dup() const {
|
||||
int fd = ::dup(getFd());
|
||||
return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
|
||||
}
|
||||
|
||||
|
||||
// --- InputPublisher ---
|
||||
|
||||
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
|
||||
mChannel(channel) {
|
||||
}
|
||||
|
||||
InputPublisher::~InputPublisher() {
|
||||
}
|
||||
|
||||
status_t InputPublisher::publishKeyEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t keyCode,
|
||||
int32_t scanCode,
|
||||
int32_t metaState,
|
||||
int32_t repeatCount,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
|
||||
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
|
||||
"downTime=%lld, eventTime=%lld",
|
||||
mChannel->getName().string(), seq,
|
||||
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
|
||||
downTime, eventTime);
|
||||
#endif
|
||||
|
||||
if (!seq) {
|
||||
ALOGE("Attempted to publish a key event with sequence number 0.");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_KEY;
|
||||
msg.body.key.seq = seq;
|
||||
msg.body.key.deviceId = deviceId;
|
||||
msg.body.key.source = source;
|
||||
msg.body.key.action = action;
|
||||
msg.body.key.flags = flags;
|
||||
msg.body.key.keyCode = keyCode;
|
||||
msg.body.key.scanCode = scanCode;
|
||||
msg.body.key.metaState = metaState;
|
||||
msg.body.key.repeatCount = repeatCount;
|
||||
msg.body.key.downTime = downTime;
|
||||
msg.body.key.eventTime = eventTime;
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
status_t InputPublisher::publishMotionEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t edgeFlags,
|
||||
int32_t metaState,
|
||||
int32_t buttonState,
|
||||
float xOffset,
|
||||
float yOffset,
|
||||
float xPrecision,
|
||||
float yPrecision,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime,
|
||||
size_t pointerCount,
|
||||
const PointerProperties* pointerProperties,
|
||||
const PointerCoords* pointerCoords) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
|
||||
"action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
|
||||
"xOffset=%f, yOffset=%f, "
|
||||
"xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
|
||||
"pointerCount=%d",
|
||||
mChannel->getName().string(), seq,
|
||||
deviceId, source, action, flags, edgeFlags, metaState, buttonState,
|
||||
xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
|
||||
#endif
|
||||
|
||||
if (!seq) {
|
||||
ALOGE("Attempted to publish a motion event with sequence number 0.");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (pointerCount > MAX_POINTERS || pointerCount < 1) {
|
||||
ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
|
||||
mChannel->getName().string(), pointerCount);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_MOTION;
|
||||
msg.body.motion.seq = seq;
|
||||
msg.body.motion.deviceId = deviceId;
|
||||
msg.body.motion.source = source;
|
||||
msg.body.motion.action = action;
|
||||
msg.body.motion.flags = flags;
|
||||
msg.body.motion.edgeFlags = edgeFlags;
|
||||
msg.body.motion.metaState = metaState;
|
||||
msg.body.motion.buttonState = buttonState;
|
||||
msg.body.motion.xOffset = xOffset;
|
||||
msg.body.motion.yOffset = yOffset;
|
||||
msg.body.motion.xPrecision = xPrecision;
|
||||
msg.body.motion.yPrecision = yPrecision;
|
||||
msg.body.motion.downTime = downTime;
|
||||
msg.body.motion.eventTime = eventTime;
|
||||
msg.body.motion.pointerCount = pointerCount;
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
|
||||
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
|
||||
}
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
|
||||
InputMessage msg;
|
||||
status_t result = mChannel->receiveMessage(&msg);
|
||||
if (result) {
|
||||
*outSeq = 0;
|
||||
*outHandled = false;
|
||||
return result;
|
||||
}
|
||||
if (msg.header.type != InputMessage::TYPE_FINISHED) {
|
||||
ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
|
||||
mChannel->getName().string(), msg.header.type);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
*outSeq = msg.body.finished.seq;
|
||||
*outHandled = msg.body.finished.handled;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// --- InputConsumer ---
|
||||
|
||||
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
|
||||
mResampleTouch(isTouchResamplingEnabled()),
|
||||
mChannel(channel), mMsgDeferred(false) {
|
||||
}
|
||||
|
||||
InputConsumer::~InputConsumer() {
|
||||
}
|
||||
|
||||
bool InputConsumer::isTouchResamplingEnabled() {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
int length = property_get("debug.inputconsumer.resample", value, NULL);
|
||||
if (length > 0) {
|
||||
if (!strcmp("0", value)) {
|
||||
return false;
|
||||
}
|
||||
if (strcmp("1", value)) {
|
||||
ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. "
|
||||
"Use '1' or '0'.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
|
||||
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld",
|
||||
mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime);
|
||||
#endif
|
||||
|
||||
*outSeq = 0;
|
||||
*outEvent = NULL;
|
||||
|
||||
// Fetch the next input message.
|
||||
// Loop until an event can be returned or no additional events are received.
|
||||
while (!*outEvent) {
|
||||
if (mMsgDeferred) {
|
||||
// mMsg contains a valid input message from the previous call to consume
|
||||
// that has not yet been processed.
|
||||
mMsgDeferred = false;
|
||||
} else {
|
||||
// Receive a fresh message.
|
||||
status_t result = mChannel->receiveMessage(&mMsg);
|
||||
if (result) {
|
||||
// Consume the next batched event unless batches are being held for later.
|
||||
if (consumeBatches || result != WOULD_BLOCK) {
|
||||
result = consumeBatch(factory, frameTime, outSeq, outEvent);
|
||||
if (*outEvent) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
|
||||
mChannel->getName().string(), *outSeq);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mMsg.header.type) {
|
||||
case InputMessage::TYPE_KEY: {
|
||||
KeyEvent* keyEvent = factory->createKeyEvent();
|
||||
if (!keyEvent) return NO_MEMORY;
|
||||
|
||||
initializeKeyEvent(keyEvent, &mMsg);
|
||||
*outSeq = mMsg.body.key.seq;
|
||||
*outEvent = keyEvent;
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
|
||||
mChannel->getName().string(), *outSeq);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case AINPUT_EVENT_TYPE_MOTION: {
|
||||
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
|
||||
if (batchIndex >= 0) {
|
||||
Batch& batch = mBatches.editItemAt(batchIndex);
|
||||
if (canAddSample(batch, &mMsg)) {
|
||||
batch.samples.push(mMsg);
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ appended to batch event",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
break;
|
||||
} else {
|
||||
// We cannot append to the batch in progress, so we need to consume
|
||||
// the previous batch right now and defer the new message until later.
|
||||
mMsgDeferred = true;
|
||||
status_t result = consumeSamples(factory,
|
||||
batch, batch.samples.size(), outSeq, outEvent);
|
||||
mBatches.removeAt(batchIndex);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ consumed batch event and "
|
||||
"deferred current event, seq=%u",
|
||||
mChannel->getName().string(), *outSeq);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Start a new batch if needed.
|
||||
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
|
||||
|| mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
|
||||
mBatches.push();
|
||||
Batch& batch = mBatches.editTop();
|
||||
batch.samples.push(mMsg);
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ started batch event",
|
||||
mChannel->getName().string());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
MotionEvent* motionEvent = factory->createMotionEvent();
|
||||
if (! motionEvent) return NO_MEMORY;
|
||||
|
||||
updateTouchState(&mMsg);
|
||||
initializeMotionEvent(motionEvent, &mMsg);
|
||||
*outSeq = mMsg.body.motion.seq;
|
||||
*outEvent = motionEvent;
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
|
||||
mChannel->getName().string(), *outSeq);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
|
||||
mChannel->getName().string(), mMsg.header.type);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
|
||||
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
|
||||
status_t result;
|
||||
for (size_t i = mBatches.size(); i-- > 0; ) {
|
||||
Batch& batch = mBatches.editItemAt(i);
|
||||
if (frameTime < 0) {
|
||||
result = consumeSamples(factory, batch, batch.samples.size(),
|
||||
outSeq, outEvent);
|
||||
mBatches.removeAt(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY;
|
||||
ssize_t split = findSampleNoLaterThan(batch, sampleTime);
|
||||
if (split < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
|
||||
const InputMessage* next;
|
||||
if (batch.samples.isEmpty()) {
|
||||
mBatches.removeAt(i);
|
||||
next = NULL;
|
||||
} else {
|
||||
next = &batch.samples.itemAt(0);
|
||||
}
|
||||
if (!result) {
|
||||
resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
|
||||
status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
|
||||
Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
|
||||
MotionEvent* motionEvent = factory->createMotionEvent();
|
||||
if (! motionEvent) return NO_MEMORY;
|
||||
|
||||
uint32_t chain = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
InputMessage& msg = batch.samples.editItemAt(i);
|
||||
updateTouchState(&msg);
|
||||
if (i) {
|
||||
SeqChain seqChain;
|
||||
seqChain.seq = msg.body.motion.seq;
|
||||
seqChain.chain = chain;
|
||||
mSeqChains.push(seqChain);
|
||||
addSample(motionEvent, &msg);
|
||||
} else {
|
||||
initializeMotionEvent(motionEvent, &msg);
|
||||
}
|
||||
chain = msg.body.motion.seq;
|
||||
}
|
||||
batch.samples.removeItemsAt(0, count);
|
||||
|
||||
*outSeq = chain;
|
||||
*outEvent = motionEvent;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void InputConsumer::updateTouchState(InputMessage* msg) {
|
||||
if (!mResampleTouch ||
|
||||
!(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t deviceId = msg->body.motion.deviceId;
|
||||
int32_t source = msg->body.motion.source;
|
||||
nsecs_t eventTime = msg->body.motion.eventTime;
|
||||
|
||||
// Update the touch state history to incorporate the new input message.
|
||||
// If the message is in the past relative to the most recently produced resampled
|
||||
// touch, then use the resampled time and coordinates instead.
|
||||
switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) {
|
||||
case AMOTION_EVENT_ACTION_DOWN: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index < 0) {
|
||||
mTouchStates.push();
|
||||
index = mTouchStates.size() - 1;
|
||||
}
|
||||
TouchState& touchState = mTouchStates.editItemAt(index);
|
||||
touchState.initialize(deviceId, source);
|
||||
touchState.addHistory(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMOTION_EVENT_ACTION_MOVE: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index >= 0) {
|
||||
TouchState& touchState = mTouchStates.editItemAt(index);
|
||||
touchState.addHistory(msg);
|
||||
if (eventTime < touchState.lastResample.eventTime) {
|
||||
rewriteMessage(touchState, msg);
|
||||
} else {
|
||||
touchState.lastResample.idBits.clear();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index >= 0) {
|
||||
TouchState& touchState = mTouchStates.editItemAt(index);
|
||||
touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
|
||||
rewriteMessage(touchState, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AMOTION_EVENT_ACTION_POINTER_UP: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index >= 0) {
|
||||
TouchState& touchState = mTouchStates.editItemAt(index);
|
||||
rewriteMessage(touchState, msg);
|
||||
touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AMOTION_EVENT_ACTION_SCROLL: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index >= 0) {
|
||||
const TouchState& touchState = mTouchStates.itemAt(index);
|
||||
rewriteMessage(touchState, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AMOTION_EVENT_ACTION_UP:
|
||||
case AMOTION_EVENT_ACTION_CANCEL: {
|
||||
ssize_t index = findTouchState(deviceId, source);
|
||||
if (index >= 0) {
|
||||
const TouchState& touchState = mTouchStates.itemAt(index);
|
||||
rewriteMessage(touchState, msg);
|
||||
mTouchStates.removeAt(index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) {
|
||||
for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
|
||||
uint32_t id = msg->body.motion.pointers[i].properties.id;
|
||||
if (state.lastResample.idBits.hasBit(id)) {
|
||||
PointerCoords& msgCoords = msg->body.motion.pointers[i].coords;
|
||||
const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
|
||||
resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y),
|
||||
msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
|
||||
#endif
|
||||
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
|
||||
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
|
||||
const InputMessage* next) {
|
||||
if (!mResampleTouch
|
||||
|| !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER)
|
||||
|| event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
|
||||
if (index < 0) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, no touch state for device.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
TouchState& touchState = mTouchStates.editItemAt(index);
|
||||
if (touchState.historySize < 1) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, no history for device.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that the current sample has all of the pointers that need to be reported.
|
||||
const History* current = touchState.getHistory(0);
|
||||
size_t pointerCount = event->getPointerCount();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t id = event->getPointerId(i);
|
||||
if (!current->idBits.hasBit(id)) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, missing id %d", id);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the data to use for resampling.
|
||||
const History* other;
|
||||
History future;
|
||||
float alpha;
|
||||
if (next) {
|
||||
// Interpolate between current sample and future sample.
|
||||
// So current->eventTime <= sampleTime <= future.eventTime.
|
||||
future.initializeFrom(next);
|
||||
other = &future;
|
||||
nsecs_t delta = future.eventTime - current->eventTime;
|
||||
if (delta < RESAMPLE_MIN_DELTA) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, delta time is %lld ns.", delta);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
alpha = float(sampleTime - current->eventTime) / delta;
|
||||
} else if (touchState.historySize >= 2) {
|
||||
// Extrapolate future sample using current sample and past sample.
|
||||
// So other->eventTime <= current->eventTime <= sampleTime.
|
||||
other = touchState.getHistory(1);
|
||||
nsecs_t delta = current->eventTime - other->eventTime;
|
||||
if (delta < RESAMPLE_MIN_DELTA) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, delta time is %lld ns.", delta);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
|
||||
if (sampleTime > maxPredict) {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Sample time is too far in the future, adjusting prediction "
|
||||
"from %lld to %lld ns.",
|
||||
sampleTime - current->eventTime, maxPredict - current->eventTime);
|
||||
#endif
|
||||
sampleTime = maxPredict;
|
||||
}
|
||||
alpha = float(current->eventTime - sampleTime) / delta;
|
||||
} else {
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("Not resampled, insufficient data.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Resample touch coordinates.
|
||||
touchState.lastResample.eventTime = sampleTime;
|
||||
touchState.lastResample.idBits.clear();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t id = event->getPointerId(i);
|
||||
touchState.lastResample.idToIndex[id] = i;
|
||||
touchState.lastResample.idBits.markBit(id);
|
||||
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
|
||||
const PointerCoords& currentCoords = current->getPointerById(id);
|
||||
if (other->idBits.hasBit(id)
|
||||
&& shouldResampleTool(event->getToolType(i))) {
|
||||
const PointerCoords& otherCoords = other->getPointerById(id);
|
||||
resampledCoords.copyFrom(currentCoords);
|
||||
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
|
||||
lerp(currentCoords.getX(), otherCoords.getX(), alpha));
|
||||
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
|
||||
lerp(currentCoords.getY(), otherCoords.getY(), alpha));
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
|
||||
"other (%0.3f, %0.3f), alpha %0.3f",
|
||||
id, resampledCoords.getX(), resampledCoords.getY(),
|
||||
currentCoords.getX(), currentCoords.getY(),
|
||||
otherCoords.getX(), otherCoords.getY(),
|
||||
alpha);
|
||||
#endif
|
||||
} else {
|
||||
resampledCoords.copyFrom(currentCoords);
|
||||
#if DEBUG_RESAMPLING
|
||||
ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
|
||||
id, resampledCoords.getX(), resampledCoords.getY(),
|
||||
currentCoords.getX(), currentCoords.getY());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
event->addSample(sampleTime, touchState.lastResample.pointers);
|
||||
}
|
||||
|
||||
bool InputConsumer::shouldResampleTool(int32_t toolType) {
|
||||
return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
|
||||
|| toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
|
||||
#if DEBUG_TRANSPORT_ACTIONS
|
||||
ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
|
||||
mChannel->getName().string(), seq, handled ? "true" : "false");
|
||||
#endif
|
||||
|
||||
if (!seq) {
|
||||
ALOGE("Attempted to send a finished signal with sequence number 0.");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Send finished signals for the batch sequence chain first.
|
||||
size_t seqChainCount = mSeqChains.size();
|
||||
if (seqChainCount) {
|
||||
uint32_t currentSeq = seq;
|
||||
uint32_t chainSeqs[seqChainCount];
|
||||
size_t chainIndex = 0;
|
||||
for (size_t i = seqChainCount; i-- > 0; ) {
|
||||
const SeqChain& seqChain = mSeqChains.itemAt(i);
|
||||
if (seqChain.seq == currentSeq) {
|
||||
currentSeq = seqChain.chain;
|
||||
chainSeqs[chainIndex++] = currentSeq;
|
||||
mSeqChains.removeAt(i);
|
||||
}
|
||||
}
|
||||
status_t status = OK;
|
||||
while (!status && chainIndex-- > 0) {
|
||||
status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
|
||||
}
|
||||
if (status) {
|
||||
// An error occurred so at least one signal was not sent, reconstruct the chain.
|
||||
do {
|
||||
SeqChain seqChain;
|
||||
seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
|
||||
seqChain.chain = chainSeqs[chainIndex];
|
||||
mSeqChains.push(seqChain);
|
||||
} while (chainIndex-- > 0);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
// Send finished signal for the last message in the batch.
|
||||
return sendUnchainedFinishedSignal(seq, handled);
|
||||
}
|
||||
|
||||
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
|
||||
InputMessage msg;
|
||||
msg.header.type = InputMessage::TYPE_FINISHED;
|
||||
msg.body.finished.seq = seq;
|
||||
msg.body.finished.handled = handled;
|
||||
return mChannel->sendMessage(&msg);
|
||||
}
|
||||
|
||||
bool InputConsumer::hasDeferredEvent() const {
|
||||
return mMsgDeferred;
|
||||
}
|
||||
|
||||
bool InputConsumer::hasPendingBatch() const {
|
||||
return !mBatches.isEmpty();
|
||||
}
|
||||
|
||||
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
|
||||
for (size_t i = 0; i < mBatches.size(); i++) {
|
||||
const Batch& batch = mBatches.itemAt(i);
|
||||
const InputMessage& head = batch.samples.itemAt(0);
|
||||
if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
|
||||
for (size_t i = 0; i < mTouchStates.size(); i++) {
|
||||
const TouchState& touchState = mTouchStates.itemAt(i);
|
||||
if (touchState.deviceId == deviceId && touchState.source == source) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
|
||||
event->initialize(
|
||||
msg->body.key.deviceId,
|
||||
msg->body.key.source,
|
||||
msg->body.key.action,
|
||||
msg->body.key.flags,
|
||||
msg->body.key.keyCode,
|
||||
msg->body.key.scanCode,
|
||||
msg->body.key.metaState,
|
||||
msg->body.key.repeatCount,
|
||||
msg->body.key.downTime,
|
||||
msg->body.key.eventTime);
|
||||
}
|
||||
|
||||
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
|
||||
size_t pointerCount = msg->body.motion.pointerCount;
|
||||
PointerProperties pointerProperties[pointerCount];
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
|
||||
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
|
||||
}
|
||||
|
||||
event->initialize(
|
||||
msg->body.motion.deviceId,
|
||||
msg->body.motion.source,
|
||||
msg->body.motion.action,
|
||||
msg->body.motion.flags,
|
||||
msg->body.motion.edgeFlags,
|
||||
msg->body.motion.metaState,
|
||||
msg->body.motion.buttonState,
|
||||
msg->body.motion.xOffset,
|
||||
msg->body.motion.yOffset,
|
||||
msg->body.motion.xPrecision,
|
||||
msg->body.motion.yPrecision,
|
||||
msg->body.motion.downTime,
|
||||
msg->body.motion.eventTime,
|
||||
pointerCount,
|
||||
pointerProperties,
|
||||
pointerCoords);
|
||||
}
|
||||
|
||||
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
|
||||
size_t pointerCount = msg->body.motion.pointerCount;
|
||||
PointerCoords pointerCoords[pointerCount];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
|
||||
}
|
||||
|
||||
event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
|
||||
event->addSample(msg->body.motion.eventTime, pointerCoords);
|
||||
}
|
||||
|
||||
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
|
||||
const InputMessage& head = batch.samples.itemAt(0);
|
||||
size_t pointerCount = msg->body.motion.pointerCount;
|
||||
if (head.body.motion.pointerCount != pointerCount
|
||||
|| head.body.motion.action != msg->body.motion.action) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
if (head.body.motion.pointers[i].properties
|
||||
!= msg->body.motion.pointers[i].properties) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
|
||||
size_t numSamples = batch.samples.size();
|
||||
size_t index = 0;
|
||||
while (index < numSamples
|
||||
&& batch.samples.itemAt(index).body.motion.eventTime <= time) {
|
||||
index += 1;
|
||||
}
|
||||
return ssize_t(index) - 1;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,443 +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 _ANDROIDFW_INPUT_TRANSPORT_H
|
||||
#define _ANDROIDFW_INPUT_TRANSPORT_H
|
||||
|
||||
/**
|
||||
* Native input transport.
|
||||
*
|
||||
* The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
|
||||
*
|
||||
* The InputPublisher and InputConsumer each handle one end-point of an input channel.
|
||||
* The InputPublisher is used by the input dispatcher to send events to the application.
|
||||
* The InputConsumer is used by the application to receive events from the input dispatcher.
|
||||
*/
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Intermediate representation used to send input events and related signals.
|
||||
*/
|
||||
struct InputMessage {
|
||||
enum {
|
||||
TYPE_KEY = 1,
|
||||
TYPE_MOTION = 2,
|
||||
TYPE_FINISHED = 3,
|
||||
};
|
||||
|
||||
struct Header {
|
||||
uint32_t type;
|
||||
uint32_t padding; // 8 byte alignment for the body that follows
|
||||
} header;
|
||||
|
||||
union Body {
|
||||
struct Key {
|
||||
uint32_t seq;
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int32_t flags;
|
||||
int32_t keyCode;
|
||||
int32_t scanCode;
|
||||
int32_t metaState;
|
||||
int32_t repeatCount;
|
||||
nsecs_t downTime;
|
||||
|
||||
inline size_t size() const {
|
||||
return sizeof(Key);
|
||||
}
|
||||
} key;
|
||||
|
||||
struct Motion {
|
||||
uint32_t seq;
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
int32_t buttonState;
|
||||
int32_t edgeFlags;
|
||||
nsecs_t downTime;
|
||||
float xOffset;
|
||||
float yOffset;
|
||||
float xPrecision;
|
||||
float yPrecision;
|
||||
size_t pointerCount;
|
||||
struct Pointer {
|
||||
PointerProperties properties;
|
||||
PointerCoords coords;
|
||||
} pointers[MAX_POINTERS];
|
||||
|
||||
int32_t getActionId() const {
|
||||
uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
|
||||
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
||||
return pointers[index].properties.id;
|
||||
}
|
||||
|
||||
inline size_t size() const {
|
||||
return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
|
||||
+ sizeof(Pointer) * pointerCount;
|
||||
}
|
||||
} motion;
|
||||
|
||||
struct Finished {
|
||||
uint32_t seq;
|
||||
bool handled;
|
||||
|
||||
inline size_t size() const {
|
||||
return sizeof(Finished);
|
||||
}
|
||||
} finished;
|
||||
} body;
|
||||
|
||||
bool isValid(size_t actualSize) const;
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* An input channel consists of a local unix domain socket used to send and receive
|
||||
* input messages across processes. Each channel has a descriptive name for debugging purposes.
|
||||
*
|
||||
* Each endpoint has its own InputChannel object that specifies its file descriptor.
|
||||
*
|
||||
* The input channel is closed when all references to it are released.
|
||||
*/
|
||||
class InputChannel : public RefBase {
|
||||
protected:
|
||||
virtual ~InputChannel();
|
||||
|
||||
public:
|
||||
InputChannel(const String8& name, int fd);
|
||||
|
||||
/* Creates a pair of input channels.
|
||||
*
|
||||
* Returns OK on success.
|
||||
*/
|
||||
static status_t openInputChannelPair(const String8& name,
|
||||
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
|
||||
|
||||
inline String8 getName() const { return mName; }
|
||||
inline int getFd() const { return mFd; }
|
||||
|
||||
/* Sends a message to the other endpoint.
|
||||
*
|
||||
* If the channel is full then the message is guaranteed not to have been sent at all.
|
||||
* Try again after the consumer has sent a finished signal indicating that it has
|
||||
* consumed some of the pending messages from the channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendMessage(const InputMessage* msg);
|
||||
|
||||
/* Receives a message sent by the other endpoint.
|
||||
*
|
||||
* If there is no message present, try again after poll() indicates that the fd
|
||||
* is readable.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no message present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveMessage(InputMessage* msg);
|
||||
|
||||
/* Returns a new object that has a duplicate of this channel's fd. */
|
||||
sp<InputChannel> dup() const;
|
||||
|
||||
private:
|
||||
String8 mName;
|
||||
int mFd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Publishes input events to an input channel.
|
||||
*/
|
||||
class InputPublisher {
|
||||
public:
|
||||
/* Creates a publisher associated with an input channel. */
|
||||
explicit InputPublisher(const sp<InputChannel>& channel);
|
||||
|
||||
/* Destroys the publisher and releases its input channel. */
|
||||
~InputPublisher();
|
||||
|
||||
/* Gets the underlying input channel. */
|
||||
inline sp<InputChannel> getChannel() { return mChannel; }
|
||||
|
||||
/* Publishes a key event to the input channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns BAD_VALUE if seq is 0.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t publishKeyEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t keyCode,
|
||||
int32_t scanCode,
|
||||
int32_t metaState,
|
||||
int32_t repeatCount,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime);
|
||||
|
||||
/* Publishes a motion event to the input channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t publishMotionEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
int32_t flags,
|
||||
int32_t edgeFlags,
|
||||
int32_t metaState,
|
||||
int32_t buttonState,
|
||||
float xOffset,
|
||||
float yOffset,
|
||||
float xPrecision,
|
||||
float yPrecision,
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime,
|
||||
size_t pointerCount,
|
||||
const PointerProperties* pointerProperties,
|
||||
const PointerCoords* pointerCoords);
|
||||
|
||||
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
|
||||
* If a signal was received, returns the message sequence number,
|
||||
* and whether the consumer handled the message.
|
||||
*
|
||||
* The returned sequence number is never 0 unless the operation failed.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no signal present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
|
||||
|
||||
private:
|
||||
sp<InputChannel> mChannel;
|
||||
};
|
||||
|
||||
/*
|
||||
* Consumes input events from an input channel.
|
||||
*/
|
||||
class InputConsumer {
|
||||
public:
|
||||
/* Creates a consumer associated with an input channel. */
|
||||
explicit InputConsumer(const sp<InputChannel>& channel);
|
||||
|
||||
/* Destroys the consumer and releases its input channel. */
|
||||
~InputConsumer();
|
||||
|
||||
/* Gets the underlying input channel. */
|
||||
inline sp<InputChannel> getChannel() { return mChannel; }
|
||||
|
||||
/* Consumes an input event from the input channel and copies its contents into
|
||||
* an InputEvent object created using the specified factory.
|
||||
*
|
||||
* Tries to combine a series of move events into larger batches whenever possible.
|
||||
*
|
||||
* If consumeBatches is false, then defers consuming pending batched events if it
|
||||
* is possible for additional samples to be added to them later. Call hasPendingBatch()
|
||||
* to determine whether a pending batch is available to be consumed.
|
||||
*
|
||||
* If consumeBatches is true, then events are still batched but they are consumed
|
||||
* immediately as soon as the input channel is exhausted.
|
||||
*
|
||||
* The frameTime parameter specifies the time when the current display frame started
|
||||
* rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
|
||||
*
|
||||
* The returned sequence number is never 0 unless the operation failed.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no event present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns NO_MEMORY if the event could not be created.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
|
||||
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
|
||||
|
||||
/* Sends a finished signal to the publisher to inform it that the message
|
||||
* with the specified sequence number has finished being process and whether
|
||||
* the message was handled by the consumer.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns BAD_VALUE if seq is 0.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendFinishedSignal(uint32_t seq, bool handled);
|
||||
|
||||
/* Returns true if there is a deferred event waiting.
|
||||
*
|
||||
* Should be called after calling consume() to determine whether the consumer
|
||||
* has a deferred event to be processed. Deferred events are somewhat special in
|
||||
* that they have already been removed from the input channel. If the input channel
|
||||
* becomes empty, the client may need to do extra work to ensure that it processes
|
||||
* the deferred event despite the fact that the input channel's file descriptor
|
||||
* is not readable.
|
||||
*
|
||||
* One option is simply to call consume() in a loop until it returns WOULD_BLOCK.
|
||||
* This guarantees that all deferred events will be processed.
|
||||
*
|
||||
* Alternately, the caller can call hasDeferredEvent() to determine whether there is
|
||||
* a deferred event waiting and then ensure that its event loop wakes up at least
|
||||
* one more time to consume the deferred event.
|
||||
*/
|
||||
bool hasDeferredEvent() const;
|
||||
|
||||
/* Returns true if there is a pending batch.
|
||||
*
|
||||
* Should be called after calling consume() with consumeBatches == false to determine
|
||||
* whether consume() should be called again later on with consumeBatches == true.
|
||||
*/
|
||||
bool hasPendingBatch() const;
|
||||
|
||||
private:
|
||||
// True if touch resampling is enabled.
|
||||
const bool mResampleTouch;
|
||||
|
||||
// The input channel.
|
||||
sp<InputChannel> mChannel;
|
||||
|
||||
// The current input message.
|
||||
InputMessage mMsg;
|
||||
|
||||
// True if mMsg contains a valid input message that was deferred from the previous
|
||||
// call to consume and that still needs to be handled.
|
||||
bool mMsgDeferred;
|
||||
|
||||
// Batched motion events per device and source.
|
||||
struct Batch {
|
||||
Vector<InputMessage> samples;
|
||||
};
|
||||
Vector<Batch> mBatches;
|
||||
|
||||
// Touch state per device and source, only for sources of class pointer.
|
||||
struct History {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
int32_t idToIndex[MAX_POINTER_ID + 1];
|
||||
PointerCoords pointers[MAX_POINTERS];
|
||||
|
||||
void initializeFrom(const InputMessage* msg) {
|
||||
eventTime = msg->body.motion.eventTime;
|
||||
idBits.clear();
|
||||
for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
|
||||
uint32_t id = msg->body.motion.pointers[i].properties.id;
|
||||
idBits.markBit(id);
|
||||
idToIndex[id] = i;
|
||||
pointers[i].copyFrom(msg->body.motion.pointers[i].coords);
|
||||
}
|
||||
}
|
||||
|
||||
const PointerCoords& getPointerById(uint32_t id) const {
|
||||
return pointers[idToIndex[id]];
|
||||
}
|
||||
};
|
||||
struct TouchState {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
size_t historyCurrent;
|
||||
size_t historySize;
|
||||
History history[2];
|
||||
History lastResample;
|
||||
|
||||
void initialize(int32_t deviceId, int32_t source) {
|
||||
this->deviceId = deviceId;
|
||||
this->source = source;
|
||||
historyCurrent = 0;
|
||||
historySize = 0;
|
||||
lastResample.eventTime = 0;
|
||||
lastResample.idBits.clear();
|
||||
}
|
||||
|
||||
void addHistory(const InputMessage* msg) {
|
||||
historyCurrent ^= 1;
|
||||
if (historySize < 2) {
|
||||
historySize += 1;
|
||||
}
|
||||
history[historyCurrent].initializeFrom(msg);
|
||||
}
|
||||
|
||||
const History* getHistory(size_t index) const {
|
||||
return &history[(historyCurrent + index) & 1];
|
||||
}
|
||||
};
|
||||
Vector<TouchState> mTouchStates;
|
||||
|
||||
// Chain of batched sequence numbers. When multiple input messages are combined into
|
||||
// a batch, we append a record here that associates the last sequence number in the
|
||||
// batch with the previous one. When the finished signal is sent, we traverse the
|
||||
// chain to individually finish all input messages that were part of the batch.
|
||||
struct SeqChain {
|
||||
uint32_t seq; // sequence number of batched input message
|
||||
uint32_t chain; // sequence number of previous batched input message
|
||||
};
|
||||
Vector<SeqChain> mSeqChains;
|
||||
|
||||
status_t consumeBatch(InputEventFactoryInterface* factory,
|
||||
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
|
||||
status_t consumeSamples(InputEventFactoryInterface* factory,
|
||||
Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
|
||||
|
||||
void updateTouchState(InputMessage* msg);
|
||||
void rewriteMessage(const TouchState& state, InputMessage* msg);
|
||||
void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
|
||||
const InputMessage *next);
|
||||
|
||||
ssize_t findBatch(int32_t deviceId, int32_t source) const;
|
||||
ssize_t findTouchState(int32_t deviceId, int32_t source) const;
|
||||
|
||||
status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
|
||||
|
||||
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
|
||||
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
|
||||
static void addSample(MotionEvent* event, const InputMessage* msg);
|
||||
static bool canAddSample(const Batch& batch, const InputMessage* msg);
|
||||
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
|
||||
static bool shouldResampleTool(int32_t toolType);
|
||||
|
||||
static bool isTouchResamplingEnabled();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_INPUT_TRANSPORT_H
|
|
@ -1,64 +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_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
|
|
@ -1,205 +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.h"
|
||||
#include "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_HARDWARE_ACCELERATED = 0x01000000,
|
||||
FLAG_HARDWARE_ACCELERATED_SYSTEM = 0x02000000,
|
||||
FLAG_SLIPPERY = 0x04000000,
|
||||
FLAG_NEEDS_MENU_KEY = 0x08000000,
|
||||
FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
|
||||
FLAG_COMPATIBLE_WINDOW = 0x20000000,
|
||||
FLAG_SYSTEM_ERROR = 0x40000000,
|
||||
};
|
||||
|
||||
// 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,
|
||||
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 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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
#define _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
#include <binder/IBinder.h>
|
||||
#endif
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Unicode.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* Describes a mapping from Android key codes to characters.
|
||||
* Also specifies other functions of the keyboard such as the keyboard type
|
||||
* and key modifier semantics.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class KeyCharacterMap : public RefBase {
|
||||
public:
|
||||
enum KeyboardType {
|
||||
KEYBOARD_TYPE_UNKNOWN = 0,
|
||||
KEYBOARD_TYPE_NUMERIC = 1,
|
||||
KEYBOARD_TYPE_PREDICTIVE = 2,
|
||||
KEYBOARD_TYPE_ALPHA = 3,
|
||||
KEYBOARD_TYPE_FULL = 4,
|
||||
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
|
||||
KEYBOARD_TYPE_OVERLAY = 6,
|
||||
};
|
||||
|
||||
enum Format {
|
||||
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
|
||||
FORMAT_BASE = 0,
|
||||
// Overlay keyboard layout, more restrictive, may be published by applications,
|
||||
// cannot override device-specific options.
|
||||
FORMAT_OVERLAY = 1,
|
||||
// Either base or overlay layout ok.
|
||||
FORMAT_ANY = 2,
|
||||
};
|
||||
|
||||
// Substitute key code and meta state for fallback action.
|
||||
struct FallbackAction {
|
||||
int32_t keyCode;
|
||||
int32_t metaState;
|
||||
};
|
||||
|
||||
/* Loads a key character map from a file. */
|
||||
static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
/* Loads a key character map from its string contents. */
|
||||
static status_t loadContents(const String8& filename,
|
||||
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
/* Combines a base key character map and an overlay. */
|
||||
static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
|
||||
const sp<KeyCharacterMap>& overlay);
|
||||
|
||||
/* Returns an empty key character map. */
|
||||
static sp<KeyCharacterMap> empty();
|
||||
|
||||
/* Gets the keyboard type. */
|
||||
int32_t getKeyboardType() const;
|
||||
|
||||
/* Gets the primary character for this key as in the label physically printed on it.
|
||||
* Returns 0 if none (eg. for non-printing keys). */
|
||||
char16_t getDisplayLabel(int32_t keyCode) const;
|
||||
|
||||
/* Gets the Unicode character for the number or symbol generated by the key
|
||||
* when the keyboard is used as a dialing pad.
|
||||
* Returns 0 if no number or symbol is generated.
|
||||
*/
|
||||
char16_t getNumber(int32_t keyCode) const;
|
||||
|
||||
/* Gets the Unicode character generated by the key and meta key modifiers.
|
||||
* Returns 0 if no character is generated.
|
||||
*/
|
||||
char16_t getCharacter(int32_t keyCode, int32_t metaState) const;
|
||||
|
||||
/* Gets the fallback action to use by default if the application does not
|
||||
* handle the specified key.
|
||||
* Returns true if an action was available, false if none.
|
||||
*/
|
||||
bool getFallbackAction(int32_t keyCode, int32_t metaState,
|
||||
FallbackAction* outFallbackAction) const;
|
||||
|
||||
/* Gets the first matching Unicode character that can be generated by the key,
|
||||
* preferring the one with the specified meta key modifiers.
|
||||
* Returns 0 if no matching character is generated.
|
||||
*/
|
||||
char16_t getMatch(int32_t keyCode, const char16_t* chars,
|
||||
size_t numChars, int32_t metaState) const;
|
||||
|
||||
/* Gets a sequence of key events that could plausibly generate the specified
|
||||
* character sequence. Returns false if some of the characters cannot be generated.
|
||||
*/
|
||||
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
|
||||
Vector<KeyEvent>& outEvents) const;
|
||||
|
||||
/* Maps a scan code and usage code to a key code, in case this key map overrides
|
||||
* the mapping in some way. */
|
||||
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
/* Reads a key map from a parcel. */
|
||||
static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
|
||||
|
||||
/* Writes a key map to a parcel. */
|
||||
void writeToParcel(Parcel* parcel) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~KeyCharacterMap();
|
||||
|
||||
private:
|
||||
struct Behavior {
|
||||
Behavior();
|
||||
Behavior(const Behavior& other);
|
||||
|
||||
/* The next behavior in the list, or NULL if none. */
|
||||
Behavior* next;
|
||||
|
||||
/* The meta key modifiers for this behavior. */
|
||||
int32_t metaState;
|
||||
|
||||
/* The character to insert. */
|
||||
char16_t character;
|
||||
|
||||
/* The fallback keycode if the key is not handled. */
|
||||
int32_t fallbackKeyCode;
|
||||
};
|
||||
|
||||
struct Key {
|
||||
Key();
|
||||
Key(const Key& other);
|
||||
~Key();
|
||||
|
||||
/* The single character label printed on the key, or 0 if none. */
|
||||
char16_t label;
|
||||
|
||||
/* The number or symbol character generated by the key, or 0 if none. */
|
||||
char16_t number;
|
||||
|
||||
/* The list of key behaviors sorted from most specific to least specific
|
||||
* meta key binding. */
|
||||
Behavior* firstBehavior;
|
||||
};
|
||||
|
||||
class Parser {
|
||||
enum State {
|
||||
STATE_TOP = 0,
|
||||
STATE_KEY = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
PROPERTY_LABEL = 1,
|
||||
PROPERTY_NUMBER = 2,
|
||||
PROPERTY_META = 3,
|
||||
};
|
||||
|
||||
struct Property {
|
||||
inline Property(int32_t property = 0, int32_t metaState = 0) :
|
||||
property(property), metaState(metaState) { }
|
||||
|
||||
int32_t property;
|
||||
int32_t metaState;
|
||||
};
|
||||
|
||||
KeyCharacterMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
Format mFormat;
|
||||
State mState;
|
||||
int32_t mKeyCode;
|
||||
|
||||
public:
|
||||
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
private:
|
||||
status_t parseType();
|
||||
status_t parseMap();
|
||||
status_t parseMapKey();
|
||||
status_t parseKey();
|
||||
status_t parseKeyProperty();
|
||||
status_t finishKey(Key* key);
|
||||
status_t parseModifier(const String8& token, int32_t* outMetaState);
|
||||
status_t parseCharacterLiteral(char16_t* outCharacter);
|
||||
};
|
||||
|
||||
static sp<KeyCharacterMap> sEmpty;
|
||||
|
||||
KeyedVector<int32_t, Key*> mKeys;
|
||||
int mType;
|
||||
|
||||
KeyedVector<int32_t, int32_t> mKeysByScanCode;
|
||||
KeyedVector<int32_t, int32_t> mKeysByUsageCode;
|
||||
|
||||
KeyCharacterMap();
|
||||
KeyCharacterMap(const KeyCharacterMap& other);
|
||||
|
||||
bool getKey(int32_t keyCode, const Key** outKey) const;
|
||||
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
|
||||
const Key** outKey, const Behavior** outBehavior) const;
|
||||
static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState);
|
||||
|
||||
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
|
||||
|
||||
static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
static void addKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
|
||||
static void addMetaKeys(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
|
||||
int32_t* currentMetaState);
|
||||
static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
|
||||
int32_t keyCode, int32_t keyMetaState,
|
||||
int32_t* currentMetaState);
|
||||
static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
|
||||
int32_t leftKeyCode, int32_t leftKeyMetaState,
|
||||
int32_t rightKeyCode, int32_t rightKeyMetaState,
|
||||
int32_t eitherKeyMetaState,
|
||||
int32_t* currentMetaState);
|
||||
static void addLockedMetaKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t metaState, nsecs_t time,
|
||||
int32_t keyCode, int32_t keyMetaState,
|
||||
int32_t* currentMetaState);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
|
|
@ -1,446 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 "KeyLayoutMap"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "android_keycodes.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
|
||||
// Enables debug output for parser performance.
|
||||
#define DEBUG_PARSER_PERFORMANCE 0
|
||||
|
||||
// Enables debug output for mapping.
|
||||
#define DEBUG_MAPPING 0
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* WHITESPACE = " \t\r";
|
||||
|
||||
// --- KeyLayoutMap ---
|
||||
|
||||
KeyLayoutMap::KeyLayoutMap() {
|
||||
}
|
||||
|
||||
KeyLayoutMap::~KeyLayoutMap() {
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::open(filename, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key layout map file %s.", status, filename.string());
|
||||
} else {
|
||||
sp<KeyLayoutMap> map = new KeyLayoutMap();
|
||||
if (!map.get()) {
|
||||
ALOGE("Error allocating key layout map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map.get(), tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (!status) {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeyCode, uint32_t* outFlags) const {
|
||||
const Key* key = getKey(scanCode, usageCode);
|
||||
if (!key) {
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
|
||||
#endif
|
||||
*outKeyCode = AKEYCODE_UNKNOWN;
|
||||
*outFlags = 0;
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
*outKeyCode = key->keyCode;
|
||||
*outFlags = key->flags;
|
||||
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
|
||||
scanCode, usageCode, *outKeyCode, *outFlags);
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
|
||||
if (usageCode) {
|
||||
ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
|
||||
if (index >= 0) {
|
||||
return &mKeysByUsageCode.valueAt(index);
|
||||
}
|
||||
}
|
||||
if (scanCode) {
|
||||
ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
|
||||
if (index >= 0) {
|
||||
return &mKeysByScanCode.valueAt(index);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
|
||||
const size_t N = mKeysByScanCode.size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
|
||||
outScanCodes->add(mKeysByScanCode.keyAt(i));
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
|
||||
ssize_t index = mAxes.indexOfKey(scanCode);
|
||||
if (index < 0) {
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
|
||||
#endif
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
*outAxisInfo = mAxes.valueAt(index);
|
||||
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
|
||||
"splitValue=%d, flatOverride=%d.",
|
||||
scanCode,
|
||||
outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
|
||||
outAxisInfo->splitValue, outAxisInfo->flatOverride);
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const {
|
||||
const size_t N = mLedsByScanCode.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (mLedsByScanCode.valueAt(i).ledCode == ledCode) {
|
||||
*outScanCode = mLedsByScanCode.keyAt(i);
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode);
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
|
||||
#endif
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const {
|
||||
const size_t N = mLedsByUsageCode.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) {
|
||||
*outUsageCode = mLedsByUsageCode.keyAt(i);
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode);
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode);
|
||||
#endif
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
// --- KeyLayoutMap::Parser ---
|
||||
|
||||
KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
|
||||
mMap(map), mTokenizer(tokenizer) {
|
||||
}
|
||||
|
||||
KeyLayoutMap::Parser::~Parser() {
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::Parser::parse() {
|
||||
while (!mTokenizer->isEof()) {
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
#endif
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
|
||||
if (keywordToken == "key") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseKey();
|
||||
if (status) return status;
|
||||
} else if (keywordToken == "axis") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseAxis();
|
||||
if (status) return status;
|
||||
} else if (keywordToken == "led") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseLed();
|
||||
if (status) return status;
|
||||
} else {
|
||||
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
|
||||
keywordToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
|
||||
mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
mTokenizer->nextLine();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::Parser::parseKey() {
|
||||
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
bool mapUsage = false;
|
||||
if (codeToken == "usage") {
|
||||
mapUsage = true;
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
}
|
||||
|
||||
char* end;
|
||||
int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
KeyedVector<int32_t, Key>& map =
|
||||
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
|
||||
if (map.indexOfKey(code) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
|
||||
if (!keyCode) {
|
||||
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
keyCodeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
for (;;) {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
|
||||
|
||||
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
|
||||
uint32_t flag = getKeyFlagByLabel(flagToken.string());
|
||||
if (!flag) {
|
||||
ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
flagToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (flags & flag) {
|
||||
ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
|
||||
flagToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
flags |= flag;
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
|
||||
mapUsage ? "usage" : "scan code", code, keyCode, flags);
|
||||
#endif
|
||||
Key key;
|
||||
key.keyCode = keyCode;
|
||||
key.flags = flags;
|
||||
map.add(code, key);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::Parser::parseAxis() {
|
||||
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
char* end;
|
||||
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
scanCodeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
|
||||
scanCodeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
AxisInfo axisInfo;
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 token = mTokenizer->nextToken(WHITESPACE);
|
||||
if (token == "invert") {
|
||||
axisInfo.mode = AxisInfo::MODE_INVERT;
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
|
||||
axisInfo.axis = getAxisByLabel(axisToken.string());
|
||||
if (axisInfo.axis < 0) {
|
||||
ALOGE("%s: Expected inverted axis label, got '%s'.",
|
||||
mTokenizer->getLocation().string(), axisToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else if (token == "split") {
|
||||
axisInfo.mode = AxisInfo::MODE_SPLIT;
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 splitToken = mTokenizer->nextToken(WHITESPACE);
|
||||
axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected split value, got '%s'.",
|
||||
mTokenizer->getLocation().string(), splitToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
|
||||
axisInfo.axis = getAxisByLabel(lowAxisToken.string());
|
||||
if (axisInfo.axis < 0) {
|
||||
ALOGE("%s: Expected low axis label, got '%s'.",
|
||||
mTokenizer->getLocation().string(), lowAxisToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
|
||||
axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
|
||||
if (axisInfo.highAxis < 0) {
|
||||
ALOGE("%s: Expected high axis label, got '%s'.",
|
||||
mTokenizer->getLocation().string(), highAxisToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
axisInfo.axis = getAxisByLabel(token.string());
|
||||
if (axisInfo.axis < 0) {
|
||||
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
|
||||
mTokenizer->getLocation().string(), token.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
|
||||
break;
|
||||
}
|
||||
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
|
||||
if (keywordToken == "flat") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 flatToken = mTokenizer->nextToken(WHITESPACE);
|
||||
axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected flat value, got '%s'.",
|
||||
mTokenizer->getLocation().string(), flatToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
ALOGE("%s: Expected keyword 'flat', got '%s'.",
|
||||
mTokenizer->getLocation().string(), keywordToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
|
||||
"splitValue=%d, flatOverride=%d.",
|
||||
scanCode,
|
||||
axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
|
||||
axisInfo.splitValue, axisInfo.flatOverride);
|
||||
#endif
|
||||
mMap->mAxes.add(scanCode, axisInfo);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::Parser::parseLed() {
|
||||
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
bool mapUsage = false;
|
||||
if (codeToken == "usage") {
|
||||
mapUsage = true;
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
}
|
||||
char* end;
|
||||
int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
|
||||
if (map.indexOfKey(code) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
int32_t ledCode = getLedByLabel(ledCodeToken.string());
|
||||
if (ledCode < 0) {
|
||||
ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
ledCodeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed led %s: code=%d, ledCode=%d.",
|
||||
mapUsage ? "usage" : "scan code", code, ledCode);
|
||||
#endif
|
||||
|
||||
Led led;
|
||||
led.ledCode = ledCode;
|
||||
map.add(code, led);
|
||||
return NO_ERROR;
|
||||
}
|
||||
};
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 _ANDROIDFW_KEY_LAYOUT_MAP_H
|
||||
#define _ANDROIDFW_KEY_LAYOUT_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
struct AxisInfo {
|
||||
enum Mode {
|
||||
// Axis value is reported directly.
|
||||
MODE_NORMAL = 0,
|
||||
// Axis value should be inverted before reporting.
|
||||
MODE_INVERT = 1,
|
||||
// Axis value should be split into two axes
|
||||
MODE_SPLIT = 2,
|
||||
};
|
||||
|
||||
// Axis mode.
|
||||
Mode mode;
|
||||
|
||||
// Axis id.
|
||||
// When split, this is the axis used for values smaller than the split position.
|
||||
int32_t axis;
|
||||
|
||||
// When split, this is the axis used for values after higher than the split position.
|
||||
int32_t highAxis;
|
||||
|
||||
// The split value, or 0 if not split.
|
||||
int32_t splitValue;
|
||||
|
||||
// The flat value, or -1 if none.
|
||||
int32_t flatOverride;
|
||||
|
||||
AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class KeyLayoutMap : public RefBase {
|
||||
public:
|
||||
static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
|
||||
|
||||
status_t mapKey(int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeyCode, uint32_t* outFlags) const;
|
||||
status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
|
||||
status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const;
|
||||
status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
|
||||
|
||||
status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
|
||||
|
||||
protected:
|
||||
virtual ~KeyLayoutMap();
|
||||
|
||||
private:
|
||||
struct Key {
|
||||
int32_t keyCode;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct Led {
|
||||
int32_t ledCode;
|
||||
};
|
||||
|
||||
|
||||
KeyedVector<int32_t, Key> mKeysByScanCode;
|
||||
KeyedVector<int32_t, Key> mKeysByUsageCode;
|
||||
KeyedVector<int32_t, AxisInfo> mAxes;
|
||||
KeyedVector<int32_t, Led> mLedsByScanCode;
|
||||
KeyedVector<int32_t, Led> mLedsByUsageCode;
|
||||
|
||||
KeyLayoutMap();
|
||||
|
||||
const Key* getKey(int32_t scanCode, int32_t usageCode) const;
|
||||
|
||||
class Parser {
|
||||
KeyLayoutMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
|
||||
public:
|
||||
Parser(KeyLayoutMap* map, Tokenizer* tokenizer);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
private:
|
||||
status_t parseKey();
|
||||
status_t parseAxis();
|
||||
status_t parseLed();
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H
|
|
@ -1,300 +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 "Keyboard"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "Keyboard.h"
|
||||
#include "KeycodeLabels.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
#include "InputDevice.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- KeyMap ---
|
||||
|
||||
KeyMap::KeyMap() {
|
||||
}
|
||||
|
||||
KeyMap::~KeyMap() {
|
||||
}
|
||||
|
||||
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
|
||||
const PropertyMap* deviceConfiguration) {
|
||||
// Use the configured key layout if available.
|
||||
if (deviceConfiguration) {
|
||||
String8 keyLayoutName;
|
||||
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
|
||||
keyLayoutName)) {
|
||||
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
|
||||
if (status == NAME_NOT_FOUND) {
|
||||
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
|
||||
"it was not found.",
|
||||
deviceIdenfifier.name.string(), keyLayoutName.string());
|
||||
}
|
||||
}
|
||||
|
||||
String8 keyCharacterMapName;
|
||||
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
|
||||
keyCharacterMapName)) {
|
||||
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
|
||||
if (status == NAME_NOT_FOUND) {
|
||||
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
|
||||
"map '%s' but it was not found.",
|
||||
deviceIdenfifier.name.string(), keyLayoutName.string());
|
||||
}
|
||||
}
|
||||
|
||||
if (isComplete()) {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Try searching by device identifier.
|
||||
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Fall back on the Generic key map.
|
||||
// TODO Apply some additional heuristics here to figure out what kind of
|
||||
// generic key map to use (US English, etc.) for typical external keyboards.
|
||||
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Try the Virtual key map as a last resort.
|
||||
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Give up!
|
||||
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
|
||||
deviceIdenfifier.name.string());
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& keyMapName) {
|
||||
if (!haveKeyLayout()) {
|
||||
loadKeyLayout(deviceIdentifier, keyMapName);
|
||||
}
|
||||
if (!haveKeyCharacterMap()) {
|
||||
loadKeyCharacterMap(deviceIdentifier, keyMapName);
|
||||
}
|
||||
return isComplete();
|
||||
}
|
||||
|
||||
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& name) {
|
||||
String8 path(getPath(deviceIdentifier, name,
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
|
||||
if (path.isEmpty()) {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
keyLayoutFile.setTo(path);
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& name) {
|
||||
String8 path(getPath(deviceIdentifier, name,
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
|
||||
if (path.isEmpty()) {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t status = KeyCharacterMap::load(path,
|
||||
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
keyCharacterMapFile.setTo(path);
|
||||
return OK;
|
||||
}
|
||||
|
||||
String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
return name.isEmpty()
|
||||
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
|
||||
: getInputDeviceConfigurationFilePathByName(name, type);
|
||||
}
|
||||
|
||||
|
||||
// --- Global functions ---
|
||||
|
||||
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
|
||||
if (!keyMap->haveKeyCharacterMap()
|
||||
|| keyMap->keyCharacterMap->getKeyboardType()
|
||||
== KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (deviceConfiguration) {
|
||||
bool builtIn = false;
|
||||
if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
|
||||
&& builtIn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return strstr(deviceIdentifier.name.string(), "-keypad");
|
||||
}
|
||||
|
||||
static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
|
||||
while (list->literal) {
|
||||
if (strcmp(literal, list->literal) == 0) {
|
||||
return list->value;
|
||||
}
|
||||
list++;
|
||||
}
|
||||
return list->value;
|
||||
}
|
||||
|
||||
static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
|
||||
while (list->literal) {
|
||||
if (list->value == value) {
|
||||
return list->literal;
|
||||
}
|
||||
list++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t getKeyCodeByLabel(const char* label) {
|
||||
return int32_t(lookupValueByLabel(label, KEYCODES));
|
||||
}
|
||||
|
||||
uint32_t getKeyFlagByLabel(const char* label) {
|
||||
return uint32_t(lookupValueByLabel(label, FLAGS));
|
||||
}
|
||||
|
||||
int32_t getAxisByLabel(const char* label) {
|
||||
return int32_t(lookupValueByLabel(label, AXES));
|
||||
}
|
||||
|
||||
const char* getAxisLabel(int32_t axisId) {
|
||||
return lookupLabelByValue(axisId, AXES);
|
||||
}
|
||||
|
||||
int32_t getLedByLabel(const char* label) {
|
||||
return int32_t(lookupValueByLabel(label, LEDS));
|
||||
}
|
||||
static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
|
||||
int32_t newMetaState;
|
||||
if (down) {
|
||||
newMetaState = oldMetaState | mask;
|
||||
} else {
|
||||
newMetaState = oldMetaState &
|
||||
~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
|
||||
}
|
||||
|
||||
if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
newMetaState |= AMETA_ALT_ON;
|
||||
}
|
||||
|
||||
if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
newMetaState |= AMETA_SHIFT_ON;
|
||||
}
|
||||
|
||||
if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
newMetaState |= AMETA_CTRL_ON;
|
||||
}
|
||||
|
||||
if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
newMetaState |= AMETA_META_ON;
|
||||
}
|
||||
return newMetaState;
|
||||
}
|
||||
|
||||
static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
|
||||
if (down) {
|
||||
return oldMetaState;
|
||||
} else {
|
||||
return oldMetaState ^ mask;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
|
||||
switch (keyCode) {
|
||||
case AKEYCODE_ALT_LEFT:
|
||||
return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
|
||||
case AKEYCODE_ALT_RIGHT:
|
||||
return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
|
||||
case AKEYCODE_SHIFT_LEFT:
|
||||
return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
|
||||
case AKEYCODE_SHIFT_RIGHT:
|
||||
return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
|
||||
case AKEYCODE_SYM:
|
||||
return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
|
||||
case AKEYCODE_FUNCTION:
|
||||
return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
|
||||
case AKEYCODE_CTRL_LEFT:
|
||||
return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
|
||||
case AKEYCODE_CTRL_RIGHT:
|
||||
return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
|
||||
case AKEYCODE_META_LEFT:
|
||||
return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
|
||||
case AKEYCODE_META_RIGHT:
|
||||
return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
|
||||
case AKEYCODE_CAPS_LOCK:
|
||||
return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
|
||||
case AKEYCODE_NUM_LOCK:
|
||||
return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
|
||||
case AKEYCODE_SCROLL_LOCK:
|
||||
return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
|
||||
default:
|
||||
return oldMetaState;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMetaKey(int32_t keyCode) {
|
||||
switch (keyCode) {
|
||||
case AKEYCODE_ALT_LEFT:
|
||||
case AKEYCODE_ALT_RIGHT:
|
||||
case AKEYCODE_SHIFT_LEFT:
|
||||
case AKEYCODE_SHIFT_RIGHT:
|
||||
case AKEYCODE_SYM:
|
||||
case AKEYCODE_FUNCTION:
|
||||
case AKEYCODE_CTRL_LEFT:
|
||||
case AKEYCODE_CTRL_RIGHT:
|
||||
case AKEYCODE_META_LEFT:
|
||||
case AKEYCODE_META_RIGHT:
|
||||
case AKEYCODE_CAPS_LOCK:
|
||||
case AKEYCODE_NUM_LOCK:
|
||||
case AKEYCODE_SCROLL_LOCK:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace android
|
|
@ -1,122 +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 _ANDROIDFW_KEYBOARD_H
|
||||
#define _ANDROIDFW_KEYBOARD_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "InputDevice.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/PropertyMap.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
/* Device id of the built in keyboard. */
|
||||
DEVICE_ID_BUILT_IN_KEYBOARD = 0,
|
||||
|
||||
/* Device id of a generic virtual keyboard with a full layout that can be used
|
||||
* to synthesize key events. */
|
||||
DEVICE_ID_VIRTUAL_KEYBOARD = -1,
|
||||
};
|
||||
|
||||
class KeyLayoutMap;
|
||||
class KeyCharacterMap;
|
||||
|
||||
/**
|
||||
* Loads the key layout map and key character map for a keyboard device.
|
||||
*/
|
||||
class KeyMap {
|
||||
public:
|
||||
String8 keyLayoutFile;
|
||||
sp<KeyLayoutMap> keyLayoutMap;
|
||||
|
||||
String8 keyCharacterMapFile;
|
||||
sp<KeyCharacterMap> keyCharacterMap;
|
||||
|
||||
KeyMap();
|
||||
~KeyMap();
|
||||
|
||||
status_t load(const InputDeviceIdentifier& deviceIdenfier,
|
||||
const PropertyMap* deviceConfiguration);
|
||||
|
||||
inline bool haveKeyLayout() const {
|
||||
return !keyLayoutFile.isEmpty();
|
||||
}
|
||||
|
||||
inline bool haveKeyCharacterMap() const {
|
||||
return !keyCharacterMapFile.isEmpty();
|
||||
}
|
||||
|
||||
inline bool isComplete() const {
|
||||
return haveKeyLayout() && haveKeyCharacterMap();
|
||||
}
|
||||
|
||||
private:
|
||||
bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
|
||||
status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
|
||||
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& name);
|
||||
String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const String8& name, InputDeviceConfigurationFileType type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the keyboard is eligible for use as a built-in keyboard.
|
||||
*/
|
||||
extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
|
||||
const PropertyMap* deviceConfiguration, const KeyMap* keyMap);
|
||||
|
||||
/**
|
||||
* Gets a key code by its short form label, eg. "HOME".
|
||||
* Returns 0 if unknown.
|
||||
*/
|
||||
extern int32_t getKeyCodeByLabel(const char* label);
|
||||
|
||||
/**
|
||||
* Gets a key flag by its short form label, eg. "WAKE".
|
||||
* Returns 0 if unknown.
|
||||
*/
|
||||
extern uint32_t getKeyFlagByLabel(const char* label);
|
||||
|
||||
/**
|
||||
* Gets a axis by its short form label, eg. "X".
|
||||
* Returns -1 if unknown.
|
||||
*/
|
||||
extern int32_t getAxisByLabel(const char* label);
|
||||
|
||||
/**
|
||||
* Gets a axis label by its id.
|
||||
* Returns NULL if unknown.
|
||||
*/
|
||||
extern const char* getAxisLabel(int32_t axisId);
|
||||
|
||||
extern int32_t getLedByLabel(const char* label);
|
||||
|
||||
/**
|
||||
* Updates a meta state field when a key is pressed or released.
|
||||
*/
|
||||
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
|
||||
|
||||
/**
|
||||
* Returns true if a key is a meta key like ALT or CAPS_LOCK.
|
||||
*/
|
||||
extern bool isMetaKey(int32_t keyCode);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_KEYBOARD_H
|
|
@ -1,380 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 _ANDROIDFW_KEYCODE_LABELS_H
|
||||
#define _ANDROIDFW_KEYCODE_LABELS_H
|
||||
|
||||
#include "android_keycodes.h"
|
||||
|
||||
struct KeycodeLabel {
|
||||
const char *literal;
|
||||
int value;
|
||||
};
|
||||
|
||||
static const KeycodeLabel KEYCODES[] = {
|
||||
{ "SOFT_LEFT", 1 },
|
||||
{ "SOFT_RIGHT", 2 },
|
||||
{ "HOME", 3 },
|
||||
{ "BACK", 4 },
|
||||
{ "CALL", 5 },
|
||||
{ "ENDCALL", 6 },
|
||||
{ "0", 7 },
|
||||
{ "1", 8 },
|
||||
{ "2", 9 },
|
||||
{ "3", 10 },
|
||||
{ "4", 11 },
|
||||
{ "5", 12 },
|
||||
{ "6", 13 },
|
||||
{ "7", 14 },
|
||||
{ "8", 15 },
|
||||
{ "9", 16 },
|
||||
{ "STAR", 17 },
|
||||
{ "POUND", 18 },
|
||||
{ "DPAD_UP", 19 },
|
||||
{ "DPAD_DOWN", 20 },
|
||||
{ "DPAD_LEFT", 21 },
|
||||
{ "DPAD_RIGHT", 22 },
|
||||
{ "DPAD_CENTER", 23 },
|
||||
{ "VOLUME_UP", 24 },
|
||||
{ "VOLUME_DOWN", 25 },
|
||||
{ "POWER", 26 },
|
||||
{ "CAMERA", 27 },
|
||||
{ "CLEAR", 28 },
|
||||
{ "A", 29 },
|
||||
{ "B", 30 },
|
||||
{ "C", 31 },
|
||||
{ "D", 32 },
|
||||
{ "E", 33 },
|
||||
{ "F", 34 },
|
||||
{ "G", 35 },
|
||||
{ "H", 36 },
|
||||
{ "I", 37 },
|
||||
{ "J", 38 },
|
||||
{ "K", 39 },
|
||||
{ "L", 40 },
|
||||
{ "M", 41 },
|
||||
{ "N", 42 },
|
||||
{ "O", 43 },
|
||||
{ "P", 44 },
|
||||
{ "Q", 45 },
|
||||
{ "R", 46 },
|
||||
{ "S", 47 },
|
||||
{ "T", 48 },
|
||||
{ "U", 49 },
|
||||
{ "V", 50 },
|
||||
{ "W", 51 },
|
||||
{ "X", 52 },
|
||||
{ "Y", 53 },
|
||||
{ "Z", 54 },
|
||||
{ "COMMA", 55 },
|
||||
{ "PERIOD", 56 },
|
||||
{ "ALT_LEFT", 57 },
|
||||
{ "ALT_RIGHT", 58 },
|
||||
{ "SHIFT_LEFT", 59 },
|
||||
{ "SHIFT_RIGHT", 60 },
|
||||
{ "TAB", 61 },
|
||||
{ "SPACE", 62 },
|
||||
{ "SYM", 63 },
|
||||
{ "EXPLORER", 64 },
|
||||
{ "ENVELOPE", 65 },
|
||||
{ "ENTER", 66 },
|
||||
{ "DEL", 67 },
|
||||
{ "GRAVE", 68 },
|
||||
{ "MINUS", 69 },
|
||||
{ "EQUALS", 70 },
|
||||
{ "LEFT_BRACKET", 71 },
|
||||
{ "RIGHT_BRACKET", 72 },
|
||||
{ "BACKSLASH", 73 },
|
||||
{ "SEMICOLON", 74 },
|
||||
{ "APOSTROPHE", 75 },
|
||||
{ "SLASH", 76 },
|
||||
{ "AT", 77 },
|
||||
{ "NUM", 78 },
|
||||
{ "HEADSETHOOK", 79 },
|
||||
{ "FOCUS", 80 },
|
||||
{ "PLUS", 81 },
|
||||
{ "MENU", 82 },
|
||||
{ "NOTIFICATION", 83 },
|
||||
{ "SEARCH", 84 },
|
||||
{ "MEDIA_PLAY_PAUSE", 85 },
|
||||
{ "MEDIA_STOP", 86 },
|
||||
{ "MEDIA_NEXT", 87 },
|
||||
{ "MEDIA_PREVIOUS", 88 },
|
||||
{ "MEDIA_REWIND", 89 },
|
||||
{ "MEDIA_FAST_FORWARD", 90 },
|
||||
{ "MUTE", 91 },
|
||||
{ "PAGE_UP", 92 },
|
||||
{ "PAGE_DOWN", 93 },
|
||||
{ "PICTSYMBOLS", 94 },
|
||||
{ "SWITCH_CHARSET", 95 },
|
||||
{ "BUTTON_A", 96 },
|
||||
{ "BUTTON_B", 97 },
|
||||
{ "BUTTON_C", 98 },
|
||||
{ "BUTTON_X", 99 },
|
||||
{ "BUTTON_Y", 100 },
|
||||
{ "BUTTON_Z", 101 },
|
||||
{ "BUTTON_L1", 102 },
|
||||
{ "BUTTON_R1", 103 },
|
||||
{ "BUTTON_L2", 104 },
|
||||
{ "BUTTON_R2", 105 },
|
||||
{ "BUTTON_THUMBL", 106 },
|
||||
{ "BUTTON_THUMBR", 107 },
|
||||
{ "BUTTON_START", 108 },
|
||||
{ "BUTTON_SELECT", 109 },
|
||||
{ "BUTTON_MODE", 110 },
|
||||
{ "ESCAPE", 111 },
|
||||
{ "FORWARD_DEL", 112 },
|
||||
{ "CTRL_LEFT", 113 },
|
||||
{ "CTRL_RIGHT", 114 },
|
||||
{ "CAPS_LOCK", 115 },
|
||||
{ "SCROLL_LOCK", 116 },
|
||||
{ "META_LEFT", 117 },
|
||||
{ "META_RIGHT", 118 },
|
||||
{ "FUNCTION", 119 },
|
||||
{ "SYSRQ", 120 },
|
||||
{ "BREAK", 121 },
|
||||
{ "MOVE_HOME", 122 },
|
||||
{ "MOVE_END", 123 },
|
||||
{ "INSERT", 124 },
|
||||
{ "FORWARD", 125 },
|
||||
{ "MEDIA_PLAY", 126 },
|
||||
{ "MEDIA_PAUSE", 127 },
|
||||
{ "MEDIA_CLOSE", 128 },
|
||||
{ "MEDIA_EJECT", 129 },
|
||||
{ "MEDIA_RECORD", 130 },
|
||||
{ "F1", 131 },
|
||||
{ "F2", 132 },
|
||||
{ "F3", 133 },
|
||||
{ "F4", 134 },
|
||||
{ "F5", 135 },
|
||||
{ "F6", 136 },
|
||||
{ "F7", 137 },
|
||||
{ "F8", 138 },
|
||||
{ "F9", 139 },
|
||||
{ "F10", 140 },
|
||||
{ "F11", 141 },
|
||||
{ "F12", 142 },
|
||||
{ "NUM_LOCK", 143 },
|
||||
{ "NUMPAD_0", 144 },
|
||||
{ "NUMPAD_1", 145 },
|
||||
{ "NUMPAD_2", 146 },
|
||||
{ "NUMPAD_3", 147 },
|
||||
{ "NUMPAD_4", 148 },
|
||||
{ "NUMPAD_5", 149 },
|
||||
{ "NUMPAD_6", 150 },
|
||||
{ "NUMPAD_7", 151 },
|
||||
{ "NUMPAD_8", 152 },
|
||||
{ "NUMPAD_9", 153 },
|
||||
{ "NUMPAD_DIVIDE", 154 },
|
||||
{ "NUMPAD_MULTIPLY", 155 },
|
||||
{ "NUMPAD_SUBTRACT", 156 },
|
||||
{ "NUMPAD_ADD", 157 },
|
||||
{ "NUMPAD_DOT", 158 },
|
||||
{ "NUMPAD_COMMA", 159 },
|
||||
{ "NUMPAD_ENTER", 160 },
|
||||
{ "NUMPAD_EQUALS", 161 },
|
||||
{ "NUMPAD_LEFT_PAREN", 162 },
|
||||
{ "NUMPAD_RIGHT_PAREN", 163 },
|
||||
{ "VOLUME_MUTE", 164 },
|
||||
{ "INFO", 165 },
|
||||
{ "CHANNEL_UP", 166 },
|
||||
{ "CHANNEL_DOWN", 167 },
|
||||
{ "ZOOM_IN", 168 },
|
||||
{ "ZOOM_OUT", 169 },
|
||||
{ "TV", 170 },
|
||||
{ "WINDOW", 171 },
|
||||
{ "GUIDE", 172 },
|
||||
{ "DVR", 173 },
|
||||
{ "BOOKMARK", 174 },
|
||||
{ "CAPTIONS", 175 },
|
||||
{ "SETTINGS", 176 },
|
||||
{ "TV_POWER", 177 },
|
||||
{ "TV_INPUT", 178 },
|
||||
{ "STB_POWER", 179 },
|
||||
{ "STB_INPUT", 180 },
|
||||
{ "AVR_POWER", 181 },
|
||||
{ "AVR_INPUT", 182 },
|
||||
{ "PROG_RED", 183 },
|
||||
{ "PROG_GREEN", 184 },
|
||||
{ "PROG_YELLOW", 185 },
|
||||
{ "PROG_BLUE", 186 },
|
||||
{ "APP_SWITCH", 187 },
|
||||
{ "BUTTON_1", 188 },
|
||||
{ "BUTTON_2", 189 },
|
||||
{ "BUTTON_3", 190 },
|
||||
{ "BUTTON_4", 191 },
|
||||
{ "BUTTON_5", 192 },
|
||||
{ "BUTTON_6", 193 },
|
||||
{ "BUTTON_7", 194 },
|
||||
{ "BUTTON_8", 195 },
|
||||
{ "BUTTON_9", 196 },
|
||||
{ "BUTTON_10", 197 },
|
||||
{ "BUTTON_11", 198 },
|
||||
{ "BUTTON_12", 199 },
|
||||
{ "BUTTON_13", 200 },
|
||||
{ "BUTTON_14", 201 },
|
||||
{ "BUTTON_15", 202 },
|
||||
{ "BUTTON_16", 203 },
|
||||
{ "LANGUAGE_SWITCH", 204 },
|
||||
{ "MANNER_MODE", 205 },
|
||||
{ "3D_MODE", 206 },
|
||||
{ "CONTACTS", 207 },
|
||||
{ "CALENDAR", 208 },
|
||||
{ "MUSIC", 209 },
|
||||
{ "CALCULATOR", 210 },
|
||||
{ "ZENKAKU_HANKAKU", 211 },
|
||||
{ "EISU", 212 },
|
||||
{ "MUHENKAN", 213 },
|
||||
{ "HENKAN", 214 },
|
||||
{ "KATAKANA_HIRAGANA", 215 },
|
||||
{ "YEN", 216 },
|
||||
{ "RO", 217 },
|
||||
{ "KANA", 218 },
|
||||
{ "ASSIST", 219 },
|
||||
{ "BRIGHTNESS_DOWN", 220 },
|
||||
{ "BRIGHTNESS_UP", 221 },
|
||||
{ "MEDIA_AUDIO_TRACK", 222 },
|
||||
{ "SLEEP", 223 },
|
||||
{ "WAKEUP", 224 },
|
||||
{ "PAIRING", 225 },
|
||||
{ "MEDIA_TOP_MENU", 226 },
|
||||
{ "11", 227 },
|
||||
{ "12", 228 },
|
||||
{ "LAST_CHANNEL", 229 },
|
||||
{ "TV_DATA_SERVICE", 230 },
|
||||
{ "VOICE_ASSIST", 231 },
|
||||
{ "TV_RADIO_SERVICE", 232 },
|
||||
{ "TV_TELETEXT", 233 },
|
||||
{ "TV_NUMBER_ENTRY", 234 },
|
||||
{ "TV_TERRESTRIAL_ANALOG", 235 },
|
||||
{ "TV_TERRESTRIAL_DIGITAL", 236 },
|
||||
{ "TV_SATELLITE", 237 },
|
||||
{ "TV_SATELLITE_BS", 238 },
|
||||
{ "TV_SATELLITE_CS", 239 },
|
||||
{ "TV_SATELLITE_SERVICE", 240 },
|
||||
{ "TV_NETWORK", 241 },
|
||||
{ "TV_ANTENNA_CABLE", 242 },
|
||||
{ "TV_INPUT_HDMI_1", 243 },
|
||||
{ "TV_INPUT_HDMI_2", 244 },
|
||||
{ "TV_INPUT_HDMI_3", 245 },
|
||||
{ "TV_INPUT_HDMI_4", 246 },
|
||||
{ "TV_INPUT_COMPOSITE_1", 247 },
|
||||
{ "TV_INPUT_COMPOSITE_2", 248 },
|
||||
{ "TV_INPUT_COMPONENT_1", 249 },
|
||||
{ "TV_INPUT_COMPONENT_2", 250 },
|
||||
{ "TV_INPUT_VGA_1", 251 },
|
||||
{ "TV_AUDIO_DESCRIPTION", 252 },
|
||||
{ "TV_AUDIO_DESCRIPTION_MIX_UP", 253 },
|
||||
{ "TV_AUDIO_DESCRIPTION_MIX_DOWN", 254 },
|
||||
{ "TV_ZOOM_MODE", 255 },
|
||||
{ "TV_CONTENTS_MENU", 256 },
|
||||
{ "TV_MEDIA_CONTEXT_MENU", 257 },
|
||||
{ "TV_TIMER_PROGRAMMING", 258 },
|
||||
{ "HELP", 259 },
|
||||
|
||||
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
// NOTE: If you edit these flags, also edit policy flags in Input.h.
|
||||
static const KeycodeLabel FLAGS[] = {
|
||||
{ "WAKE", 0x00000001 },
|
||||
{ "WAKE_DROPPED", 0x00000002 },
|
||||
{ "SHIFT", 0x00000004 },
|
||||
{ "CAPS_LOCK", 0x00000008 },
|
||||
{ "ALT", 0x00000010 },
|
||||
{ "ALT_GR", 0x00000020 },
|
||||
{ "MENU", 0x00000040 },
|
||||
{ "LAUNCHER", 0x00000080 },
|
||||
{ "VIRTUAL", 0x00000100 },
|
||||
{ "FUNCTION", 0x00000200 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static const KeycodeLabel AXES[] = {
|
||||
{ "X", 0 },
|
||||
{ "Y", 1 },
|
||||
{ "PRESSURE", 2 },
|
||||
{ "SIZE", 3 },
|
||||
{ "TOUCH_MAJOR", 4 },
|
||||
{ "TOUCH_MINOR", 5 },
|
||||
{ "TOOL_MAJOR", 6 },
|
||||
{ "TOOL_MINOR", 7 },
|
||||
{ "ORIENTATION", 8 },
|
||||
{ "VSCROLL", 9 },
|
||||
{ "HSCROLL", 10 },
|
||||
{ "Z", 11 },
|
||||
{ "RX", 12 },
|
||||
{ "RY", 13 },
|
||||
{ "RZ", 14 },
|
||||
{ "HAT_X", 15 },
|
||||
{ "HAT_Y", 16 },
|
||||
{ "LTRIGGER", 17 },
|
||||
{ "RTRIGGER", 18 },
|
||||
{ "THROTTLE", 19 },
|
||||
{ "RUDDER", 20 },
|
||||
{ "WHEEL", 21 },
|
||||
{ "GAS", 22 },
|
||||
{ "BRAKE", 23 },
|
||||
{ "DISTANCE", 24 },
|
||||
{ "TILT", 25 },
|
||||
{ "GENERIC_1", 32 },
|
||||
{ "GENERIC_2", 33 },
|
||||
{ "GENERIC_3", 34 },
|
||||
{ "GENERIC_4", 35 },
|
||||
{ "GENERIC_5", 36 },
|
||||
{ "GENERIC_6", 37 },
|
||||
{ "GENERIC_7", 38 },
|
||||
{ "GENERIC_8", 39 },
|
||||
{ "GENERIC_9", 40 },
|
||||
{ "GENERIC_10", 41 },
|
||||
{ "GENERIC_11", 42 },
|
||||
{ "GENERIC_12", 43 },
|
||||
{ "GENERIC_13", 44 },
|
||||
{ "GENERIC_14", 45 },
|
||||
{ "GENERIC_15", 46 },
|
||||
{ "GENERIC_16", 47 },
|
||||
|
||||
// NOTE: If you add a new axis here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
|
||||
|
||||
{ NULL, -1 }
|
||||
};
|
||||
|
||||
static const KeycodeLabel LEDS[] = {
|
||||
{ "NUM_LOCK", 1},
|
||||
{ "CAPS_LOCK", 2},
|
||||
{ "SCROLL_LOCK", 3},
|
||||
{ "COMPOSE", 4},
|
||||
{ "KANA", 5},
|
||||
{ "SLEEP", 6},
|
||||
{ "SUSPEND", 7},
|
||||
{ "MUTE", 8},
|
||||
{ "MISC", 9},
|
||||
{ "MAIL", 10},
|
||||
{ "CHARGING", 11},
|
||||
{ "CONTROLLER_1", 12},
|
||||
{ "CONTROLLER_2", 13},
|
||||
{ "CONTROLLER_3", 14},
|
||||
{ "CONTROLLER_4", 15},
|
||||
|
||||
// NOTE: If you add new LEDs here, you must also add them to Input.h
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
#endif // _ANDROIDFW_KEYCODE_LABELS_H
|
|
@ -1,604 +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 "PointerController"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about pointer updates
|
||||
#define DEBUG_POINTER_UPDATES 0
|
||||
|
||||
#include "PointerController.h"
|
||||
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <SkCanvas.h>
|
||||
#include <SkColor.h>
|
||||
#include <SkPaint.h>
|
||||
#include <SkXfermode.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- PointerController ---
|
||||
|
||||
// Time to wait before starting the fade when the pointer is inactive.
|
||||
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
|
||||
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
|
||||
|
||||
// Time to wait between animation frames.
|
||||
static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
|
||||
|
||||
// Time to spend fading out the spot completely.
|
||||
static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
|
||||
|
||||
// Time to spend fading out the pointer completely.
|
||||
static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
|
||||
|
||||
|
||||
// --- PointerController ---
|
||||
|
||||
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
|
||||
const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
|
||||
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
|
||||
mHandler = new WeakMessageHandler(this);
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
mLocked.animationPending = false;
|
||||
|
||||
mLocked.displayWidth = -1;
|
||||
mLocked.displayHeight = -1;
|
||||
mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
|
||||
|
||||
mLocked.presentation = PRESENTATION_POINTER;
|
||||
mLocked.presentationChanged = false;
|
||||
|
||||
mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
|
||||
|
||||
mLocked.pointerFadeDirection = 0;
|
||||
mLocked.pointerX = 0;
|
||||
mLocked.pointerY = 0;
|
||||
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
|
||||
mLocked.pointerSprite = mSpriteController->createSprite();
|
||||
mLocked.pointerIconChanged = false;
|
||||
|
||||
mLocked.buttonState = 0;
|
||||
|
||||
loadResources();
|
||||
}
|
||||
|
||||
PointerController::~PointerController() {
|
||||
mLooper->removeMessages(mHandler);
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
mLocked.pointerSprite.clear();
|
||||
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
delete mLocked.spots.itemAt(i);
|
||||
}
|
||||
mLocked.spots.clear();
|
||||
mLocked.recycledSprites.clear();
|
||||
}
|
||||
|
||||
bool PointerController::getBounds(float* outMinX, float* outMinY,
|
||||
float* outMaxX, float* outMaxY) const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
|
||||
}
|
||||
|
||||
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
|
||||
float* outMaxX, float* outMaxY) const {
|
||||
if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*outMinX = 0;
|
||||
*outMinY = 0;
|
||||
switch (mLocked.displayOrientation) {
|
||||
case DISPLAY_ORIENTATION_90:
|
||||
case DISPLAY_ORIENTATION_270:
|
||||
*outMaxX = mLocked.displayHeight - 1;
|
||||
*outMaxY = mLocked.displayWidth - 1;
|
||||
break;
|
||||
default:
|
||||
*outMaxX = mLocked.displayWidth - 1;
|
||||
*outMaxY = mLocked.displayHeight - 1;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointerController::move(float deltaX, float deltaY) {
|
||||
#if DEBUG_POINTER_UPDATES
|
||||
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
|
||||
#endif
|
||||
if (deltaX == 0.0f && deltaY == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
|
||||
}
|
||||
|
||||
void PointerController::setButtonState(int32_t buttonState) {
|
||||
#if DEBUG_POINTER_UPDATES
|
||||
ALOGD("Set button state 0x%08x", buttonState);
|
||||
#endif
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
if (mLocked.buttonState != buttonState) {
|
||||
mLocked.buttonState = buttonState;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t PointerController::getButtonState() const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
return mLocked.buttonState;
|
||||
}
|
||||
|
||||
void PointerController::setPosition(float x, float y) {
|
||||
#if DEBUG_POINTER_UPDATES
|
||||
ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
|
||||
#endif
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
setPositionLocked(x, y);
|
||||
}
|
||||
|
||||
void PointerController::setPositionLocked(float x, float y) {
|
||||
float minX, minY, maxX, maxY;
|
||||
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
|
||||
if (x <= minX) {
|
||||
mLocked.pointerX = minX;
|
||||
} else if (x >= maxX) {
|
||||
mLocked.pointerX = maxX;
|
||||
} else {
|
||||
mLocked.pointerX = x;
|
||||
}
|
||||
if (y <= minY) {
|
||||
mLocked.pointerY = minY;
|
||||
} else if (y >= maxY) {
|
||||
mLocked.pointerY = maxY;
|
||||
} else {
|
||||
mLocked.pointerY = y;
|
||||
}
|
||||
updatePointerLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::getPosition(float* outX, float* outY) const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
*outX = mLocked.pointerX;
|
||||
*outY = mLocked.pointerY;
|
||||
}
|
||||
|
||||
void PointerController::fade(Transition transition) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
// Remove the inactivity timeout, since we are fading now.
|
||||
removeInactivityTimeoutLocked();
|
||||
|
||||
// Start fading.
|
||||
if (transition == TRANSITION_IMMEDIATE) {
|
||||
mLocked.pointerFadeDirection = 0;
|
||||
mLocked.pointerAlpha = 0.0f;
|
||||
updatePointerLocked();
|
||||
} else {
|
||||
mLocked.pointerFadeDirection = -1;
|
||||
startAnimationLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::unfade(Transition transition) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
// Always reset the inactivity timer.
|
||||
resetInactivityTimeoutLocked();
|
||||
|
||||
// Start unfading.
|
||||
if (transition == TRANSITION_IMMEDIATE) {
|
||||
mLocked.pointerFadeDirection = 0;
|
||||
mLocked.pointerAlpha = 1.0f;
|
||||
updatePointerLocked();
|
||||
} else {
|
||||
mLocked.pointerFadeDirection = 1;
|
||||
startAnimationLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::setPresentation(Presentation presentation) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
if (mLocked.presentation != presentation) {
|
||||
mLocked.presentation = presentation;
|
||||
mLocked.presentationChanged = true;
|
||||
|
||||
if (presentation != PRESENTATION_SPOT) {
|
||||
fadeOutAndReleaseAllSpotsLocked();
|
||||
}
|
||||
|
||||
updatePointerLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::setSpots(const PointerCoords* spotCoords,
|
||||
const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
|
||||
#if DEBUG_POINTER_UPDATES
|
||||
ALOGD("setSpots: idBits=%08x", spotIdBits.value);
|
||||
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
|
||||
uint32_t id = idBits.firstMarkedBit();
|
||||
idBits.clearBit(id);
|
||||
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
|
||||
ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
|
||||
c.getAxisValue(AMOTION_EVENT_AXIS_X),
|
||||
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
|
||||
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
|
||||
}
|
||||
#endif
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
mSpriteController->openTransaction();
|
||||
|
||||
// Add or move spots for fingers that are down.
|
||||
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
|
||||
uint32_t id = idBits.clearFirstMarkedBit();
|
||||
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
|
||||
const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
|
||||
? mResources.spotTouch : mResources.spotHover;
|
||||
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
|
||||
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
|
||||
|
||||
Spot* spot = getSpotLocked(id);
|
||||
if (!spot) {
|
||||
spot = createAndAddSpotLocked(id);
|
||||
}
|
||||
|
||||
spot->updateSprite(&icon, x, y);
|
||||
}
|
||||
|
||||
// Remove spots for fingers that went up.
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
Spot* spot = mLocked.spots.itemAt(i);
|
||||
if (spot->id != Spot::INVALID_ID
|
||||
&& !spotIdBits.hasBit(spot->id)) {
|
||||
fadeOutAndReleaseSpotLocked(spot);
|
||||
}
|
||||
}
|
||||
|
||||
mSpriteController->closeTransaction();
|
||||
}
|
||||
|
||||
void PointerController::clearSpots() {
|
||||
#if DEBUG_POINTER_UPDATES
|
||||
ALOGD("clearSpots");
|
||||
#endif
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
fadeOutAndReleaseAllSpotsLocked();
|
||||
}
|
||||
|
||||
void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
if (mLocked.inactivityTimeout != inactivityTimeout) {
|
||||
mLocked.inactivityTimeout = inactivityTimeout;
|
||||
resetInactivityTimeoutLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
// Adjust to use the display's unrotated coordinate frame.
|
||||
if (orientation == DISPLAY_ORIENTATION_90
|
||||
|| orientation == DISPLAY_ORIENTATION_270) {
|
||||
int32_t temp = height;
|
||||
height = width;
|
||||
width = temp;
|
||||
}
|
||||
|
||||
if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
|
||||
mLocked.displayWidth = width;
|
||||
mLocked.displayHeight = height;
|
||||
|
||||
float minX, minY, maxX, maxY;
|
||||
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
|
||||
mLocked.pointerX = (minX + maxX) * 0.5f;
|
||||
mLocked.pointerY = (minY + maxY) * 0.5f;
|
||||
} else {
|
||||
mLocked.pointerX = 0;
|
||||
mLocked.pointerY = 0;
|
||||
}
|
||||
|
||||
fadeOutAndReleaseAllSpotsLocked();
|
||||
}
|
||||
|
||||
if (mLocked.displayOrientation != orientation) {
|
||||
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
|
||||
// This creates an invariant frame of reference that we can easily rotate when
|
||||
// taking into account that the pointer may be located at fractional pixel offsets.
|
||||
float x = mLocked.pointerX + 0.5f;
|
||||
float y = mLocked.pointerY + 0.5f;
|
||||
float temp;
|
||||
|
||||
// Undo the previous rotation.
|
||||
switch (mLocked.displayOrientation) {
|
||||
case DISPLAY_ORIENTATION_90:
|
||||
temp = x;
|
||||
x = mLocked.displayWidth - y;
|
||||
y = temp;
|
||||
break;
|
||||
case DISPLAY_ORIENTATION_180:
|
||||
x = mLocked.displayWidth - x;
|
||||
y = mLocked.displayHeight - y;
|
||||
break;
|
||||
case DISPLAY_ORIENTATION_270:
|
||||
temp = x;
|
||||
x = y;
|
||||
y = mLocked.displayHeight - temp;
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform the new rotation.
|
||||
switch (orientation) {
|
||||
case DISPLAY_ORIENTATION_90:
|
||||
temp = x;
|
||||
x = y;
|
||||
y = mLocked.displayWidth - temp;
|
||||
break;
|
||||
case DISPLAY_ORIENTATION_180:
|
||||
x = mLocked.displayWidth - x;
|
||||
y = mLocked.displayHeight - y;
|
||||
break;
|
||||
case DISPLAY_ORIENTATION_270:
|
||||
temp = x;
|
||||
x = mLocked.displayHeight - y;
|
||||
y = temp;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply offsets to convert from the pixel center to the pixel top-left corner position
|
||||
// and save the results.
|
||||
mLocked.pointerX = x - 0.5f;
|
||||
mLocked.pointerY = y - 0.5f;
|
||||
mLocked.displayOrientation = orientation;
|
||||
}
|
||||
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
void PointerController::setPointerIcon(const SpriteIcon& icon) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
mLocked.pointerIcon = icon.copy();
|
||||
mLocked.pointerIconChanged = true;
|
||||
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
void PointerController::handleMessage(const Message& message) {
|
||||
switch (message.what) {
|
||||
case MSG_ANIMATE:
|
||||
doAnimate();
|
||||
break;
|
||||
case MSG_INACTIVITY_TIMEOUT:
|
||||
doInactivityTimeout();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::doAnimate() {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
bool keepAnimating = false;
|
||||
mLocked.animationPending = false;
|
||||
nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
|
||||
|
||||
// Animate pointer fade.
|
||||
if (mLocked.pointerFadeDirection < 0) {
|
||||
mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
|
||||
if (mLocked.pointerAlpha <= 0.0f) {
|
||||
mLocked.pointerAlpha = 0.0f;
|
||||
mLocked.pointerFadeDirection = 0;
|
||||
} else {
|
||||
keepAnimating = true;
|
||||
}
|
||||
updatePointerLocked();
|
||||
} else if (mLocked.pointerFadeDirection > 0) {
|
||||
mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
|
||||
if (mLocked.pointerAlpha >= 1.0f) {
|
||||
mLocked.pointerAlpha = 1.0f;
|
||||
mLocked.pointerFadeDirection = 0;
|
||||
} else {
|
||||
keepAnimating = true;
|
||||
}
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
// Animate spots that are fading out and being removed.
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
Spot* spot = mLocked.spots.itemAt(i);
|
||||
if (spot->id == Spot::INVALID_ID) {
|
||||
spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
|
||||
if (spot->alpha <= 0) {
|
||||
mLocked.spots.removeAt(i--);
|
||||
releaseSpotLocked(spot);
|
||||
} else {
|
||||
spot->sprite->setAlpha(spot->alpha);
|
||||
keepAnimating = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keepAnimating) {
|
||||
startAnimationLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::doInactivityTimeout() {
|
||||
fade(TRANSITION_GRADUAL);
|
||||
}
|
||||
|
||||
void PointerController::startAnimationLocked() {
|
||||
if (!mLocked.animationPending) {
|
||||
mLocked.animationPending = true;
|
||||
mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::resetInactivityTimeoutLocked() {
|
||||
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
|
||||
nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
|
||||
? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
|
||||
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
void PointerController::removeInactivityTimeoutLocked() {
|
||||
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
void PointerController::updatePointerLocked() {
|
||||
mSpriteController->openTransaction();
|
||||
|
||||
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
|
||||
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
|
||||
|
||||
if (mLocked.pointerAlpha > 0) {
|
||||
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
|
||||
mLocked.pointerSprite->setVisible(true);
|
||||
} else {
|
||||
mLocked.pointerSprite->setVisible(false);
|
||||
}
|
||||
|
||||
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
|
||||
mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
|
||||
? mLocked.pointerIcon : mResources.spotAnchor);
|
||||
mLocked.pointerIconChanged = false;
|
||||
mLocked.presentationChanged = false;
|
||||
}
|
||||
|
||||
mSpriteController->closeTransaction();
|
||||
}
|
||||
|
||||
PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
Spot* spot = mLocked.spots.itemAt(i);
|
||||
if (spot->id == id) {
|
||||
return spot;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
|
||||
// Remove spots until we have fewer than MAX_SPOTS remaining.
|
||||
while (mLocked.spots.size() >= MAX_SPOTS) {
|
||||
Spot* spot = removeFirstFadingSpotLocked();
|
||||
if (!spot) {
|
||||
spot = mLocked.spots.itemAt(0);
|
||||
mLocked.spots.removeAt(0);
|
||||
}
|
||||
releaseSpotLocked(spot);
|
||||
}
|
||||
|
||||
// Obtain a sprite from the recycled pool.
|
||||
sp<Sprite> sprite;
|
||||
if (! mLocked.recycledSprites.isEmpty()) {
|
||||
sprite = mLocked.recycledSprites.top();
|
||||
mLocked.recycledSprites.pop();
|
||||
} else {
|
||||
sprite = mSpriteController->createSprite();
|
||||
}
|
||||
|
||||
// Return the new spot.
|
||||
Spot* spot = new Spot(id, sprite);
|
||||
mLocked.spots.push(spot);
|
||||
return spot;
|
||||
}
|
||||
|
||||
PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
Spot* spot = mLocked.spots.itemAt(i);
|
||||
if (spot->id == Spot::INVALID_ID) {
|
||||
mLocked.spots.removeAt(i);
|
||||
return spot;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PointerController::releaseSpotLocked(Spot* spot) {
|
||||
spot->sprite->clearIcon();
|
||||
|
||||
if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
|
||||
mLocked.recycledSprites.push(spot->sprite);
|
||||
}
|
||||
|
||||
delete spot;
|
||||
}
|
||||
|
||||
void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
|
||||
if (spot->id != Spot::INVALID_ID) {
|
||||
spot->id = Spot::INVALID_ID;
|
||||
startAnimationLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::fadeOutAndReleaseAllSpotsLocked() {
|
||||
for (size_t i = 0; i < mLocked.spots.size(); i++) {
|
||||
Spot* spot = mLocked.spots.itemAt(i);
|
||||
fadeOutAndReleaseSpotLocked(spot);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::loadResources() {
|
||||
mPolicy->loadPointerResources(&mResources);
|
||||
}
|
||||
|
||||
|
||||
// --- PointerController::Spot ---
|
||||
|
||||
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
|
||||
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
|
||||
sprite->setAlpha(alpha);
|
||||
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
|
||||
sprite->setPosition(x, y);
|
||||
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
|
||||
if (icon != lastIcon) {
|
||||
lastIcon = icon;
|
||||
if (icon) {
|
||||
sprite->setIcon(*icon);
|
||||
sprite->setVisible(true);
|
||||
} else {
|
||||
sprite->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,266 +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_POINTER_CONTROLLER_H
|
||||
#define _UI_POINTER_CONTROLLER_H
|
||||
|
||||
#include "SpriteController.h"
|
||||
|
||||
#include <ui/DisplayInfo.h>
|
||||
#include "Input.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <SkBitmap.h>
|
||||
|
||||
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.
|
||||
*/
|
||||
struct PointerResources {
|
||||
SpriteIcon spotHover;
|
||||
SpriteIcon spotTouch;
|
||||
SpriteIcon spotAnchor;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Pointer controller policy interface.
|
||||
*
|
||||
* The pointer controller policy is used by the pointer controller 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 PointerControllerPolicyInterface : public virtual RefBase {
|
||||
protected:
|
||||
PointerControllerPolicyInterface() { }
|
||||
virtual ~PointerControllerPolicyInterface() { }
|
||||
|
||||
public:
|
||||
virtual void loadPointerResources(PointerResources* outResources) = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Tracks pointer movements and draws the pointer sprite to a surface.
|
||||
*
|
||||
* Handles pointer acceleration and animation.
|
||||
*/
|
||||
class PointerController : public PointerControllerInterface, public MessageHandler {
|
||||
protected:
|
||||
virtual ~PointerController();
|
||||
|
||||
public:
|
||||
enum InactivityTimeout {
|
||||
INACTIVITY_TIMEOUT_NORMAL = 0,
|
||||
INACTIVITY_TIMEOUT_SHORT = 1,
|
||||
};
|
||||
|
||||
PointerController(const sp<PointerControllerPolicyInterface>& policy,
|
||||
const sp<Looper>& looper, const sp<SpriteController>& spriteController);
|
||||
|
||||
virtual bool getBounds(float* outMinX, float* outMinY,
|
||||
float* outMaxX, float* outMaxY) const;
|
||||
virtual void move(float deltaX, float deltaY);
|
||||
virtual void setButtonState(int32_t buttonState);
|
||||
virtual int32_t getButtonState() const;
|
||||
virtual void setPosition(float x, float y);
|
||||
virtual void getPosition(float* outX, float* outY) const;
|
||||
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();
|
||||
|
||||
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
|
||||
void setPointerIcon(const SpriteIcon& icon);
|
||||
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
|
||||
|
||||
private:
|
||||
static const size_t MAX_RECYCLED_SPRITES = 12;
|
||||
static const size_t MAX_SPOTS = 12;
|
||||
|
||||
enum {
|
||||
MSG_ANIMATE,
|
||||
MSG_INACTIVITY_TIMEOUT,
|
||||
};
|
||||
|
||||
struct Spot {
|
||||
static const uint32_t INVALID_ID = 0xffffffff;
|
||||
|
||||
uint32_t id;
|
||||
sp<Sprite> sprite;
|
||||
float alpha;
|
||||
float scale;
|
||||
float x, y;
|
||||
|
||||
inline Spot(uint32_t id, const sp<Sprite>& sprite)
|
||||
: id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
|
||||
x(0.0f), y(0.0f), lastIcon(NULL) { }
|
||||
|
||||
void updateSprite(const SpriteIcon* icon, float x, float y);
|
||||
|
||||
private:
|
||||
const SpriteIcon* lastIcon;
|
||||
};
|
||||
|
||||
mutable Mutex mLock;
|
||||
|
||||
sp<PointerControllerPolicyInterface> mPolicy;
|
||||
sp<Looper> mLooper;
|
||||
sp<SpriteController> mSpriteController;
|
||||
sp<WeakMessageHandler> mHandler;
|
||||
|
||||
PointerResources mResources;
|
||||
|
||||
struct Locked {
|
||||
bool animationPending;
|
||||
nsecs_t animationTime;
|
||||
|
||||
int32_t displayWidth;
|
||||
int32_t displayHeight;
|
||||
int32_t displayOrientation;
|
||||
|
||||
InactivityTimeout inactivityTimeout;
|
||||
|
||||
Presentation presentation;
|
||||
bool presentationChanged;
|
||||
|
||||
int32_t pointerFadeDirection;
|
||||
float pointerX;
|
||||
float pointerY;
|
||||
float pointerAlpha;
|
||||
sp<Sprite> pointerSprite;
|
||||
SpriteIcon pointerIcon;
|
||||
bool pointerIconChanged;
|
||||
|
||||
int32_t buttonState;
|
||||
|
||||
Vector<Spot*> spots;
|
||||
Vector<sp<Sprite> > recycledSprites;
|
||||
} mLocked;
|
||||
|
||||
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
|
||||
void setPositionLocked(float x, float y);
|
||||
|
||||
void handleMessage(const Message& message);
|
||||
void doAnimate();
|
||||
void doInactivityTimeout();
|
||||
|
||||
void startAnimationLocked();
|
||||
|
||||
void resetInactivityTimeoutLocked();
|
||||
void removeInactivityTimeoutLocked();
|
||||
void updatePointerLocked();
|
||||
|
||||
Spot* getSpotLocked(uint32_t id);
|
||||
Spot* createAndAddSpotLocked(uint32_t id);
|
||||
Spot* removeFirstFadingSpotLocked();
|
||||
void releaseSpotLocked(Spot* spot);
|
||||
void fadeOutAndReleaseSpotLocked(Spot* spot);
|
||||
void fadeOutAndReleaseAllSpotsLocked();
|
||||
|
||||
void loadResources();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_POINTER_CONTROLLER_H
|
|
@ -1,33 +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 _ANDROIDFW_POWER_MANAGER_H
|
||||
#define _ANDROIDFW_POWER_MANAGER_H
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
USER_ACTIVITY_EVENT_OTHER = 0,
|
||||
USER_ACTIVITY_EVENT_BUTTON = 1,
|
||||
USER_ACTIVITY_EVENT_TOUCH = 2,
|
||||
|
||||
USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code.
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_POWER_MANAGER_H
|
|
@ -1,515 +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 "Sprites"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "SpriteController.h"
|
||||
|
||||
#include "cutils_log.h"
|
||||
#include <utils/String8.h>
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <gui/Surface.h>
|
||||
#endif
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <SkCanvas.h>
|
||||
#include <SkColor.h>
|
||||
#include <SkPaint.h>
|
||||
#include <SkXfermode.h>
|
||||
#include <android/native_window.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- SpriteController ---
|
||||
|
||||
SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
|
||||
mLooper(looper), mOverlayLayer(overlayLayer) {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mHandler = new WeakMessageHandler(this);
|
||||
#endif
|
||||
|
||||
mLocked.transactionNestingCount = 0;
|
||||
mLocked.deferredSpriteUpdate = false;
|
||||
}
|
||||
|
||||
SpriteController::~SpriteController() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->removeMessages(mHandler);
|
||||
|
||||
if (mSurfaceComposerClient != NULL) {
|
||||
mSurfaceComposerClient->dispose();
|
||||
mSurfaceComposerClient.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
sp<Sprite> SpriteController::createSprite() {
|
||||
return new SpriteImpl(this);
|
||||
}
|
||||
|
||||
void SpriteController::openTransaction() {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
mLocked.transactionNestingCount += 1;
|
||||
}
|
||||
|
||||
void SpriteController::closeTransaction() {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
LOG_ALWAYS_FATAL_IF(mLocked.transactionNestingCount == 0,
|
||||
"Sprite closeTransaction() called but there is no open sprite transaction");
|
||||
|
||||
mLocked.transactionNestingCount -= 1;
|
||||
if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
|
||||
mLocked.deferredSpriteUpdate = false;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
|
||||
bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
|
||||
mLocked.invalidatedSprites.push(sprite);
|
||||
if (wasEmpty) {
|
||||
if (mLocked.transactionNestingCount != 0) {
|
||||
mLocked.deferredSpriteUpdate = true;
|
||||
} else {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
|
||||
bool wasEmpty = mLocked.disposedSurfaces.isEmpty();
|
||||
mLocked.disposedSurfaces.push(surfaceControl);
|
||||
if (wasEmpty) {
|
||||
mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::handleMessage(const Message& message) {
|
||||
switch (message.what) {
|
||||
case MSG_UPDATE_SPRITES:
|
||||
doUpdateSprites();
|
||||
break;
|
||||
case MSG_DISPOSE_SURFACES:
|
||||
doDisposeSurfaces();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SpriteController::doUpdateSprites() {
|
||||
// Collect information about sprite updates.
|
||||
// Each sprite update record includes a reference to its associated sprite so we can
|
||||
// be certain the sprites will not be deleted while this function runs. Sprites
|
||||
// may invalidate themselves again during this time but we will handle those changes
|
||||
// in the next iteration.
|
||||
Vector<SpriteUpdate> updates;
|
||||
size_t numSprites;
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
numSprites = mLocked.invalidatedSprites.size();
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
|
||||
|
||||
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
|
||||
sprite->resetDirtyLocked();
|
||||
}
|
||||
mLocked.invalidatedSprites.clear();
|
||||
} // release lock
|
||||
|
||||
// Create missing surfaces.
|
||||
bool surfaceChanged = false;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
SpriteUpdate& update = updates.editItemAt(i);
|
||||
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
|
||||
update.state.surfaceWidth = update.state.icon.bitmap.width();
|
||||
update.state.surfaceHeight = update.state.icon.bitmap.height();
|
||||
update.state.surfaceDrawn = false;
|
||||
update.state.surfaceVisible = false;
|
||||
update.state.surfaceControl = obtainSurface(
|
||||
update.state.surfaceWidth, update.state.surfaceHeight);
|
||||
if (update.state.surfaceControl != NULL) {
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Resize sprites if needed, inside a global transaction.
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
bool haveGlobalTransaction = false;
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
SpriteUpdate& update = updates.editItemAt(i);
|
||||
if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
|
||||
int32_t desiredWidth = update.state.icon.bitmap.width();
|
||||
int32_t desiredHeight = update.state.icon.bitmap.height();
|
||||
if (update.state.surfaceWidth < desiredWidth
|
||||
|| update.state.surfaceHeight < desiredHeight) {
|
||||
if (!haveGlobalTransaction) {
|
||||
SurfaceComposerClient::openGlobalTransaction();
|
||||
haveGlobalTransaction = true;
|
||||
}
|
||||
|
||||
status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
|
||||
if (status) {
|
||||
ALOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
|
||||
status, update.state.surfaceWidth, update.state.surfaceHeight,
|
||||
desiredWidth, desiredHeight);
|
||||
} else {
|
||||
update.state.surfaceWidth = desiredWidth;
|
||||
update.state.surfaceHeight = desiredHeight;
|
||||
update.state.surfaceDrawn = false;
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
|
||||
if (update.state.surfaceVisible) {
|
||||
status = update.state.surfaceControl->hide();
|
||||
if (status) {
|
||||
ALOGE("Error %d hiding sprite surface after resize.", status);
|
||||
} else {
|
||||
update.state.surfaceVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
if (haveGlobalTransaction) {
|
||||
SurfaceComposerClient::closeGlobalTransaction();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Redraw sprites if needed.
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
SpriteUpdate& update = updates.editItemAt(i);
|
||||
|
||||
if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
|
||||
update.state.surfaceDrawn = false;
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
|
||||
&& update.state.wantSurfaceVisible()) {
|
||||
sp<Surface> surface = update.state.surfaceControl->getSurface();
|
||||
ANativeWindow_Buffer outBuffer;
|
||||
status_t status = surface->lock(&outBuffer, NULL);
|
||||
if (status) {
|
||||
ALOGE("Error %d locking sprite surface before drawing.", status);
|
||||
} 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);
|
||||
|
||||
SkCanvas surfaceCanvas(surfaceBitmap);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
||||
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
|
||||
|
||||
if (outBuffer.width > uint32_t(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())) {
|
||||
paint.setColor(0); // transparent fill color
|
||||
surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
|
||||
outBuffer.width, outBuffer.height, paint);
|
||||
}
|
||||
|
||||
status = surface->unlockAndPost();
|
||||
if (status) {
|
||||
ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
|
||||
} else {
|
||||
update.state.surfaceDrawn = true;
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
// Set sprite surface properties and make them visible.
|
||||
bool haveTransaction = false;
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
SpriteUpdate& update = updates.editItemAt(i);
|
||||
bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
|
||||
&& update.state.surfaceDrawn;
|
||||
bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
|
||||
bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
|
||||
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|
||||
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
|
||||
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
|
||||
| DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
|
||||
status_t status;
|
||||
if (!haveTransaction) {
|
||||
SurfaceComposerClient::openGlobalTransaction();
|
||||
haveTransaction = true;
|
||||
}
|
||||
|
||||
if (wantSurfaceVisibleAndDrawn
|
||||
&& (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
|
||||
status = update.state.surfaceControl->setAlpha(update.state.alpha);
|
||||
if (status) {
|
||||
ALOGE("Error %d setting sprite surface alpha.", status);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantSurfaceVisibleAndDrawn
|
||||
&& (becomingVisible || (update.state.dirty & (DIRTY_POSITION
|
||||
| DIRTY_HOTSPOT)))) {
|
||||
status = update.state.surfaceControl->setPosition(
|
||||
update.state.positionX - update.state.icon.hotSpotX,
|
||||
update.state.positionY - update.state.icon.hotSpotY);
|
||||
if (status) {
|
||||
ALOGE("Error %d setting sprite surface position.", status);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantSurfaceVisibleAndDrawn
|
||||
&& (becomingVisible
|
||||
|| (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
|
||||
status = update.state.surfaceControl->setMatrix(
|
||||
update.state.transformationMatrix.dsdx,
|
||||
update.state.transformationMatrix.dtdx,
|
||||
update.state.transformationMatrix.dsdy,
|
||||
update.state.transformationMatrix.dtdy);
|
||||
if (status) {
|
||||
ALOGE("Error %d setting sprite surface transformation matrix.", status);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
|
||||
if (wantSurfaceVisibleAndDrawn
|
||||
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
|
||||
status = update.state.surfaceControl->setLayer(surfaceLayer);
|
||||
if (status) {
|
||||
ALOGE("Error %d setting sprite surface layer.", status);
|
||||
}
|
||||
}
|
||||
|
||||
if (becomingVisible) {
|
||||
status = update.state.surfaceControl->show();
|
||||
if (status) {
|
||||
ALOGE("Error %d showing sprite surface.", status);
|
||||
} else {
|
||||
update.state.surfaceVisible = true;
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
}
|
||||
} else if (becomingHidden) {
|
||||
status = update.state.surfaceControl->hide();
|
||||
if (status) {
|
||||
ALOGE("Error %d hiding sprite surface.", status);
|
||||
} else {
|
||||
update.state.surfaceVisible = false;
|
||||
update.surfaceChanged = surfaceChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
if (haveTransaction) {
|
||||
SurfaceComposerClient::closeGlobalTransaction();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
// If any surfaces were changed, write back the new surface properties to the sprites.
|
||||
if (surfaceChanged) { // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
for (size_t i = 0; i < numSprites; i++) {
|
||||
const SpriteUpdate& update = updates.itemAt(i);
|
||||
|
||||
if (update.surfaceChanged) {
|
||||
update.sprite->setSurfaceLocked(update.state.surfaceControl,
|
||||
update.state.surfaceWidth, update.state.surfaceHeight,
|
||||
update.state.surfaceDrawn, update.state.surfaceVisible);
|
||||
}
|
||||
}
|
||||
} // release lock
|
||||
#endif
|
||||
|
||||
// Clear the sprite update vector outside the lock. It is very important that
|
||||
// we do not clear sprite references inside the lock since we could be releasing
|
||||
// the last remaining reference to the sprite here which would result in the
|
||||
// sprite being deleted and the lock being reacquired by the sprite destructor
|
||||
// while already held.
|
||||
updates.clear();
|
||||
}
|
||||
|
||||
void SpriteController::doDisposeSurfaces() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
// Collect disposed surfaces.
|
||||
Vector<sp<SurfaceControl> > disposedSurfaces;
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
disposedSurfaces = mLocked.disposedSurfaces;
|
||||
mLocked.disposedSurfaces.clear();
|
||||
} // release lock
|
||||
|
||||
// Release the last reference to each surface outside of the lock.
|
||||
// We don't want the surfaces to be deleted while we are holding our lock.
|
||||
disposedSurfaces.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpriteController::ensureSurfaceComposerClient() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
if (mSurfaceComposerClient == NULL) {
|
||||
mSurfaceComposerClient = new SurfaceComposerClient();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
|
||||
ensureSurfaceComposerClient();
|
||||
|
||||
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
|
||||
String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
|
||||
ISurfaceComposerClient::eHidden);
|
||||
if (surfaceControl == NULL || !surfaceControl->isValid()) {
|
||||
ALOGE("Error creating sprite surface.");
|
||||
return NULL;
|
||||
}
|
||||
return surfaceControl;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// --- SpriteController::SpriteImpl ---
|
||||
|
||||
SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
|
||||
mController(controller) {
|
||||
}
|
||||
|
||||
SpriteController::SpriteImpl::~SpriteImpl() {
|
||||
AutoMutex _m(mController->mLock);
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
// Let the controller take care of deleting the last reference to sprite
|
||||
// surfaces so that we do not block the caller on an IPC here.
|
||||
if (mLocked.state.surfaceControl != NULL) {
|
||||
mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
|
||||
mLocked.state.surfaceControl.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
uint32_t dirty;
|
||||
if (icon.isValid()) {
|
||||
icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config);
|
||||
|
||||
if (!mLocked.state.icon.isValid()
|
||||
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
|
||||
|| mLocked.state.icon.hotSpotY != icon.hotSpotY) {
|
||||
mLocked.state.icon.hotSpotX = icon.hotSpotX;
|
||||
mLocked.state.icon.hotSpotY = icon.hotSpotY;
|
||||
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
|
||||
} else {
|
||||
dirty = DIRTY_BITMAP;
|
||||
}
|
||||
} else if (mLocked.state.icon.isValid()) {
|
||||
mLocked.state.icon.bitmap.reset();
|
||||
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
|
||||
} else {
|
||||
return; // setting to invalid icon and already invalid so nothing to do
|
||||
}
|
||||
|
||||
invalidateLocked(dirty);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setVisible(bool visible) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
if (mLocked.state.visible != visible) {
|
||||
mLocked.state.visible = visible;
|
||||
invalidateLocked(DIRTY_VISIBILITY);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setPosition(float x, float y) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
|
||||
mLocked.state.positionX = x;
|
||||
mLocked.state.positionY = y;
|
||||
invalidateLocked(DIRTY_POSITION);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setLayer(int32_t layer) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
if (mLocked.state.layer != layer) {
|
||||
mLocked.state.layer = layer;
|
||||
invalidateLocked(DIRTY_LAYER);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setAlpha(float alpha) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
if (mLocked.state.alpha != alpha) {
|
||||
mLocked.state.alpha = alpha;
|
||||
invalidateLocked(DIRTY_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::setTransformationMatrix(
|
||||
const SpriteTransformationMatrix& matrix) {
|
||||
AutoMutex _l(mController->mLock);
|
||||
|
||||
if (mLocked.state.transformationMatrix != matrix) {
|
||||
mLocked.state.transformationMatrix = matrix;
|
||||
invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
|
||||
bool wasDirty = mLocked.state.dirty;
|
||||
mLocked.state.dirty |= dirty;
|
||||
|
||||
if (!wasDirty) {
|
||||
mController->invalidateSpriteLocked(this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,319 +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_SPRITES_H
|
||||
#define _UI_SPRITES_H
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <gui/SurfaceComposerClient.h>
|
||||
#endif
|
||||
|
||||
#include <SkBitmap.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Transformation matrix for a sprite.
|
||||
*/
|
||||
struct SpriteTransformationMatrix {
|
||||
inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
|
||||
inline SpriteTransformationMatrix(float dsdx, float dtdx, float dsdy, float dtdy) :
|
||||
dsdx(dsdx), dtdx(dtdx), dsdy(dsdy), dtdy(dtdy) { }
|
||||
|
||||
float dsdx;
|
||||
float dtdx;
|
||||
float dsdy;
|
||||
float dtdy;
|
||||
|
||||
inline bool operator== (const SpriteTransformationMatrix& other) {
|
||||
return dsdx == other.dsdx
|
||||
&& dtdx == other.dtdx
|
||||
&& dsdy == other.dsdy
|
||||
&& dtdy == other.dtdy;
|
||||
}
|
||||
|
||||
inline bool operator!= (const SpriteTransformationMatrix& other) {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Icon that a sprite displays, including its hotspot.
|
||||
*/
|
||||
struct SpriteIcon {
|
||||
inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
|
||||
bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
|
||||
|
||||
SkBitmap bitmap;
|
||||
#endif
|
||||
float hotSpotX;
|
||||
float hotSpotY;
|
||||
|
||||
inline SpriteIcon copy() const {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
SkBitmap bitmapCopy;
|
||||
bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config);
|
||||
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
|
||||
#else
|
||||
return SpriteIcon();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
bitmap.reset();
|
||||
hotSpotX = 0;
|
||||
hotSpotY = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool isValid() const {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
return !bitmap.isNull() && !bitmap.empty();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* A sprite is a simple graphical object that is displayed on-screen above other layers.
|
||||
* The basic sprite class is an interface.
|
||||
* The implementation is provided by the sprite controller.
|
||||
*/
|
||||
class Sprite : public RefBase {
|
||||
protected:
|
||||
Sprite() { }
|
||||
virtual ~Sprite() { }
|
||||
|
||||
public:
|
||||
enum {
|
||||
// The base layer for pointer sprites.
|
||||
BASE_LAYER_POINTER = 0, // reserve space for 1 pointer
|
||||
|
||||
// The base layer for spot sprites.
|
||||
BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots
|
||||
};
|
||||
|
||||
/* Sets the bitmap that is drawn by the sprite.
|
||||
* The sprite retains a copy of the bitmap for subsequent rendering. */
|
||||
virtual void setIcon(const SpriteIcon& icon) = 0;
|
||||
|
||||
inline void clearIcon() {
|
||||
setIcon(SpriteIcon());
|
||||
}
|
||||
|
||||
/* Sets whether the sprite is visible. */
|
||||
virtual void setVisible(bool visible) = 0;
|
||||
|
||||
/* Sets the sprite position on screen, relative to the sprite's hot spot. */
|
||||
virtual void setPosition(float x, float y) = 0;
|
||||
|
||||
/* Sets the layer of the sprite, relative to the system sprite overlay layer.
|
||||
* Layer 0 is the overlay layer, > 0 appear above this layer. */
|
||||
virtual void setLayer(int32_t layer) = 0;
|
||||
|
||||
/* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
|
||||
virtual void setAlpha(float alpha) = 0;
|
||||
|
||||
/* Sets the sprite transformation matrix. */
|
||||
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Displays sprites on the screen.
|
||||
*
|
||||
* This interface is used by PointerController and SpotController to draw pointers or
|
||||
* spot representations of fingers. It is not intended for general purpose use
|
||||
* by other components.
|
||||
*
|
||||
* All sprite position updates and rendering is performed asynchronously.
|
||||
*
|
||||
* Clients are responsible for animating sprites by periodically updating their properties.
|
||||
*/
|
||||
class SpriteController : public MessageHandler {
|
||||
protected:
|
||||
virtual ~SpriteController();
|
||||
|
||||
public:
|
||||
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
|
||||
|
||||
/* Creates a new sprite, initially invisible. */
|
||||
sp<Sprite> createSprite();
|
||||
|
||||
/* Opens or closes a transaction to perform a batch of sprite updates as part of
|
||||
* a single operation such as setPosition and setAlpha. It is not necessary to
|
||||
* open a transaction when updating a single property.
|
||||
* Calls to openTransaction() nest and must be matched by an equal number
|
||||
* of calls to closeTransaction(). */
|
||||
void openTransaction();
|
||||
void closeTransaction();
|
||||
|
||||
private:
|
||||
enum {
|
||||
MSG_UPDATE_SPRITES,
|
||||
MSG_DISPOSE_SURFACES,
|
||||
};
|
||||
|
||||
enum {
|
||||
DIRTY_BITMAP = 1 << 0,
|
||||
DIRTY_ALPHA = 1 << 1,
|
||||
DIRTY_POSITION = 1 << 2,
|
||||
DIRTY_TRANSFORMATION_MATRIX = 1 << 3,
|
||||
DIRTY_LAYER = 1 << 4,
|
||||
DIRTY_VISIBILITY = 1 << 5,
|
||||
DIRTY_HOTSPOT = 1 << 6,
|
||||
};
|
||||
|
||||
/* Describes the state of a sprite.
|
||||
* This structure is designed so that it can be copied during updates so that
|
||||
* surfaces can be resized and redrawn without blocking the client by holding a lock
|
||||
* on the sprites for a long time.
|
||||
* Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
|
||||
struct SpriteState {
|
||||
inline SpriteState() :
|
||||
dirty(0), visible(false),
|
||||
positionX(0), positionY(0), layer(0), alpha(1.0f),
|
||||
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
|
||||
}
|
||||
|
||||
uint32_t dirty;
|
||||
|
||||
SpriteIcon icon;
|
||||
bool visible;
|
||||
float positionX;
|
||||
float positionY;
|
||||
int32_t layer;
|
||||
float alpha;
|
||||
SpriteTransformationMatrix transformationMatrix;
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sp<SurfaceControl> surfaceControl;
|
||||
#endif
|
||||
int32_t surfaceWidth;
|
||||
int32_t surfaceHeight;
|
||||
bool surfaceDrawn;
|
||||
bool surfaceVisible;
|
||||
|
||||
inline bool wantSurfaceVisible() const {
|
||||
return visible && alpha > 0.0f && icon.isValid();
|
||||
}
|
||||
};
|
||||
|
||||
/* Client interface for a sprite.
|
||||
* Requests acquire a lock on the controller, update local state and request the
|
||||
* controller to invalidate the sprite.
|
||||
* The real heavy lifting of creating, resizing and redrawing surfaces happens
|
||||
* asynchronously with no locks held except in short critical section to copy
|
||||
* the sprite state before the work and update the sprite surface control afterwards.
|
||||
*/
|
||||
class SpriteImpl : public Sprite {
|
||||
protected:
|
||||
virtual ~SpriteImpl();
|
||||
|
||||
public:
|
||||
SpriteImpl(const sp<SpriteController> controller);
|
||||
|
||||
virtual void setIcon(const SpriteIcon& icon);
|
||||
virtual void setVisible(bool visible);
|
||||
virtual void setPosition(float x, float y);
|
||||
virtual void setLayer(int32_t layer);
|
||||
virtual void setAlpha(float alpha);
|
||||
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
|
||||
|
||||
inline const SpriteState& getStateLocked() const {
|
||||
return mLocked.state;
|
||||
}
|
||||
|
||||
inline void resetDirtyLocked() {
|
||||
mLocked.state.dirty = 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
|
||||
int32_t width, int32_t height, bool drawn, bool visible) {
|
||||
mLocked.state.surfaceControl = surfaceControl;
|
||||
mLocked.state.surfaceWidth = width;
|
||||
mLocked.state.surfaceHeight = height;
|
||||
mLocked.state.surfaceDrawn = drawn;
|
||||
mLocked.state.surfaceVisible = visible;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
sp<SpriteController> mController;
|
||||
|
||||
struct Locked {
|
||||
SpriteState state;
|
||||
} mLocked; // guarded by mController->mLock
|
||||
|
||||
void invalidateLocked(uint32_t dirty);
|
||||
};
|
||||
|
||||
/* Stores temporary information collected during the sprite update cycle. */
|
||||
struct SpriteUpdate {
|
||||
inline SpriteUpdate() : surfaceChanged(false) { }
|
||||
inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) :
|
||||
sprite(sprite), state(state), surfaceChanged(false) {
|
||||
}
|
||||
|
||||
sp<SpriteImpl> sprite;
|
||||
SpriteState state;
|
||||
bool surfaceChanged;
|
||||
};
|
||||
|
||||
mutable Mutex mLock;
|
||||
|
||||
sp<Looper> mLooper;
|
||||
const int32_t mOverlayLayer;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sp<WeakMessageHandler> mHandler;
|
||||
|
||||
sp<SurfaceComposerClient> mSurfaceComposerClient;
|
||||
#endif
|
||||
|
||||
struct Locked {
|
||||
Vector<sp<SpriteImpl> > invalidatedSprites;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
Vector<sp<SurfaceControl> > disposedSurfaces;
|
||||
#endif
|
||||
uint32_t transactionNestingCount;
|
||||
bool deferredSpriteUpdate;
|
||||
} mLocked; // guarded by mLock
|
||||
|
||||
void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
|
||||
|
||||
void handleMessage(const Message& message);
|
||||
#endif
|
||||
void doUpdateSprites();
|
||||
void doDisposeSurfaces();
|
||||
|
||||
void ensureSurfaceComposerClient();
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_SPRITES_H
|
|
@ -1,175 +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 "Tokenizer"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Tokenizer.h"
|
||||
|
||||
// Enables debug output for the tokenizer.
|
||||
#define DEBUG_TOKENIZER 0
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
static inline bool isDelimiter(char ch, const char* delimiters) {
|
||||
return strchr(delimiters, ch) != NULL;
|
||||
}
|
||||
|
||||
Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
|
||||
bool ownBuffer, size_t length) :
|
||||
mFilename(filename), mFileMap(fileMap),
|
||||
mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),
|
||||
mCurrent(buffer), mLineNumber(1) {
|
||||
}
|
||||
|
||||
Tokenizer::~Tokenizer() {
|
||||
if (mFileMap) {
|
||||
mFileMap->release();
|
||||
}
|
||||
if (mOwnBuffer) {
|
||||
delete[] mBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
|
||||
*outTokenizer = NULL;
|
||||
|
||||
int result = NO_ERROR;
|
||||
int fd = ::open(filename.string(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
|
||||
} else {
|
||||
struct stat stat;
|
||||
if (fstat(fd, &stat)) {
|
||||
result = -errno;
|
||||
ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
|
||||
} else {
|
||||
size_t length = size_t(stat.st_size);
|
||||
|
||||
FileMap* fileMap = new FileMap();
|
||||
bool ownBuffer = false;
|
||||
char* buffer;
|
||||
if (fileMap->create(NULL, fd, 0, length, true)) {
|
||||
fileMap->advise(FileMap::SEQUENTIAL);
|
||||
buffer = static_cast<char*>(fileMap->getDataPtr());
|
||||
} else {
|
||||
fileMap->release();
|
||||
fileMap = NULL;
|
||||
|
||||
// Fall back to reading into a buffer since we can't mmap files in sysfs.
|
||||
// The length we obtained from stat is wrong too (it will always be 4096)
|
||||
// so we must trust that read will read the entire file.
|
||||
buffer = new char[length];
|
||||
ownBuffer = true;
|
||||
ssize_t nrd = read(fd, buffer, length);
|
||||
if (nrd < 0) {
|
||||
result = -errno;
|
||||
ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
|
||||
delete[] buffer;
|
||||
buffer = NULL;
|
||||
} else {
|
||||
length = size_t(nrd);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
*outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t Tokenizer::fromContents(const String8& filename,
|
||||
const char* contents, Tokenizer** outTokenizer) {
|
||||
*outTokenizer = new Tokenizer(filename, NULL,
|
||||
const_cast<char*>(contents), false, strlen(contents));
|
||||
return OK;
|
||||
}
|
||||
|
||||
String8 Tokenizer::getLocation() const {
|
||||
String8 result;
|
||||
result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
|
||||
return result;
|
||||
}
|
||||
|
||||
String8 Tokenizer::peekRemainderOfLine() const {
|
||||
const char* end = getEnd();
|
||||
const char* eol = mCurrent;
|
||||
while (eol != end) {
|
||||
char ch = *eol;
|
||||
if (ch == '\n') {
|
||||
break;
|
||||
}
|
||||
eol += 1;
|
||||
}
|
||||
return String8(mCurrent, eol - mCurrent);
|
||||
}
|
||||
|
||||
String8 Tokenizer::nextToken(const char* delimiters) {
|
||||
#if DEBUG_TOKENIZER
|
||||
ALOGD("nextToken");
|
||||
#endif
|
||||
const char* end = getEnd();
|
||||
const char* tokenStart = mCurrent;
|
||||
while (mCurrent != end) {
|
||||
char ch = *mCurrent;
|
||||
if (ch == '\n' || isDelimiter(ch, delimiters)) {
|
||||
break;
|
||||
}
|
||||
mCurrent += 1;
|
||||
}
|
||||
return String8(tokenStart, mCurrent - tokenStart);
|
||||
}
|
||||
|
||||
void Tokenizer::nextLine() {
|
||||
#if DEBUG_TOKENIZER
|
||||
ALOGD("nextLine");
|
||||
#endif
|
||||
const char* end = getEnd();
|
||||
while (mCurrent != end) {
|
||||
char ch = *(mCurrent++);
|
||||
if (ch == '\n') {
|
||||
mLineNumber += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tokenizer::skipDelimiters(const char* delimiters) {
|
||||
#if DEBUG_TOKENIZER
|
||||
ALOGD("skipDelimiters");
|
||||
#endif
|
||||
const char* end = getEnd();
|
||||
while (mCurrent != end) {
|
||||
char ch = *mCurrent;
|
||||
if (ch == '\n' || !isDelimiter(ch, delimiters)) {
|
||||
break;
|
||||
}
|
||||
mCurrent += 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,136 +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 _UTILS_TOKENIZER_H
|
||||
#define _UTILS_TOKENIZER_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* A simple tokenizer for loading and parsing ASCII text files line by line.
|
||||
*/
|
||||
class Tokenizer {
|
||||
Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
|
||||
bool ownBuffer, size_t length);
|
||||
|
||||
public:
|
||||
~Tokenizer();
|
||||
|
||||
/**
|
||||
* Opens a file and maps it into memory.
|
||||
*
|
||||
* Returns NO_ERROR and a tokenizer for the file, if successful.
|
||||
* Otherwise returns an error and sets outTokenizer to NULL.
|
||||
*/
|
||||
static status_t open(const String8& filename, Tokenizer** outTokenizer);
|
||||
|
||||
/**
|
||||
* Prepares to tokenize the contents of a string.
|
||||
*
|
||||
* Returns NO_ERROR and a tokenizer for the string, if successful.
|
||||
* Otherwise returns an error and sets outTokenizer to NULL.
|
||||
*/
|
||||
static status_t fromContents(const String8& filename,
|
||||
const char* contents, Tokenizer** outTokenizer);
|
||||
|
||||
/**
|
||||
* Returns true if at the end of the file.
|
||||
*/
|
||||
inline bool isEof() const { return mCurrent == getEnd(); }
|
||||
|
||||
/**
|
||||
* Returns true if at the end of the line or end of the file.
|
||||
*/
|
||||
inline bool isEol() const { return isEof() || *mCurrent == '\n'; }
|
||||
|
||||
/**
|
||||
* Gets the name of the file.
|
||||
*/
|
||||
inline String8 getFilename() const { return mFilename; }
|
||||
|
||||
/**
|
||||
* Gets a 1-based line number index for the current position.
|
||||
*/
|
||||
inline int32_t getLineNumber() const { return mLineNumber; }
|
||||
|
||||
/**
|
||||
* Formats a location string consisting of the filename and current line number.
|
||||
* Returns a string like "MyFile.txt:33".
|
||||
*/
|
||||
String8 getLocation() const;
|
||||
|
||||
/**
|
||||
* Gets the character at the current position.
|
||||
* Returns null at end of file.
|
||||
*/
|
||||
inline char peekChar() const { return isEof() ? '\0' : *mCurrent; }
|
||||
|
||||
/**
|
||||
* Gets the remainder of the current line as a string, excluding the newline character.
|
||||
*/
|
||||
String8 peekRemainderOfLine() const;
|
||||
|
||||
/**
|
||||
* Gets the character at the current position and advances past it.
|
||||
* Returns null at end of file.
|
||||
*/
|
||||
inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); }
|
||||
|
||||
/**
|
||||
* Gets the next token on this line stopping at the specified delimiters
|
||||
* or the end of the line whichever comes first and advances past it.
|
||||
* Also stops at embedded nulls.
|
||||
* Returns the token or an empty string if the current character is a delimiter
|
||||
* or is at the end of the line.
|
||||
*/
|
||||
String8 nextToken(const char* delimiters);
|
||||
|
||||
/**
|
||||
* Advances to the next line.
|
||||
* Does nothing if already at the end of the file.
|
||||
*/
|
||||
void nextLine();
|
||||
|
||||
/**
|
||||
* Skips over the specified delimiters in the line.
|
||||
* Also skips embedded nulls.
|
||||
*/
|
||||
void skipDelimiters(const char* delimiters);
|
||||
|
||||
private:
|
||||
Tokenizer(const Tokenizer& other); // not copyable
|
||||
|
||||
String8 mFilename;
|
||||
FileMap* mFileMap;
|
||||
char* mBuffer;
|
||||
bool mOwnBuffer;
|
||||
size_t mLength;
|
||||
|
||||
const char* mCurrent;
|
||||
int32_t mLineNumber;
|
||||
|
||||
inline const char* getEnd() const { return mBuffer + mLength; }
|
||||
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UTILS_TOKENIZER_H
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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_TRACE_H
|
||||
#define ANDROID_TRACE_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/threads.h>
|
||||
#include "cutils_trace.h"
|
||||
|
||||
// See <cutils/trace.h> for more ATRACE_* macros.
|
||||
|
||||
// ATRACE_NAME traces the beginning and end of the current scope. To trace
|
||||
// the correct start and end times this macro should be declared first in the
|
||||
// scope body.
|
||||
#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
|
||||
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
|
||||
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
|
||||
|
||||
namespace android {
|
||||
|
||||
class ScopedTrace {
|
||||
public:
|
||||
inline ScopedTrace(uint64_t tag, const char* name)
|
||||
: mTag(tag) {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
atrace_begin(mTag,name);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ~ScopedTrace() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
atrace_end(mTag);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t mTag;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_TRACE_H
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "VelocityControl"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about acceleration.
|
||||
#define DEBUG_ACCELERATION 0
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "VelocityControl.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- VelocityControl ---
|
||||
|
||||
const nsecs_t VelocityControl::STOP_TIME;
|
||||
|
||||
VelocityControl::VelocityControl() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
|
||||
mParameters = parameters;
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::reset() {
|
||||
mLastMovementTime = LLONG_MIN;
|
||||
mRawPosition.x = 0;
|
||||
mRawPosition.y = 0;
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
|
||||
void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
|
||||
if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
|
||||
if (eventTime >= mLastMovementTime + STOP_TIME) {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl: stopped, last movement was %0.3fms ago",
|
||||
(eventTime - mLastMovementTime) * 0.000001f);
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
|
||||
mLastMovementTime = eventTime;
|
||||
if (deltaX) {
|
||||
mRawPosition.x += *deltaX;
|
||||
}
|
||||
if (deltaY) {
|
||||
mRawPosition.y += *deltaY;
|
||||
}
|
||||
mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
|
||||
|
||||
float vx, vy;
|
||||
float scale = mParameters.scale;
|
||||
if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
|
||||
float speed = hypotf(vx, vy) * scale;
|
||||
if (speed >= mParameters.highThreshold) {
|
||||
// Apply full acceleration above the high speed threshold.
|
||||
scale *= mParameters.acceleration;
|
||||
} else if (speed > mParameters.lowThreshold) {
|
||||
// Linearly interpolate the acceleration to apply between the low and high
|
||||
// speed thresholds.
|
||||
scale *= 1 + (speed - mParameters.lowThreshold)
|
||||
/ (mParameters.highThreshold - mParameters.lowThreshold)
|
||||
* (mParameters.acceleration - 1);
|
||||
}
|
||||
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
|
||||
"vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration,
|
||||
vx, vy, speed, scale / mParameters.scale);
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (deltaX) {
|
||||
*deltaX *= scale;
|
||||
}
|
||||
if (deltaY) {
|
||||
*deltaY *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* 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 _ANDROIDFW_VELOCITY_CONTROL_H
|
||||
#define _ANDROIDFW_VELOCITY_CONTROL_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "VelocityTracker.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Specifies parameters that govern pointer or wheel acceleration.
|
||||
*/
|
||||
struct VelocityControlParameters {
|
||||
// A scale factor that is multiplied with the raw velocity deltas
|
||||
// prior to applying any other velocity control factors. The scale
|
||||
// factor should be used to adapt the input device resolution
|
||||
// (eg. counts per inch) to the output device resolution (eg. pixels per inch).
|
||||
//
|
||||
// Must be a positive value.
|
||||
// Default is 1.0 (no scaling).
|
||||
float scale;
|
||||
|
||||
// The scaled speed at which acceleration begins to be applied.
|
||||
// This value establishes the upper bound of a low speed regime for
|
||||
// small precise motions that are performed without any acceleration.
|
||||
//
|
||||
// Must be a non-negative value.
|
||||
// Default is 0.0 (no low threshold).
|
||||
float lowThreshold;
|
||||
|
||||
// The scaled speed at which maximum acceleration is applied.
|
||||
// The difference between highThreshold and lowThreshold controls
|
||||
// the range of speeds over which the acceleration factor is interpolated.
|
||||
// The wider the range, the smoother the acceleration.
|
||||
//
|
||||
// Must be a non-negative value greater than or equal to lowThreshold.
|
||||
// Default is 0.0 (no high threshold).
|
||||
float highThreshold;
|
||||
|
||||
// The acceleration factor.
|
||||
// When the speed is above the low speed threshold, the velocity will scaled
|
||||
// by an interpolated value between 1.0 and this amount.
|
||||
//
|
||||
// Must be a positive greater than or equal to 1.0.
|
||||
// Default is 1.0 (no acceleration).
|
||||
float acceleration;
|
||||
|
||||
VelocityControlParameters() :
|
||||
scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
|
||||
}
|
||||
|
||||
VelocityControlParameters(float scale, float lowThreshold,
|
||||
float highThreshold, float acceleration) :
|
||||
scale(scale), lowThreshold(lowThreshold),
|
||||
highThreshold(highThreshold), acceleration(acceleration) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implements mouse pointer and wheel speed control and acceleration.
|
||||
*/
|
||||
class VelocityControl {
|
||||
public:
|
||||
VelocityControl();
|
||||
|
||||
/* Sets the various parameters. */
|
||||
void setParameters(const VelocityControlParameters& parameters);
|
||||
|
||||
/* Resets the current movement counters to zero.
|
||||
* This has the effect of nullifying any acceleration. */
|
||||
void reset();
|
||||
|
||||
/* Translates a raw movement delta into an appropriately
|
||||
* scaled / accelerated delta based on the current velocity. */
|
||||
void move(nsecs_t eventTime, float* deltaX, float* deltaY);
|
||||
|
||||
private:
|
||||
// If no movements are received within this amount of time,
|
||||
// we assume the movement has stopped and reset the movement counters.
|
||||
static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms
|
||||
|
||||
VelocityControlParameters mParameters;
|
||||
|
||||
nsecs_t mLastMovementTime;
|
||||
VelocityTracker::Position mRawPosition;
|
||||
VelocityTracker mVelocityTracker;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_VELOCITY_CONTROL_H
|
|
@ -1,929 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "VelocityTracker"
|
||||
//#define LOG_NDEBUG 0
|
||||
#include "cutils_log.h"
|
||||
|
||||
// Log debug messages about velocity tracking.
|
||||
#define DEBUG_VELOCITY 0
|
||||
|
||||
// Log debug messages about the progress of the algorithm itself.
|
||||
#define DEBUG_STRATEGY 0
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "VelocityTracker.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// Nanoseconds per milliseconds.
|
||||
static const nsecs_t NANOS_PER_MS = 1000000;
|
||||
|
||||
// Threshold for determining that a pointer has stopped moving.
|
||||
// Some input devices do not send ACTION_MOVE events in the case where a pointer has
|
||||
// stopped. We need to detect this case so that we can accurately predict the
|
||||
// velocity after the pointer starts moving again.
|
||||
static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS;
|
||||
|
||||
|
||||
static float vectorDot(const float* a, const float* b, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
r += *(a++) * *(b++);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static float vectorNorm(const float* a, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
float t = *(a++);
|
||||
r += t * t;
|
||||
}
|
||||
return sqrtf(r);
|
||||
}
|
||||
|
||||
#if DEBUG_STRATEGY || DEBUG_VELOCITY
|
||||
static String8 vectorToString(const float* a, uint32_t m) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
while (m--) {
|
||||
str.appendFormat(" %f", *(a++));
|
||||
if (m) {
|
||||
str.append(",");
|
||||
}
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
|
||||
static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
if (i) {
|
||||
str.append(",");
|
||||
}
|
||||
str.append(" [");
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
if (j) {
|
||||
str.append(",");
|
||||
}
|
||||
str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
|
||||
}
|
||||
str.append(" ]");
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// --- VelocityTracker ---
|
||||
|
||||
// The default velocity tracker strategy.
|
||||
// Although other strategies are available for testing and comparison purposes,
|
||||
// this is the strategy that applications will actually use. Be very careful
|
||||
// when adjusting the default strategy because it can dramatically affect
|
||||
// (often in a bad way) the user experience.
|
||||
const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
|
||||
|
||||
VelocityTracker::VelocityTracker(const char* strategy) :
|
||||
mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
// Allow the default strategy to be overridden using a system property for debugging.
|
||||
if (!strategy) {
|
||||
int length = property_get("debug.velocitytracker.strategy", value, NULL);
|
||||
if (length > 0) {
|
||||
strategy = value;
|
||||
} else {
|
||||
strategy = DEFAULT_STRATEGY;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the strategy.
|
||||
if (!configureStrategy(strategy)) {
|
||||
ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
|
||||
if (!configureStrategy(DEFAULT_STRATEGY)) {
|
||||
LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
|
||||
strategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VelocityTracker::~VelocityTracker() {
|
||||
delete mStrategy;
|
||||
}
|
||||
|
||||
bool VelocityTracker::configureStrategy(const char* strategy) {
|
||||
mStrategy = createStrategy(strategy);
|
||||
return mStrategy != NULL;
|
||||
}
|
||||
|
||||
VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
|
||||
if (!strcmp("lsq1", strategy)) {
|
||||
// 1st order least squares. Quality: POOR.
|
||||
// Frequently underfits the touch data especially when the finger accelerates
|
||||
// or changes direction. Often underestimates velocity. The direction
|
||||
// is overly influenced by historical touch points.
|
||||
return new LeastSquaresVelocityTrackerStrategy(1);
|
||||
}
|
||||
if (!strcmp("lsq2", strategy)) {
|
||||
// 2nd order least squares. Quality: VERY GOOD.
|
||||
// Pretty much ideal, but can be confused by certain kinds of touch data,
|
||||
// particularly if the panel has a tendency to generate delayed,
|
||||
// duplicate or jittery touch coordinates when the finger is released.
|
||||
return new LeastSquaresVelocityTrackerStrategy(2);
|
||||
}
|
||||
if (!strcmp("lsq3", strategy)) {
|
||||
// 3rd order least squares. Quality: UNUSABLE.
|
||||
// Frequently overfits the touch data yielding wildly divergent estimates
|
||||
// of the velocity when the finger is released.
|
||||
return new LeastSquaresVelocityTrackerStrategy(3);
|
||||
}
|
||||
if (!strcmp("wlsq2-delta", strategy)) {
|
||||
// 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
|
||||
}
|
||||
if (!strcmp("wlsq2-central", strategy)) {
|
||||
// 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
|
||||
}
|
||||
if (!strcmp("wlsq2-recent", strategy)) {
|
||||
// 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
|
||||
}
|
||||
if (!strcmp("int1", strategy)) {
|
||||
// 1st order integrating filter. Quality: GOOD.
|
||||
// Not as good as 'lsq2' because it cannot estimate acceleration but it is
|
||||
// more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
|
||||
// the velocity of a fling but this strategy tends to respond to changes in
|
||||
// direction more quickly and accurately.
|
||||
return new IntegratingVelocityTrackerStrategy(1);
|
||||
}
|
||||
if (!strcmp("int2", strategy)) {
|
||||
// 2nd order integrating filter. Quality: EXPERIMENTAL.
|
||||
// For comparison purposes only. Unlike 'int1' this strategy can compensate
|
||||
// for acceleration but it typically overestimates the effect.
|
||||
return new IntegratingVelocityTrackerStrategy(2);
|
||||
}
|
||||
if (!strcmp("legacy", strategy)) {
|
||||
// Legacy velocity tracker algorithm. Quality: POOR.
|
||||
// For comparison purposes only. This algorithm is strongly influenced by
|
||||
// old data points, consistently underestimates velocity and takes a very long
|
||||
// time to adjust to changes in direction.
|
||||
return new LegacyVelocityTrackerStrategy();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VelocityTracker::clear() {
|
||||
mCurrentPointerIdBits.clear();
|
||||
mActivePointerId = -1;
|
||||
|
||||
mStrategy->clear();
|
||||
}
|
||||
|
||||
void VelocityTracker::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value);
|
||||
mCurrentPointerIdBits = remainingIdBits;
|
||||
|
||||
if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
|
||||
}
|
||||
|
||||
mStrategy->clearPointers(idBits);
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
|
||||
while (idBits.count() > MAX_POINTERS) {
|
||||
idBits.clearLastMarkedBit();
|
||||
}
|
||||
|
||||
if ((mCurrentPointerIdBits.value & idBits.value)
|
||||
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
|
||||
#if DEBUG_VELOCITY
|
||||
ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
|
||||
(eventTime - mLastEventTime) * 0.000001f);
|
||||
#endif
|
||||
// We have not received any movements for too long. Assume that all pointers
|
||||
// have stopped.
|
||||
mStrategy->clear();
|
||||
}
|
||||
mLastEventTime = eventTime;
|
||||
|
||||
mCurrentPointerIdBits = idBits;
|
||||
if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
|
||||
}
|
||||
|
||||
mStrategy->addMovement(eventTime, idBits, positions);
|
||||
|
||||
#if DEBUG_VELOCITY
|
||||
ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
|
||||
eventTime, idBits.value, mActivePointerId);
|
||||
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
|
||||
uint32_t id = iterBits.firstMarkedBit();
|
||||
uint32_t index = idBits.getIndexOfBit(id);
|
||||
iterBits.clearBit(id);
|
||||
Estimator estimator;
|
||||
getEstimator(id, &estimator);
|
||||
ALOGD(" %d: position (%0.3f, %0.3f), "
|
||||
"estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
|
||||
id, positions[index].x, positions[index].y,
|
||||
int(estimator.degree),
|
||||
vectorToString(estimator.xCoeff, estimator.degree + 1).string(),
|
||||
vectorToString(estimator.yCoeff, estimator.degree + 1).string(),
|
||||
estimator.confidence);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(const MotionEvent* event) {
|
||||
int32_t actionMasked = event->getActionMasked();
|
||||
|
||||
switch (actionMasked) {
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_HOVER_ENTER:
|
||||
// Clear all pointers on down before adding the new movement.
|
||||
clear();
|
||||
break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
|
||||
// Start a new movement trace for a pointer that just went down.
|
||||
// We do this on down instead of on up because the client may want to query the
|
||||
// final velocity for a pointer that just went up.
|
||||
BitSet32 downIdBits;
|
||||
downIdBits.markBit(event->getPointerId(event->getActionIndex()));
|
||||
clearPointers(downIdBits);
|
||||
break;
|
||||
}
|
||||
case AMOTION_EVENT_ACTION_MOVE:
|
||||
case AMOTION_EVENT_ACTION_HOVER_MOVE:
|
||||
break;
|
||||
default:
|
||||
// Ignore all other actions because they do not convey any new information about
|
||||
// pointer movement. We also want to preserve the last known velocity of the pointers.
|
||||
// Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
|
||||
// of the pointers that went up. ACTION_POINTER_UP does include the new position of
|
||||
// pointers that remained down but we will also receive an ACTION_MOVE with this
|
||||
// information if any of them actually moved. Since we don't know how many pointers
|
||||
// will be going up at once it makes sense to just wait for the following ACTION_MOVE
|
||||
// before adding the movement.
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pointerCount = event->getPointerCount();
|
||||
if (pointerCount > MAX_POINTERS) {
|
||||
pointerCount = MAX_POINTERS;
|
||||
}
|
||||
|
||||
BitSet32 idBits;
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
idBits.markBit(event->getPointerId(i));
|
||||
}
|
||||
|
||||
uint32_t pointerIndex[MAX_POINTERS];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
|
||||
}
|
||||
|
||||
nsecs_t eventTime;
|
||||
Position positions[pointerCount];
|
||||
|
||||
size_t historySize = event->getHistorySize();
|
||||
for (size_t h = 0; h < historySize; h++) {
|
||||
eventTime = event->getHistoricalEventTime(h);
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event->getHistoricalX(i, h);
|
||||
positions[index].y = event->getHistoricalY(i, h);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
eventTime = event->getEventTime();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event->getX(i);
|
||||
positions[index].y = event->getY(i);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
|
||||
Estimator estimator;
|
||||
if (getEstimator(id, &estimator) && estimator.degree >= 1) {
|
||||
*outVx = estimator.xCoeff[1];
|
||||
*outVy = estimator.yCoeff[1];
|
||||
return true;
|
||||
}
|
||||
*outVx = 0;
|
||||
*outVy = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
|
||||
return mStrategy->getEstimator(id, outEstimator);
|
||||
}
|
||||
|
||||
|
||||
// --- LeastSquaresVelocityTrackerStrategy ---
|
||||
|
||||
const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON;
|
||||
const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE;
|
||||
|
||||
LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
|
||||
uint32_t degree, Weighting weighting) :
|
||||
mDegree(degree), mWeighting(weighting) {
|
||||
clear();
|
||||
}
|
||||
|
||||
LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
|
||||
mMovements[mIndex].idBits = remainingIdBits;
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves a linear least squares problem to obtain a N degree polynomial that fits
|
||||
* the specified input data as nearly as possible.
|
||||
*
|
||||
* Returns true if a solution is found, false otherwise.
|
||||
*
|
||||
* The input consists of two vectors of data points X and Y with indices 0..m-1
|
||||
* along with a weight vector W of the same size.
|
||||
*
|
||||
* The output is a vector B with indices 0..n that describes a polynomial
|
||||
* that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i]
|
||||
* + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized.
|
||||
*
|
||||
* Accordingly, the weight vector W should be initialized by the caller with the
|
||||
* reciprocal square root of the variance of the error in each input data point.
|
||||
* In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]).
|
||||
* The weights express the relative importance of each data point. If the weights are
|
||||
* all 1, then the data points are considered to be of equal importance when fitting
|
||||
* the polynomial. It is a good idea to choose weights that diminish the importance
|
||||
* of data points that may have higher than usual error margins.
|
||||
*
|
||||
* Errors among data points are assumed to be independent. W is represented here
|
||||
* as a vector although in the literature it is typically taken to be a diagonal matrix.
|
||||
*
|
||||
* That is to say, the function that generated the input data can be approximated
|
||||
* by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
|
||||
*
|
||||
* The coefficient of determination (R^2) is also returned to describe the goodness
|
||||
* of fit of the model for the given data. It is a value between 0 and 1, where 1
|
||||
* indicates perfect correspondence.
|
||||
*
|
||||
* This function first expands the X vector to a m by n matrix A such that
|
||||
* A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
|
||||
* multiplies it by w[i]./
|
||||
*
|
||||
* Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
|
||||
* and an m by n upper triangular matrix R. Because R is upper triangular (lower
|
||||
* part is all zeroes), we can simplify the decomposition into an m by n matrix
|
||||
* Q1 and a n by n matrix R1 such that A = Q1 R1.
|
||||
*
|
||||
* Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y)
|
||||
* to find B.
|
||||
*
|
||||
* For efficiency, we lay out A and Q column-wise in memory because we frequently
|
||||
* operate on the column vectors. Conversely, we lay out R row-wise.
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
|
||||
* http://en.wikipedia.org/wiki/Gram-Schmidt
|
||||
*/
|
||||
static bool solveLeastSquares(const float* x, const float* y,
|
||||
const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
|
||||
vectorToString(x, m).string(), vectorToString(y, m).string(),
|
||||
vectorToString(w, m).string());
|
||||
#endif
|
||||
|
||||
// Expand the X vector to a matrix A, pre-multiplied by the weights.
|
||||
float a[n][m]; // column-major order
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
a[0][h] = w[h];
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
a[i][h] = a[i - 1][h] * x[h];
|
||||
}
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
|
||||
float q[n][m]; // orthonormal basis, column-major order
|
||||
float r[n][n]; // upper triangular matrix, row-major order
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] = a[j][h];
|
||||
}
|
||||
for (uint32_t i = 0; i < j; i++) {
|
||||
float dot = vectorDot(&q[j][0], &q[i][0], m);
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] -= dot * q[i][h];
|
||||
}
|
||||
}
|
||||
|
||||
float norm = vectorNorm(&q[j][0], m);
|
||||
if (norm < 0.000001f) {
|
||||
// vectors are linearly dependent or zero so no solution
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - no solution, norm=%f", norm);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
float invNorm = 1.0f / norm;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] *= invNorm;
|
||||
}
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
|
||||
}
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
|
||||
ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
|
||||
|
||||
// calculate QR, if we factored A correctly then QR should equal A
|
||||
float qr[n][m];
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
qr[i][h] = 0;
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
qr[i][h] += q[j][h] * r[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
|
||||
// We just work from bottom-right to top-left calculating B's coefficients.
|
||||
float wy[m];
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
wy[h] = y[h] * w[h];
|
||||
}
|
||||
for (uint32_t i = n; i-- != 0; ) {
|
||||
outB[i] = vectorDot(&q[i][0], wy, m);
|
||||
for (uint32_t j = n - 1; j > i; j--) {
|
||||
outB[i] -= r[i][j] * outB[j];
|
||||
}
|
||||
outB[i] /= r[i][i];
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - b=%s", vectorToString(outB, n).string());
|
||||
#endif
|
||||
|
||||
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
|
||||
// SSerr is the residual sum of squares (variance of the error),
|
||||
// and SStot is the total sum of squares (variance of the data) where each
|
||||
// has been weighted.
|
||||
float ymean = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
ymean += y[h];
|
||||
}
|
||||
ymean /= m;
|
||||
|
||||
float sserr = 0;
|
||||
float sstot = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
float err = y[h] - outB[0];
|
||||
float term = 1;
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
term *= x[h];
|
||||
err -= term * outB[i];
|
||||
}
|
||||
sserr += w[h] * w[h] * err * err;
|
||||
float var = y[h] - ymean;
|
||||
sstot += w[h] * w[h] * var * var;
|
||||
}
|
||||
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - sserr=%f", sserr);
|
||||
ALOGD(" - sstot=%f", sstot);
|
||||
ALOGD(" - det=%f", *outDet);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
// Iterate over movement samples in reverse time order and collect samples.
|
||||
float x[HISTORY_SIZE];
|
||||
float y[HISTORY_SIZE];
|
||||
float w[HISTORY_SIZE];
|
||||
float time[HISTORY_SIZE];
|
||||
uint32_t m = 0;
|
||||
uint32_t index = mIndex;
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
do {
|
||||
const Movement& movement = mMovements[index];
|
||||
if (!movement.idBits.hasBit(id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsecs_t age = newestMovement.eventTime - movement.eventTime;
|
||||
if (age > HORIZON) {
|
||||
break;
|
||||
}
|
||||
|
||||
const VelocityTracker::Position& position = movement.getPosition(id);
|
||||
x[m] = position.x;
|
||||
y[m] = position.y;
|
||||
w[m] = chooseWeight(index);
|
||||
time[m] = -age * 0.000000001f;
|
||||
index = (index == 0 ? HISTORY_SIZE : index) - 1;
|
||||
} while (++m < HISTORY_SIZE);
|
||||
|
||||
if (m == 0) {
|
||||
return false; // no data
|
||||
}
|
||||
|
||||
// Calculate a least squares polynomial fit.
|
||||
uint32_t degree = mDegree;
|
||||
if (degree > m - 1) {
|
||||
degree = m - 1;
|
||||
}
|
||||
if (degree >= 1) {
|
||||
float xdet, ydet;
|
||||
uint32_t n = degree + 1;
|
||||
if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
|
||||
&& solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->degree = degree;
|
||||
outEstimator->confidence = xdet * ydet;
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
|
||||
int(outEstimator->degree),
|
||||
vectorToString(outEstimator->xCoeff, n).string(),
|
||||
vectorToString(outEstimator->yCoeff, n).string(),
|
||||
outEstimator->confidence);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No velocity data available for this pointer, but we do have its current position.
|
||||
outEstimator->xCoeff[0] = x[0];
|
||||
outEstimator->yCoeff[0] = y[0];
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->degree = 0;
|
||||
outEstimator->confidence = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const {
|
||||
switch (mWeighting) {
|
||||
case WEIGHTING_DELTA: {
|
||||
// Weight points based on how much time elapsed between them and the next
|
||||
// point so that points that "cover" a shorter time span are weighed less.
|
||||
// delta 0ms: 0.5
|
||||
// delta 10ms: 1.0
|
||||
if (index == mIndex) {
|
||||
return 1.0f;
|
||||
}
|
||||
uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
|
||||
float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (deltaMillis < 0) {
|
||||
return 0.5f;
|
||||
}
|
||||
if (deltaMillis < 10) {
|
||||
return 0.5f + deltaMillis * 0.05;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
case WEIGHTING_CENTRAL: {
|
||||
// Weight points based on their age, weighing very recent and very old points less.
|
||||
// age 0ms: 0.5
|
||||
// age 10ms: 1.0
|
||||
// age 50ms: 1.0
|
||||
// age 60ms: 0.5
|
||||
float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (ageMillis < 0) {
|
||||
return 0.5f;
|
||||
}
|
||||
if (ageMillis < 10) {
|
||||
return 0.5f + ageMillis * 0.05;
|
||||
}
|
||||
if (ageMillis < 50) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (ageMillis < 60) {
|
||||
return 0.5f + (60 - ageMillis) * 0.05;
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
case WEIGHTING_RECENT: {
|
||||
// Weight points based on their age, weighing older points less.
|
||||
// age 0ms: 1.0
|
||||
// age 50ms: 1.0
|
||||
// age 100ms: 0.5
|
||||
float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (ageMillis < 50) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (ageMillis < 100) {
|
||||
return 0.5f + (100 - ageMillis) * 0.01f;
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
case WEIGHTING_NONE:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- IntegratingVelocityTrackerStrategy ---
|
||||
|
||||
IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) :
|
||||
mDegree(degree) {
|
||||
}
|
||||
|
||||
IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::clear() {
|
||||
mPointerIdBits.clear();
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
mPointerIdBits.value &= ~idBits.value;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
uint32_t index = 0;
|
||||
for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
|
||||
uint32_t id = iterIdBits.clearFirstMarkedBit();
|
||||
State& state = mPointerState[id];
|
||||
const VelocityTracker::Position& position = positions[index++];
|
||||
if (mPointerIdBits.hasBit(id)) {
|
||||
updateState(state, eventTime, position.x, position.y);
|
||||
} else {
|
||||
initState(state, eventTime, position.x, position.y);
|
||||
}
|
||||
}
|
||||
|
||||
mPointerIdBits = idBits;
|
||||
}
|
||||
|
||||
bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
if (mPointerIdBits.hasBit(id)) {
|
||||
const State& state = mPointerState[id];
|
||||
populateEstimator(state, outEstimator);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::initState(State& state,
|
||||
nsecs_t eventTime, float xpos, float ypos) const {
|
||||
state.updateTime = eventTime;
|
||||
state.degree = 0;
|
||||
|
||||
state.xpos = xpos;
|
||||
state.xvel = 0;
|
||||
state.xaccel = 0;
|
||||
state.ypos = ypos;
|
||||
state.yvel = 0;
|
||||
state.yaccel = 0;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::updateState(State& state,
|
||||
nsecs_t eventTime, float xpos, float ypos) const {
|
||||
const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
|
||||
const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
|
||||
|
||||
if (eventTime <= state.updateTime + MIN_TIME_DELTA) {
|
||||
return;
|
||||
}
|
||||
|
||||
float dt = (eventTime - state.updateTime) * 0.000000001f;
|
||||
state.updateTime = eventTime;
|
||||
|
||||
float xvel = (xpos - state.xpos) / dt;
|
||||
float yvel = (ypos - state.ypos) / dt;
|
||||
if (state.degree == 0) {
|
||||
state.xvel = xvel;
|
||||
state.yvel = yvel;
|
||||
state.degree = 1;
|
||||
} else {
|
||||
float alpha = dt / (FILTER_TIME_CONSTANT + dt);
|
||||
if (mDegree == 1) {
|
||||
state.xvel += (xvel - state.xvel) * alpha;
|
||||
state.yvel += (yvel - state.yvel) * alpha;
|
||||
} else {
|
||||
float xaccel = (xvel - state.xvel) / dt;
|
||||
float yaccel = (yvel - state.yvel) / dt;
|
||||
if (state.degree == 1) {
|
||||
state.xaccel = xaccel;
|
||||
state.yaccel = yaccel;
|
||||
state.degree = 2;
|
||||
} else {
|
||||
state.xaccel += (xaccel - state.xaccel) * alpha;
|
||||
state.yaccel += (yaccel - state.yaccel) * alpha;
|
||||
}
|
||||
state.xvel += (state.xaccel * dt) * alpha;
|
||||
state.yvel += (state.yaccel * dt) * alpha;
|
||||
}
|
||||
}
|
||||
state.xpos = xpos;
|
||||
state.ypos = ypos;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->time = state.updateTime;
|
||||
outEstimator->confidence = 1.0f;
|
||||
outEstimator->degree = state.degree;
|
||||
outEstimator->xCoeff[0] = state.xpos;
|
||||
outEstimator->xCoeff[1] = state.xvel;
|
||||
outEstimator->xCoeff[2] = state.xaccel / 2;
|
||||
outEstimator->yCoeff[0] = state.ypos;
|
||||
outEstimator->yCoeff[1] = state.yvel;
|
||||
outEstimator->yCoeff[2] = state.yaccel / 2;
|
||||
}
|
||||
|
||||
|
||||
// --- LegacyVelocityTrackerStrategy ---
|
||||
|
||||
const nsecs_t LegacyVelocityTrackerStrategy::HORIZON;
|
||||
const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE;
|
||||
const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION;
|
||||
|
||||
LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
|
||||
clear();
|
||||
}
|
||||
|
||||
LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
|
||||
mMovements[mIndex].idBits = remainingIdBits;
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
if (!newestMovement.idBits.hasBit(id)) {
|
||||
return false; // no data
|
||||
}
|
||||
|
||||
// Find the oldest sample that contains the pointer and that is not older than HORIZON.
|
||||
nsecs_t minTime = newestMovement.eventTime - HORIZON;
|
||||
uint32_t oldestIndex = mIndex;
|
||||
uint32_t numTouches = 1;
|
||||
do {
|
||||
uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
|
||||
const Movement& nextOldestMovement = mMovements[nextOldestIndex];
|
||||
if (!nextOldestMovement.idBits.hasBit(id)
|
||||
|| nextOldestMovement.eventTime < minTime) {
|
||||
break;
|
||||
}
|
||||
oldestIndex = nextOldestIndex;
|
||||
} while (++numTouches < HISTORY_SIZE);
|
||||
|
||||
// Calculate an exponentially weighted moving average of the velocity estimate
|
||||
// at different points in time measured relative to the oldest sample.
|
||||
// This is essentially an IIR filter. Newer samples are weighted more heavily
|
||||
// than older samples. Samples at equal time points are weighted more or less
|
||||
// equally.
|
||||
//
|
||||
// One tricky problem is that the sample data may be poorly conditioned.
|
||||
// Sometimes samples arrive very close together in time which can cause us to
|
||||
// overestimate the velocity at that time point. Most samples might be measured
|
||||
// 16ms apart but some consecutive samples could be only 0.5sm apart because
|
||||
// the hardware or driver reports them irregularly or in bursts.
|
||||
float accumVx = 0;
|
||||
float accumVy = 0;
|
||||
uint32_t index = oldestIndex;
|
||||
uint32_t samplesUsed = 0;
|
||||
const Movement& oldestMovement = mMovements[oldestIndex];
|
||||
const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
|
||||
nsecs_t lastDuration = 0;
|
||||
|
||||
while (numTouches-- > 1) {
|
||||
if (++index == HISTORY_SIZE) {
|
||||
index = 0;
|
||||
}
|
||||
const Movement& movement = mMovements[index];
|
||||
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
|
||||
|
||||
// If the duration between samples is small, we may significantly overestimate
|
||||
// the velocity. Consequently, we impose a minimum duration constraint on the
|
||||
// samples that we include in the calculation.
|
||||
if (duration >= MIN_DURATION) {
|
||||
const VelocityTracker::Position& position = movement.getPosition(id);
|
||||
float scale = 1000000000.0f / duration; // one over time delta in seconds
|
||||
float vx = (position.x - oldestPosition.x) * scale;
|
||||
float vy = (position.y - oldestPosition.y) * scale;
|
||||
accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
|
||||
accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
|
||||
lastDuration = duration;
|
||||
samplesUsed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Report velocity.
|
||||
const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->confidence = 1;
|
||||
outEstimator->xCoeff[0] = newestPosition.x;
|
||||
outEstimator->yCoeff[0] = newestPosition.y;
|
||||
if (samplesUsed) {
|
||||
outEstimator->xCoeff[1] = accumVx;
|
||||
outEstimator->yCoeff[1] = accumVy;
|
||||
outEstimator->degree = 1;
|
||||
} else {
|
||||
outEstimator->degree = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,269 +0,0 @@
|
|||
/*
|
||||
* 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 _ANDROIDFW_VELOCITY_TRACKER_H
|
||||
#define _ANDROIDFW_VELOCITY_TRACKER_H
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class VelocityTrackerStrategy;
|
||||
|
||||
/*
|
||||
* Calculates the velocity of pointer movements over time.
|
||||
*/
|
||||
class VelocityTracker {
|
||||
public:
|
||||
struct Position {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct Estimator {
|
||||
static const size_t MAX_DEGREE = 4;
|
||||
|
||||
// Estimator time base.
|
||||
nsecs_t time;
|
||||
|
||||
// Polynomial coefficients describing motion in X and Y.
|
||||
float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
|
||||
|
||||
// Polynomial degree (number of coefficients), or zero if no information is
|
||||
// available.
|
||||
uint32_t degree;
|
||||
|
||||
// Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
|
||||
float confidence;
|
||||
|
||||
inline void clear() {
|
||||
time = 0;
|
||||
degree = 0;
|
||||
confidence = 0;
|
||||
for (size_t i = 0; i <= MAX_DEGREE; i++) {
|
||||
xCoeff[i] = 0;
|
||||
yCoeff[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a velocity tracker using the specified strategy.
|
||||
// If strategy is NULL, uses the default strategy for the platform.
|
||||
VelocityTracker(const char* strategy = NULL);
|
||||
|
||||
~VelocityTracker();
|
||||
|
||||
// Resets the velocity tracker state.
|
||||
void clear();
|
||||
|
||||
// Resets the velocity tracker state for specific pointers.
|
||||
// Call this method when some pointers have changed and may be reusing
|
||||
// an id that was assigned to a different pointer earlier.
|
||||
void clearPointers(BitSet32 idBits);
|
||||
|
||||
// Adds movement information for a set of pointers.
|
||||
// The idBits bitfield specifies the pointer ids of the pointers whose positions
|
||||
// are included in the movement.
|
||||
// The positions array contains position information for each pointer in order by
|
||||
// increasing id. Its size should be equal to the number of one bits in idBits.
|
||||
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
|
||||
|
||||
// Adds movement information for all pointers in a MotionEvent, including historical samples.
|
||||
void addMovement(const MotionEvent* event);
|
||||
|
||||
// Gets the velocity of the specified pointer id in position units per second.
|
||||
// Returns false and sets the velocity components to zero if there is
|
||||
// insufficient movement information for the pointer.
|
||||
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
|
||||
|
||||
// Gets an estimator for the recent movements of the specified pointer id.
|
||||
// Returns false and clears the estimator if there is no information available
|
||||
// about the pointer.
|
||||
bool getEstimator(uint32_t id, Estimator* outEstimator) const;
|
||||
|
||||
// Gets the active pointer id, or -1 if none.
|
||||
inline int32_t getActivePointerId() const { return mActivePointerId; }
|
||||
|
||||
// Gets a bitset containing all pointer ids from the most recent movement.
|
||||
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
|
||||
|
||||
private:
|
||||
static const char* DEFAULT_STRATEGY;
|
||||
|
||||
nsecs_t mLastEventTime;
|
||||
BitSet32 mCurrentPointerIdBits;
|
||||
int32_t mActivePointerId;
|
||||
VelocityTrackerStrategy* mStrategy;
|
||||
|
||||
bool configureStrategy(const char* strategy);
|
||||
|
||||
static VelocityTrackerStrategy* createStrategy(const char* strategy);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Implements a particular velocity tracker algorithm.
|
||||
*/
|
||||
class VelocityTrackerStrategy {
|
||||
protected:
|
||||
VelocityTrackerStrategy() { }
|
||||
|
||||
public:
|
||||
virtual ~VelocityTrackerStrategy() { }
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual void clearPointers(BitSet32 idBits) = 0;
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) = 0;
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker algorithm based on least-squares linear regression.
|
||||
*/
|
||||
class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
enum Weighting {
|
||||
// No weights applied. All data points are equally reliable.
|
||||
WEIGHTING_NONE,
|
||||
|
||||
// Weight by time delta. Data points clustered together are weighted less.
|
||||
WEIGHTING_DELTA,
|
||||
|
||||
// Weight such that points within a certain horizon are weighed more than those
|
||||
// outside of that horizon.
|
||||
WEIGHTING_CENTRAL,
|
||||
|
||||
// Weight such that points older than a certain amount are weighed less.
|
||||
WEIGHTING_RECENT,
|
||||
};
|
||||
|
||||
// Degree must be no greater than Estimator::MAX_DEGREE.
|
||||
LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE);
|
||||
virtual ~LeastSquaresVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Sample horizon.
|
||||
// We don't use too much history by default since we want to react to quick
|
||||
// changes in direction.
|
||||
static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
|
||||
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 20;
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
VelocityTracker::Position positions[MAX_POINTERS];
|
||||
|
||||
inline const VelocityTracker::Position& getPosition(uint32_t id) const {
|
||||
return positions[idBits.getIndexOfBit(id)];
|
||||
}
|
||||
};
|
||||
|
||||
float chooseWeight(uint32_t index) const;
|
||||
|
||||
const uint32_t mDegree;
|
||||
const Weighting mWeighting;
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker algorithm that uses an IIR filter.
|
||||
*/
|
||||
class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
// Degree must be 1 or 2.
|
||||
IntegratingVelocityTrackerStrategy(uint32_t degree);
|
||||
~IntegratingVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Current state estimate for a particular pointer.
|
||||
struct State {
|
||||
nsecs_t updateTime;
|
||||
uint32_t degree;
|
||||
|
||||
float xpos, xvel, xaccel;
|
||||
float ypos, yvel, yaccel;
|
||||
};
|
||||
|
||||
const uint32_t mDegree;
|
||||
BitSet32 mPointerIdBits;
|
||||
State mPointerState[MAX_POINTER_ID + 1];
|
||||
|
||||
void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
|
||||
void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
|
||||
void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker strategy used prior to ICS.
|
||||
*/
|
||||
class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
LegacyVelocityTrackerStrategy();
|
||||
virtual ~LegacyVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Oldest sample to consider when calculating the velocity.
|
||||
static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
|
||||
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 20;
|
||||
|
||||
// The minimum duration between samples when estimating velocity.
|
||||
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
VelocityTracker::Position positions[MAX_POINTERS];
|
||||
|
||||
inline const VelocityTracker::Position& getPosition(uint32_t id) const {
|
||||
return positions[idBits.getIndexOfBit(id)];
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_VELOCITY_TRACKER_H
|
|
@ -1,171 +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 "VirtualKeyMap"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "VirtualKeyMap.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
|
||||
// Enables debug output for parser performance.
|
||||
#define DEBUG_PARSER_PERFORMANCE 0
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* WHITESPACE = " \t\r";
|
||||
static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:";
|
||||
|
||||
|
||||
// --- VirtualKeyMap ---
|
||||
|
||||
VirtualKeyMap::VirtualKeyMap() {
|
||||
}
|
||||
|
||||
VirtualKeyMap::~VirtualKeyMap() {
|
||||
}
|
||||
|
||||
status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
|
||||
*outMap = NULL;
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::open(filename, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening virtual key map file %s.", status, filename.string());
|
||||
} else {
|
||||
VirtualKeyMap* map = new VirtualKeyMap();
|
||||
if (!map) {
|
||||
ALOGE("Error allocating virtual key map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map, tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (status) {
|
||||
delete map;
|
||||
} else {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// --- VirtualKeyMap::Parser ---
|
||||
|
||||
VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) :
|
||||
mMap(map), mTokenizer(tokenizer) {
|
||||
}
|
||||
|
||||
VirtualKeyMap::Parser::~Parser() {
|
||||
}
|
||||
|
||||
status_t VirtualKeyMap::Parser::parse() {
|
||||
while (!mTokenizer->isEof()) {
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
#endif
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
// Multiple keys can appear on one line or they can be broken up across multiple lines.
|
||||
do {
|
||||
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
|
||||
if (token != "0x01") {
|
||||
ALOGE("%s: Unknown virtual key type, expected 0x01.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
VirtualKeyDefinition defn;
|
||||
bool success = parseNextIntField(&defn.scanCode)
|
||||
&& parseNextIntField(&defn.centerX)
|
||||
&& parseNextIntField(&defn.centerY)
|
||||
&& parseNextIntField(&defn.width)
|
||||
&& parseNextIntField(&defn.height);
|
||||
if (!success) {
|
||||
ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "
|
||||
"width=%d, height=%d",
|
||||
defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
|
||||
#endif
|
||||
mMap->mVirtualKeys.push(defn);
|
||||
} while (consumeFieldDelimiterAndSkipWhitespace());
|
||||
|
||||
if (!mTokenizer->isEol()) {
|
||||
ALOGE("%s: Expected end of line, got '%s'.",
|
||||
mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
mTokenizer->nextLine();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (mTokenizer->peekChar() == ':') {
|
||||
mTokenizer->nextChar();
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
|
||||
if (!consumeFieldDelimiterAndSkipWhitespace()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
|
||||
char* end;
|
||||
*outValue = strtol(token.string(), &end, 0);
|
||||
if (token.isEmpty() || *end != '\0') {
|
||||
ALOGE("Expected an integer, got '%s'.", token.string());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,81 +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 _ANDROIDFW_VIRTUAL_KEY_MAP_H
|
||||
#define _ANDROIDFW_VIRTUAL_KEY_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Unicode.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/* Describes a virtual key. */
|
||||
struct VirtualKeyDefinition {
|
||||
int32_t scanCode;
|
||||
|
||||
// configured position data, specified in display coords
|
||||
int32_t centerX;
|
||||
int32_t centerY;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Describes a collection of virtual keys on a touch screen in terms of
|
||||
* virtual scan codes and hit rectangles.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class VirtualKeyMap {
|
||||
public:
|
||||
~VirtualKeyMap();
|
||||
|
||||
static status_t load(const String8& filename, VirtualKeyMap** outMap);
|
||||
|
||||
inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
|
||||
return mVirtualKeys;
|
||||
}
|
||||
|
||||
private:
|
||||
class Parser {
|
||||
VirtualKeyMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
|
||||
public:
|
||||
Parser(VirtualKeyMap* map, Tokenizer* tokenizer);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
private:
|
||||
bool consumeFieldDelimiterAndSkipWhitespace();
|
||||
bool parseNextIntField(int32_t* outValue);
|
||||
};
|
||||
|
||||
Vector<VirtualKeyDefinition> mVirtualKeys;
|
||||
|
||||
VirtualKeyMap();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
|
|
@ -1,850 +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_INPUT_H
|
||||
#define _ANDROID_INPUT_H
|
||||
|
||||
/******************************************************************
|
||||
*
|
||||
* IMPORTANT NOTICE:
|
||||
*
|
||||
* This file is part of Android's set of stable system headers
|
||||
* exposed by the Android NDK (Native Development Kit).
|
||||
*
|
||||
* Third-party source AND binary code relies on the definitions
|
||||
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
|
||||
*
|
||||
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
|
||||
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
|
||||
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
|
||||
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
|
||||
*/
|
||||
|
||||
/*
|
||||
* Structures and functions to receive and process input events in
|
||||
* native code.
|
||||
*
|
||||
* NOTE: These functions MUST be implemented by /system/lib/libui.so
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include "android_keycodes.h"
|
||||
#include <android/looper.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Key states (may be returned by queries about the current state of a
|
||||
* particular key code, scan code or switch).
|
||||
*/
|
||||
enum {
|
||||
/* The key state is unknown or the requested key itself is not supported. */
|
||||
AKEY_STATE_UNKNOWN = -1,
|
||||
|
||||
/* The key is up. */
|
||||
AKEY_STATE_UP = 0,
|
||||
|
||||
/* The key is down. */
|
||||
AKEY_STATE_DOWN = 1,
|
||||
|
||||
/* The key is down but is a virtual key press that is being emulated by the system. */
|
||||
AKEY_STATE_VIRTUAL = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Meta key / modifer state.
|
||||
*/
|
||||
enum {
|
||||
/* No meta keys are pressed. */
|
||||
AMETA_NONE = 0,
|
||||
|
||||
/* This mask is used to check whether one of the ALT meta keys is pressed. */
|
||||
AMETA_ALT_ON = 0x02,
|
||||
|
||||
/* This mask is used to check whether the left ALT meta key is pressed. */
|
||||
AMETA_ALT_LEFT_ON = 0x10,
|
||||
|
||||
/* This mask is used to check whether the right ALT meta key is pressed. */
|
||||
AMETA_ALT_RIGHT_ON = 0x20,
|
||||
|
||||
/* This mask is used to check whether one of the SHIFT meta keys is pressed. */
|
||||
AMETA_SHIFT_ON = 0x01,
|
||||
|
||||
/* This mask is used to check whether the left SHIFT meta key is pressed. */
|
||||
AMETA_SHIFT_LEFT_ON = 0x40,
|
||||
|
||||
/* This mask is used to check whether the right SHIFT meta key is pressed. */
|
||||
AMETA_SHIFT_RIGHT_ON = 0x80,
|
||||
|
||||
/* This mask is used to check whether the SYM meta key is pressed. */
|
||||
AMETA_SYM_ON = 0x04,
|
||||
|
||||
/* This mask is used to check whether the FUNCTION meta key is pressed. */
|
||||
AMETA_FUNCTION_ON = 0x08,
|
||||
|
||||
/* This mask is used to check whether one of the CTRL meta keys is pressed. */
|
||||
AMETA_CTRL_ON = 0x1000,
|
||||
|
||||
/* This mask is used to check whether the left CTRL meta key is pressed. */
|
||||
AMETA_CTRL_LEFT_ON = 0x2000,
|
||||
|
||||
/* This mask is used to check whether the right CTRL meta key is pressed. */
|
||||
AMETA_CTRL_RIGHT_ON = 0x4000,
|
||||
|
||||
/* This mask is used to check whether one of the META meta keys is pressed. */
|
||||
AMETA_META_ON = 0x10000,
|
||||
|
||||
/* This mask is used to check whether the left META meta key is pressed. */
|
||||
AMETA_META_LEFT_ON = 0x20000,
|
||||
|
||||
/* This mask is used to check whether the right META meta key is pressed. */
|
||||
AMETA_META_RIGHT_ON = 0x40000,
|
||||
|
||||
/* This mask is used to check whether the CAPS LOCK meta key is on. */
|
||||
AMETA_CAPS_LOCK_ON = 0x100000,
|
||||
|
||||
/* This mask is used to check whether the NUM LOCK meta key is on. */
|
||||
AMETA_NUM_LOCK_ON = 0x200000,
|
||||
|
||||
/* This mask is used to check whether the SCROLL LOCK meta key is on. */
|
||||
AMETA_SCROLL_LOCK_ON = 0x400000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Input events.
|
||||
*
|
||||
* Input events are opaque structures. Use the provided accessors functions to
|
||||
* read their properties.
|
||||
*/
|
||||
struct AInputEvent;
|
||||
typedef struct AInputEvent AInputEvent;
|
||||
|
||||
/*
|
||||
* Input event types.
|
||||
*/
|
||||
enum {
|
||||
/* Indicates that the input event is a key event. */
|
||||
AINPUT_EVENT_TYPE_KEY = 1,
|
||||
|
||||
/* Indicates that the input event is a motion event. */
|
||||
AINPUT_EVENT_TYPE_MOTION = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Key event actions.
|
||||
*/
|
||||
enum {
|
||||
/* The key has been pressed down. */
|
||||
AKEY_EVENT_ACTION_DOWN = 0,
|
||||
|
||||
/* The key has been released. */
|
||||
AKEY_EVENT_ACTION_UP = 1,
|
||||
|
||||
/* Multiple duplicate key events have occurred in a row, or a complex string is
|
||||
* being delivered. The repeat_count property of the key event contains the number
|
||||
* of times the given key code should be executed.
|
||||
*/
|
||||
AKEY_EVENT_ACTION_MULTIPLE = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Key event flags.
|
||||
*/
|
||||
enum {
|
||||
/* This mask is set if the device woke because of this key event. */
|
||||
AKEY_EVENT_FLAG_WOKE_HERE = 0x1,
|
||||
|
||||
/* This mask is set if the key event was generated by a software keyboard. */
|
||||
AKEY_EVENT_FLAG_SOFT_KEYBOARD = 0x2,
|
||||
|
||||
/* This mask is set if we don't want the key event to cause us to leave touch mode. */
|
||||
AKEY_EVENT_FLAG_KEEP_TOUCH_MODE = 0x4,
|
||||
|
||||
/* This mask is set if an event was known to come from a trusted part
|
||||
* of the system. That is, the event is known to come from the user,
|
||||
* and could not have been spoofed by a third party component. */
|
||||
AKEY_EVENT_FLAG_FROM_SYSTEM = 0x8,
|
||||
|
||||
/* This mask is used for compatibility, to identify enter keys that are
|
||||
* coming from an IME whose enter key has been auto-labelled "next" or
|
||||
* "done". This allows TextView to dispatch these as normal enter keys
|
||||
* for old applications, but still do the appropriate action when
|
||||
* receiving them. */
|
||||
AKEY_EVENT_FLAG_EDITOR_ACTION = 0x10,
|
||||
|
||||
/* When associated with up key events, this indicates that the key press
|
||||
* has been canceled. Typically this is used with virtual touch screen
|
||||
* keys, where the user can slide from the virtual key area on to the
|
||||
* display: in that case, the application will receive a canceled up
|
||||
* event and should not perform the action normally associated with the
|
||||
* key. Note that for this to work, the application can not perform an
|
||||
* action for a key until it receives an up or the long press timeout has
|
||||
* expired. */
|
||||
AKEY_EVENT_FLAG_CANCELED = 0x20,
|
||||
|
||||
/* This key event was generated by a virtual (on-screen) hard key area.
|
||||
* Typically this is an area of the touchscreen, outside of the regular
|
||||
* display, dedicated to "hardware" buttons. */
|
||||
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY = 0x40,
|
||||
|
||||
/* This flag is set for the first key repeat that occurs after the
|
||||
* long press timeout. */
|
||||
AKEY_EVENT_FLAG_LONG_PRESS = 0x80,
|
||||
|
||||
/* Set when a key event has AKEY_EVENT_FLAG_CANCELED set because a long
|
||||
* press action was executed while it was down. */
|
||||
AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100,
|
||||
|
||||
/* Set for AKEY_EVENT_ACTION_UP when this event's key code is still being
|
||||
* tracked from its initial down. That is, somebody requested that tracking
|
||||
* started on the key down and a long press has not caused
|
||||
* the tracking to be canceled. */
|
||||
AKEY_EVENT_FLAG_TRACKING = 0x200,
|
||||
|
||||
/* Set when a key event has been synthesized to implement default behavior
|
||||
* for an event that the application did not handle.
|
||||
* Fallback key events are generated by unhandled trackball motions
|
||||
* (to emulate a directional keypad) and by certain unhandled key presses
|
||||
* that are declared in the key map (such as special function numeric keypad
|
||||
* keys when numlock is off). */
|
||||
AKEY_EVENT_FLAG_FALLBACK = 0x400,
|
||||
};
|
||||
|
||||
/*
|
||||
* Motion event actions.
|
||||
*/
|
||||
|
||||
/* Bit shift for the action bits holding the pointer index as
|
||||
* defined by AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
|
||||
*/
|
||||
#define AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8
|
||||
|
||||
enum {
|
||||
/* Bit mask of the parts of the action code that are the action itself.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_MASK = 0xff,
|
||||
|
||||
/* Bits in the action code that represent a pointer index, used with
|
||||
* AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP. Shifting
|
||||
* down by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
|
||||
* index where the data for the pointer going up or down can be found.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00,
|
||||
|
||||
/* A pressed gesture has started, the motion contains the initial starting location.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_DOWN = 0,
|
||||
|
||||
/* A pressed gesture has finished, the motion contains the final release location
|
||||
* as well as any intermediate points since the last down or move event.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_UP = 1,
|
||||
|
||||
/* A change has happened during a press gesture (between AMOTION_EVENT_ACTION_DOWN and
|
||||
* AMOTION_EVENT_ACTION_UP). The motion contains the most recent point, as well as
|
||||
* any intermediate points since the last down or move event.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_MOVE = 2,
|
||||
|
||||
/* The current gesture has been aborted.
|
||||
* You will not receive any more points in it. You should treat this as
|
||||
* an up event, but not perform any action that you normally would.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_CANCEL = 3,
|
||||
|
||||
/* A movement has happened outside of the normal bounds of the UI element.
|
||||
* This does not provide a full gesture, but only the initial location of the movement/touch.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_OUTSIDE = 4,
|
||||
|
||||
/* A non-primary pointer has gone down.
|
||||
* The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_POINTER_DOWN = 5,
|
||||
|
||||
/* A non-primary pointer has gone up.
|
||||
* The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_POINTER_UP = 6,
|
||||
|
||||
/* A change happened but the pointer is not down (unlike AMOTION_EVENT_ACTION_MOVE).
|
||||
* The motion contains the most recent point, as well as any intermediate points since
|
||||
* the last hover move event.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_HOVER_MOVE = 7,
|
||||
|
||||
/* The motion event contains relative vertical and/or horizontal scroll offsets.
|
||||
* Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL
|
||||
* and AMOTION_EVENT_AXIS_HSCROLL.
|
||||
* The pointer may or may not be down when this event is dispatched.
|
||||
* This action is always delivered to the winder under the pointer, which
|
||||
* may not be the window currently touched.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_SCROLL = 8,
|
||||
|
||||
/* The pointer is not down but has entered the boundaries of a window or view.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_HOVER_ENTER = 9,
|
||||
|
||||
/* The pointer is not down but has exited the boundaries of a window or view.
|
||||
*/
|
||||
AMOTION_EVENT_ACTION_HOVER_EXIT = 10,
|
||||
};
|
||||
|
||||
/*
|
||||
* Motion event flags.
|
||||
*/
|
||||
enum {
|
||||
/* This flag indicates that the window that received this motion event is partly
|
||||
* or wholly obscured by another visible window above it. This flag is set to true
|
||||
* even if the event did not directly pass through the obscured area.
|
||||
* A security sensitive application can check this flag to identify situations in which
|
||||
* a malicious application may have covered up part of its content for the purpose
|
||||
* of misleading the user or hijacking touches. An appropriate response might be
|
||||
* to drop the suspect touches or to take additional precautions to confirm the user's
|
||||
* actual intent.
|
||||
*/
|
||||
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Motion event edge touch flags.
|
||||
*/
|
||||
enum {
|
||||
/* No edges intersected */
|
||||
AMOTION_EVENT_EDGE_FLAG_NONE = 0,
|
||||
|
||||
/* Flag indicating the motion event intersected the top edge of the screen. */
|
||||
AMOTION_EVENT_EDGE_FLAG_TOP = 0x01,
|
||||
|
||||
/* Flag indicating the motion event intersected the bottom edge of the screen. */
|
||||
AMOTION_EVENT_EDGE_FLAG_BOTTOM = 0x02,
|
||||
|
||||
/* Flag indicating the motion event intersected the left edge of the screen. */
|
||||
AMOTION_EVENT_EDGE_FLAG_LEFT = 0x04,
|
||||
|
||||
/* Flag indicating the motion event intersected the right edge of the screen. */
|
||||
AMOTION_EVENT_EDGE_FLAG_RIGHT = 0x08
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants that identify each individual axis of a motion event.
|
||||
* Refer to the documentation on the MotionEvent class for descriptions of each axis.
|
||||
*/
|
||||
enum {
|
||||
AMOTION_EVENT_AXIS_X = 0,
|
||||
AMOTION_EVENT_AXIS_Y = 1,
|
||||
AMOTION_EVENT_AXIS_PRESSURE = 2,
|
||||
AMOTION_EVENT_AXIS_SIZE = 3,
|
||||
AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4,
|
||||
AMOTION_EVENT_AXIS_TOUCH_MINOR = 5,
|
||||
AMOTION_EVENT_AXIS_TOOL_MAJOR = 6,
|
||||
AMOTION_EVENT_AXIS_TOOL_MINOR = 7,
|
||||
AMOTION_EVENT_AXIS_ORIENTATION = 8,
|
||||
AMOTION_EVENT_AXIS_VSCROLL = 9,
|
||||
AMOTION_EVENT_AXIS_HSCROLL = 10,
|
||||
AMOTION_EVENT_AXIS_Z = 11,
|
||||
AMOTION_EVENT_AXIS_RX = 12,
|
||||
AMOTION_EVENT_AXIS_RY = 13,
|
||||
AMOTION_EVENT_AXIS_RZ = 14,
|
||||
AMOTION_EVENT_AXIS_HAT_X = 15,
|
||||
AMOTION_EVENT_AXIS_HAT_Y = 16,
|
||||
AMOTION_EVENT_AXIS_LTRIGGER = 17,
|
||||
AMOTION_EVENT_AXIS_RTRIGGER = 18,
|
||||
AMOTION_EVENT_AXIS_THROTTLE = 19,
|
||||
AMOTION_EVENT_AXIS_RUDDER = 20,
|
||||
AMOTION_EVENT_AXIS_WHEEL = 21,
|
||||
AMOTION_EVENT_AXIS_GAS = 22,
|
||||
AMOTION_EVENT_AXIS_BRAKE = 23,
|
||||
AMOTION_EVENT_AXIS_DISTANCE = 24,
|
||||
AMOTION_EVENT_AXIS_TILT = 25,
|
||||
AMOTION_EVENT_AXIS_GENERIC_1 = 32,
|
||||
AMOTION_EVENT_AXIS_GENERIC_2 = 33,
|
||||
AMOTION_EVENT_AXIS_GENERIC_3 = 34,
|
||||
AMOTION_EVENT_AXIS_GENERIC_4 = 35,
|
||||
AMOTION_EVENT_AXIS_GENERIC_5 = 36,
|
||||
AMOTION_EVENT_AXIS_GENERIC_6 = 37,
|
||||
AMOTION_EVENT_AXIS_GENERIC_7 = 38,
|
||||
AMOTION_EVENT_AXIS_GENERIC_8 = 39,
|
||||
AMOTION_EVENT_AXIS_GENERIC_9 = 40,
|
||||
AMOTION_EVENT_AXIS_GENERIC_10 = 41,
|
||||
AMOTION_EVENT_AXIS_GENERIC_11 = 42,
|
||||
AMOTION_EVENT_AXIS_GENERIC_12 = 43,
|
||||
AMOTION_EVENT_AXIS_GENERIC_13 = 44,
|
||||
AMOTION_EVENT_AXIS_GENERIC_14 = 45,
|
||||
AMOTION_EVENT_AXIS_GENERIC_15 = 46,
|
||||
AMOTION_EVENT_AXIS_GENERIC_16 = 47,
|
||||
|
||||
// NOTE: If you add a new axis here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants that identify buttons that are associated with motion events.
|
||||
* Refer to the documentation on the MotionEvent class for descriptions of each button.
|
||||
*/
|
||||
enum {
|
||||
AMOTION_EVENT_BUTTON_PRIMARY = 1 << 0,
|
||||
AMOTION_EVENT_BUTTON_SECONDARY = 1 << 1,
|
||||
AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2,
|
||||
AMOTION_EVENT_BUTTON_BACK = 1 << 3,
|
||||
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants that identify tool types.
|
||||
* Refer to the documentation on the MotionEvent class for descriptions of each tool type.
|
||||
*/
|
||||
enum {
|
||||
AMOTION_EVENT_TOOL_TYPE_UNKNOWN = 0,
|
||||
AMOTION_EVENT_TOOL_TYPE_FINGER = 1,
|
||||
AMOTION_EVENT_TOOL_TYPE_STYLUS = 2,
|
||||
AMOTION_EVENT_TOOL_TYPE_MOUSE = 3,
|
||||
AMOTION_EVENT_TOOL_TYPE_ERASER = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* Input sources.
|
||||
*
|
||||
* Refer to the documentation on android.view.InputDevice for more details about input sources
|
||||
* and their correct interpretation.
|
||||
*/
|
||||
enum {
|
||||
AINPUT_SOURCE_CLASS_MASK = 0x000000ff,
|
||||
|
||||
AINPUT_SOURCE_CLASS_NONE = 0x00000000,
|
||||
AINPUT_SOURCE_CLASS_BUTTON = 0x00000001,
|
||||
AINPUT_SOURCE_CLASS_POINTER = 0x00000002,
|
||||
AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004,
|
||||
AINPUT_SOURCE_CLASS_POSITION = 0x00000008,
|
||||
AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010,
|
||||
};
|
||||
|
||||
enum {
|
||||
AINPUT_SOURCE_UNKNOWN = 0x00000000,
|
||||
|
||||
AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON,
|
||||
AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON,
|
||||
AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON,
|
||||
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
|
||||
AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
|
||||
AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER,
|
||||
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
|
||||
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
|
||||
AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE,
|
||||
AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
|
||||
|
||||
AINPUT_SOURCE_ANY = 0xffffff00,
|
||||
};
|
||||
|
||||
/*
|
||||
* Keyboard types.
|
||||
*
|
||||
* Refer to the documentation on android.view.InputDevice for more details.
|
||||
*/
|
||||
enum {
|
||||
AINPUT_KEYBOARD_TYPE_NONE = 0,
|
||||
AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1,
|
||||
AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants used to retrieve information about the range of motion for a particular
|
||||
* coordinate of a motion event.
|
||||
*
|
||||
* Refer to the documentation on android.view.InputDevice for more details about input sources
|
||||
* and their correct interpretation.
|
||||
*
|
||||
* DEPRECATION NOTICE: These constants are deprecated. Use AMOTION_EVENT_AXIS_* constants instead.
|
||||
*/
|
||||
enum {
|
||||
AINPUT_MOTION_RANGE_X = AMOTION_EVENT_AXIS_X,
|
||||
AINPUT_MOTION_RANGE_Y = AMOTION_EVENT_AXIS_Y,
|
||||
AINPUT_MOTION_RANGE_PRESSURE = AMOTION_EVENT_AXIS_PRESSURE,
|
||||
AINPUT_MOTION_RANGE_SIZE = AMOTION_EVENT_AXIS_SIZE,
|
||||
AINPUT_MOTION_RANGE_TOUCH_MAJOR = AMOTION_EVENT_AXIS_TOUCH_MAJOR,
|
||||
AINPUT_MOTION_RANGE_TOUCH_MINOR = AMOTION_EVENT_AXIS_TOUCH_MINOR,
|
||||
AINPUT_MOTION_RANGE_TOOL_MAJOR = AMOTION_EVENT_AXIS_TOOL_MAJOR,
|
||||
AINPUT_MOTION_RANGE_TOOL_MINOR = AMOTION_EVENT_AXIS_TOOL_MINOR,
|
||||
AINPUT_MOTION_RANGE_ORIENTATION = AMOTION_EVENT_AXIS_ORIENTATION,
|
||||
} __attribute__ ((deprecated));
|
||||
|
||||
|
||||
/*
|
||||
* Input event accessors.
|
||||
*
|
||||
* Note that most functions can only be used on input events that are of a given type.
|
||||
* Calling these functions on input events of other types will yield undefined behavior.
|
||||
*/
|
||||
|
||||
/*** Accessors for all input events. ***/
|
||||
|
||||
/* Get the input event type. */
|
||||
int32_t AInputEvent_getType(const AInputEvent* event);
|
||||
|
||||
/* Get the id for the device that an input event came from.
|
||||
*
|
||||
* Input events can be generated by multiple different input devices.
|
||||
* Use the input device id to obtain information about the input
|
||||
* device that was responsible for generating a particular event.
|
||||
*
|
||||
* An input device id of 0 indicates that the event didn't come from a physical device;
|
||||
* other numbers are arbitrary and you shouldn't depend on the values.
|
||||
* Use the provided input device query API to obtain information about input devices.
|
||||
*/
|
||||
int32_t AInputEvent_getDeviceId(const AInputEvent* event);
|
||||
|
||||
/* Get the input event source. */
|
||||
int32_t AInputEvent_getSource(const AInputEvent* event);
|
||||
|
||||
/*** Accessors for key events only. ***/
|
||||
|
||||
/* Get the key event action. */
|
||||
int32_t AKeyEvent_getAction(const AInputEvent* key_event);
|
||||
|
||||
/* Get the key event flags. */
|
||||
int32_t AKeyEvent_getFlags(const AInputEvent* key_event);
|
||||
|
||||
/* Get the key code of the key event.
|
||||
* This is the physical key that was pressed, not the Unicode character. */
|
||||
int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event);
|
||||
|
||||
/* Get the hardware key id of this key event.
|
||||
* These values are not reliable and vary from device to device. */
|
||||
int32_t AKeyEvent_getScanCode(const AInputEvent* key_event);
|
||||
|
||||
/* Get the meta key state. */
|
||||
int32_t AKeyEvent_getMetaState(const AInputEvent* key_event);
|
||||
|
||||
/* Get the repeat count of the event.
|
||||
* For both key up an key down events, this is the number of times the key has
|
||||
* repeated with the first down starting at 0 and counting up from there. For
|
||||
* multiple key events, this is the number of down/up pairs that have occurred. */
|
||||
int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event);
|
||||
|
||||
/* Get the time of the most recent key down event, in the
|
||||
* java.lang.System.nanoTime() time base. If this is a down event,
|
||||
* this will be the same as eventTime.
|
||||
* Note that when chording keys, this value is the down time of the most recently
|
||||
* pressed key, which may not be the same physical key of this event. */
|
||||
int64_t AKeyEvent_getDownTime(const AInputEvent* key_event);
|
||||
|
||||
/* Get the time this event occurred, in the
|
||||
* java.lang.System.nanoTime() time base. */
|
||||
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
|
||||
|
||||
/*** Accessors for motion events only. ***/
|
||||
|
||||
/* Get the combined motion event action code and pointer index. */
|
||||
int32_t AMotionEvent_getAction(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the motion event flags. */
|
||||
int32_t AMotionEvent_getFlags(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the state of any meta / modifier keys that were in effect when the
|
||||
* event was generated. */
|
||||
int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the button state of all buttons that are pressed. */
|
||||
int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
|
||||
|
||||
/* Get a bitfield indicating which edges, if any, were touched by this motion event.
|
||||
* For touch events, clients can use this to determine if the user's finger was
|
||||
* touching the edge of the display. */
|
||||
int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the time when the user originally pressed down to start a stream of
|
||||
* position events, in the java.lang.System.nanoTime() time base. */
|
||||
int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the time when this specific event was generated,
|
||||
* in the java.lang.System.nanoTime() time base. */
|
||||
int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the X coordinate offset.
|
||||
* For touch events on the screen, this is the delta that was added to the raw
|
||||
* screen coordinates to adjust for the absolute position of the containing windows
|
||||
* and views. */
|
||||
float AMotionEvent_getXOffset(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the precision of the Y coordinates being reported.
|
||||
* For touch events on the screen, this is the delta that was added to the raw
|
||||
* screen coordinates to adjust for the absolute position of the containing windows
|
||||
* and views. */
|
||||
float AMotionEvent_getYOffset(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the precision of the X coordinates being reported.
|
||||
* You can multiply this number with an X coordinate sample to find the
|
||||
* actual hardware value of the X coordinate. */
|
||||
float AMotionEvent_getXPrecision(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the precision of the Y coordinates being reported.
|
||||
* You can multiply this number with a Y coordinate sample to find the
|
||||
* actual hardware value of the Y coordinate. */
|
||||
float AMotionEvent_getYPrecision(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the number of pointers of data contained in this event.
|
||||
* Always >= 1. */
|
||||
size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the pointer identifier associated with a particular pointer
|
||||
* data index in this event. The identifier tells you the actual pointer
|
||||
* number associated with the data, accounting for individual pointers
|
||||
* going up and down since the start of the current gesture. */
|
||||
int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the tool type of a pointer for the given pointer index.
|
||||
* The tool type indicates the type of tool used to make contact such as a
|
||||
* finger or stylus, if known. */
|
||||
int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the original raw X coordinate of this event.
|
||||
* For touch events on the screen, this is the original location of the event
|
||||
* on the screen, before it had been adjusted for the containing window
|
||||
* and views. */
|
||||
float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the original raw X coordinate of this event.
|
||||
* For touch events on the screen, this is the original location of the event
|
||||
* on the screen, before it had been adjusted for the containing window
|
||||
* and views. */
|
||||
float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current X coordinate of this event for the given pointer index.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current Y coordinate of this event for the given pointer index.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current pressure of this event for the given pointer index.
|
||||
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
|
||||
* although values higher than 1 may be generated depending on the calibration of
|
||||
* the input device. */
|
||||
float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current scaled value of the approximate size for the given pointer index.
|
||||
* This represents some approximation of the area of the screen being
|
||||
* pressed; the actual value in pixels corresponding to the
|
||||
* touch is normalized with the device specific range of values
|
||||
* and scaled to a value between 0 and 1. The value of size can be used to
|
||||
* determine fat touch events. */
|
||||
float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current length of the major axis of an ellipse that describes the touch area
|
||||
* at the point of contact for the given pointer index. */
|
||||
float AMotionEvent_getTouchMajor(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current length of the minor axis of an ellipse that describes the touch area
|
||||
* at the point of contact for the given pointer index. */
|
||||
float AMotionEvent_getTouchMinor(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current length of the major axis of an ellipse that describes the size
|
||||
* of the approaching tool for the given pointer index.
|
||||
* The tool area represents the estimated size of the finger or pen that is
|
||||
* touching the device independent of its actual touch area at the point of contact. */
|
||||
float AMotionEvent_getToolMajor(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current length of the minor axis of an ellipse that describes the size
|
||||
* of the approaching tool for the given pointer index.
|
||||
* The tool area represents the estimated size of the finger or pen that is
|
||||
* touching the device independent of its actual touch area at the point of contact. */
|
||||
float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the current orientation of the touch area and tool area in radians clockwise from
|
||||
* vertical for the given pointer index.
|
||||
* An angle of 0 degrees indicates that the major axis of contact is oriented
|
||||
* upwards, is perfectly circular or is of unknown orientation. A positive angle
|
||||
* indicates that the major axis of contact is oriented to the right. A negative angle
|
||||
* indicates that the major axis of contact is oriented to the left.
|
||||
* The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
|
||||
* (finger pointing fully right). */
|
||||
float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
|
||||
|
||||
/* Get the value of the request axis for the given pointer index. */
|
||||
float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
|
||||
int32_t axis, size_t pointer_index);
|
||||
|
||||
/* Get the number of historical points in this event. These are movements that
|
||||
* have occurred between this event and the previous event. This only applies
|
||||
* to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
|
||||
* Historical samples are indexed from oldest to newest. */
|
||||
size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event);
|
||||
|
||||
/* Get the time that a historical movement occurred between this event and
|
||||
* the previous event, in the java.lang.System.nanoTime() time base. */
|
||||
int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical raw X coordinate of this event for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* For touch events on the screen, this is the original location of the event
|
||||
* on the screen, before it had been adjusted for the containing window
|
||||
* and views.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical raw Y coordinate of this event for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* For touch events on the screen, this is the original location of the event
|
||||
* on the screen, before it had been adjusted for the containing window
|
||||
* and views.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical X coordinate of this event for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical Y coordinate of this event for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* Whole numbers are pixels; the value may have a fraction for input devices
|
||||
* that are sub-pixel precise. */
|
||||
float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical pressure of this event for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
|
||||
* although values higher than 1 may be generated depending on the calibration of
|
||||
* the input device. */
|
||||
float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the current scaled value of the approximate size for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* This represents some approximation of the area of the screen being
|
||||
* pressed; the actual value in pixels corresponding to the
|
||||
* touch is normalized with the device specific range of values
|
||||
* and scaled to a value between 0 and 1. The value of size can be used to
|
||||
* determine fat touch events. */
|
||||
float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical length of the major axis of an ellipse that describes the touch area
|
||||
* at the point of contact for the given pointer index that
|
||||
* occurred between this event and the previous motion event. */
|
||||
float AMotionEvent_getHistoricalTouchMajor(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical length of the minor axis of an ellipse that describes the touch area
|
||||
* at the point of contact for the given pointer index that
|
||||
* occurred between this event and the previous motion event. */
|
||||
float AMotionEvent_getHistoricalTouchMinor(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical length of the major axis of an ellipse that describes the size
|
||||
* of the approaching tool for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* The tool area represents the estimated size of the finger or pen that is
|
||||
* touching the device independent of its actual touch area at the point of contact. */
|
||||
float AMotionEvent_getHistoricalToolMajor(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical length of the minor axis of an ellipse that describes the size
|
||||
* of the approaching tool for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* The tool area represents the estimated size of the finger or pen that is
|
||||
* touching the device independent of its actual touch area at the point of contact. */
|
||||
float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical orientation of the touch area and tool area in radians clockwise from
|
||||
* vertical for the given pointer index that
|
||||
* occurred between this event and the previous motion event.
|
||||
* An angle of 0 degrees indicates that the major axis of contact is oriented
|
||||
* upwards, is perfectly circular or is of unknown orientation. A positive angle
|
||||
* indicates that the major axis of contact is oriented to the right. A negative angle
|
||||
* indicates that the major axis of contact is oriented to the left.
|
||||
* The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
|
||||
* (finger pointing fully right). */
|
||||
float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
|
||||
size_t history_index);
|
||||
|
||||
/* Get the historical value of the request axis for the given pointer index
|
||||
* that occurred between this event and the previous motion event. */
|
||||
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
|
||||
int32_t axis, size_t pointer_index, size_t history_index);
|
||||
|
||||
|
||||
/*
|
||||
* Input queue
|
||||
*
|
||||
* An input queue is the facility through which you retrieve input
|
||||
* events.
|
||||
*/
|
||||
struct AInputQueue;
|
||||
typedef struct AInputQueue AInputQueue;
|
||||
|
||||
/*
|
||||
* Add this input queue to a looper for processing. See
|
||||
* ALooper_addFd() for information on the ident, callback, and data params.
|
||||
*/
|
||||
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
|
||||
int ident, ALooper_callbackFunc callback, void* data);
|
||||
|
||||
/*
|
||||
* Remove the input queue from the looper it is currently attached to.
|
||||
*/
|
||||
void AInputQueue_detachLooper(AInputQueue* queue);
|
||||
|
||||
/*
|
||||
* Returns true if there are one or more events available in the
|
||||
* input queue. Returns 1 if the queue has events; 0 if
|
||||
* it does not have events; and a negative value if there is an error.
|
||||
*/
|
||||
int32_t AInputQueue_hasEvents(AInputQueue* queue);
|
||||
|
||||
/*
|
||||
* Returns the next available event from the queue. Returns a negative
|
||||
* value if no events are available or an error has occurred.
|
||||
*/
|
||||
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent);
|
||||
|
||||
/*
|
||||
* Sends the key for standard pre-dispatching -- that is, possibly deliver
|
||||
* it to the current IME to be consumed before the app. Returns 0 if it
|
||||
* was not pre-dispatched, meaning you can process it right now. If non-zero
|
||||
* is returned, you must abandon the current event processing and allow the
|
||||
* event to appear again in the event queue (if it does not get consumed during
|
||||
* pre-dispatching).
|
||||
*/
|
||||
int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event);
|
||||
|
||||
/*
|
||||
* Report that dispatching has finished with the given event.
|
||||
* This must be called after receiving an event with AInputQueue_get_event().
|
||||
*/
|
||||
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ANDROID_INPUT_H
|
|
@ -1,339 +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_KEYCODES_H
|
||||
#define _ANDROID_KEYCODES_H
|
||||
|
||||
/******************************************************************
|
||||
*
|
||||
* IMPORTANT NOTICE:
|
||||
*
|
||||
* This file is part of Android's set of stable system headers
|
||||
* exposed by the Android NDK (Native Development Kit).
|
||||
*
|
||||
* Third-party source AND binary code relies on the definitions
|
||||
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
|
||||
*
|
||||
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
|
||||
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
|
||||
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
|
||||
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Key codes.
|
||||
*/
|
||||
enum {
|
||||
AKEYCODE_UNKNOWN = 0,
|
||||
AKEYCODE_SOFT_LEFT = 1,
|
||||
AKEYCODE_SOFT_RIGHT = 2,
|
||||
AKEYCODE_HOME = 3,
|
||||
AKEYCODE_BACK = 4,
|
||||
AKEYCODE_CALL = 5,
|
||||
AKEYCODE_ENDCALL = 6,
|
||||
AKEYCODE_0 = 7,
|
||||
AKEYCODE_1 = 8,
|
||||
AKEYCODE_2 = 9,
|
||||
AKEYCODE_3 = 10,
|
||||
AKEYCODE_4 = 11,
|
||||
AKEYCODE_5 = 12,
|
||||
AKEYCODE_6 = 13,
|
||||
AKEYCODE_7 = 14,
|
||||
AKEYCODE_8 = 15,
|
||||
AKEYCODE_9 = 16,
|
||||
AKEYCODE_STAR = 17,
|
||||
AKEYCODE_POUND = 18,
|
||||
AKEYCODE_DPAD_UP = 19,
|
||||
AKEYCODE_DPAD_DOWN = 20,
|
||||
AKEYCODE_DPAD_LEFT = 21,
|
||||
AKEYCODE_DPAD_RIGHT = 22,
|
||||
AKEYCODE_DPAD_CENTER = 23,
|
||||
AKEYCODE_VOLUME_UP = 24,
|
||||
AKEYCODE_VOLUME_DOWN = 25,
|
||||
AKEYCODE_POWER = 26,
|
||||
AKEYCODE_CAMERA = 27,
|
||||
AKEYCODE_CLEAR = 28,
|
||||
AKEYCODE_A = 29,
|
||||
AKEYCODE_B = 30,
|
||||
AKEYCODE_C = 31,
|
||||
AKEYCODE_D = 32,
|
||||
AKEYCODE_E = 33,
|
||||
AKEYCODE_F = 34,
|
||||
AKEYCODE_G = 35,
|
||||
AKEYCODE_H = 36,
|
||||
AKEYCODE_I = 37,
|
||||
AKEYCODE_J = 38,
|
||||
AKEYCODE_K = 39,
|
||||
AKEYCODE_L = 40,
|
||||
AKEYCODE_M = 41,
|
||||
AKEYCODE_N = 42,
|
||||
AKEYCODE_O = 43,
|
||||
AKEYCODE_P = 44,
|
||||
AKEYCODE_Q = 45,
|
||||
AKEYCODE_R = 46,
|
||||
AKEYCODE_S = 47,
|
||||
AKEYCODE_T = 48,
|
||||
AKEYCODE_U = 49,
|
||||
AKEYCODE_V = 50,
|
||||
AKEYCODE_W = 51,
|
||||
AKEYCODE_X = 52,
|
||||
AKEYCODE_Y = 53,
|
||||
AKEYCODE_Z = 54,
|
||||
AKEYCODE_COMMA = 55,
|
||||
AKEYCODE_PERIOD = 56,
|
||||
AKEYCODE_ALT_LEFT = 57,
|
||||
AKEYCODE_ALT_RIGHT = 58,
|
||||
AKEYCODE_SHIFT_LEFT = 59,
|
||||
AKEYCODE_SHIFT_RIGHT = 60,
|
||||
AKEYCODE_TAB = 61,
|
||||
AKEYCODE_SPACE = 62,
|
||||
AKEYCODE_SYM = 63,
|
||||
AKEYCODE_EXPLORER = 64,
|
||||
AKEYCODE_ENVELOPE = 65,
|
||||
AKEYCODE_ENTER = 66,
|
||||
AKEYCODE_DEL = 67,
|
||||
AKEYCODE_GRAVE = 68,
|
||||
AKEYCODE_MINUS = 69,
|
||||
AKEYCODE_EQUALS = 70,
|
||||
AKEYCODE_LEFT_BRACKET = 71,
|
||||
AKEYCODE_RIGHT_BRACKET = 72,
|
||||
AKEYCODE_BACKSLASH = 73,
|
||||
AKEYCODE_SEMICOLON = 74,
|
||||
AKEYCODE_APOSTROPHE = 75,
|
||||
AKEYCODE_SLASH = 76,
|
||||
AKEYCODE_AT = 77,
|
||||
AKEYCODE_NUM = 78,
|
||||
AKEYCODE_HEADSETHOOK = 79,
|
||||
AKEYCODE_FOCUS = 80, // *Camera* focus
|
||||
AKEYCODE_PLUS = 81,
|
||||
AKEYCODE_MENU = 82,
|
||||
AKEYCODE_NOTIFICATION = 83,
|
||||
AKEYCODE_SEARCH = 84,
|
||||
AKEYCODE_MEDIA_PLAY_PAUSE= 85,
|
||||
AKEYCODE_MEDIA_STOP = 86,
|
||||
AKEYCODE_MEDIA_NEXT = 87,
|
||||
AKEYCODE_MEDIA_PREVIOUS = 88,
|
||||
AKEYCODE_MEDIA_REWIND = 89,
|
||||
AKEYCODE_MEDIA_FAST_FORWARD = 90,
|
||||
AKEYCODE_MUTE = 91,
|
||||
AKEYCODE_PAGE_UP = 92,
|
||||
AKEYCODE_PAGE_DOWN = 93,
|
||||
AKEYCODE_PICTSYMBOLS = 94,
|
||||
AKEYCODE_SWITCH_CHARSET = 95,
|
||||
AKEYCODE_BUTTON_A = 96,
|
||||
AKEYCODE_BUTTON_B = 97,
|
||||
AKEYCODE_BUTTON_C = 98,
|
||||
AKEYCODE_BUTTON_X = 99,
|
||||
AKEYCODE_BUTTON_Y = 100,
|
||||
AKEYCODE_BUTTON_Z = 101,
|
||||
AKEYCODE_BUTTON_L1 = 102,
|
||||
AKEYCODE_BUTTON_R1 = 103,
|
||||
AKEYCODE_BUTTON_L2 = 104,
|
||||
AKEYCODE_BUTTON_R2 = 105,
|
||||
AKEYCODE_BUTTON_THUMBL = 106,
|
||||
AKEYCODE_BUTTON_THUMBR = 107,
|
||||
AKEYCODE_BUTTON_START = 108,
|
||||
AKEYCODE_BUTTON_SELECT = 109,
|
||||
AKEYCODE_BUTTON_MODE = 110,
|
||||
AKEYCODE_ESCAPE = 111,
|
||||
AKEYCODE_FORWARD_DEL = 112,
|
||||
AKEYCODE_CTRL_LEFT = 113,
|
||||
AKEYCODE_CTRL_RIGHT = 114,
|
||||
AKEYCODE_CAPS_LOCK = 115,
|
||||
AKEYCODE_SCROLL_LOCK = 116,
|
||||
AKEYCODE_META_LEFT = 117,
|
||||
AKEYCODE_META_RIGHT = 118,
|
||||
AKEYCODE_FUNCTION = 119,
|
||||
AKEYCODE_SYSRQ = 120,
|
||||
AKEYCODE_BREAK = 121,
|
||||
AKEYCODE_MOVE_HOME = 122,
|
||||
AKEYCODE_MOVE_END = 123,
|
||||
AKEYCODE_INSERT = 124,
|
||||
AKEYCODE_FORWARD = 125,
|
||||
AKEYCODE_MEDIA_PLAY = 126,
|
||||
AKEYCODE_MEDIA_PAUSE = 127,
|
||||
AKEYCODE_MEDIA_CLOSE = 128,
|
||||
AKEYCODE_MEDIA_EJECT = 129,
|
||||
AKEYCODE_MEDIA_RECORD = 130,
|
||||
AKEYCODE_F1 = 131,
|
||||
AKEYCODE_F2 = 132,
|
||||
AKEYCODE_F3 = 133,
|
||||
AKEYCODE_F4 = 134,
|
||||
AKEYCODE_F5 = 135,
|
||||
AKEYCODE_F6 = 136,
|
||||
AKEYCODE_F7 = 137,
|
||||
AKEYCODE_F8 = 138,
|
||||
AKEYCODE_F9 = 139,
|
||||
AKEYCODE_F10 = 140,
|
||||
AKEYCODE_F11 = 141,
|
||||
AKEYCODE_F12 = 142,
|
||||
AKEYCODE_NUM_LOCK = 143,
|
||||
AKEYCODE_NUMPAD_0 = 144,
|
||||
AKEYCODE_NUMPAD_1 = 145,
|
||||
AKEYCODE_NUMPAD_2 = 146,
|
||||
AKEYCODE_NUMPAD_3 = 147,
|
||||
AKEYCODE_NUMPAD_4 = 148,
|
||||
AKEYCODE_NUMPAD_5 = 149,
|
||||
AKEYCODE_NUMPAD_6 = 150,
|
||||
AKEYCODE_NUMPAD_7 = 151,
|
||||
AKEYCODE_NUMPAD_8 = 152,
|
||||
AKEYCODE_NUMPAD_9 = 153,
|
||||
AKEYCODE_NUMPAD_DIVIDE = 154,
|
||||
AKEYCODE_NUMPAD_MULTIPLY = 155,
|
||||
AKEYCODE_NUMPAD_SUBTRACT = 156,
|
||||
AKEYCODE_NUMPAD_ADD = 157,
|
||||
AKEYCODE_NUMPAD_DOT = 158,
|
||||
AKEYCODE_NUMPAD_COMMA = 159,
|
||||
AKEYCODE_NUMPAD_ENTER = 160,
|
||||
AKEYCODE_NUMPAD_EQUALS = 161,
|
||||
AKEYCODE_NUMPAD_LEFT_PAREN = 162,
|
||||
AKEYCODE_NUMPAD_RIGHT_PAREN = 163,
|
||||
AKEYCODE_VOLUME_MUTE = 164,
|
||||
AKEYCODE_INFO = 165,
|
||||
AKEYCODE_CHANNEL_UP = 166,
|
||||
AKEYCODE_CHANNEL_DOWN = 167,
|
||||
AKEYCODE_ZOOM_IN = 168,
|
||||
AKEYCODE_ZOOM_OUT = 169,
|
||||
AKEYCODE_TV = 170,
|
||||
AKEYCODE_WINDOW = 171,
|
||||
AKEYCODE_GUIDE = 172,
|
||||
AKEYCODE_DVR = 173,
|
||||
AKEYCODE_BOOKMARK = 174,
|
||||
AKEYCODE_CAPTIONS = 175,
|
||||
AKEYCODE_SETTINGS = 176,
|
||||
AKEYCODE_TV_POWER = 177,
|
||||
AKEYCODE_TV_INPUT = 178,
|
||||
AKEYCODE_STB_POWER = 179,
|
||||
AKEYCODE_STB_INPUT = 180,
|
||||
AKEYCODE_AVR_POWER = 181,
|
||||
AKEYCODE_AVR_INPUT = 182,
|
||||
AKEYCODE_PROG_RED = 183,
|
||||
AKEYCODE_PROG_GREEN = 184,
|
||||
AKEYCODE_PROG_YELLOW = 185,
|
||||
AKEYCODE_PROG_BLUE = 186,
|
||||
AKEYCODE_APP_SWITCH = 187,
|
||||
AKEYCODE_BUTTON_1 = 188,
|
||||
AKEYCODE_BUTTON_2 = 189,
|
||||
AKEYCODE_BUTTON_3 = 190,
|
||||
AKEYCODE_BUTTON_4 = 191,
|
||||
AKEYCODE_BUTTON_5 = 192,
|
||||
AKEYCODE_BUTTON_6 = 193,
|
||||
AKEYCODE_BUTTON_7 = 194,
|
||||
AKEYCODE_BUTTON_8 = 195,
|
||||
AKEYCODE_BUTTON_9 = 196,
|
||||
AKEYCODE_BUTTON_10 = 197,
|
||||
AKEYCODE_BUTTON_11 = 198,
|
||||
AKEYCODE_BUTTON_12 = 199,
|
||||
AKEYCODE_BUTTON_13 = 200,
|
||||
AKEYCODE_BUTTON_14 = 201,
|
||||
AKEYCODE_BUTTON_15 = 202,
|
||||
AKEYCODE_BUTTON_16 = 203,
|
||||
AKEYCODE_LANGUAGE_SWITCH = 204,
|
||||
AKEYCODE_MANNER_MODE = 205,
|
||||
AKEYCODE_3D_MODE = 206,
|
||||
AKEYCODE_CONTACTS = 207,
|
||||
AKEYCODE_CALENDAR = 208,
|
||||
AKEYCODE_MUSIC = 209,
|
||||
AKEYCODE_CALCULATOR = 210,
|
||||
AKEYCODE_ZENKAKU_HANKAKU = 211,
|
||||
AKEYCODE_EISU = 212,
|
||||
AKEYCODE_MUHENKAN = 213,
|
||||
AKEYCODE_HENKAN = 214,
|
||||
AKEYCODE_KATAKANA_HIRAGANA = 215,
|
||||
AKEYCODE_YEN = 216,
|
||||
AKEYCODE_RO = 217,
|
||||
AKEYCODE_KANA = 218,
|
||||
AKEYCODE_ASSIST = 219,
|
||||
AKEYCODE_BRIGHTNESS_DOWN = 220,
|
||||
AKEYCODE_BRIGHTNESS_UP = 221,
|
||||
AKEYCODE_MEDIA_AUDIO_TRACK = 222,
|
||||
AKEYCODE_SLEEP = 223,
|
||||
AKEYCODE_WAKEUP = 224,
|
||||
AKEYCODE_PAIRING = 225,
|
||||
AKEYCODE_MEDIA_TOP_MENU = 226,
|
||||
AKEYCODE_11 = 227,
|
||||
AKEYCODE_12 = 228,
|
||||
AKEYCODE_LAST_CHANNEL = 229,
|
||||
AKEYCODE_TV_DATA_SERVICE = 230,
|
||||
AKEYCODE_VOICE_ASSIST = 231,
|
||||
AKEYCODE_TV_RADIO_SERVICE = 232,
|
||||
AKEYCODE_TV_TELETEXT = 233,
|
||||
AKEYCODE_TV_NUMBER_ENTRY = 234,
|
||||
AKEYCODE_TV_TERRESTRIAL_ANALOG = 235,
|
||||
AKEYCODE_TV_TERRESTRIAL_DIGITAL = 236,
|
||||
AKEYCODE_TV_SATELLITE = 237,
|
||||
AKEYCODE_TV_SATELLITE_BS = 238,
|
||||
AKEYCODE_TV_SATELLITE_CS = 239,
|
||||
AKEYCODE_TV_SATELLITE_SERVICE = 240,
|
||||
AKEYCODE_TV_NETWORK = 241,
|
||||
AKEYCODE_TV_ANTENNA_CABLE = 242,
|
||||
AKEYCODE_TV_INPUT_HDMI_1 = 243,
|
||||
AKEYCODE_TV_INPUT_HDMI_2 = 244,
|
||||
AKEYCODE_TV_INPUT_HDMI_3 = 245,
|
||||
AKEYCODE_TV_INPUT_HDMI_4 = 246,
|
||||
AKEYCODE_TV_INPUT_COMPOSITE_1 = 247,
|
||||
AKEYCODE_TV_INPUT_COMPOSITE_2 = 248,
|
||||
AKEYCODE_TV_INPUT_COMPONENT_1 = 249,
|
||||
AKEYCODE_TV_INPUT_COMPONENT_2 = 250,
|
||||
AKEYCODE_TV_INPUT_VGA_1 = 251,
|
||||
AKEYCODE_TV_AUDIO_DESCRIPTION = 252,
|
||||
AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253,
|
||||
AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254,
|
||||
AKEYCODE_TV_ZOOM_MODE = 255,
|
||||
AKEYCODE_TV_CONTENTS_MENU = 256,
|
||||
AKEYCODE_TV_MEDIA_CONTEXT_MENU = 257,
|
||||
AKEYCODE_TV_TIMER_PROGRAMMING = 258,
|
||||
AKEYCODE_HELP = 259,
|
||||
AKEYCODE_NAVIGATE_PREVIOUS = 260,
|
||||
AKEYCODE_NAVIGATE_NEXT = 261,
|
||||
AKEYCODE_NAVIGATE_IN = 262,
|
||||
AKEYCODE_NAVIGATE_OUT = 263,
|
||||
AKEYCODE_STEM_PRIMARY = 264,
|
||||
AKEYCODE_STEM_1 = 265,
|
||||
AKEYCODE_STEM_2 = 266,
|
||||
AKEYCODE_STEM_3 = 267,
|
||||
AKEYCODE_DPAD_UP_LEFT = 268,
|
||||
AKEYCODE_DPAD_DOWN_LEFT = 269,
|
||||
AKEYCODE_DPAD_UP_RIGHT = 270,
|
||||
AKEYCODE_DPAD_DOWN_RIGHT = 271,
|
||||
AKEYCODE_MEDIA_SKIP_FORWARD = 272,
|
||||
AKEYCODE_MEDIA_SKIP_BACKWARD = 273,
|
||||
AKEYCODE_MEDIA_STEP_FORWARD = 274,
|
||||
AKEYCODE_MEDIA_STEP_BACKWARD = 275,
|
||||
AKEYCODE_SOFT_SLEEP = 276,
|
||||
AKEYCODE_CUT = 277,
|
||||
AKEYCODE_COPY = 278,
|
||||
AKEYCODE_PASTE = 279,
|
||||
AKEYCODE_SYSTEM_NAVIGATION_UP = 280,
|
||||
AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281,
|
||||
AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282,
|
||||
AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283,
|
||||
|
||||
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ANDROID_KEYCODES_H
|
|
@ -1,569 +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.
|
||||
*/
|
||||
|
||||
//
|
||||
// C/C++ logging functions. See the logging documentation for API details.
|
||||
//
|
||||
// We'd like these to be available from C code (in case we import some from
|
||||
// somewhere), so this has a C interface.
|
||||
//
|
||||
// The output will be correct when the log file is shared between multiple
|
||||
// threads and/or multiple processes so long as the operating system
|
||||
// supports O_APPEND. These calls have mutex-protected data structures
|
||||
// and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
//
|
||||
#if !defined(_LIBS_CUTILS_LOG_H) && !defined(_LIBS_LOG_LOG_H)
|
||||
#define _LIBS_LOG_LOG_H
|
||||
#define _LIBS_CUTILS_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_PTHREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
#include <log/uio.h>
|
||||
#include <log/logd.h>
|
||||
#else
|
||||
#include <cutils/uio.h>
|
||||
#include <cutils/logd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Normally we strip ALOGV (VERBOSE messages) from release builds.
|
||||
* You can modify this (for example with "#define LOG_NDEBUG 0"
|
||||
* at the top of your source file) to change that behavior.
|
||||
*/
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGV
|
||||
#if LOG_NDEBUG
|
||||
#define ALOGV(...) ((void)0)
|
||||
#else
|
||||
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
|
||||
#ifndef ALOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define ALOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define ALOGV_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD_IF
|
||||
#define ALOGD_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGI
|
||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGI_IF
|
||||
#define ALOGI_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW_IF
|
||||
#define ALOGW_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE_IF
|
||||
#define ALOGE_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* verbose priority.
|
||||
*/
|
||||
#ifndef IF_ALOGV
|
||||
#if LOG_NDEBUG
|
||||
#define IF_ALOGV() if (false)
|
||||
#else
|
||||
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* debug priority.
|
||||
*/
|
||||
#ifndef IF_ALOGD
|
||||
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* info priority.
|
||||
*/
|
||||
#ifndef IF_ALOGI
|
||||
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* warn priority.
|
||||
*/
|
||||
#ifndef IF_ALOGW
|
||||
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* error priority.
|
||||
*/
|
||||
#ifndef IF_ALOGE
|
||||
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
|
||||
#endif
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose system log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGV
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV(...) ((void)0)
|
||||
#else
|
||||
#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
|
||||
#ifndef SLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define SLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define SLOGV_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug system log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGD
|
||||
#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGD_IF
|
||||
#define SLOGD_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info system log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGI
|
||||
#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGI_IF
|
||||
#define SLOGI_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning system log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGW
|
||||
#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGW_IF
|
||||
#define SLOGW_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error system log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef SLOGE
|
||||
#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SLOGE_IF
|
||||
#define SLOGE_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGV
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV(...) ((void)0)
|
||||
#else
|
||||
#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
|
||||
#ifndef RLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define RLOGV_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGD
|
||||
#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGD_IF
|
||||
#define RLOGD_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGI
|
||||
#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGI_IF
|
||||
#define RLOGI_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGW
|
||||
#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGW_IF
|
||||
#define RLOGW_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGE
|
||||
#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGE_IF
|
||||
#define RLOGE_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Log a fatal error. If the given condition fails, this stops program
|
||||
* execution like a normal assertion, but also generating the given message.
|
||||
* It is NOT stripped from release builds. Note that the condition test
|
||||
* is -inverted- from the normal assert() semantics.
|
||||
*/
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
|
||||
* are stripped out of release builds.
|
||||
*/
|
||||
#if LOG_NDEBUG
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) ((void)0)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assertion that generates a log message when the assertion fails.
|
||||
* Stripped out of release builds. Uses the current LOG_TAG.
|
||||
*/
|
||||
#ifndef ALOG_ASSERT
|
||||
#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
|
||||
//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Basic log message macro.
|
||||
*
|
||||
* Example:
|
||||
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
|
||||
*
|
||||
* The second argument may be NULL or "" to indicate the "global" tag.
|
||||
*/
|
||||
#ifndef ALOG
|
||||
#define ALOG(priority, tag, ...) \
|
||||
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to specify a number for the priority.
|
||||
*/
|
||||
#ifndef LOG_PRI
|
||||
#define LOG_PRI(priority, tag, ...) \
|
||||
android_printLog(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to pass in a varargs ("args" is a va_list).
|
||||
*/
|
||||
#ifndef LOG_PRI_VA
|
||||
#define LOG_PRI_VA(priority, tag, fmt, args) \
|
||||
android_vprintLog(priority, NULL, tag, fmt, args)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional given a desired logging priority and tag.
|
||||
*/
|
||||
#ifndef IF_ALOG
|
||||
#define IF_ALOG(priority, tag) \
|
||||
if (android_testLog(ANDROID_##priority, tag))
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Event logging.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Event log entry types. These must match up with the declarations in
|
||||
* java/android/android/util/EventLog.java.
|
||||
*/
|
||||
typedef enum {
|
||||
EVENT_TYPE_INT = 0,
|
||||
EVENT_TYPE_LONG = 1,
|
||||
EVENT_TYPE_STRING = 2,
|
||||
EVENT_TYPE_LIST = 3,
|
||||
} AndroidEventLogType;
|
||||
|
||||
|
||||
#ifndef LOG_EVENT_INT
|
||||
#define LOG_EVENT_INT(_tag, _value) { \
|
||||
int intBuf = _value; \
|
||||
(void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
|
||||
sizeof(intBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_LONG
|
||||
#define LOG_EVENT_LONG(_tag, _value) { \
|
||||
long long longBuf = _value; \
|
||||
(void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
|
||||
sizeof(longBuf)); \
|
||||
}
|
||||
#endif
|
||||
#ifndef LOG_EVENT_STRING
|
||||
#define LOG_EVENT_STRING(_tag, _value) \
|
||||
((void) 0) /* not implemented -- must combine len with string */
|
||||
#endif
|
||||
/* TODO: something for LIST */
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
*
|
||||
* The stuff in the rest of this file should not be used directly.
|
||||
*/
|
||||
|
||||
#define android_printLog(prio, tag, fmt...) \
|
||||
__android_log_print(prio, tag, fmt)
|
||||
|
||||
#define android_vprintLog(prio, cond, tag, fmt...) \
|
||||
__android_log_vprint(prio, tag, fmt)
|
||||
|
||||
/* XXX Macros to work around syntax errors in places where format string
|
||||
* arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
|
||||
* (happens only in debug builds).
|
||||
*/
|
||||
|
||||
/* Returns 2nd arg. Used to substitute default value if caller's vararg list
|
||||
* is empty.
|
||||
*/
|
||||
#define __android_second(dummy, second, ...) second
|
||||
|
||||
/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
|
||||
* returns nothing.
|
||||
*/
|
||||
#define __android_rest(first, ...) , ## __VA_ARGS__
|
||||
|
||||
#define android_printAssert(cond, tag, fmt...) \
|
||||
__android_log_assert(cond, tag, \
|
||||
__android_second(0, ## fmt, NULL) __android_rest(fmt))
|
||||
|
||||
#define android_writeLog(prio, tag, text) \
|
||||
__android_log_write(prio, tag, text)
|
||||
|
||||
#define android_bWriteLog(tag, payload, len) \
|
||||
__android_log_bwrite(tag, payload, len)
|
||||
#define android_btWriteLog(tag, type, payload, len) \
|
||||
__android_log_btwrite(tag, type, payload, len)
|
||||
|
||||
// TODO: remove these prototypes and their users
|
||||
#define android_testLog(prio, tag) (1)
|
||||
#define android_writevLog(vec,num) do{}while(0)
|
||||
#define android_write1Log(str,len) do{}while (0)
|
||||
#define android_setMinPriority(tag, prio) do{}while(0)
|
||||
//#define android_logToCallback(func) do{}while(0)
|
||||
#define android_logToFile(tag, file) (0)
|
||||
#define android_logToFd(tag, fd) (0)
|
||||
|
||||
typedef enum {
|
||||
LOG_ID_MAIN = 0,
|
||||
LOG_ID_RADIO = 1,
|
||||
LOG_ID_EVENTS = 2,
|
||||
LOG_ID_SYSTEM = 3,
|
||||
|
||||
LOG_ID_MAX
|
||||
} log_id_t;
|
||||
|
||||
/*
|
||||
* Send a simple string to the log.
|
||||
*/
|
||||
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
|
||||
int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _LIBS_CUTILS_LOG_H
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* 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 _LIBS_CUTILS_TRACE_H
|
||||
#define _LIBS_CUTILS_TRACE_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
#ifdef ANDROID_SMP
|
||||
#include <cutils/atomic-inline.h>
|
||||
#else
|
||||
#include <cutils/atomic.h>
|
||||
#endif
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* The ATRACE_TAG macro can be defined before including this header to trace
|
||||
* using one of the tags defined below. It must be defined to one of the
|
||||
* following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
|
||||
* userland to avoid some of the runtime cost of tracing when it is not desired.
|
||||
*
|
||||
* Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
|
||||
* being enabled - this should ONLY be done for debug code, as userland tracing
|
||||
* has a performance cost even when the trace is not being recorded. Defining
|
||||
* ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
|
||||
* in the tracing always being disabled.
|
||||
*
|
||||
* ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing
|
||||
* within a hardware module. For example a camera hardware module would set:
|
||||
* #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
|
||||
*
|
||||
* Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
|
||||
*/
|
||||
#define ATRACE_TAG_NEVER 0 // This tag is never enabled.
|
||||
#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled.
|
||||
#define ATRACE_TAG_GRAPHICS (1<<1)
|
||||
#define ATRACE_TAG_INPUT (1<<2)
|
||||
#define ATRACE_TAG_VIEW (1<<3)
|
||||
#define ATRACE_TAG_WEBVIEW (1<<4)
|
||||
#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
|
||||
#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
|
||||
#define ATRACE_TAG_SYNC_MANAGER (1<<7)
|
||||
#define ATRACE_TAG_AUDIO (1<<8)
|
||||
#define ATRACE_TAG_VIDEO (1<<9)
|
||||
#define ATRACE_TAG_CAMERA (1<<10)
|
||||
#define ATRACE_TAG_HAL (1<<11)
|
||||
#define ATRACE_TAG_APP (1<<12)
|
||||
#define ATRACE_TAG_RESOURCES (1<<13)
|
||||
#define ATRACE_TAG_DALVIK (1<<14)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_DALVIK
|
||||
|
||||
// Reserved for initialization.
|
||||
#define ATRACE_TAG_NOT_READY (1LL<<63)
|
||||
|
||||
#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
|
||||
|
||||
#ifndef ATRACE_TAG
|
||||
#define ATRACE_TAG ATRACE_TAG_NEVER
|
||||
#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK
|
||||
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
/**
|
||||
* Maximum size of a message that can be logged to the trace buffer.
|
||||
* Note this message includes a tag, the pid, and the string given as the name.
|
||||
* Names should be kept short to get the most use of the trace buffer.
|
||||
*/
|
||||
#define ATRACE_MESSAGE_LENGTH 1024
|
||||
|
||||
/**
|
||||
* Opens the trace file for writing and reads the property for initial tags.
|
||||
* The atrace.tags.enableflags property sets the tags to trace.
|
||||
* This function should not be explicitly called, the first call to any normal
|
||||
* trace function will cause it to be run safely.
|
||||
*/
|
||||
void atrace_setup();
|
||||
|
||||
/**
|
||||
* If tracing is ready, set atrace_enabled_tags to the system property
|
||||
* debug.atrace.tags.enableflags. Can be used as a sysprop change callback.
|
||||
*/
|
||||
void atrace_update_tags();
|
||||
|
||||
/**
|
||||
* Set whether the process is debuggable. By default the process is not
|
||||
* considered debuggable. If the process is not debuggable then application-
|
||||
* level tracing is not allowed unless the ro.debuggable system property is
|
||||
* set to '1'.
|
||||
*/
|
||||
void atrace_set_debuggable(bool debuggable);
|
||||
|
||||
/**
|
||||
* Set whether tracing is enabled for the current process. This is used to
|
||||
* prevent tracing within the Zygote process.
|
||||
*/
|
||||
void atrace_set_tracing_enabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Flag indicating whether setup has been completed, initialized to 0.
|
||||
* Nonzero indicates setup has completed.
|
||||
* Note: This does NOT indicate whether or not setup was successful.
|
||||
*/
|
||||
extern volatile int32_t atrace_is_ready;
|
||||
|
||||
/**
|
||||
* Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
|
||||
* A value of zero indicates setup has failed.
|
||||
* Any other nonzero value indicates setup has succeeded, and tracing is on.
|
||||
*/
|
||||
extern uint64_t atrace_enabled_tags;
|
||||
|
||||
/**
|
||||
* Handle to the kernel's trace buffer, initialized to -1.
|
||||
* Any other value indicates setup has succeeded, and is a valid fd for tracing.
|
||||
*/
|
||||
extern int atrace_marker_fd;
|
||||
|
||||
/**
|
||||
* atrace_init readies the process for tracing by opening the trace_marker file.
|
||||
* Calling any trace function causes this to be run, so calling it is optional.
|
||||
* This can be explicitly run to avoid setup delay on first trace function.
|
||||
*/
|
||||
#define ATRACE_INIT() atrace_init()
|
||||
static inline void atrace_init()
|
||||
{
|
||||
if (CC_UNLIKELY(!android_atomic_acquire_load(&atrace_is_ready))) {
|
||||
atrace_setup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mask of all tags currently enabled.
|
||||
* It can be used as a guard condition around more expensive trace calculations.
|
||||
* Every trace function calls this, which ensures atrace_init is run.
|
||||
*/
|
||||
#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
|
||||
static inline uint64_t atrace_get_enabled_tags()
|
||||
{
|
||||
atrace_init();
|
||||
return atrace_enabled_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given tag is currently enabled.
|
||||
* Returns nonzero if the tag is enabled, otherwise zero.
|
||||
* It can be used as a guard condition around more expensive trace calculations.
|
||||
*/
|
||||
#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
|
||||
static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
|
||||
{
|
||||
return atrace_get_enabled_tags() & tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the beginning of a context. name is used to identify the context.
|
||||
* This is often used to time function execution.
|
||||
*/
|
||||
#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
|
||||
static inline void atrace_begin(uint64_t tag, const char* name)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the end of a context.
|
||||
* This should match up (and occur after) a corresponding ATRACE_BEGIN.
|
||||
*/
|
||||
#define ATRACE_END() atrace_end(ATRACE_TAG)
|
||||
static inline void atrace_end(uint64_t tag)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char c = 'E';
|
||||
write(atrace_marker_fd, &c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
|
||||
* contexts, asynchronous events do not need to be nested. The name describes
|
||||
* the event, and the cookie provides a unique identifier for distinguishing
|
||||
* simultaneous events. The name and cookie used to begin an event must be
|
||||
* used to end it.
|
||||
*/
|
||||
#define ATRACE_ASYNC_BEGIN(name, cookie) \
|
||||
atrace_async_begin(ATRACE_TAG, name, cookie)
|
||||
static inline void atrace_async_begin(uint64_t tag, const char* name,
|
||||
int32_t cookie)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(),
|
||||
name, cookie);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the end of an asynchronous event.
|
||||
* This should have a corresponding ATRACE_ASYNC_BEGIN.
|
||||
*/
|
||||
#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
|
||||
static inline void atrace_async_end(uint64_t tag, const char* name,
|
||||
int32_t cookie)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(),
|
||||
name, cookie);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Traces an integer counter value. name is used to identify the counter.
|
||||
* This can be used to track how a value changes over time.
|
||||
*/
|
||||
#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
|
||||
static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d",
|
||||
getpid(), name, value);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
#else // not HAVE_ANDROID_OS
|
||||
|
||||
#define ATRACE_INIT()
|
||||
#define ATRACE_GET_ENABLED_TAGS()
|
||||
#define ATRACE_ENABLED()
|
||||
#define ATRACE_BEGIN(name)
|
||||
#define ATRACE_END()
|
||||
#define ATRACE_ASYNC_BEGIN(name, cookie)
|
||||
#define ATRACE_ASYNC_END(name, cookie)
|
||||
#define ATRACE_INT(name, value)
|
||||
|
||||
#endif // not HAVE_ANDROID_OS
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif // _LIBS_CUTILS_TRACE_H
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,289 +0,0 @@
|
|||
/* $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ */
|
||||
/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */
|
||||
|
||||
/*
|
||||
* SHA-1 in C
|
||||
* By Steve Reid <steve@edmweb.com>
|
||||
* 100% Public Domain
|
||||
*
|
||||
* Test Vectors (from FIPS PUB 180-1)
|
||||
* "abc"
|
||||
* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
* A million repetitions of "a"
|
||||
* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
#define SHA1HANDSOFF /* Copies data before messing with it. */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#if defined(_KERNEL) || defined(_STANDALONE)
|
||||
__KERNEL_RCSID(0, "$NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $");
|
||||
|
||||
#include <lib/libkern/libkern.h>
|
||||
|
||||
#else
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
__RCSID("$NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $");
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "sha1.h"
|
||||
|
||||
#define _DIAGASSERT assert
|
||||
|
||||
#if HAVE_NBTOOL_CONFIG_H
|
||||
#include "nbtool_config.h"
|
||||
#endif
|
||||
|
||||
#if !HAVE_SHA1_H
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/*
|
||||
* blk0() and blk() perform the initial expand.
|
||||
* I got the idea of expanding during the round function from SSLeay
|
||||
*/
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#else
|
||||
# define blk0(i) block->l[i]
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/*
|
||||
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
|
||||
*/
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
#if !defined(_KERNEL) && !defined(_STANDALONE)
|
||||
#if defined(__weak_alias)
|
||||
__weak_alias(SHA1Transform,_SHA1Transform)
|
||||
__weak_alias(SHA1Init,_SHA1Init)
|
||||
__weak_alias(SHA1Update,_SHA1Update)
|
||||
__weak_alias(SHA1Final,_SHA1Final)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
|
||||
/* old sparc64 gcc could not compile this */
|
||||
#undef SPARC64_GCC_WORKAROUND
|
||||
#if defined(__sparc64__) && defined(__GNUC__) && __GNUC__ < 3
|
||||
#define SPARC64_GCC_WORKAROUND
|
||||
#endif
|
||||
|
||||
#ifdef SPARC64_GCC_WORKAROUND
|
||||
void do_R01(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
|
||||
void do_R2(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
|
||||
void do_R3(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
|
||||
void do_R4(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
|
||||
|
||||
#define nR0(v,w,x,y,z,i) R0(*v,*w,*x,*y,*z,i)
|
||||
#define nR1(v,w,x,y,z,i) R1(*v,*w,*x,*y,*z,i)
|
||||
#define nR2(v,w,x,y,z,i) R2(*v,*w,*x,*y,*z,i)
|
||||
#define nR3(v,w,x,y,z,i) R3(*v,*w,*x,*y,*z,i)
|
||||
#define nR4(v,w,x,y,z,i) R4(*v,*w,*x,*y,*z,i)
|
||||
|
||||
void
|
||||
do_R01(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
|
||||
{
|
||||
nR0(a,b,c,d,e, 0); nR0(e,a,b,c,d, 1); nR0(d,e,a,b,c, 2); nR0(c,d,e,a,b, 3);
|
||||
nR0(b,c,d,e,a, 4); nR0(a,b,c,d,e, 5); nR0(e,a,b,c,d, 6); nR0(d,e,a,b,c, 7);
|
||||
nR0(c,d,e,a,b, 8); nR0(b,c,d,e,a, 9); nR0(a,b,c,d,e,10); nR0(e,a,b,c,d,11);
|
||||
nR0(d,e,a,b,c,12); nR0(c,d,e,a,b,13); nR0(b,c,d,e,a,14); nR0(a,b,c,d,e,15);
|
||||
nR1(e,a,b,c,d,16); nR1(d,e,a,b,c,17); nR1(c,d,e,a,b,18); nR1(b,c,d,e,a,19);
|
||||
}
|
||||
|
||||
void
|
||||
do_R2(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
|
||||
{
|
||||
nR2(a,b,c,d,e,20); nR2(e,a,b,c,d,21); nR2(d,e,a,b,c,22); nR2(c,d,e,a,b,23);
|
||||
nR2(b,c,d,e,a,24); nR2(a,b,c,d,e,25); nR2(e,a,b,c,d,26); nR2(d,e,a,b,c,27);
|
||||
nR2(c,d,e,a,b,28); nR2(b,c,d,e,a,29); nR2(a,b,c,d,e,30); nR2(e,a,b,c,d,31);
|
||||
nR2(d,e,a,b,c,32); nR2(c,d,e,a,b,33); nR2(b,c,d,e,a,34); nR2(a,b,c,d,e,35);
|
||||
nR2(e,a,b,c,d,36); nR2(d,e,a,b,c,37); nR2(c,d,e,a,b,38); nR2(b,c,d,e,a,39);
|
||||
}
|
||||
|
||||
void
|
||||
do_R3(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
|
||||
{
|
||||
nR3(a,b,c,d,e,40); nR3(e,a,b,c,d,41); nR3(d,e,a,b,c,42); nR3(c,d,e,a,b,43);
|
||||
nR3(b,c,d,e,a,44); nR3(a,b,c,d,e,45); nR3(e,a,b,c,d,46); nR3(d,e,a,b,c,47);
|
||||
nR3(c,d,e,a,b,48); nR3(b,c,d,e,a,49); nR3(a,b,c,d,e,50); nR3(e,a,b,c,d,51);
|
||||
nR3(d,e,a,b,c,52); nR3(c,d,e,a,b,53); nR3(b,c,d,e,a,54); nR3(a,b,c,d,e,55);
|
||||
nR3(e,a,b,c,d,56); nR3(d,e,a,b,c,57); nR3(c,d,e,a,b,58); nR3(b,c,d,e,a,59);
|
||||
}
|
||||
|
||||
void
|
||||
do_R4(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
|
||||
{
|
||||
nR4(a,b,c,d,e,60); nR4(e,a,b,c,d,61); nR4(d,e,a,b,c,62); nR4(c,d,e,a,b,63);
|
||||
nR4(b,c,d,e,a,64); nR4(a,b,c,d,e,65); nR4(e,a,b,c,d,66); nR4(d,e,a,b,c,67);
|
||||
nR4(c,d,e,a,b,68); nR4(b,c,d,e,a,69); nR4(a,b,c,d,e,70); nR4(e,a,b,c,d,71);
|
||||
nR4(d,e,a,b,c,72); nR4(c,d,e,a,b,73); nR4(b,c,d,e,a,74); nR4(a,b,c,d,e,75);
|
||||
nR4(e,a,b,c,d,76); nR4(d,e,a,b,c,77); nR4(c,d,e,a,b,78); nR4(b,c,d,e,a,79);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hash a single 512-bit block. This is the core of the algorithm.
|
||||
*/
|
||||
void SHA1Transform(uint32_t state[5], const uint8_t buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
CHAR64LONG16 *block;
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 workspace;
|
||||
#endif
|
||||
|
||||
_DIAGASSERT(buffer != 0);
|
||||
_DIAGASSERT(state != 0);
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
block = &workspace;
|
||||
(void)memcpy(block, buffer, 64);
|
||||
#else
|
||||
block = (CHAR64LONG16 *)(void *)buffer;
|
||||
#endif
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
#ifdef SPARC64_GCC_WORKAROUND
|
||||
do_R01(&a, &b, &c, &d, &e, block);
|
||||
do_R2(&a, &b, &c, &d, &e, block);
|
||||
do_R3(&a, &b, &c, &d, &e, block);
|
||||
do_R4(&a, &b, &c, &d, &e, block);
|
||||
#else
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
#endif
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SHA1Init - Initialize new context
|
||||
*/
|
||||
void SHA1Init(SHA1_CTX *context)
|
||||
{
|
||||
|
||||
_DIAGASSERT(context != 0);
|
||||
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run your data through this.
|
||||
*/
|
||||
void SHA1Update(SHA1_CTX *context, const uint8_t *data, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
_DIAGASSERT(context != 0);
|
||||
_DIAGASSERT(data != 0);
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1] += (len>>29)+1;
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
(void)memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64)
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
j = 0;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
(void)memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add padding and return the message digest.
|
||||
*/
|
||||
void SHA1Final(uint8_t digest[20], SHA1_CTX *context)
|
||||
{
|
||||
unsigned int i;
|
||||
uint8_t finalcount[8];
|
||||
|
||||
_DIAGASSERT(digest != 0);
|
||||
_DIAGASSERT(context != 0);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
SHA1Update(context, (const uint8_t *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448)
|
||||
SHA1Update(context, (const uint8_t *)"\0", 1);
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
|
||||
if (digest) {
|
||||
for (i = 0; i < 20; i++)
|
||||
digest[i] = (uint8_t)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_SHA1_H */
|
|
@ -1,31 +0,0 @@
|
|||
/* $NetBSD: sha1.h,v 1.13 2005/12/26 18:41:36 perry Exp $ */
|
||||
|
||||
/*
|
||||
* SHA-1 in C
|
||||
* By Steve Reid <steve@edmweb.com>
|
||||
* 100% Public Domain
|
||||
*/
|
||||
|
||||
#ifndef _SYS_SHA1_H_
|
||||
#define _SYS_SHA1_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define SHA1_DIGEST_LENGTH 20
|
||||
#define SHA1_DIGEST_STRING_LENGTH 41
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
u_char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
__BEGIN_DECLS
|
||||
void SHA1Transform(uint32_t[5], const u_char[64]);
|
||||
void SHA1Init(SHA1_CTX *);
|
||||
void SHA1Update(SHA1_CTX *, const u_char *, u_int);
|
||||
void SHA1Final(u_char[SHA1_DIGEST_LENGTH], SHA1_CTX *);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _SYS_SHA1_H_ */
|
|
@ -1,703 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (C) 2013 Mozilla Foundation
|
||||
*
|
||||
* 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 "FakeSurfaceComposer"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/atomic.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <gui/IDisplayEventConnection.h>
|
||||
#include <gui/GraphicBufferAlloc.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <ui/DisplayInfo.h>
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
#include <ui/Rect.h>
|
||||
#endif
|
||||
|
||||
#include "../libdisplay/GonkDisplay.h"
|
||||
#include "../nsScreenManagerGonk.h"
|
||||
#include "FakeSurfaceComposer.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace android {
|
||||
|
||||
/* static */
|
||||
void FakeSurfaceComposer::instantiate() {
|
||||
defaultServiceManager()->addService(
|
||||
String16("SurfaceFlinger"), new FakeSurfaceComposer());
|
||||
}
|
||||
|
||||
FakeSurfaceComposer::FakeSurfaceComposer()
|
||||
: BnSurfaceComposer()
|
||||
{
|
||||
}
|
||||
|
||||
FakeSurfaceComposer::~FakeSurfaceComposer()
|
||||
{
|
||||
}
|
||||
|
||||
status_t FakeSurfaceComposer::onTransact(uint32_t code, const Parcel& data,
|
||||
Parcel* reply, uint32_t flags)
|
||||
{
|
||||
switch (code) {
|
||||
case CREATE_CONNECTION:
|
||||
case CREATE_DISPLAY:
|
||||
case SET_TRANSACTION_STATE:
|
||||
case CAPTURE_SCREEN:
|
||||
{
|
||||
// codes that require permission check
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
const int pid = ipc->getCallingPid();
|
||||
const int uid = ipc->getCallingUid();
|
||||
// Accept request only when uid is root.
|
||||
if (uid != AID_ROOT) {
|
||||
ALOGE("Permission Denial: "
|
||||
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
|
||||
return PERMISSION_DENIED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BnSurfaceComposer::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
sp<ISurfaceComposerClient> FakeSurfaceComposer::createConnection()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sp<IGraphicBufferAlloc> FakeSurfaceComposer::createGraphicBufferAlloc()
|
||||
{
|
||||
sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());
|
||||
return gba;
|
||||
}
|
||||
|
||||
class DestroyDisplayRunnable : public Runnable {
|
||||
public:
|
||||
DestroyDisplayRunnable(FakeSurfaceComposer* aComposer, ssize_t aIndex)
|
||||
: mComposer(aComposer), mIndex(aIndex) { }
|
||||
NS_IMETHOD Run() override {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
Mutex::Autolock _l(mComposer->mStateLock);
|
||||
RefPtr<nsScreenManagerGonk> screenManager =
|
||||
nsScreenManagerGonk::GetInstance();
|
||||
screenManager->RemoveScreen(GonkDisplay::DISPLAY_VIRTUAL);
|
||||
mComposer->mDisplays.removeItemsAt(mIndex);
|
||||
return NS_OK;
|
||||
}
|
||||
sp<FakeSurfaceComposer> mComposer;
|
||||
ssize_t mIndex;
|
||||
};
|
||||
|
||||
sp<IBinder> FakeSurfaceComposer::createDisplay(const String8& displayName,
|
||||
bool secure)
|
||||
{
|
||||
#if ANDROID_VERSION >= 19
|
||||
class DisplayToken : public BBinder {
|
||||
sp<FakeSurfaceComposer> composer;
|
||||
virtual ~DisplayToken() {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
// no more references, this display must be terminated
|
||||
Mutex::Autolock _l(composer->mStateLock);
|
||||
ssize_t idx = composer->mDisplays.indexOfKey(this);
|
||||
if (idx >= 0) {
|
||||
nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(composer.get(), idx));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
public:
|
||||
DisplayToken(const sp<FakeSurfaceComposer>& composer)
|
||||
: composer(composer) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<BBinder> token = new DisplayToken(this);
|
||||
|
||||
Mutex::Autolock _l(mStateLock);
|
||||
DisplayDeviceState info(HWC_DISPLAY_VIRTUAL);
|
||||
info.displayName = displayName;
|
||||
info.displayId = GonkDisplay::DISPLAY_VIRTUAL;
|
||||
info.isSecure = secure;
|
||||
mDisplays.add(token, info);
|
||||
return token;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
void FakeSurfaceComposer::destroyDisplay(const sp<IBinder>& display)
|
||||
{
|
||||
Mutex::Autolock _l(mStateLock);
|
||||
|
||||
ssize_t idx = mDisplays.indexOfKey(display);
|
||||
if (idx < 0) {
|
||||
ALOGW("destroyDisplay: invalid display token");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(this, idx));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
#endif
|
||||
|
||||
sp<IBinder> FakeSurfaceComposer::getBuiltInDisplay(int32_t id)
|
||||
{
|
||||
// support only primary display
|
||||
if (uint32_t(id) != HWC_DISPLAY_PRIMARY) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!mPrimaryDisplay.get()) {
|
||||
mPrimaryDisplay = new BBinder();
|
||||
}
|
||||
return mPrimaryDisplay;
|
||||
}
|
||||
|
||||
void FakeSurfaceComposer::setTransactionState(
|
||||
const Vector<ComposerState>& state,
|
||||
const Vector<DisplayState>& displays,
|
||||
uint32_t flags)
|
||||
{
|
||||
Mutex::Autolock _l(mStateLock);
|
||||
size_t count = displays.size();
|
||||
for (size_t i=0 ; i<count ; i++) {
|
||||
const DisplayState& s(displays[i]);
|
||||
setDisplayStateLocked(s);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FakeSurfaceComposer::setDisplayStateLocked(const DisplayState& s)
|
||||
{
|
||||
ssize_t dpyIdx = mDisplays.indexOfKey(s.token);
|
||||
if (dpyIdx < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
DisplayDeviceState& disp(mDisplays.editValueAt(dpyIdx));
|
||||
|
||||
if (!disp.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t what = s.what;
|
||||
if (what & DisplayState::eSurfaceChanged) {
|
||||
if (disp.surface->asBinder() != s.surface->asBinder()) {
|
||||
disp.surface = s.surface;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
}
|
||||
if (what & DisplayState::eLayerStackChanged) {
|
||||
if (disp.layerStack != s.layerStack) {
|
||||
disp.layerStack = s.layerStack;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
}
|
||||
if (what & DisplayState::eDisplayProjectionChanged) {
|
||||
if (disp.orientation != s.orientation) {
|
||||
disp.orientation = s.orientation;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
if (disp.frame != s.frame) {
|
||||
disp.frame = s.frame;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
if (disp.viewport != s.viewport) {
|
||||
disp.viewport = s.viewport;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
}
|
||||
#if ANDROID_VERSION >= 21
|
||||
if (what & DisplayState::eDisplaySizeChanged) {
|
||||
if (disp.width != s.width) {
|
||||
disp.width = s.width;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
if (disp.height != s.height) {
|
||||
disp.height = s.height;
|
||||
flags |= eDisplayTransactionNeeded;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (what & DisplayState::eSurfaceChanged) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([&]() {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
RefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance();
|
||||
screenManager->AddScreen(GonkDisplay::DISPLAY_VIRTUAL, disp.surface.get());
|
||||
});
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void FakeSurfaceComposer::bootFinished()
|
||||
{
|
||||
}
|
||||
|
||||
bool FakeSurfaceComposer::authenticateSurfaceTexture(
|
||||
const sp<IGraphicBufferProducer>& bufferProducer) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
sp<IDisplayEventConnection> FakeSurfaceComposer::createDisplayEventConnection() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Capture screen into an IGraphiBufferProducer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
inline Barrier() : state(CLOSED) { }
|
||||
inline ~Barrier() { }
|
||||
|
||||
// Release any threads waiting at the Barrier.
|
||||
// Provides release semantics: preceding loads and stores will be visible
|
||||
// to other threads before they wake up.
|
||||
void open() {
|
||||
Mutex::Autolock _l(lock);
|
||||
state = OPENED;
|
||||
cv.broadcast();
|
||||
}
|
||||
|
||||
// Reset the Barrier, so wait() will block until open() has been called.
|
||||
void close() {
|
||||
Mutex::Autolock _l(lock);
|
||||
state = CLOSED;
|
||||
}
|
||||
|
||||
// Wait until the Barrier is OPEN.
|
||||
// Provides acquire semantics: no subsequent loads or stores will occur
|
||||
// until wait() returns.
|
||||
void wait() const {
|
||||
Mutex::Autolock _l(lock);
|
||||
while (state == CLOSED) {
|
||||
cv.wait(lock);
|
||||
}
|
||||
}
|
||||
private:
|
||||
enum { OPENED, CLOSED };
|
||||
mutable Mutex lock;
|
||||
mutable Condition cv;
|
||||
volatile int state;
|
||||
};
|
||||
|
||||
/* The code below is here to handle b/8734824
|
||||
*
|
||||
* We create a IGraphicBufferProducer wrapper that forwards all calls
|
||||
* to the calling binder thread, where they are executed. This allows
|
||||
* the calling thread to be reused (on the other side) and not
|
||||
* depend on having "enough" binder threads to handle the requests.
|
||||
*
|
||||
*/
|
||||
|
||||
class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
||||
sp<IGraphicBufferProducer> impl;
|
||||
sp<Looper> looper;
|
||||
status_t result;
|
||||
bool exitPending;
|
||||
bool exitRequested;
|
||||
mutable Barrier barrier;
|
||||
volatile int32_t memoryBarrier;
|
||||
uint32_t code;
|
||||
Parcel const* data;
|
||||
Parcel* reply;
|
||||
|
||||
enum {
|
||||
MSG_API_CALL,
|
||||
MSG_EXIT
|
||||
};
|
||||
|
||||
/*
|
||||
* this is called by our "fake" BpGraphicBufferProducer. We package the
|
||||
* data and reply Parcel and forward them to the calling thread.
|
||||
*/
|
||||
virtual status_t transact(uint32_t code,
|
||||
const Parcel& data, Parcel* reply, uint32_t flags) {
|
||||
this->code = code;
|
||||
this->data = &data;
|
||||
this->reply = reply;
|
||||
android_atomic_acquire_store(0, &memoryBarrier);
|
||||
if (exitPending) {
|
||||
// if we've exited, we run the message synchronously right here
|
||||
handleMessage(Message(MSG_API_CALL));
|
||||
} else {
|
||||
barrier.close();
|
||||
looper->sendMessage(this, Message(MSG_API_CALL));
|
||||
barrier.wait();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* here we run on the binder calling thread. All we've got to do is
|
||||
* call the real BpGraphicBufferProducer.
|
||||
*/
|
||||
virtual void handleMessage(const Message& message) {
|
||||
android_atomic_release_load(&memoryBarrier);
|
||||
if (message.what == MSG_API_CALL) {
|
||||
impl->asBinder()->transact(code, data[0], reply);
|
||||
barrier.open();
|
||||
} else if (message.what == MSG_EXIT) {
|
||||
exitRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) :
|
||||
impl(impl), looper(new Looper(true)), result(NO_ERROR),
|
||||
exitPending(false), exitRequested(false) {
|
||||
}
|
||||
|
||||
status_t waitForResponse() {
|
||||
do {
|
||||
looper->pollOnce(-1);
|
||||
} while (!exitRequested);
|
||||
return result;
|
||||
}
|
||||
|
||||
void exit(status_t result) {
|
||||
this->result = result;
|
||||
exitPending = true;
|
||||
looper->sendMessage(this, Message(MSG_EXIT));
|
||||
}
|
||||
};
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::captureScreen(const sp<IBinder>& display
|
||||
, const sp<IGraphicBufferProducer>& producer
|
||||
#if ANDROID_VERSION >= 21
|
||||
, Rect sourceCrop
|
||||
#endif
|
||||
, uint32_t reqWidth
|
||||
, uint32_t reqHeight
|
||||
, uint32_t minLayerZ
|
||||
, uint32_t maxLayerZ
|
||||
#if ANDROID_VERSION >= 21
|
||||
, bool useIdentityTransform
|
||||
, Rotation rotation
|
||||
#elif ANDROID_VERSION < 19
|
||||
, bool isCpuConsumer
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (display == 0 || producer == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Limit only to primary display
|
||||
if (display != mPrimaryDisplay) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// this creates a "fake" BBinder which will serve as a "fake" remote
|
||||
// binder to receive the marshaled calls and forward them to the
|
||||
// real remote (a BpGraphicBufferProducer)
|
||||
sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
|
||||
// the asInterface() call below creates our "fake" BpGraphicBufferProducer
|
||||
// which does the marshaling work forwards to our "fake remote" above.
|
||||
sp<IGraphicBufferProducer> fakeProducer = IGraphicBufferProducer::asInterface(wrapper);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([&]() {
|
||||
captureScreenImp(fakeProducer, reqWidth, reqHeight, wrapper.get());
|
||||
});
|
||||
NS_DispatchToMainThread(runnable);
|
||||
|
||||
status_t result = wrapper->waitForResponse();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class RunnableCallTask final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit RunnableCallTask(nsIRunnable* aRunnable)
|
||||
: mRunnable(aRunnable) {}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
return mRunnable->Run();
|
||||
}
|
||||
protected:
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
void
|
||||
FakeSurfaceComposer::captureScreenImp(const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth,
|
||||
uint32_t reqHeight,
|
||||
const sp<GraphicProducerWrapper>& wrapper)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(wrapper.get());
|
||||
|
||||
RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
|
||||
|
||||
// get screen geometry
|
||||
nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect();
|
||||
const uint32_t hw_w = screenBounds.width;
|
||||
const uint32_t hw_h = screenBounds.height;
|
||||
|
||||
if (reqWidth > hw_w || reqHeight > hw_h) {
|
||||
ALOGE("size mismatch (%d, %d) > (%d, %d)",
|
||||
reqWidth, reqHeight, hw_w, hw_h);
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
reqWidth = (!reqWidth) ? hw_w : reqWidth;
|
||||
reqHeight = (!reqHeight) ? hw_h : reqHeight;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([screen, reqWidth, reqHeight, producer, wrapper]() {
|
||||
// create a surface (because we're a producer, and we need to
|
||||
// dequeue/queue a buffer)
|
||||
sp<Surface> sur = new Surface(producer);
|
||||
ANativeWindow* window = sur.get();
|
||||
// The closure makes screen const and we can't call forget() on it.
|
||||
RefPtr<nsScreenGonk> screenAlias = screen;
|
||||
|
||||
if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != NO_ERROR) {
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
|
||||
NS_ReleaseOnMainThread(screenAlias.forget());
|
||||
return;
|
||||
}
|
||||
uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
|
||||
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
|
||||
|
||||
int err = 0;
|
||||
err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
|
||||
err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
|
||||
err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
|
||||
err |= native_window_set_usage(window, usage);
|
||||
|
||||
status_t result = NO_ERROR;
|
||||
if (err == NO_ERROR) {
|
||||
ANativeWindowBuffer* buffer;
|
||||
result = native_window_dequeue_buffer_and_wait(window, &buffer);
|
||||
if (result == NO_ERROR) {
|
||||
nsresult rv = screen->MakeSnapshot(buffer);
|
||||
if (rv != NS_OK) {
|
||||
result = INVALID_OPERATION;
|
||||
}
|
||||
window->queueBuffer(window, buffer, -1);
|
||||
}
|
||||
} else {
|
||||
result = BAD_VALUE;
|
||||
}
|
||||
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
|
||||
NS_ReleaseOnMainThread(screenAlias.forget());
|
||||
});
|
||||
|
||||
layers::CompositorThreadHolder::Loop()->PostTask(
|
||||
MakeAndAddRef<RunnableCallTask>(runnable));
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
void
|
||||
FakeSurfaceComposer::setPowerMode(const sp<IBinder>& display, int mode)
|
||||
{
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs)
|
||||
{
|
||||
if (configs == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Limit DisplayConfigs only to primary display
|
||||
if (!display.get() || display != mPrimaryDisplay) {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
configs->clear();
|
||||
DisplayInfo info = DisplayInfo();
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([&]() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
getPrimaryDisplayInfo(&info);
|
||||
});
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
|
||||
|
||||
configs->push_back(info);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats)
|
||||
{
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
int
|
||||
FakeSurfaceComposer::getActiveConfig(const sp<IBinder>& display)
|
||||
{
|
||||
// Only support primary display.
|
||||
if (display.get() && (display == mPrimaryDisplay)) {
|
||||
return 0;
|
||||
}
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::setActiveConfig(const sp<IBinder>& display, int id)
|
||||
{
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::clearAnimationFrameStats()
|
||||
{
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::getAnimationFrameStats(FrameStats* outStats) const
|
||||
{
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
#else
|
||||
void
|
||||
FakeSurfaceComposer::blank(const sp<IBinder>& display)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FakeSurfaceComposer::unblank(const sp<IBinder>& display)
|
||||
{
|
||||
}
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Limit DisplayConfigs only to primary display
|
||||
if (!display.get() || display != mPrimaryDisplay) {
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([&]() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
getPrimaryDisplayInfo(info);
|
||||
});
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define VSYNC_EVENT_PHASE_OFFSET_NS 0
|
||||
#define SF_VSYNC_EVENT_PHASE_OFFSET_NS 0
|
||||
|
||||
void
|
||||
FakeSurfaceComposer::getPrimaryDisplayInfo(DisplayInfo* info)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
// Implementation mimic android SurfaceFlinger::getDisplayConfigs().
|
||||
|
||||
class Density {
|
||||
static int getDensityFromProperty(char const* propName) {
|
||||
char property[PROPERTY_VALUE_MAX];
|
||||
int density = 0;
|
||||
if (property_get(propName, property, NULL) > 0) {
|
||||
density = atoi(property);
|
||||
}
|
||||
return density;
|
||||
}
|
||||
public:
|
||||
static int getEmuDensity() {
|
||||
return getDensityFromProperty("qemu.sf.lcd_density"); }
|
||||
static int getBuildDensity() {
|
||||
return getDensityFromProperty("ro.sf.lcd_density"); }
|
||||
};
|
||||
|
||||
RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
|
||||
|
||||
float xdpi = screen->GetDpi();
|
||||
float ydpi = screen->GetDpi();
|
||||
int fps = 60; // XXX set a value from hwc hal
|
||||
nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect();
|
||||
|
||||
// The density of the device is provided by a build property
|
||||
float density = Density::getBuildDensity() / 160.0f;
|
||||
if (density == 0) {
|
||||
// the build doesn't provide a density -- this is wrong!
|
||||
// use xdpi instead
|
||||
ALOGE("ro.sf.lcd_density must be defined as a build property");
|
||||
density = xdpi / 160.0f;
|
||||
}
|
||||
info->density = density;
|
||||
info->orientation = screen->EffectiveScreenRotation();
|
||||
|
||||
info->w = screenBounds.width;
|
||||
info->h = screenBounds.height;
|
||||
info->xdpi = xdpi;
|
||||
info->ydpi = ydpi;
|
||||
info->fps = fps;
|
||||
#if ANDROID_VERSION >= 21
|
||||
info->appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
|
||||
|
||||
// This is how far in advance a buffer must be queued for
|
||||
// presentation at a given time. If you want a buffer to appear
|
||||
// on the screen at time N, you must submit the buffer before
|
||||
// (N - presentationDeadline).
|
||||
//
|
||||
// Normally it's one full refresh period (to give SF a chance to
|
||||
// latch the buffer), but this can be reduced by configuring a
|
||||
// DispSync offset. Any additional delays introduced by the hardware
|
||||
// composer or panel must be accounted for here.
|
||||
//
|
||||
// We add an additional 1ms to allow for processing time and
|
||||
// differences between the ideal and actual refresh rate.
|
||||
info->presentationDeadline =
|
||||
(1e9 / fps) - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
|
||||
#endif
|
||||
// All non-virtual displays are currently considered secure.
|
||||
info->secure = true;
|
||||
}
|
||||
|
||||
}; // namespace android
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (C) 2013 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
|
||||
#define NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#include <binder/BinderService.h>
|
||||
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
#include <gui/ISurfaceComposerClient.h>
|
||||
#include <hardware/hwcomposer.h>
|
||||
#include <private/gui/LayerState.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
|
||||
class nsIWidget;
|
||||
|
||||
namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class GraphicProducerWrapper;
|
||||
class IGraphicBufferAlloc;
|
||||
|
||||
enum {
|
||||
eTransactionNeeded = 0x01,
|
||||
eTraversalNeeded = 0x02,
|
||||
eDisplayTransactionNeeded = 0x04,
|
||||
eTransactionMask = 0x07
|
||||
};
|
||||
|
||||
class FakeSurfaceComposer : public BinderService<FakeSurfaceComposer>,
|
||||
public BnSurfaceComposer
|
||||
{
|
||||
public:
|
||||
static char const* getServiceName() {
|
||||
return "FakeSurfaceComposer";
|
||||
}
|
||||
|
||||
// Instantiate FakeSurfaceComposer and register to service manager.
|
||||
// If service manager is not present, wait until service manager becomes present.
|
||||
static void instantiate();
|
||||
|
||||
#if ANDROID_VERSION >= 19
|
||||
virtual void destroyDisplay(const sp<android::IBinder>& display);
|
||||
#endif
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
virtual status_t captureScreen(const sp<IBinder>& display,
|
||||
const sp<IGraphicBufferProducer>& producer,
|
||||
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
|
||||
uint32_t minLayerZ, uint32_t maxLayerZ,
|
||||
bool useIdentityTransform,
|
||||
Rotation rotation = eRotateNone);
|
||||
#elif ANDROID_VERSION >= 19
|
||||
virtual status_t captureScreen(const sp<IBinder>& display,
|
||||
const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth, uint32_t reqHeight,
|
||||
uint32_t minLayerZ, uint32_t maxLayerZ);
|
||||
#else
|
||||
virtual status_t captureScreen(const sp<IBinder>& display,
|
||||
const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth, uint32_t reqHeight,
|
||||
uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer);
|
||||
#endif
|
||||
|
||||
private:
|
||||
FakeSurfaceComposer();
|
||||
// We're reference counted, never destroy FakeSurfaceComposer directly
|
||||
virtual ~FakeSurfaceComposer();
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* IBinder interface
|
||||
*/
|
||||
virtual status_t onTransact(uint32_t code, const Parcel& data,
|
||||
Parcel* reply, uint32_t flags);
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* ISurfaceComposer interface
|
||||
*/
|
||||
virtual sp<ISurfaceComposerClient> createConnection();
|
||||
virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
|
||||
virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
|
||||
virtual sp<IBinder> getBuiltInDisplay(int32_t id);
|
||||
virtual void setTransactionState(const Vector<ComposerState>& state,
|
||||
const Vector<DisplayState>& displays, uint32_t flags);
|
||||
virtual void bootFinished();
|
||||
virtual bool authenticateSurfaceTexture(
|
||||
const sp<IGraphicBufferProducer>& bufferProducer) const;
|
||||
virtual sp<IDisplayEventConnection> createDisplayEventConnection();
|
||||
#if ANDROID_VERSION >= 21
|
||||
virtual void setPowerMode(const sp<IBinder>& display, int mode);
|
||||
virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs);
|
||||
virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats);
|
||||
virtual int getActiveConfig(const sp<IBinder>& display);
|
||||
virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
|
||||
virtual status_t clearAnimationFrameStats();
|
||||
virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
|
||||
#elif ANDROID_VERSION >= 17
|
||||
// called when screen needs to turn off
|
||||
virtual void blank(const sp<IBinder>& display);
|
||||
// called when screen is turning back on
|
||||
virtual void unblank(const sp<IBinder>& display);
|
||||
virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info);
|
||||
#endif
|
||||
void getPrimaryDisplayInfo(DisplayInfo* info);
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Transactions
|
||||
*/
|
||||
uint32_t setDisplayStateLocked(const DisplayState& s);
|
||||
|
||||
void captureScreenImp(const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth,
|
||||
uint32_t reqHeight,
|
||||
const sp<GraphicProducerWrapper>& wrapper);
|
||||
|
||||
sp<IBinder> mPrimaryDisplay;
|
||||
|
||||
struct DisplayDeviceState {
|
||||
enum {
|
||||
NO_LAYER_STACK = 0xFFFFFFFF,
|
||||
};
|
||||
DisplayDeviceState()
|
||||
: type(-1), displayId(-1), width(0), height(0) {
|
||||
}
|
||||
DisplayDeviceState(int type)
|
||||
: type(type), displayId(-1), layerStack(NO_LAYER_STACK), orientation(0), width(0), height(0) {
|
||||
viewport.makeInvalid();
|
||||
frame.makeInvalid();
|
||||
}
|
||||
bool isValid() const { return type >= 0; }
|
||||
int type;
|
||||
int displayId;
|
||||
sp<IGraphicBufferProducer> surface;
|
||||
uint32_t layerStack;
|
||||
Rect viewport;
|
||||
Rect frame;
|
||||
uint8_t orientation;
|
||||
uint32_t width, height;
|
||||
String8 displayName;
|
||||
bool isSecure;
|
||||
};
|
||||
|
||||
// access must be protected by mStateLock
|
||||
mutable Mutex mStateLock;
|
||||
DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> mDisplays;
|
||||
|
||||
friend class DestroyDisplayRunnable;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
|
|
@ -1,22 +0,0 @@
|
|||
/* Copyright 2013 Mozilla Foundation and Mozilla contributors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
|
||||
# include "GonkBufferQueueLL.h"
|
||||
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
|
||||
# include "GonkBufferQueueKK.h"
|
||||
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
# include "GonkBufferQueueJB.h"
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,653 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2013 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUE_JB_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUE_JB_H
|
||||
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#if ANDROID_VERSION == 17
|
||||
#include <gui/ISurfaceTexture.h>
|
||||
#else
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
#endif
|
||||
|
||||
#include <ui/Fence.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
|
||||
#if ANDROID_VERSION == 17
|
||||
#define IGraphicBufferProducer ISurfaceTexture
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if ANDROID_VERSION == 17
|
||||
class GonkBufferQueue : public BnSurfaceTexture {
|
||||
#else
|
||||
class GonkBufferQueue : public BnGraphicBufferProducer {
|
||||
#endif
|
||||
typedef mozilla::layers::TextureClient TextureClient;
|
||||
|
||||
public:
|
||||
enum { MIN_UNDEQUEUED_BUFFERS = 2 };
|
||||
enum { NUM_BUFFER_SLOTS = 32 };
|
||||
enum { NO_CONNECTED_API = 0 };
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE };
|
||||
|
||||
// When in async mode we reserve two slots in order to guarantee that the
|
||||
// producer and consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// ConsumerListener is the interface through which the GonkBufferQueue notifies
|
||||
// the consumer of events that the consumer may wish to react to. Because
|
||||
// the consumer will generally have a mutex that is locked during calls from
|
||||
// the consumer to the GonkBufferQueue, these calls from the GonkBufferQueue to the
|
||||
// consumer *MUST* be called only when the GonkBufferQueue mutex is NOT locked.
|
||||
struct ConsumerListener : public virtual RefBase {
|
||||
// onFrameAvailable is called from queueBuffer each time an additional
|
||||
// frame becomes available for consumption. This means that frames that
|
||||
// are queued while in asynchronous mode only trigger the callback if no
|
||||
// previous frames are pending. Frames queued while in synchronous mode
|
||||
// always trigger the callback.
|
||||
//
|
||||
// This is called without any lock held and can be called concurrently
|
||||
// by multiple threads.
|
||||
virtual void onFrameAvailable() = 0;
|
||||
|
||||
// onBuffersReleased is called to notify the buffer consumer that the
|
||||
// GonkBufferQueue has released its references to one or more GraphicBuffers
|
||||
// contained in its slots. The buffer consumer should then call
|
||||
// GonkBufferQueue::getReleasedBuffers to retrieve the list of buffers
|
||||
//
|
||||
// This is called without any lock held and can be called concurrently
|
||||
// by multiple threads.
|
||||
virtual void onBuffersReleased() = 0;
|
||||
};
|
||||
|
||||
// ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
|
||||
// reference to the actual consumer object. It forwards all calls to that
|
||||
// consumer object so long as it exists.
|
||||
//
|
||||
// This class exists to avoid having a circular reference between the
|
||||
// GonkBufferQueue object and the consumer object. The reason this can't be a weak
|
||||
// reference in the GonkBufferQueue class is because we're planning to expose the
|
||||
// consumer side of a GonkBufferQueue as a binder interface, which doesn't support
|
||||
// weak references.
|
||||
class ProxyConsumerListener : public GonkBufferQueue::ConsumerListener {
|
||||
public:
|
||||
|
||||
ProxyConsumerListener(const wp<GonkBufferQueue::ConsumerListener>& consumerListener);
|
||||
virtual ~ProxyConsumerListener();
|
||||
virtual void onFrameAvailable();
|
||||
virtual void onBuffersReleased();
|
||||
|
||||
private:
|
||||
|
||||
// mConsumerListener is a weak reference to the ConsumerListener. This is
|
||||
// the raison d'etre of ProxyConsumerListener.
|
||||
wp<GonkBufferQueue::ConsumerListener> mConsumerListener;
|
||||
};
|
||||
|
||||
|
||||
// GonkBufferQueue manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allowSynchronousMode specifies whether or not
|
||||
// synchronous mode can be enabled by the producer. allocator is used to
|
||||
// allocate all the needed gralloc buffers.
|
||||
GonkBufferQueue(bool allowSynchronousMode = true,
|
||||
const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
virtual ~GonkBufferQueue();
|
||||
|
||||
// Query native window attributes. The "what" values are enumerated in
|
||||
// window.h (e.g. NATIVE_WINDOW_FORMAT).
|
||||
virtual int query(int what, int* value);
|
||||
|
||||
// setBufferCount updates the number of available buffer slots. If this
|
||||
// method succeeds, buffer slots will be both unallocated and owned by
|
||||
// the GonkBufferQueue object (i.e. they are not owned by the producer or
|
||||
// consumer).
|
||||
//
|
||||
// This will fail if the producer has dequeued any buffers, or if
|
||||
// bufferCount is invalid. bufferCount must generally be a value
|
||||
// between the minimum undequeued buffer count and NUM_BUFFER_SLOTS
|
||||
// (inclusive). It may also be set to zero (the default) to indicate
|
||||
// that the producer does not wish to set a value. The minimum value
|
||||
// can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
|
||||
// ...).
|
||||
//
|
||||
// This may only be called by the producer. The consumer will be told
|
||||
// to discard buffers through the onBuffersReleased callback.
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
|
||||
// requestBuffer returns the GraphicBuffer for slot N.
|
||||
//
|
||||
// In normal operation, this is called the first time slot N is returned
|
||||
// by dequeueBuffer. It must be called again if dequeueBuffer returns
|
||||
// flags indicating that previously-returned buffers are no longer valid.
|
||||
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
|
||||
|
||||
// dequeueBuffer gets the next buffer slot index for the producer to use.
|
||||
// If a buffer slot is available then that slot index is written to the
|
||||
// location pointed to by the buf argument and a status of OK is returned.
|
||||
// If no slot is available then a status of -EBUSY is returned and buf is
|
||||
// unmodified.
|
||||
//
|
||||
// The fence parameter will be updated to hold the fence associated with
|
||||
// the buffer. The contents of the buffer must not be overwritten until the
|
||||
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
|
||||
// written immediately.
|
||||
//
|
||||
// The width and height parameters must be no greater than the minimum of
|
||||
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
|
||||
// An error due to invalid dimensions might not be reported until
|
||||
// updateTexImage() is called. If width and height are both zero, the
|
||||
// default values specified by setDefaultBufferSize() are used instead.
|
||||
//
|
||||
// The pixel formats are enumerated in graphics.h, e.g.
|
||||
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
|
||||
// will be used.
|
||||
//
|
||||
// The usage argument specifies gralloc buffer usage flags. The values
|
||||
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
|
||||
// will be merged with the usage flags specified by setConsumerUsageBits.
|
||||
//
|
||||
// The return value may be a negative error value or a non-negative
|
||||
// collection of flags. If the flags are set, the return values are
|
||||
// valid, but additional actions must be performed.
|
||||
//
|
||||
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
|
||||
// producer must discard cached GraphicBuffer references for the slot
|
||||
// returned in buf.
|
||||
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
|
||||
// must discard cached GraphicBuffer references for all slots.
|
||||
//
|
||||
// In both cases, the producer will need to call requestBuffer to get a
|
||||
// GraphicBuffer handle for the returned slot.
|
||||
#if ANDROID_VERSION == 17
|
||||
virtual status_t dequeueBuffer(int *buf, sp<Fence>& fence,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
|
||||
return dequeueBuffer(buf, &fence, width, height, format, usage);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
|
||||
|
||||
// queueBuffer returns a filled buffer to the GonkBufferQueue.
|
||||
//
|
||||
// Additional data is provided in the QueueBufferInput struct. Notably,
|
||||
// a timestamp must be provided for the buffer. The timestamp is in
|
||||
// nanoseconds, and must be monotonically increasing. Its other semantics
|
||||
// (zero point, etc) are producer-specific and should be documented by the
|
||||
// producer.
|
||||
//
|
||||
// The caller may provide a fence that signals when all rendering
|
||||
// operations have completed. Alternatively, NO_FENCE may be used,
|
||||
// indicating that the buffer is ready immediately.
|
||||
//
|
||||
// Some values are returned in the output struct: the current settings
|
||||
// for default width and height, the current transform hint, and the
|
||||
// number of queued buffers.
|
||||
virtual status_t queueBuffer(int buf,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
|
||||
// cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
|
||||
// queue it for use by the consumer.
|
||||
//
|
||||
// The buffer will not be overwritten until the fence signals. The fence
|
||||
// will usually be the one obtained from dequeueBuffer.
|
||||
#if ANDROID_VERSION == 17
|
||||
virtual void cancelBuffer(int buf, sp<Fence> fence);
|
||||
#else
|
||||
virtual void cancelBuffer(int buf, const sp<Fence>& fence);
|
||||
#endif
|
||||
|
||||
// setSynchronousMode sets whether dequeueBuffer is synchronous or
|
||||
// asynchronous. In synchronous mode, dequeueBuffer blocks until
|
||||
// a buffer is available, the currently bound buffer can be dequeued and
|
||||
// queued buffers will be acquired in order. In asynchronous mode,
|
||||
// a queued buffer may be replaced by a subsequently queued buffer.
|
||||
//
|
||||
// The default mode is synchronous.
|
||||
// This should be called only during initialization.
|
||||
virtual status_t setSynchronousMode(bool enabled);
|
||||
|
||||
// connect attempts to connect a producer API to the GonkBufferQueue. This
|
||||
// must be called before any other IGraphicBufferProducer methods are
|
||||
// called except for getAllocator. A consumer must already be connected.
|
||||
//
|
||||
// This method will fail if connect was previously called on the
|
||||
// GonkBufferQueue and no corresponding disconnect call was made (i.e. if
|
||||
// it's still connected to a producer).
|
||||
//
|
||||
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
|
||||
virtual status_t connect(int api, QueueBufferOutput* output);
|
||||
|
||||
// disconnect attempts to disconnect a producer API from the GonkBufferQueue.
|
||||
// Calling this method will cause any subsequent calls to other
|
||||
// IGraphicBufferProducer methods to fail except for getAllocator and connect.
|
||||
// Successfully calling connect after this will allow the other methods to
|
||||
// succeed again.
|
||||
//
|
||||
// This method will fail if the the GonkBufferQueue is not currently
|
||||
// connected to the specified producer API.
|
||||
virtual status_t disconnect(int api);
|
||||
|
||||
// dump our state in a String
|
||||
virtual void dumpToString(String8& result) const;
|
||||
virtual void dumpToString(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
||||
|
||||
// public facing structure for BufferSlot
|
||||
struct BufferItem {
|
||||
|
||||
BufferItem()
|
||||
:
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mFrameNumber(0),
|
||||
mBuf(INVALID_BUFFER_SLOT) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
|
||||
// if the buffer in this slot has been acquired in the past (see
|
||||
// BufferSlot.mAcquireCalled).
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot.
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot.
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mBuf is the slot index of this buffer
|
||||
int mBuf;
|
||||
|
||||
// mFence is a fence that will signal when the buffer is idle.
|
||||
sp<Fence> mFence;
|
||||
};
|
||||
|
||||
// The following public functions are the consumer-facing interface
|
||||
|
||||
// acquireBuffer attempts to acquire ownership of the next pending buffer in
|
||||
// the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a
|
||||
// buffer is successfully acquired, the information about the buffer is
|
||||
// returned in BufferItem. If the buffer returned had previously been
|
||||
// acquired then the BufferItem::mGraphicBuffer field of buffer is set to
|
||||
// NULL and it is assumed that the consumer still holds a reference to the
|
||||
// buffer.
|
||||
status_t acquireBuffer(BufferItem *buffer);
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// GonkBufferQueue. This may be done while the buffer's contents are still
|
||||
// being accessed. The fence will signal when the buffer is no longer
|
||||
// in use.
|
||||
//
|
||||
// If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
|
||||
// any references to the just-released buffer that it might have, as if it
|
||||
// had received a onBuffersReleased() call with a mask set for the released
|
||||
// buffer.
|
||||
//
|
||||
// Note that the dependencies on EGL will be removed once we switch to using
|
||||
// the Android HW Sync HAL.
|
||||
status_t releaseBuffer(int buf, const sp<Fence>& releaseFence);
|
||||
|
||||
// consumerConnect connects a consumer to the GonkBufferQueue. Only one
|
||||
// consumer may be connected, and when that consumer disconnects the
|
||||
// GonkBufferQueue is placed into the "abandoned" state, causing most
|
||||
// interactions with the GonkBufferQueue by the producer to fail.
|
||||
//
|
||||
// consumer may not be NULL.
|
||||
status_t consumerConnect(const sp<ConsumerListener>& consumer);
|
||||
|
||||
// consumerDisconnect disconnects a consumer from the GonkBufferQueue. All
|
||||
// buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
|
||||
// state, causing most interactions with the GonkBufferQueue by the producer to
|
||||
// fail.
|
||||
status_t consumerDisconnect();
|
||||
|
||||
// getReleasedBuffers sets the value pointed to by slotMask to a bit mask
|
||||
// indicating which buffer slots have been released by the GonkBufferQueue
|
||||
// but have not yet been released by the consumer.
|
||||
//
|
||||
// This should be called from the onBuffersReleased() callback.
|
||||
status_t getReleasedBuffers(uint32_t* slotMask);
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// dequeueBuffer when a width and height of zero is requested. Default
|
||||
// is 1x1.
|
||||
status_t setDefaultBufferSize(uint32_t w, uint32_t h);
|
||||
|
||||
// setDefaultMaxBufferCount sets the default value for the maximum buffer
|
||||
// count (the initial default is 2). If the producer has requested a
|
||||
// buffer count using setBufferCount, the default buffer count will only
|
||||
// take effect if the producer sets the count back to zero.
|
||||
//
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
status_t setDefaultMaxBufferCount(int bufferCount);
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the GonkBufferQueue.
|
||||
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
|
||||
|
||||
// isSynchronousMode returns whether the GonkBufferQueue is currently in
|
||||
// synchronous mode.
|
||||
bool isSynchronousMode() const;
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
void setConsumerName(const String8& name);
|
||||
|
||||
// setDefaultBufferFormat allows the GonkBufferQueue to create
|
||||
// GraphicBuffers of a defaultFormat if no format is specified
|
||||
// in dequeueBuffer. Formats are enumerated in graphics.h; the
|
||||
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
|
||||
status_t setDefaultBufferFormat(uint32_t defaultFormat);
|
||||
|
||||
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
|
||||
// These are merged with the bits passed to dequeueBuffer. The values are
|
||||
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
|
||||
status_t setConsumerUsageBits(uint32_t usage);
|
||||
|
||||
// setTransformHint bakes in rotation to buffers so overlays can be used.
|
||||
// The values are enumerated in window.h, e.g.
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
status_t setTransformHint(uint32_t hint);
|
||||
|
||||
already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
|
||||
|
||||
int getSlotFromTextureClientLocked(TextureClient* client) const;
|
||||
|
||||
private:
|
||||
// freeBufferLocked frees the GraphicBuffer and sync resources for the
|
||||
// given slot.
|
||||
//void freeBufferLocked(int index);
|
||||
|
||||
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
|
||||
// all slots.
|
||||
//void freeAllBuffersLocked();
|
||||
void freeAllBuffersLocked();
|
||||
|
||||
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
|
||||
// that will be used if the producer does not override the buffer slot
|
||||
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
// The initial default is 2.
|
||||
status_t setDefaultMaxBufferCountLocked(int count);
|
||||
|
||||
// getMinBufferCountLocked returns the minimum number of buffers allowed
|
||||
// given the current GonkBufferQueue state.
|
||||
int getMinMaxBufferCountLocked() const;
|
||||
|
||||
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
|
||||
// that must remain in a state other than DEQUEUED.
|
||||
int getMinUndequeuedBufferCountLocked() const;
|
||||
|
||||
// getMaxBufferCountLocked returns the maximum number of buffers that can
|
||||
// be allocated at once. This value depends upon the following member
|
||||
// variables:
|
||||
//
|
||||
// mSynchronousMode
|
||||
// mMaxAcquiredBufferCount
|
||||
// mDefaultMaxBufferCount
|
||||
// mOverrideMaxBufferCount
|
||||
//
|
||||
// Any time one of these member variables is changed while a producer is
|
||||
// connected, mDequeueCondition must be broadcast.
|
||||
int getMaxBufferCountLocked() const;
|
||||
|
||||
struct BufferSlot {
|
||||
|
||||
BufferSlot()
|
||||
: mBufferState(BufferSlot::FREE),
|
||||
mRequestBufferCalled(false),
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mFrameNumber(0),
|
||||
mAcquireCalled(false),
|
||||
mNeedsCleanupOnRelease(false) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mTextureClient is a thin abstraction over remotely allocated GraphicBuffer.
|
||||
RefPtr<TextureClient> mTextureClient;
|
||||
|
||||
// BufferState represents the different states in which a buffer slot
|
||||
// can be. All slots are initially FREE.
|
||||
enum BufferState {
|
||||
// FREE indicates that the buffer is available to be dequeued
|
||||
// by the producer. The buffer may be in use by the consumer for
|
||||
// a finite time, so the buffer must not be modified until the
|
||||
// associated fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED
|
||||
// when dequeueBuffer is called.
|
||||
FREE = 0,
|
||||
|
||||
// DEQUEUED indicates that the buffer has been dequeued by the
|
||||
// producer, but has not yet been queued or canceled. The
|
||||
// producer may modify the buffer's contents as soon as the
|
||||
// associated ready fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the producer. It can transition to
|
||||
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
|
||||
DEQUEUED = 1,
|
||||
|
||||
// QUEUED indicates that the buffer has been filled by the
|
||||
// producer and queued for use by the consumer. The buffer
|
||||
// contents may continue to be modified for a finite time, so
|
||||
// the contents must not be accessed until the associated fence
|
||||
// is signaled.
|
||||
//
|
||||
// The slot is "owned" by GonkBufferQueue. It can transition to
|
||||
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
|
||||
// queued in asynchronous mode).
|
||||
QUEUED = 2,
|
||||
|
||||
// ACQUIRED indicates that the buffer has been acquired by the
|
||||
// consumer. As with QUEUED, the contents must not be accessed
|
||||
// by the consumer until the fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the consumer. It transitions to FREE
|
||||
// when releaseBuffer is called.
|
||||
ACQUIRED = 3
|
||||
};
|
||||
|
||||
// mBufferState is the current state of this buffer slot.
|
||||
BufferState mBufferState;
|
||||
|
||||
// mRequestBufferCalled is used for validating that the producer did
|
||||
// call requestBuffer() when told to do so. Technically this is not
|
||||
// needed but useful for debugging and catching producer bugs.
|
||||
bool mRequestBufferCalled;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot.
|
||||
// (example: NATIVE_WINDOW_TRANSFORM_ROT_90)
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot.
|
||||
// (example: NATIVE_WINDOW_SCALING_MODE_FREEZE)
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot. This
|
||||
// is used to dequeue buffers in LRU order (useful because buffers
|
||||
// may be released before their release fence is signaled).
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mEglFence is the EGL sync object that must signal before the buffer
|
||||
// associated with this buffer slot may be dequeued. It is initialized
|
||||
// to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
|
||||
// new sync object in releaseBuffer. (This is deprecated in favor of
|
||||
// mFence, below.)
|
||||
//EGLSyncKHR mEglFence;
|
||||
|
||||
// mFence is a fence which will signal when work initiated by the
|
||||
// previous owner of the buffer is finished. When the buffer is FREE,
|
||||
// the fence indicates when the consumer has finished reading
|
||||
// from the buffer, or when the producer has finished writing if it
|
||||
// called cancelBuffer after queueing some writes. When the buffer is
|
||||
// QUEUED, it indicates when the producer has finished filling the
|
||||
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
|
||||
// passed to the consumer or producer along with ownership of the
|
||||
// buffer, and mFence is set to NO_FENCE.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates whether this buffer needs to be cleaned up by the
|
||||
// consumer. This is set when a buffer in ACQUIRED state is freed.
|
||||
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
|
||||
bool mNeedsCleanupOnRelease;
|
||||
};
|
||||
|
||||
// mSlots is the array of buffer slots that must be mirrored on the
|
||||
// producer side. This allows buffer ownership to be transferred between
|
||||
// the producer and consumer without sending a GraphicBuffer over binder.
|
||||
// The entire array is initialized to NULL at construction time, and
|
||||
// buffers are allocated for a slot when requestBuffer is called with
|
||||
// that slot's index.
|
||||
BufferSlot mSlots[NUM_BUFFER_SLOTS];
|
||||
|
||||
// mDefaultWidth holds the default width of allocated buffers. It is used
|
||||
// in dequeueBuffer() if a width and height of zero is specified.
|
||||
uint32_t mDefaultWidth;
|
||||
|
||||
// mDefaultHeight holds the default height of allocated buffers. It is used
|
||||
// in dequeueBuffer() if a width and height of zero is specified.
|
||||
uint32_t mDefaultHeight;
|
||||
|
||||
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
|
||||
// acquire at one time. It defaults to 1 and can be changed by the
|
||||
// consumer via the setMaxAcquiredBufferCount method, but this may only be
|
||||
// done when no producer is connected to the GonkBufferQueue.
|
||||
//
|
||||
// This value is used to derive the value returned for the
|
||||
// MIN_UNDEQUEUED_BUFFERS query by the producer.
|
||||
int mMaxAcquiredBufferCount;
|
||||
|
||||
// mDefaultMaxBufferCount is the default limit on the number of buffers
|
||||
// that will be allocated at one time. This default limit is set by the
|
||||
// consumer. The limit (as opposed to the default limit) may be
|
||||
// overridden by the producer.
|
||||
int mDefaultMaxBufferCount;
|
||||
|
||||
// mOverrideMaxBufferCount is the limit on the number of buffers that will
|
||||
// be allocated at one time. This value is set by the image producer by
|
||||
// calling setBufferCount. The default is zero, which means the producer
|
||||
// doesn't care about the number of buffers in the pool. In that case
|
||||
// mDefaultMaxBufferCount is used as the limit.
|
||||
int mOverrideMaxBufferCount;
|
||||
|
||||
// mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
|
||||
// allocate new GraphicBuffer objects.
|
||||
sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
|
||||
|
||||
// mConsumerListener is used to notify the connected consumer of
|
||||
// asynchronous events that it may wish to react to. It is initially set
|
||||
// to NULL and is written by consumerConnect and consumerDisconnect.
|
||||
sp<ConsumerListener> mConsumerListener;
|
||||
|
||||
// mSynchronousMode whether we're in synchronous mode or not
|
||||
bool mSynchronousMode;
|
||||
|
||||
// mAllowSynchronousMode whether we allow synchronous mode or not. Set
|
||||
// when the GonkBufferQueue is created (by the consumer).
|
||||
const bool mAllowSynchronousMode;
|
||||
|
||||
// mConnectedApi indicates the producer API that is currently connected
|
||||
// to this GonkBufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets
|
||||
// updated by the connect and disconnect methods.
|
||||
int mConnectedApi;
|
||||
|
||||
// mDequeueCondition condition used for dequeueBuffer in synchronous mode
|
||||
mutable Condition mDequeueCondition;
|
||||
|
||||
// mQueue is a FIFO of queued buffers used in synchronous mode
|
||||
typedef Vector<int> Fifo;
|
||||
Fifo mQueue;
|
||||
|
||||
// mAbandoned indicates that the GonkBufferQueue will no longer be used to
|
||||
// consume image buffers pushed to it using the IGraphicBufferProducer
|
||||
// interface. It is initialized to false, and set to true in the
|
||||
// consumerDisconnect method. A GonkBufferQueue that has been abandoned will
|
||||
// return the NO_INIT error from all IGraphicBufferProducer methods
|
||||
// capable of returning an error.
|
||||
bool mAbandoned;
|
||||
|
||||
// mConsumerName is a string used to identify the GonkBufferQueue in log
|
||||
// messages. It is set by the setConsumerName method.
|
||||
String8 mConsumerName;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of GonkBufferQueue objects. It must be locked whenever the
|
||||
// member variables are accessed.
|
||||
mutable Mutex mMutex;
|
||||
|
||||
// mFrameCounter is the free running counter, incremented on every
|
||||
// successful queueBuffer call.
|
||||
uint64_t mFrameCounter;
|
||||
|
||||
// mBufferHasBeenQueued is true once a buffer has been queued. It is
|
||||
// reset when something causes all buffers to be freed (e.g. changing the
|
||||
// buffer count).
|
||||
bool mBufferHasBeenQueued;
|
||||
|
||||
// mDefaultBufferFormat can be set so it will override
|
||||
// the buffer format when it isn't specified in dequeueBuffer
|
||||
uint32_t mDefaultBufferFormat;
|
||||
|
||||
// mConsumerUsageBits contains flags the consumer wants for GraphicBuffers
|
||||
uint32_t mConsumerUsageBits;
|
||||
|
||||
// mTransformHint is used to optimize for screen rotations
|
||||
uint32_t mTransformHint;
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_BUFFERQUEUE_H
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,583 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2013 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUE_KK_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUE_KK_H
|
||||
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
#include "IGonkGraphicBufferConsumer.h"
|
||||
|
||||
#include <ui/Fence.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class GonkBufferQueue : public BnGraphicBufferProducer,
|
||||
public BnGonkGraphicBufferConsumer,
|
||||
private IBinder::DeathRecipient
|
||||
{
|
||||
typedef mozilla::layers::TextureClient TextureClient;
|
||||
|
||||
public:
|
||||
enum { MIN_UNDEQUEUED_BUFFERS = 2 };
|
||||
enum { NUM_BUFFER_SLOTS = 32 };
|
||||
enum { NO_CONNECTED_API = 0 };
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE, PRESENT_LATER };
|
||||
|
||||
// When in async mode we reserve two slots in order to guarantee that the
|
||||
// producer and consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// for backward source compatibility
|
||||
typedef ::android::ConsumerListener ConsumerListener;
|
||||
|
||||
// ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
|
||||
// reference to the actual consumer object. It forwards all calls to that
|
||||
// consumer object so long as it exists.
|
||||
//
|
||||
// This class exists to avoid having a circular reference between the
|
||||
// GonkBufferQueue object and the consumer object. The reason this can't be a weak
|
||||
// reference in the GonkBufferQueue class is because we're planning to expose the
|
||||
// consumer side of a GonkBufferQueue as a binder interface, which doesn't support
|
||||
// weak references.
|
||||
class ProxyConsumerListener : public BnConsumerListener {
|
||||
public:
|
||||
ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
|
||||
virtual ~ProxyConsumerListener();
|
||||
virtual void onFrameAvailable();
|
||||
virtual void onBuffersReleased();
|
||||
private:
|
||||
// mConsumerListener is a weak reference to the IConsumerListener. This is
|
||||
// the raison d'etre of ProxyConsumerListener.
|
||||
wp<ConsumerListener> mConsumerListener;
|
||||
};
|
||||
|
||||
|
||||
// BufferQueue manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allocator is used to allocate all the
|
||||
// needed gralloc buffers.
|
||||
GonkBufferQueue(bool allowSynchronousMode = true,
|
||||
const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
virtual ~GonkBufferQueue();
|
||||
|
||||
/*
|
||||
* IBinder::DeathRecipient interface
|
||||
*/
|
||||
|
||||
virtual void binderDied(const wp<IBinder>& who);
|
||||
|
||||
/*
|
||||
* IGraphicBufferProducer interface
|
||||
*/
|
||||
|
||||
// Query native window attributes. The "what" values are enumerated in
|
||||
// window.h (e.g. NATIVE_WINDOW_FORMAT).
|
||||
virtual int query(int what, int* value);
|
||||
|
||||
// setBufferCount updates the number of available buffer slots. If this
|
||||
// method succeeds, buffer slots will be both unallocated and owned by
|
||||
// the GonkBufferQueue object (i.e. they are not owned by the producer or
|
||||
// consumer).
|
||||
//
|
||||
// This will fail if the producer has dequeued any buffers, or if
|
||||
// bufferCount is invalid. bufferCount must generally be a value
|
||||
// between the minimum undequeued buffer count and NUM_BUFFER_SLOTS
|
||||
// (inclusive). It may also be set to zero (the default) to indicate
|
||||
// that the producer does not wish to set a value. The minimum value
|
||||
// can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
|
||||
// ...).
|
||||
//
|
||||
// This may only be called by the producer. The consumer will be told
|
||||
// to discard buffers through the onBuffersReleased callback.
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
|
||||
// requestBuffer returns the GraphicBuffer for slot N.
|
||||
//
|
||||
// In normal operation, this is called the first time slot N is returned
|
||||
// by dequeueBuffer. It must be called again if dequeueBuffer returns
|
||||
// flags indicating that previously-returned buffers are no longer valid.
|
||||
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
|
||||
|
||||
// dequeueBuffer gets the next buffer slot index for the producer to use.
|
||||
// If a buffer slot is available then that slot index is written to the
|
||||
// location pointed to by the buf argument and a status of OK is returned.
|
||||
// If no slot is available then a status of -EBUSY is returned and buf is
|
||||
// unmodified.
|
||||
//
|
||||
// The fence parameter will be updated to hold the fence associated with
|
||||
// the buffer. The contents of the buffer must not be overwritten until the
|
||||
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
|
||||
// written immediately.
|
||||
//
|
||||
// The width and height parameters must be no greater than the minimum of
|
||||
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
|
||||
// An error due to invalid dimensions might not be reported until
|
||||
// updateTexImage() is called. If width and height are both zero, the
|
||||
// default values specified by setDefaultBufferSize() are used instead.
|
||||
//
|
||||
// The pixel formats are enumerated in graphics.h, e.g.
|
||||
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
|
||||
// will be used.
|
||||
//
|
||||
// The usage argument specifies gralloc buffer usage flags. The values
|
||||
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
|
||||
// will be merged with the usage flags specified by setConsumerUsageBits.
|
||||
//
|
||||
// The return value may be a negative error value or a non-negative
|
||||
// collection of flags. If the flags are set, the return values are
|
||||
// valid, but additional actions must be performed.
|
||||
//
|
||||
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
|
||||
// producer must discard cached GraphicBuffer references for the slot
|
||||
// returned in buf.
|
||||
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
|
||||
// must discard cached GraphicBuffer references for all slots.
|
||||
//
|
||||
// In both cases, the producer will need to call requestBuffer to get a
|
||||
// GraphicBuffer handle for the returned slot.
|
||||
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
|
||||
|
||||
// queueBuffer returns a filled buffer to the GonkBufferQueue.
|
||||
//
|
||||
// Additional data is provided in the QueueBufferInput struct. Notably,
|
||||
// a timestamp must be provided for the buffer. The timestamp is in
|
||||
// nanoseconds, and must be monotonically increasing. Its other semantics
|
||||
// (zero point, etc) are producer-specific and should be documented by the
|
||||
// producer.
|
||||
//
|
||||
// The caller may provide a fence that signals when all rendering
|
||||
// operations have completed. Alternatively, NO_FENCE may be used,
|
||||
// indicating that the buffer is ready immediately.
|
||||
//
|
||||
// Some values are returned in the output struct: the current settings
|
||||
// for default width and height, the current transform hint, and the
|
||||
// number of queued buffers.
|
||||
virtual status_t queueBuffer(int buf,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
|
||||
// cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
|
||||
// queue it for use by the consumer.
|
||||
//
|
||||
// The buffer will not be overwritten until the fence signals. The fence
|
||||
// will usually be the one obtained from dequeueBuffer.
|
||||
virtual void cancelBuffer(int buf, const sp<Fence>& fence);
|
||||
|
||||
// setSynchronousMode sets whether dequeueBuffer is synchronous or
|
||||
// asynchronous. In synchronous mode, dequeueBuffer blocks until
|
||||
// a buffer is available, the currently bound buffer can be dequeued and
|
||||
// queued buffers will be acquired in order. In asynchronous mode,
|
||||
// a queued buffer may be replaced by a subsequently queued buffer.
|
||||
//
|
||||
// The default mode is synchronous.
|
||||
// This should be called only during initialization.
|
||||
virtual status_t setSynchronousMode(bool enabled);
|
||||
|
||||
// connect attempts to connect a producer API to the GonkBufferQueue. This
|
||||
// must be called before any other IGraphicBufferProducer methods are
|
||||
// called except for getAllocator. A consumer must already be connected.
|
||||
//
|
||||
// This method will fail if connect was previously called on the
|
||||
// GonkBufferQueue and no corresponding disconnect call was made (i.e. if
|
||||
// it's still connected to a producer).
|
||||
//
|
||||
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
|
||||
virtual status_t connect(const sp<IBinder>& token,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput* output);
|
||||
|
||||
// disconnect attempts to disconnect a producer API from the GonkBufferQueue.
|
||||
// Calling this method will cause any subsequent calls to other
|
||||
// IGraphicBufferProducer methods to fail except for getAllocator and connect.
|
||||
// Successfully calling connect after this will allow the other methods to
|
||||
// succeed again.
|
||||
//
|
||||
// This method will fail if the the GonkBufferQueue is not currently
|
||||
// connected to the specified producer API.
|
||||
virtual status_t disconnect(int api);
|
||||
|
||||
/*
|
||||
* IGraphicBufferConsumer interface
|
||||
*/
|
||||
|
||||
// acquireBuffer attempts to acquire ownership of the next pending buffer in
|
||||
// the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a
|
||||
// buffer is successfully acquired, the information about the buffer is
|
||||
// returned in BufferItem. If the buffer returned had previously been
|
||||
// acquired then the BufferItem::mGraphicBuffer field of buffer is set to
|
||||
// NULL and it is assumed that the consumer still holds a reference to the
|
||||
// buffer.
|
||||
//
|
||||
// If presentWhen is nonzero, it indicates the time when the buffer will
|
||||
// be displayed on screen. If the buffer's timestamp is farther in the
|
||||
// future, the buffer won't be acquired, and PRESENT_LATER will be
|
||||
// returned. The presentation time is in nanoseconds, and the time base
|
||||
// is CLOCK_MONOTONIC.
|
||||
virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen);
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// GonkBufferQueue. This may be done while the buffer's contents are still
|
||||
// being accessed. The fence will signal when the buffer is no longer
|
||||
// in use. frameNumber is used to indentify the exact buffer returned.
|
||||
//
|
||||
// If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
|
||||
// any references to the just-released buffer that it might have, as if it
|
||||
// had received a onBuffersReleased() call with a mask set for the released
|
||||
// buffer.
|
||||
//
|
||||
// Note that the dependencies on EGL will be removed once we switch to using
|
||||
// the Android HW Sync HAL.
|
||||
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
|
||||
const sp<Fence>& releaseFence);
|
||||
|
||||
// consumerConnect connects a consumer to the GonkBufferQueue. Only one
|
||||
// consumer may be connected, and when that consumer disconnects the
|
||||
// GonkBufferQueue is placed into the "abandoned" state, causing most
|
||||
// interactions with the GonkBufferQueue by the producer to fail.
|
||||
// controlledByApp indicates whether the consumer is controlled by
|
||||
// the application.
|
||||
//
|
||||
// consumer may not be NULL.
|
||||
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp);
|
||||
|
||||
// consumerDisconnect disconnects a consumer from the GonkBufferQueue. All
|
||||
// buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
|
||||
// state, causing most interactions with the GonkBufferQueue by the producer to
|
||||
// fail.
|
||||
virtual status_t consumerDisconnect();
|
||||
|
||||
// getReleasedBuffers sets the value pointed to by slotMask to a bit mask
|
||||
// indicating which buffer slots have been released by the GonkBufferQueue
|
||||
// but have not yet been released by the consumer.
|
||||
//
|
||||
// This should be called from the onBuffersReleased() callback.
|
||||
virtual status_t getReleasedBuffers(uint32_t* slotMask);
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// dequeueBuffer when a width and height of zero is requested. Default
|
||||
// is 1x1.
|
||||
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h);
|
||||
|
||||
// setDefaultMaxBufferCount sets the default value for the maximum buffer
|
||||
// count (the initial default is 2). If the producer has requested a
|
||||
// buffer count using setBufferCount, the default buffer count will only
|
||||
// take effect if the producer sets the count back to zero.
|
||||
//
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
virtual status_t setDefaultMaxBufferCount(int bufferCount);
|
||||
|
||||
// disableAsyncBuffer disables the extra buffer used in async mode
|
||||
// (when both producer and consumer have set their "isControlledByApp"
|
||||
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
|
||||
//
|
||||
// This can only be called before consumerConnect().
|
||||
virtual status_t disableAsyncBuffer();
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the GonkBufferQueue.
|
||||
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
virtual void setConsumerName(const String8& name);
|
||||
|
||||
// setDefaultBufferFormat allows the GonkBufferQueue to create
|
||||
// GraphicBuffers of a defaultFormat if no format is specified
|
||||
// in dequeueBuffer. Formats are enumerated in graphics.h; the
|
||||
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
|
||||
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
|
||||
|
||||
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
|
||||
// These are merged with the bits passed to dequeueBuffer. The values are
|
||||
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
|
||||
virtual status_t setConsumerUsageBits(uint32_t usage);
|
||||
|
||||
// setTransformHint bakes in rotation to buffers so overlays can be used.
|
||||
// The values are enumerated in window.h, e.g.
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
virtual status_t setTransformHint(uint32_t hint);
|
||||
|
||||
// dump our state in a String
|
||||
virtual void dumpToString(String8& result, const char* prefix) const;
|
||||
|
||||
already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
|
||||
|
||||
int getSlotFromTextureClientLocked(TextureClient* client) const;
|
||||
|
||||
private:
|
||||
// freeBufferLocked frees the GraphicBuffer and sync resources for the
|
||||
// given slot.
|
||||
//void freeBufferLocked(int index);
|
||||
|
||||
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
|
||||
// all slots.
|
||||
void freeAllBuffersLocked();
|
||||
|
||||
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
|
||||
// that will be used if the producer does not override the buffer slot
|
||||
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
// The initial default is 2.
|
||||
status_t setDefaultMaxBufferCountLocked(int count);
|
||||
|
||||
// getMinUndequeuedBufferCount returns the minimum number of buffers
|
||||
// that must remain in a state other than DEQUEUED.
|
||||
// The async parameter tells whether we're in asynchronous mode.
|
||||
int getMinUndequeuedBufferCount(bool async) const;
|
||||
|
||||
// getMinBufferCountLocked returns the minimum number of buffers allowed
|
||||
// given the current GonkBufferQueue state.
|
||||
// The async parameter tells whether we're in asynchronous mode.
|
||||
int getMinMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// getMaxBufferCountLocked returns the maximum number of buffers that can
|
||||
// be allocated at once. This value depends upon the following member
|
||||
// variables:
|
||||
//
|
||||
// mDequeueBufferCannotBlock
|
||||
// mMaxAcquiredBufferCount
|
||||
// mDefaultMaxBufferCount
|
||||
// mOverrideMaxBufferCount
|
||||
// async parameter
|
||||
//
|
||||
// Any time one of these member variables is changed while a producer is
|
||||
// connected, mDequeueCondition must be broadcast.
|
||||
int getMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// stillTracking returns true iff the buffer item is still being tracked
|
||||
// in one of the slots.
|
||||
bool stillTracking(const BufferItem *item) const;
|
||||
|
||||
struct BufferSlot {
|
||||
|
||||
BufferSlot()
|
||||
: mBufferState(BufferSlot::FREE),
|
||||
mRequestBufferCalled(false),
|
||||
mFrameNumber(0),
|
||||
mAcquireCalled(false),
|
||||
mNeedsCleanupOnRelease(false) {
|
||||
}
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mTextureClient is a thin abstraction over remotely allocated GraphicBuffer.
|
||||
RefPtr<TextureClient> mTextureClient;
|
||||
|
||||
// BufferState represents the different states in which a buffer slot
|
||||
// can be. All slots are initially FREE.
|
||||
enum BufferState {
|
||||
// FREE indicates that the buffer is available to be dequeued
|
||||
// by the producer. The buffer may be in use by the consumer for
|
||||
// a finite time, so the buffer must not be modified until the
|
||||
// associated fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED
|
||||
// when dequeueBuffer is called.
|
||||
FREE = 0,
|
||||
|
||||
// DEQUEUED indicates that the buffer has been dequeued by the
|
||||
// producer, but has not yet been queued or canceled. The
|
||||
// producer may modify the buffer's contents as soon as the
|
||||
// associated ready fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the producer. It can transition to
|
||||
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
|
||||
DEQUEUED = 1,
|
||||
|
||||
// QUEUED indicates that the buffer has been filled by the
|
||||
// producer and queued for use by the consumer. The buffer
|
||||
// contents may continue to be modified for a finite time, so
|
||||
// the contents must not be accessed until the associated fence
|
||||
// is signaled.
|
||||
//
|
||||
// The slot is "owned" by GonkBufferQueue. It can transition to
|
||||
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
|
||||
// queued in asynchronous mode).
|
||||
QUEUED = 2,
|
||||
|
||||
// ACQUIRED indicates that the buffer has been acquired by the
|
||||
// consumer. As with QUEUED, the contents must not be accessed
|
||||
// by the consumer until the fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the consumer. It transitions to FREE
|
||||
// when releaseBuffer is called.
|
||||
ACQUIRED = 3
|
||||
};
|
||||
|
||||
// mBufferState is the current state of this buffer slot.
|
||||
BufferState mBufferState;
|
||||
|
||||
// mRequestBufferCalled is used for validating that the producer did
|
||||
// call requestBuffer() when told to do so. Technically this is not
|
||||
// needed but useful for debugging and catching producer bugs.
|
||||
bool mRequestBufferCalled;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot. This
|
||||
// is used to dequeue buffers in LRU order (useful because buffers
|
||||
// may be released before their release fence is signaled).
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mFence is a fence which will signal when work initiated by the
|
||||
// previous owner of the buffer is finished. When the buffer is FREE,
|
||||
// the fence indicates when the consumer has finished reading
|
||||
// from the buffer, or when the producer has finished writing if it
|
||||
// called cancelBuffer after queueing some writes. When the buffer is
|
||||
// QUEUED, it indicates when the producer has finished filling the
|
||||
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
|
||||
// passed to the consumer or producer along with ownership of the
|
||||
// buffer, and mFence is set to NO_FENCE.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates whether this buffer needs to be cleaned up by the
|
||||
// consumer. This is set when a buffer in ACQUIRED state is freed.
|
||||
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
|
||||
bool mNeedsCleanupOnRelease;
|
||||
};
|
||||
|
||||
// mSlots is the array of buffer slots that must be mirrored on the
|
||||
// producer side. This allows buffer ownership to be transferred between
|
||||
// the producer and consumer without sending a GraphicBuffer over binder.
|
||||
// The entire array is initialized to NULL at construction time, and
|
||||
// buffers are allocated for a slot when requestBuffer is called with
|
||||
// that slot's index.
|
||||
BufferSlot mSlots[NUM_BUFFER_SLOTS];
|
||||
|
||||
// mDefaultWidth holds the default width of allocated buffers. It is used
|
||||
// in dequeueBuffer() if a width and height of zero is specified.
|
||||
uint32_t mDefaultWidth;
|
||||
|
||||
// mDefaultHeight holds the default height of allocated buffers. It is used
|
||||
// in dequeueBuffer() if a width and height of zero is specified.
|
||||
uint32_t mDefaultHeight;
|
||||
|
||||
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
|
||||
// acquire at one time. It defaults to 1 and can be changed by the
|
||||
// consumer via the setMaxAcquiredBufferCount method, but this may only be
|
||||
// done when no producer is connected to the GonkBufferQueue.
|
||||
//
|
||||
// This value is used to derive the value returned for the
|
||||
// MIN_UNDEQUEUED_BUFFERS query by the producer.
|
||||
int mMaxAcquiredBufferCount;
|
||||
|
||||
// mDefaultMaxBufferCount is the default limit on the number of buffers
|
||||
// that will be allocated at one time. This default limit is set by the
|
||||
// consumer. The limit (as opposed to the default limit) may be
|
||||
// overridden by the producer.
|
||||
int mDefaultMaxBufferCount;
|
||||
|
||||
// mOverrideMaxBufferCount is the limit on the number of buffers that will
|
||||
// be allocated at one time. This value is set by the image producer by
|
||||
// calling setBufferCount. The default is zero, which means the producer
|
||||
// doesn't care about the number of buffers in the pool. In that case
|
||||
// mDefaultMaxBufferCount is used as the limit.
|
||||
int mOverrideMaxBufferCount;
|
||||
|
||||
// mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
|
||||
// allocate new GraphicBuffer objects.
|
||||
sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
|
||||
|
||||
// mConsumerListener is used to notify the connected consumer of
|
||||
// asynchronous events that it may wish to react to. It is initially set
|
||||
// to NULL and is written by consumerConnect and consumerDisconnect.
|
||||
sp<IConsumerListener> mConsumerListener;
|
||||
|
||||
// mSynchronousMode whether we're in synchronous mode or not
|
||||
bool mSynchronousMode;
|
||||
|
||||
// mConsumerControlledByApp whether the connected consumer is controlled by the
|
||||
// application.
|
||||
bool mConsumerControlledByApp;
|
||||
|
||||
// mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block.
|
||||
// this flag is set during connect() when both consumer and producer are controlled
|
||||
// by the application.
|
||||
bool mDequeueBufferCannotBlock;
|
||||
|
||||
// mUseAsyncBuffer whether an extra buffer is used in async mode to prevent
|
||||
// dequeueBuffer() from ever blocking.
|
||||
bool mUseAsyncBuffer;
|
||||
|
||||
// mConnectedApi indicates the producer API that is currently connected
|
||||
// to this GonkBufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets
|
||||
// updated by the connect and disconnect methods.
|
||||
int mConnectedApi;
|
||||
|
||||
// mDequeueCondition condition used for dequeueBuffer in synchronous mode
|
||||
mutable Condition mDequeueCondition;
|
||||
|
||||
// mQueue is a FIFO of queued buffers used in synchronous mode
|
||||
typedef Vector<BufferItem> Fifo;
|
||||
Fifo mQueue;
|
||||
|
||||
// mAbandoned indicates that the GonkBufferQueue will no longer be used to
|
||||
// consume image buffers pushed to it using the IGraphicBufferProducer
|
||||
// interface. It is initialized to false, and set to true in the
|
||||
// consumerDisconnect method. A GonkBufferQueue that has been abandoned will
|
||||
// return the NO_INIT error from all IGraphicBufferProducer methods
|
||||
// capable of returning an error.
|
||||
bool mAbandoned;
|
||||
|
||||
// mConsumerName is a string used to identify the GonkBufferQueue in log
|
||||
// messages. It is set by the setConsumerName method.
|
||||
String8 mConsumerName;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of GonkBufferQueue objects. It must be locked whenever the
|
||||
// member variables are accessed.
|
||||
mutable Mutex mMutex;
|
||||
|
||||
// mFrameCounter is the free running counter, incremented on every
|
||||
// successful queueBuffer call, and buffer allocation.
|
||||
uint64_t mFrameCounter;
|
||||
|
||||
// mBufferHasBeenQueued is true once a buffer has been queued. It is
|
||||
// reset when something causes all buffers to be freed (e.g. changing the
|
||||
// buffer count).
|
||||
bool mBufferHasBeenQueued;
|
||||
|
||||
// mDefaultBufferFormat can be set so it will override
|
||||
// the buffer format when it isn't specified in dequeueBuffer
|
||||
uint32_t mDefaultBufferFormat;
|
||||
|
||||
// mConsumerUsageBits contains flags the consumer wants for GraphicBuffers
|
||||
uint32_t mConsumerUsageBits;
|
||||
|
||||
// mTransformHint is used to optimize for screen rotations
|
||||
uint32_t mTransformHint;
|
||||
|
||||
// mConnectedProducerToken is used to set a binder death notification on the producer
|
||||
sp<IBinder> mConnectedProducerToken;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_BUFFERQUEUE_H
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 "GonkBufferItem.h"
|
||||
|
||||
#include <ui/Fence.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <system/window.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
GonkBufferItem::GonkBufferItem() :
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mIsAutoTimestamp(false),
|
||||
mFrameNumber(0),
|
||||
mSlot(INVALID_BUFFER_SLOT),
|
||||
mIsDroppable(false),
|
||||
mAcquireCalled(false),
|
||||
mTransformToDisplayInverse(false) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
GonkBufferItem::operator IGonkGraphicBufferConsumer::BufferItem() const {
|
||||
IGonkGraphicBufferConsumer::BufferItem bufferItem;
|
||||
bufferItem.mGraphicBuffer = mGraphicBuffer;
|
||||
bufferItem.mFence = mFence;
|
||||
bufferItem.mCrop = mCrop;
|
||||
bufferItem.mTransform = mTransform;
|
||||
bufferItem.mScalingMode = mScalingMode;
|
||||
bufferItem.mTimestamp = mTimestamp;
|
||||
bufferItem.mIsAutoTimestamp = mIsAutoTimestamp;
|
||||
bufferItem.mFrameNumber = mFrameNumber;
|
||||
bufferItem.mBuf = mSlot;
|
||||
bufferItem.mIsDroppable = mIsDroppable;
|
||||
bufferItem.mAcquireCalled = mAcquireCalled;
|
||||
bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse;
|
||||
return bufferItem;
|
||||
}
|
||||
|
||||
size_t GonkBufferItem::getPodSize() const {
|
||||
size_t c = sizeof(mCrop) +
|
||||
sizeof(mTransform) +
|
||||
sizeof(mScalingMode) +
|
||||
sizeof(mTimestamp) +
|
||||
sizeof(mIsAutoTimestamp) +
|
||||
sizeof(mFrameNumber) +
|
||||
sizeof(mSlot) +
|
||||
sizeof(mIsDroppable) +
|
||||
sizeof(mAcquireCalled) +
|
||||
sizeof(mTransformToDisplayInverse);
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t GonkBufferItem::getFlattenedSize() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFlattenedSize();
|
||||
FlattenableUtils::align<4>(c);
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFlattenedSize();
|
||||
FlattenableUtils::align<4>(c);
|
||||
}
|
||||
return sizeof(int32_t) + c + getPodSize();
|
||||
}
|
||||
|
||||
size_t GonkBufferItem::getFdCount() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFdCount();
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFdCount();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
status_t GonkBufferItem::flatten(
|
||||
void*& buffer, size_t& size, int*& fds, size_t& count) const {
|
||||
|
||||
// make sure we have enough space
|
||||
if (count < GonkBufferItem::getFlattenedSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// content flags are stored first
|
||||
uint32_t& flags = *static_cast<uint32_t*>(buffer);
|
||||
|
||||
// advance the pointer
|
||||
FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
|
||||
|
||||
flags = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 1;
|
||||
}
|
||||
if (mFence != 0) {
|
||||
status_t err = mFence->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 2;
|
||||
}
|
||||
|
||||
// check we have enough space (in case flattening the fence/graphicbuffer lied to us)
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::write(buffer, size, mCrop);
|
||||
FlattenableUtils::write(buffer, size, mTransform);
|
||||
FlattenableUtils::write(buffer, size, mScalingMode);
|
||||
FlattenableUtils::write(buffer, size, mTimestamp);
|
||||
FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
|
||||
FlattenableUtils::write(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::write(buffer, size, mSlot);
|
||||
FlattenableUtils::write(buffer, size, mIsDroppable);
|
||||
FlattenableUtils::write(buffer, size, mAcquireCalled);
|
||||
FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferItem::unflatten(
|
||||
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
|
||||
|
||||
if (size < sizeof(uint32_t))
|
||||
return NO_MEMORY;
|
||||
|
||||
uint32_t flags = 0;
|
||||
FlattenableUtils::read(buffer, size, flags);
|
||||
|
||||
if (flags & 1) {
|
||||
mGraphicBuffer = new GraphicBuffer();
|
||||
status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
if (flags & 2) {
|
||||
mFence = new Fence();
|
||||
status_t err = mFence->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
// check we have enough space
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::read(buffer, size, mCrop);
|
||||
FlattenableUtils::read(buffer, size, mTransform);
|
||||
FlattenableUtils::read(buffer, size, mScalingMode);
|
||||
FlattenableUtils::read(buffer, size, mTimestamp);
|
||||
FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
|
||||
FlattenableUtils::read(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::read(buffer, size, mSlot);
|
||||
FlattenableUtils::read(buffer, size, mIsDroppable);
|
||||
FlattenableUtils::read(buffer, size, mAcquireCalled);
|
||||
FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const char* GonkBufferItem::scalingModeName(uint32_t scalingMode) {
|
||||
switch (scalingMode) {
|
||||
case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERITEM_LL_H
|
||||
#define NATIVEWINDOW_GONKBUFFERITEM_LL_H
|
||||
|
||||
#include "IGonkGraphicBufferConsumerLL.h"
|
||||
|
||||
#include <ui/Rect.h>
|
||||
|
||||
#include <utils/Flattenable.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class Fence;
|
||||
class GraphicBuffer;
|
||||
|
||||
class GonkBufferItem : public Flattenable<GonkBufferItem> {
|
||||
friend class Flattenable<GonkBufferItem>;
|
||||
size_t getPodSize() const;
|
||||
size_t getFlattenedSize() const;
|
||||
size_t getFdCount() const;
|
||||
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
|
||||
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
|
||||
|
||||
public:
|
||||
// The default value of mBuf, used to indicate this doesn't correspond to a slot.
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
GonkBufferItem();
|
||||
operator IGonkGraphicBufferConsumer::BufferItem() const;
|
||||
|
||||
static const char* scalingModeName(uint32_t scalingMode);
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
|
||||
// if the buffer in this slot has been acquired in the past (see
|
||||
// BufferSlot.mAcquireCalled).
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mFence is a fence that will signal when the buffer is idle.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_SCALING_* in <window.h>
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued. This value
|
||||
// is guaranteed to be monotonically increasing for each newly
|
||||
// acquired buffer.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mIsAutoTimestamp indicates whether mTimestamp was generated
|
||||
// automatically when the buffer was queued.
|
||||
bool mIsAutoTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
|
||||
int mSlot;
|
||||
|
||||
// mIsDroppable whether this buffer was queued with the
|
||||
// property that it can be replaced by a new buffer for the purpose of
|
||||
// making sure dequeueBuffer() won't block.
|
||||
// i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
|
||||
// was queued.
|
||||
bool mIsDroppable;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates this buffer must be transformed by the inverse transform of the screen
|
||||
// it is displayed onto. This is applied after mTransform.
|
||||
bool mTransformToDisplayInverse;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -1,559 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
|
||||
#define LOG_TAG "GonkBufferQueueConsumer"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "GonkBufferItem.h"
|
||||
#include "GonkBufferQueueConsumer.h"
|
||||
#include "GonkBufferQueueCore.h"
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
GonkBufferQueueConsumer::GonkBufferQueueConsumer(const sp<GonkBufferQueueCore>& core) :
|
||||
mCore(core),
|
||||
mSlots(core->mSlots),
|
||||
mConsumerName() {}
|
||||
|
||||
GonkBufferQueueConsumer::~GonkBufferQueueConsumer() {}
|
||||
|
||||
status_t GonkBufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
|
||||
nsecs_t expectedPresent) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// Check that the consumer doesn't currently have the maximum number of
|
||||
// buffers acquired. We allow the max buffer count to be exceeded by one
|
||||
// buffer so that the consumer can successfully set up the newly acquired
|
||||
// buffer before releasing the old one.
|
||||
int numAcquiredBuffers = 0;
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) {
|
||||
++numAcquiredBuffers;
|
||||
}
|
||||
}
|
||||
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
|
||||
ALOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
|
||||
numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Check if the queue is empty.
|
||||
// In asynchronous mode the list is guaranteed to be one buffer deep,
|
||||
// while in synchronous mode we use the oldest buffer.
|
||||
if (mCore->mQueue.empty()) {
|
||||
return NO_BUFFER_AVAILABLE;
|
||||
}
|
||||
|
||||
GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
|
||||
|
||||
// If expectedPresent is specified, we may not want to return a buffer yet.
|
||||
// If it's specified and there's more than one buffer queued, we may want
|
||||
// to drop a buffer.
|
||||
if (expectedPresent != 0) {
|
||||
const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
|
||||
|
||||
// The 'expectedPresent' argument indicates when the buffer is expected
|
||||
// to be presented on-screen. If the buffer's desired present time is
|
||||
// earlier (less) than expectedPresent -- meaning it will be displayed
|
||||
// on time or possibly late if we show it as soon as possible -- we
|
||||
// acquire and return it. If we don't want to display it until after the
|
||||
// expectedPresent time, we return PRESENT_LATER without acquiring it.
|
||||
//
|
||||
// To be safe, we don't defer acquisition if expectedPresent is more
|
||||
// than one second in the future beyond the desired present time
|
||||
// (i.e., we'd be holding the buffer for a long time).
|
||||
//
|
||||
// NOTE: Code assumes monotonic time values from the system clock
|
||||
// are positive.
|
||||
|
||||
// Start by checking to see if we can drop frames. We skip this check if
|
||||
// the timestamps are being auto-generated by Surface. If the app isn't
|
||||
// generating timestamps explicitly, it probably doesn't want frames to
|
||||
// be discarded based on them.
|
||||
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
|
||||
// If entry[1] is timely, drop entry[0] (and repeat). We apply an
|
||||
// additional criterion here: we only drop the earlier buffer if our
|
||||
// desiredPresent falls within +/- 1 second of the expected present.
|
||||
// Otherwise, bogus desiredPresent times (e.g., 0 or a small
|
||||
// relative timestamp), which normally mean "ignore the timestamp
|
||||
// and acquire immediately", would cause us to drop frames.
|
||||
//
|
||||
// We may want to add an additional criterion: don't drop the
|
||||
// earlier buffer if entry[1]'s fence hasn't signaled yet.
|
||||
const BufferItem& bufferItem(mCore->mQueue[1]);
|
||||
nsecs_t desiredPresent = bufferItem.mTimestamp;
|
||||
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
|
||||
desiredPresent > expectedPresent) {
|
||||
// This buffer is set to display in the near future, or
|
||||
// desiredPresent is garbage. Either way we don't want to drop
|
||||
// the previous buffer just to get this on the screen sooner.
|
||||
ALOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
|
||||
PRId64 " (%" PRId64 ") now=%" PRId64,
|
||||
desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
break;
|
||||
}
|
||||
|
||||
ALOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
|
||||
" size=%zu",
|
||||
desiredPresent, expectedPresent, mCore->mQueue.size());
|
||||
if (mCore->stillTracking(front)) {
|
||||
// Front buffer is still in mSlots, so mark the slot as free
|
||||
mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE;
|
||||
}
|
||||
mCore->mQueue.erase(front);
|
||||
front = mCore->mQueue.begin();
|
||||
}
|
||||
|
||||
// See if the front buffer is due
|
||||
nsecs_t desiredPresent = front->mTimestamp;
|
||||
if (desiredPresent > expectedPresent &&
|
||||
desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
|
||||
ALOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
|
||||
" (%" PRId64 ") now=%" PRId64,
|
||||
desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
return PRESENT_LATER;
|
||||
}
|
||||
|
||||
ALOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
|
||||
"(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
}
|
||||
|
||||
int slot = front->mSlot;
|
||||
//*outBuffer = *front;
|
||||
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
|
||||
outBuffer->mFrameNumber = mSlots[slot].mFrameNumber;
|
||||
outBuffer->mBuf = slot;
|
||||
outBuffer->mFence = mSlots[slot].mFence;
|
||||
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
|
||||
ALOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
|
||||
slot, front->mFrameNumber, front->mGraphicBuffer->handle);
|
||||
// If the front buffer is still being tracked, update its slot state
|
||||
if (mCore->stillTracking(front)) {
|
||||
mSlots[slot].mAcquireCalled = true;
|
||||
mSlots[slot].mNeedsCleanupOnRelease = false;
|
||||
mSlots[slot].mBufferState = GonkBufferSlot::ACQUIRED;
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
}
|
||||
|
||||
// If the buffer has previously been acquired by the consumer, set
|
||||
// mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
|
||||
// on the consumer side
|
||||
//if (outBuffer->mAcquireCalled) {
|
||||
// outBuffer->mGraphicBuffer = NULL;
|
||||
//}
|
||||
|
||||
mCore->mQueue.erase(front);
|
||||
|
||||
// We might have freed a slot while dropping old buffers, or the producer
|
||||
// may be blocked waiting for the number of buffers in the queue to
|
||||
// decrease.
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::detachBuffer(int slot) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
ALOGV("detachBuffer(C): slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("detachBuffer(C): GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGE("detachBuffer(C): slot index %d out of range [0, %d)",
|
||||
slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != GonkBufferSlot::ACQUIRED) {
|
||||
ALOGE("detachBuffer(C): slot %d is not owned by the consumer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->freeBufferLocked(slot);
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::attachBuffer(int* outSlot,
|
||||
const sp<android::GraphicBuffer>& buffer) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlot == NULL) {
|
||||
ALOGE("attachBuffer(P): outSlot must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (buffer == NULL) {
|
||||
ALOGE("attachBuffer(P): cannot attach NULL buffer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// Make sure we don't have too many acquired buffers and find a free slot
|
||||
// to put the buffer into (the oldest if there are multiple).
|
||||
int numAcquiredBuffers = 0;
|
||||
int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) {
|
||||
++numAcquiredBuffers;
|
||||
} else if (mSlots[s].mBufferState == GonkBufferSlot::FREE) {
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
|
||||
found = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
|
||||
ALOGE("attachBuffer(P): max acquired buffer count reached: %d "
|
||||
"(max %d)", numAcquiredBuffers,
|
||||
mCore->mMaxAcquiredBufferCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
ALOGE("attachBuffer(P): could not find free buffer slot");
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
ATRACE_BUFFER_INDEX(*outSlot);
|
||||
ALOGV("attachBuffer(C): returning slot %d", *outSlot);
|
||||
|
||||
mSlots[*outSlot].mGraphicBuffer = buffer;
|
||||
mSlots[*outSlot].mBufferState = GonkBufferSlot::ACQUIRED;
|
||||
mSlots[*outSlot].mAttachedByConsumer = true;
|
||||
mSlots[*outSlot].mNeedsCleanupOnRelease = false;
|
||||
mSlots[*outSlot].mFence = Fence::NO_FENCE;
|
||||
mSlots[*outSlot].mFrameNumber = 0;
|
||||
|
||||
// mAcquireCalled tells GonkBufferQueue that it doesn't need to send a valid
|
||||
// GraphicBuffer pointer on the next acquireBuffer call, which decreases
|
||||
// Binder traffic by not un/flattening the GraphicBuffer. However, it
|
||||
// requires that the consumer maintain a cached copy of the slot <--> buffer
|
||||
// mappings, which is why the consumer doesn't need the valid pointer on
|
||||
// acquire.
|
||||
//
|
||||
// The StreamSplitter is one of the primary users of the attach/detach
|
||||
// logic, and while it is running, all buffers it acquires are immediately
|
||||
// detached, and all buffers it eventually releases are ones that were
|
||||
// attached (as opposed to having been obtained from acquireBuffer), so it
|
||||
// doesn't make sense to maintain the slot/buffer mappings, which would
|
||||
// become invalid for every buffer during detach/attach. By setting this to
|
||||
// false, the valid GraphicBuffer pointer will always be sent with acquire
|
||||
// for attached buffers.
|
||||
mSlots[*outSlot].mAcquireCalled = false;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
|
||||
const sp<Fence>& releaseFence) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS ||
|
||||
releaseFence == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<IProducerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// If the frame number has changed because the buffer has been reallocated,
|
||||
// we can ignore this releaseBuffer for the old buffer
|
||||
//if (frameNumber != mSlots[slot].mFrameNumber) {
|
||||
// return STALE_BUFFER_SLOT;
|
||||
//}
|
||||
|
||||
// Make sure this buffer hasn't been queued while acquired by the consumer
|
||||
GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
|
||||
while (current != mCore->mQueue.end()) {
|
||||
if (current->mSlot == slot) {
|
||||
ALOGE("releaseBuffer: buffer slot %d pending release is "
|
||||
"currently queued", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) {
|
||||
mSlots[slot].mFence = releaseFence;
|
||||
mSlots[slot].mBufferState = GonkBufferSlot::FREE;
|
||||
listener = mCore->mConnectedProducerListener;
|
||||
ALOGV("releaseBuffer: releasing slot %d", slot);
|
||||
} else if (mSlots[slot].mNeedsCleanupOnRelease) {
|
||||
ALOGV("releaseBuffer: releasing a stale buffer slot %d "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
mSlots[slot].mNeedsCleanupOnRelease = false;
|
||||
return STALE_BUFFER_SLOT;
|
||||
} else {
|
||||
ALOGV("releaseBuffer: attempted to release buffer slot %d "
|
||||
"but its state was %d", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBufferReleased();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::connect(
|
||||
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (consumerListener == NULL) {
|
||||
ALOGE("connect(C): consumerListener may not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ALOGV("connect(C): controlledByApp=%s",
|
||||
controlledByApp ? "true" : "false");
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("connect(C): GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
mCore->mConsumerListener = consumerListener;
|
||||
mCore->mConsumerControlledByApp = controlledByApp;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::disconnect() {
|
||||
ATRACE_CALL();
|
||||
|
||||
ALOGV("disconnect(C)");
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConsumerListener == NULL) {
|
||||
ALOGE("disconnect(C): no consumer is connected");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->mIsAbandoned = true;
|
||||
mCore->mConsumerListener = NULL;
|
||||
mCore->mQueue.clear();
|
||||
mCore->freeAllBuffersLocked();
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlotMask == NULL) {
|
||||
ALOGE("getReleasedBuffers: outSlotMask may not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("getReleasedBuffers: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
uint64_t mask = 0;
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (!mSlots[s].mAcquireCalled) {
|
||||
mask |= (1ULL << s);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the mask queued buffers for which acquire has been called,
|
||||
// since the consumer will not receive their buffer addresses and so must
|
||||
// retain their cached information
|
||||
GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
|
||||
while (current != mCore->mQueue.end()) {
|
||||
if (current->mAcquireCalled) {
|
||||
mask &= ~(1ULL << current->mSlot);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
ALOGV("getReleasedBuffers: returning mask %#" PRIx64, mask);
|
||||
*outSlotMask = mask;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setDefaultBufferSize(uint32_t width,
|
||||
uint32_t height) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
ALOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
|
||||
"height=%u)", width, height);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ALOGV("setDefaultBufferSize: width=%u height=%u", width, height);
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mDefaultWidth = width;
|
||||
mCore->mDefaultHeight = height;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
return mCore->setDefaultMaxBufferCountLocked(bufferCount);
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::disableAsyncBuffer() {
|
||||
ATRACE_CALL();
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConsumerListener != NULL) {
|
||||
ALOGE("disableAsyncBuffer: consumer already connected");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ALOGV("disableAsyncBuffer");
|
||||
mCore->mUseAsyncBuffer = false;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setMaxAcquiredBufferCount(
|
||||
int maxAcquiredBuffers) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (maxAcquiredBuffers < 1 ||
|
||||
maxAcquiredBuffers > GonkBufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
|
||||
ALOGE("setMaxAcquiredBufferCount: invalid count %d",
|
||||
maxAcquiredBuffers);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) {
|
||||
ALOGE("setMaxAcquiredBufferCount: producer is already connected");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ALOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
|
||||
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void GonkBufferQueueConsumer::setConsumerName(const String8& name) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("setConsumerName: '%s'", name.string());
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mConsumerName = name;
|
||||
mConsumerName = name;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("setDefaultBufferFormat: %u", defaultFormat);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mDefaultBufferFormat = defaultFormat;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("setConsumerUsageBits: %#x", usage);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mConsumerUsageBits = usage;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueConsumer::setTransformHint(uint32_t hint) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("setTransformHint: %#x", hint);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mTransformHint = hint;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
sp<NativeHandle> GonkBufferQueueConsumer::getSidebandStream() const {
|
||||
return mCore->mSidebandStream;
|
||||
}
|
||||
|
||||
void GonkBufferQueueConsumer::dumpToString(String8& result, const char* prefix) const {
|
||||
mCore->dump(result, prefix);
|
||||
}
|
||||
|
||||
already_AddRefed<GonkBufferSlot::TextureClient>
|
||||
GonkBufferQueueConsumer::getTextureClientFromBuffer(ANativeWindowBuffer* buffer)
|
||||
{
|
||||
Mutex::Autolock _l(mCore->mMutex);
|
||||
if (buffer == NULL) {
|
||||
ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
|
||||
if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) {
|
||||
RefPtr<TextureClient> client(mSlots[i].mTextureClient);
|
||||
return client.forget();
|
||||
}
|
||||
}
|
||||
ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int
|
||||
GonkBufferQueueConsumer::getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const
|
||||
{
|
||||
if (client == NULL) {
|
||||
ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
|
||||
if (mSlots[i].mTextureClient == client) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ALOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H
|
||||
|
||||
#include "GonkBufferQueueDefs.h"
|
||||
#include "IGonkGraphicBufferConsumerLL.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkBufferQueueCore;
|
||||
|
||||
class GonkBufferQueueConsumer : public BnGonkGraphicBufferConsumer {
|
||||
|
||||
public:
|
||||
GonkBufferQueueConsumer(const sp<GonkBufferQueueCore>& core);
|
||||
virtual ~GonkBufferQueueConsumer();
|
||||
|
||||
// acquireBuffer attempts to acquire ownership of the next pending buffer in
|
||||
// the GonkBufferQueue. If no buffer is pending then it returns
|
||||
// NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
|
||||
// information about the buffer is returned in BufferItem. If the buffer
|
||||
// returned had previously been acquired then the BufferItem::mGraphicBuffer
|
||||
// field of buffer is set to NULL and it is assumed that the consumer still
|
||||
// holds a reference to the buffer.
|
||||
//
|
||||
// If expectedPresent is nonzero, it indicates the time when the buffer
|
||||
// will be displayed on screen. If the buffer's timestamp is farther in the
|
||||
// future, the buffer won't be acquired, and PRESENT_LATER will be
|
||||
// returned. The presentation time is in nanoseconds, and the time base
|
||||
// is CLOCK_MONOTONIC.
|
||||
virtual status_t acquireBuffer(BufferItem* outBuffer,
|
||||
nsecs_t expectedPresent);
|
||||
|
||||
// See IGonkGraphicBufferConsumer::detachBuffer
|
||||
virtual status_t detachBuffer(int slot);
|
||||
|
||||
// See IGonkGraphicBufferConsumer::attachBuffer
|
||||
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// GonkBufferQueue. This may be done while the buffer's contents are still
|
||||
// being accessed. The fence will signal when the buffer is no longer
|
||||
// in use. frameNumber is used to indentify the exact buffer returned.
|
||||
//
|
||||
// If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
|
||||
// any references to the just-released buffer that it might have, as if it
|
||||
// had received a onBuffersReleased() call with a mask set for the released
|
||||
// buffer.
|
||||
virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
|
||||
const sp<Fence>& releaseFence);
|
||||
|
||||
// connect connects a consumer to the GonkBufferQueue. Only one
|
||||
// consumer may be connected, and when that consumer disconnects the
|
||||
// GonkBufferQueue is placed into the "abandoned" state, causing most
|
||||
// interactions with the GonkBufferQueue by the producer to fail.
|
||||
// controlledByApp indicates whether the consumer is controlled by
|
||||
// the application.
|
||||
//
|
||||
// consumerListener may not be NULL.
|
||||
virtual status_t connect(const sp<IConsumerListener>& consumerListener,
|
||||
bool controlledByApp);
|
||||
|
||||
// disconnect disconnects a consumer from the GonkBufferQueue. All
|
||||
// buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
|
||||
// state, causing most interactions with the GonkBufferQueue by the producer to
|
||||
// fail.
|
||||
virtual status_t disconnect();
|
||||
|
||||
// getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask
|
||||
// indicating which buffer slots have been released by the GonkBufferQueue
|
||||
// but have not yet been released by the consumer.
|
||||
//
|
||||
// This should be called from the onBuffersReleased() callback.
|
||||
virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// dequeueBuffer when a width and height of zero is requested. Default
|
||||
// is 1x1.
|
||||
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
|
||||
|
||||
// setDefaultMaxBufferCount sets the default value for the maximum buffer
|
||||
// count (the initial default is 2). If the producer has requested a
|
||||
// buffer count using setBufferCount, the default buffer count will only
|
||||
// take effect if the producer sets the count back to zero.
|
||||
//
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
virtual status_t setDefaultMaxBufferCount(int bufferCount);
|
||||
|
||||
// disableAsyncBuffer disables the extra buffer used in async mode
|
||||
// (when both producer and consumer have set their "isControlledByApp"
|
||||
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
|
||||
//
|
||||
// This can only be called before connect().
|
||||
virtual status_t disableAsyncBuffer();
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the GonkBufferQueue.
|
||||
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
virtual void setConsumerName(const String8& name);
|
||||
|
||||
// setDefaultBufferFormat allows the GonkBufferQueue to create
|
||||
// GraphicBuffers of a defaultFormat if no format is specified
|
||||
// in dequeueBuffer. Formats are enumerated in graphics.h; the
|
||||
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
|
||||
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
|
||||
|
||||
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
|
||||
// These are merged with the bits passed to dequeueBuffer. The values are
|
||||
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
|
||||
virtual status_t setConsumerUsageBits(uint32_t usage);
|
||||
|
||||
// setTransformHint bakes in rotation to buffers so overlays can be used.
|
||||
// The values are enumerated in window.h, e.g.
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
virtual status_t setTransformHint(uint32_t hint);
|
||||
|
||||
// Retrieve the sideband buffer stream, if any.
|
||||
virtual sp<NativeHandle> getSidebandStream() const;
|
||||
|
||||
// dump our state in a String
|
||||
virtual void dumpToString(String8& result, const char* prefix) const;
|
||||
|
||||
// Added by mozilla
|
||||
virtual already_AddRefed<GonkBufferSlot::TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
|
||||
|
||||
virtual int getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const;
|
||||
|
||||
// Functions required for backwards compatibility.
|
||||
// These will be modified/renamed in IGonkGraphicBufferConsumer and will be
|
||||
// removed from this class at that time. See b/13306289.
|
||||
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
|
||||
bool controlledByApp) {
|
||||
return connect(consumer, controlledByApp);
|
||||
}
|
||||
|
||||
virtual status_t consumerDisconnect() { return disconnect(); }
|
||||
|
||||
// End functions required for backwards compatibility
|
||||
|
||||
private:
|
||||
sp<GonkBufferQueueCore> mCore;
|
||||
|
||||
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
|
||||
GonkBufferQueueDefs::SlotsType& mSlots;
|
||||
|
||||
// This is a cached copy of the name stored in the GonkBufferQueueCore.
|
||||
// It's updated during setConsumerName.
|
||||
String8 mConsumerName;
|
||||
|
||||
}; // class GonkBufferQueueConsumer
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -1,243 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 "GonkBufferQueueCore"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "GonkBufferItem.h"
|
||||
#include "GonkBufferQueueCore.h"
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
#include <private/gui/ComposerService.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include "mozilla/layers/GrallocTextureClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
|
||||
template <typename T>
|
||||
static inline T max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
namespace android {
|
||||
|
||||
static String8 getUniqueName() {
|
||||
static volatile int32_t counter = 0;
|
||||
return String8::format("unnamed-%d-%d", getpid(),
|
||||
android_atomic_inc(&counter));
|
||||
}
|
||||
|
||||
GonkBufferQueueCore::GonkBufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
|
||||
mAllocator(allocator),
|
||||
mMutex(),
|
||||
mIsAbandoned(false),
|
||||
mConsumerControlledByApp(false),
|
||||
mConsumerName(getUniqueName()),
|
||||
mConsumerListener(),
|
||||
mConsumerUsageBits(0),
|
||||
mConnectedApi(NO_CONNECTED_API),
|
||||
mConnectedProducerListener(),
|
||||
mSlots(),
|
||||
mQueue(),
|
||||
mOverrideMaxBufferCount(0),
|
||||
mDequeueCondition(),
|
||||
mUseAsyncBuffer(true),
|
||||
mDequeueBufferCannotBlock(false),
|
||||
mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
|
||||
mDefaultWidth(1),
|
||||
mDefaultHeight(1),
|
||||
mDefaultMaxBufferCount(2),
|
||||
mMaxAcquiredBufferCount(1),
|
||||
mBufferHasBeenQueued(false),
|
||||
mFrameCounter(0),
|
||||
mTransformHint(0),
|
||||
mIsAllocating(false),
|
||||
mIsAllocatingCondition()
|
||||
{
|
||||
ALOGV("GonkBufferQueueCore");
|
||||
}
|
||||
|
||||
GonkBufferQueueCore::~GonkBufferQueueCore() {}
|
||||
|
||||
void GonkBufferQueueCore::dump(String8& result, const char* prefix) const {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
String8 fifo;
|
||||
Fifo::const_iterator current(mQueue.begin());
|
||||
while (current != mQueue.end()) {
|
||||
fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
|
||||
"xform=0x%02x, time=%#" PRIx64 ", scale=%s\n",
|
||||
current->mSlot, current->mGraphicBuffer.get(),
|
||||
current->mCrop.left, current->mCrop.top, current->mCrop.right,
|
||||
current->mCrop.bottom, current->mTransform, current->mTimestamp,
|
||||
GonkBufferItem::scalingModeName(current->mScalingMode));
|
||||
++current;
|
||||
}
|
||||
|
||||
result.appendFormat("%s-GonkBufferQueue mMaxAcquiredBufferCount=%d, "
|
||||
"mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
|
||||
"default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n",
|
||||
prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
|
||||
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
|
||||
mQueue.size(), fifo.string());
|
||||
|
||||
// Trim the free buffers so as to not spam the dump
|
||||
int maxBufferCount = 0;
|
||||
for (int s = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
|
||||
const GonkBufferSlot& slot(mSlots[s]);
|
||||
if (slot.mBufferState != GonkBufferSlot::FREE ||
|
||||
slot.mGraphicBuffer != NULL) {
|
||||
maxBufferCount = s + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int s = 0; s < maxBufferCount; ++s) {
|
||||
const GonkBufferSlot& slot(mSlots[s]);
|
||||
const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
|
||||
result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
|
||||
(slot.mBufferState == GonkBufferSlot::ACQUIRED) ? ">" : " ",
|
||||
s, buffer.get(),
|
||||
GonkBufferSlot::bufferStateName(slot.mBufferState));
|
||||
|
||||
if (buffer != NULL) {
|
||||
result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
|
||||
buffer->width, buffer->height, buffer->stride,
|
||||
buffer->format);
|
||||
}
|
||||
|
||||
result.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int GonkBufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const {
|
||||
// If dequeueBuffer is allowed to error out, we don't have to add an
|
||||
// extra buffer.
|
||||
if (!mUseAsyncBuffer) {
|
||||
return mMaxAcquiredBufferCount;
|
||||
}
|
||||
|
||||
if (mDequeueBufferCannotBlock || async) {
|
||||
return mMaxAcquiredBufferCount + 1;
|
||||
}
|
||||
|
||||
return mMaxAcquiredBufferCount;
|
||||
}
|
||||
|
||||
int GonkBufferQueueCore::getMinMaxBufferCountLocked(bool async) const {
|
||||
return getMinUndequeuedBufferCountLocked(async) + 1;
|
||||
}
|
||||
|
||||
int GonkBufferQueueCore::getMaxBufferCountLocked(bool async) const {
|
||||
int minMaxBufferCount = getMinMaxBufferCountLocked(async);
|
||||
|
||||
int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount);
|
||||
if (mOverrideMaxBufferCount != 0) {
|
||||
assert(mOverrideMaxBufferCount >= minMaxBufferCount);
|
||||
maxBufferCount = mOverrideMaxBufferCount;
|
||||
}
|
||||
|
||||
// Any buffers that are dequeued by the producer or sitting in the queue
|
||||
// waiting to be consumed need to have their slots preserved. Such buffers
|
||||
// will temporarily keep the max buffer count up until the slots no longer
|
||||
// need to be preserved.
|
||||
for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
GonkBufferSlot::BufferState state = mSlots[s].mBufferState;
|
||||
if (state == GonkBufferSlot::QUEUED || state == GonkBufferSlot::DEQUEUED) {
|
||||
maxBufferCount = s + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return maxBufferCount;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueCore::setDefaultMaxBufferCountLocked(int count) {
|
||||
const int minBufferCount = 2;
|
||||
if (count < minBufferCount || count > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
|
||||
"[%d, %d]",
|
||||
count, minBufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ALOGV("setDefaultMaxBufferCount: setting count to %d", count);
|
||||
mDefaultMaxBufferCount = count;
|
||||
mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void GonkBufferQueueCore::freeBufferLocked(int slot) {
|
||||
ALOGV("freeBufferLocked: slot %d", slot);
|
||||
|
||||
if (mSlots[slot].mTextureClient) {
|
||||
mSlots[slot].mTextureClient->ClearRecycleCallback();
|
||||
// release TextureClient in ImageBridge thread
|
||||
RefPtr<TextureClientReleaseTask> task =
|
||||
MakeAndAddRef<TextureClientReleaseTask>(mSlots[slot].mTextureClient);
|
||||
mSlots[slot].mTextureClient = NULL;
|
||||
ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
mSlots[slot].mGraphicBuffer.clear();
|
||||
if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) {
|
||||
mSlots[slot].mNeedsCleanupOnRelease = true;
|
||||
}
|
||||
mSlots[slot].mBufferState = GonkBufferSlot::FREE;
|
||||
mSlots[slot].mFrameNumber = UINT32_MAX;
|
||||
mSlots[slot].mAcquireCalled = false;
|
||||
|
||||
// Destroy fence as GonkBufferQueue now takes ownership
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
}
|
||||
|
||||
void GonkBufferQueueCore::freeAllBuffersLocked() {
|
||||
ALOGW_IF(!mQueue.isEmpty(),
|
||||
"freeAllBuffersLocked called but mQueue is not empty");
|
||||
mQueue.clear();
|
||||
mBufferHasBeenQueued = false;
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
freeBufferLocked(s);
|
||||
}
|
||||
}
|
||||
|
||||
bool GonkBufferQueueCore::stillTracking(const GonkBufferItem* item) const {
|
||||
const GonkBufferSlot& slot = mSlots[item->mSlot];
|
||||
|
||||
ALOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
|
||||
"slot { slot=%d/%" PRIu64 " buffer=%p }",
|
||||
item->mSlot, item->mFrameNumber,
|
||||
(item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
|
||||
item->mSlot, slot.mFrameNumber,
|
||||
(slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
|
||||
|
||||
// Compare item with its original buffer slot. We can check the slot as
|
||||
// the buffer would not be moved to a different slot by the producer.
|
||||
return (slot.mGraphicBuffer != NULL) &&
|
||||
(item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
|
||||
}
|
||||
|
||||
void GonkBufferQueueCore::waitWhileAllocatingLocked() const {
|
||||
ATRACE_CALL();
|
||||
while (mIsAllocating) {
|
||||
mIsAllocatingCondition.wait(mMutex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,251 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H
|
||||
|
||||
#include "GonkBufferQueueDefs.h"
|
||||
#include "GonkBufferSlot.h"
|
||||
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
|
||||
#define ATRACE_BUFFER_INDEX(index)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkBufferItem;
|
||||
class IConsumerListener;
|
||||
class IGraphicBufferAlloc;
|
||||
class IProducerListener;
|
||||
|
||||
class GonkBufferQueueCore : public virtual RefBase {
|
||||
|
||||
friend class GonkBufferQueueProducer;
|
||||
friend class GonkBufferQueueConsumer;
|
||||
|
||||
public:
|
||||
// Used as a placeholder slot number when the value isn't pointing to an
|
||||
// existing buffer.
|
||||
enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem
|
||||
|
||||
// We reserve two slots in order to guarantee that the producer and
|
||||
// consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// The default API number used to indicate that no producer is connected
|
||||
enum { NO_CONNECTED_API = 0 };
|
||||
|
||||
typedef Vector<GonkBufferItem> Fifo;
|
||||
typedef mozilla::layers::TextureClient TextureClient;
|
||||
|
||||
// GonkBufferQueueCore manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allocator is used to allocate all the needed
|
||||
// gralloc buffers.
|
||||
GonkBufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
virtual ~GonkBufferQueueCore();
|
||||
|
||||
private:
|
||||
// Dump our state in a string
|
||||
void dump(String8& result, const char* prefix) const;
|
||||
|
||||
int getSlotFromTextureClientLocked(TextureClient* client) const;
|
||||
|
||||
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
|
||||
// that must remain in a state other than DEQUEUED. The async parameter
|
||||
// tells whether we're in asynchronous mode.
|
||||
int getMinUndequeuedBufferCountLocked(bool async) const;
|
||||
|
||||
// getMinMaxBufferCountLocked returns the minimum number of buffers allowed
|
||||
// given the current GonkBufferQueue state. The async parameter tells whether
|
||||
// we're in asynchonous mode.
|
||||
int getMinMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// getMaxBufferCountLocked returns the maximum number of buffers that can be
|
||||
// allocated at once. This value depends on the following member variables:
|
||||
//
|
||||
// mDequeueBufferCannotBlock
|
||||
// mMaxAcquiredBufferCount
|
||||
// mDefaultMaxBufferCount
|
||||
// mOverrideMaxBufferCount
|
||||
// async parameter
|
||||
//
|
||||
// Any time one of these member variables is changed while a producer is
|
||||
// connected, mDequeueCondition must be broadcast.
|
||||
int getMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
|
||||
// that will be used if the producer does not override the buffer slot
|
||||
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The
|
||||
// initial default is 2.
|
||||
status_t setDefaultMaxBufferCountLocked(int count);
|
||||
|
||||
// freeBufferLocked frees the GraphicBuffer and sync resources for the
|
||||
// given slot.
|
||||
void freeBufferLocked(int slot);
|
||||
|
||||
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
|
||||
// all slots.
|
||||
void freeAllBuffersLocked();
|
||||
|
||||
// stillTracking returns true iff the buffer item is still being tracked
|
||||
// in one of the slots.
|
||||
bool stillTracking(const GonkBufferItem* item) const;
|
||||
|
||||
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
|
||||
void waitWhileAllocatingLocked() const;
|
||||
|
||||
// mAllocator is the connection to SurfaceFlinger that is used to allocate
|
||||
// new GraphicBuffer objects.
|
||||
sp<IGraphicBufferAlloc> mAllocator;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of GonkBufferQueueCore objects. It must be locked whenever any
|
||||
// member variable is accessed.
|
||||
mutable Mutex mMutex;
|
||||
|
||||
// mIsAbandoned indicates that the GonkBufferQueue will no longer be used to
|
||||
// consume image buffers pushed to it using the IGraphicBufferProducer
|
||||
// interface. It is initialized to false, and set to true in the
|
||||
// consumerDisconnect method. A GonkBufferQueue that is abandoned will return
|
||||
// the NO_INIT error from all IGraphicBufferProducer methods capable of
|
||||
// returning an error.
|
||||
bool mIsAbandoned;
|
||||
|
||||
// mConsumerControlledByApp indicates whether the connected consumer is
|
||||
// controlled by the application.
|
||||
bool mConsumerControlledByApp;
|
||||
|
||||
// mConsumerName is a string used to identify the GonkBufferQueue in log
|
||||
// messages. It is set by the IGraphicBufferConsumer::setConsumerName
|
||||
// method.
|
||||
String8 mConsumerName;
|
||||
|
||||
// mConsumerListener is used to notify the connected consumer of
|
||||
// asynchronous events that it may wish to react to. It is initially
|
||||
// set to NULL and is written by consumerConnect and consumerDisconnect.
|
||||
sp<IConsumerListener> mConsumerListener;
|
||||
|
||||
// mConsumerUsageBits contains flags that the consumer wants for
|
||||
// GraphicBuffers.
|
||||
uint32_t mConsumerUsageBits;
|
||||
|
||||
// mConnectedApi indicates the producer API that is currently connected
|
||||
// to this GonkBufferQueue. It defaults to NO_CONNECTED_API, and gets updated
|
||||
// by the connect and disconnect methods.
|
||||
int mConnectedApi;
|
||||
|
||||
// mConnectedProducerToken is used to set a binder death notification on
|
||||
// the producer.
|
||||
sp<IProducerListener> mConnectedProducerListener;
|
||||
|
||||
// mSlots is an array of buffer slots that must be mirrored on the producer
|
||||
// side. This allows buffer ownership to be transferred between the producer
|
||||
// and consumer without sending a GraphicBuffer over Binder. The entire
|
||||
// array is initialized to NULL at construction time, and buffers are
|
||||
// allocated for a slot when requestBuffer is called with that slot's index.
|
||||
GonkBufferQueueDefs::SlotsType mSlots;
|
||||
|
||||
// mQueue is a FIFO of queued buffers used in synchronous mode.
|
||||
Fifo mQueue;
|
||||
|
||||
// mOverrideMaxBufferCount is the limit on the number of buffers that will
|
||||
// be allocated at one time. This value is set by the producer by calling
|
||||
// setBufferCount. The default is 0, which means that the producer doesn't
|
||||
// care about the number of buffers in the pool. In that case,
|
||||
// mDefaultMaxBufferCount is used as the limit.
|
||||
int mOverrideMaxBufferCount;
|
||||
|
||||
// mDequeueCondition is a condition variable used for dequeueBuffer in
|
||||
// synchronous mode.
|
||||
mutable Condition mDequeueCondition;
|
||||
|
||||
// mUseAsyncBuffer indicates whether an extra buffer is used in async mode
|
||||
// to prevent dequeueBuffer from blocking.
|
||||
bool mUseAsyncBuffer;
|
||||
|
||||
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
|
||||
// block. This flag is set during connect when both the producer and
|
||||
// consumer are controlled by the application.
|
||||
bool mDequeueBufferCannotBlock;
|
||||
|
||||
// mDefaultBufferFormat can be set so it will override the buffer format
|
||||
// when it isn't specified in dequeueBuffer.
|
||||
uint32_t mDefaultBufferFormat;
|
||||
|
||||
// mDefaultWidth holds the default width of allocated buffers. It is used
|
||||
// in dequeueBuffer if a width and height of 0 are specified.
|
||||
int mDefaultWidth;
|
||||
|
||||
// mDefaultHeight holds the default height of allocated buffers. It is used
|
||||
// in dequeueBuffer if a width and height of 0 are specified.
|
||||
int mDefaultHeight;
|
||||
|
||||
// mDefaultMaxBufferCount is the default limit on the number of buffers that
|
||||
// will be allocated at one time. This default limit is set by the consumer.
|
||||
// The limit (as opposed to the default limit) may be overriden by the
|
||||
// producer.
|
||||
int mDefaultMaxBufferCount;
|
||||
|
||||
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
|
||||
// acquire at one time. It defaults to 1, and can be changed by the consumer
|
||||
// via setMaxAcquiredBufferCount, but this may only be done while no
|
||||
// producer is connected to the GonkBufferQueue. This value is used to derive
|
||||
// the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.
|
||||
int mMaxAcquiredBufferCount;
|
||||
|
||||
// mBufferHasBeenQueued is true once a buffer has been queued. It is reset
|
||||
// when something causes all buffers to be freed (e.g., changing the buffer
|
||||
// count).
|
||||
bool mBufferHasBeenQueued;
|
||||
|
||||
// mFrameCounter is the free running counter, incremented on every
|
||||
// successful queueBuffer call and buffer allocation.
|
||||
uint64_t mFrameCounter;
|
||||
|
||||
// mTransformHint is used to optimize for screen rotations.
|
||||
uint32_t mTransformHint;
|
||||
|
||||
// mSidebandStream is a handle to the sideband buffer stream, if any
|
||||
sp<NativeHandle> mSidebandStream;
|
||||
|
||||
// mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
|
||||
// releases mMutex while doing the allocation proper). Producers should not modify any of the
|
||||
// FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
|
||||
// false.
|
||||
bool mIsAllocating;
|
||||
|
||||
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
|
||||
// becomes false.
|
||||
mutable Condition mIsAllocatingCondition;
|
||||
}; // class GonkBufferQueueCore
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_BUFFERQUEUECOREDEFS_H
|
||||
#define NATIVEWINDOW_BUFFERQUEUECOREDEFS_H
|
||||
|
||||
#include "GonkBufferSlot.h"
|
||||
|
||||
namespace android {
|
||||
class GonkBufferQueueCore;
|
||||
|
||||
namespace GonkBufferQueueDefs {
|
||||
// GonkBufferQueue will keep track of at most this value of buffers.
|
||||
// Attempts at runtime to increase the number of buffers past this
|
||||
// will fail.
|
||||
enum { NUM_BUFFER_SLOTS = 64 };
|
||||
|
||||
typedef GonkBufferSlot SlotsType[NUM_BUFFER_SLOTS];
|
||||
} // namespace GonkBufferQueueDefs
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 "GonkBufferQueue"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
#define LOG_NDEBUG 0
|
||||
|
||||
#include "GonkBufferQueue.h"
|
||||
#include "GonkBufferQueueConsumer.h"
|
||||
#include "GonkBufferQueueCore.h"
|
||||
#include "GonkBufferQueueProducer.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener(
|
||||
const wp<ConsumerListener>& consumerListener):
|
||||
mConsumerListener(consumerListener) {}
|
||||
|
||||
GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
|
||||
|
||||
#if ANDROID_VERSION == 21
|
||||
void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onFrameAvailable();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable(const ::android::BufferItem& item) {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onFrameAvailable(item);
|
||||
}
|
||||
}
|
||||
|
||||
void GonkBufferQueue::ProxyConsumerListener::onFrameReplaced(const ::android::BufferItem& item) {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onFrameReplaced(item);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
}
|
||||
|
||||
void GonkBufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onSidebandStreamChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GonkBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
|
||||
sp<IGonkGraphicBufferConsumer>* outConsumer,
|
||||
const sp<IGraphicBufferAlloc>& allocator) {
|
||||
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
|
||||
"GonkBufferQueue: outProducer must not be NULL");
|
||||
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
|
||||
"GonkBufferQueue: outConsumer must not be NULL");
|
||||
|
||||
sp<GonkBufferQueueCore> core(new GonkBufferQueueCore(allocator));
|
||||
LOG_ALWAYS_FATAL_IF(core == NULL,
|
||||
"GonkBufferQueue: failed to create GonkBufferQueueCore");
|
||||
|
||||
sp<IGraphicBufferProducer> producer(new GonkBufferQueueProducer(core));
|
||||
LOG_ALWAYS_FATAL_IF(producer == NULL,
|
||||
"GonkBufferQueue: failed to create GonkBufferQueueProducer");
|
||||
|
||||
sp<IGonkGraphicBufferConsumer> consumer(new GonkBufferQueueConsumer(core));
|
||||
LOG_ALWAYS_FATAL_IF(consumer == NULL,
|
||||
"GonkBufferQueue: failed to create GonkBufferQueueConsumer");
|
||||
|
||||
*outProducer = producer;
|
||||
*outConsumer = consumer;
|
||||
}
|
||||
|
||||
}; // namespace android
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
|
||||
|
||||
#include "GonkBufferQueueDefs.h"
|
||||
#include "IGonkGraphicBufferConsumerLL.h"
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
|
||||
// These are only required to keep other parts of the framework with incomplete
|
||||
// dependencies building successfully
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkBufferQueue {
|
||||
public:
|
||||
// GonkBufferQueue will keep track of at most this value of buffers.
|
||||
// Attempts at runtime to increase the number of buffers past this will fail.
|
||||
enum { NUM_BUFFER_SLOTS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS };
|
||||
// Used as a placeholder slot# when the value isn't pointing to an existing buffer.
|
||||
enum { INVALID_BUFFER_SLOT = IGonkGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT };
|
||||
// Alias to <IGonkGraphicBufferConsumer.h> -- please scope from there in future code!
|
||||
enum {
|
||||
NO_BUFFER_AVAILABLE = IGonkGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
|
||||
PRESENT_LATER = IGonkGraphicBufferConsumer::PRESENT_LATER,
|
||||
};
|
||||
|
||||
// When in async mode we reserve two slots in order to guarantee that the
|
||||
// producer and consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// for backward source compatibility
|
||||
typedef ::android::ConsumerListener ConsumerListener;
|
||||
typedef IGonkGraphicBufferConsumer::BufferItem BufferItem;
|
||||
|
||||
// ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
|
||||
// reference to the actual consumer object. It forwards all calls to that
|
||||
// consumer object so long as it exists.
|
||||
//
|
||||
// This class exists to avoid having a circular reference between the
|
||||
// GonkBufferQueue object and the consumer object. The reason this can't be a weak
|
||||
// reference in the GonkBufferQueue class is because we're planning to expose the
|
||||
// consumer side of a GonkBufferQueue as a binder interface, which doesn't support
|
||||
// weak references.
|
||||
class ProxyConsumerListener : public BnConsumerListener {
|
||||
public:
|
||||
ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
|
||||
virtual ~ProxyConsumerListener();
|
||||
#if ANDROID_VERSION == 21
|
||||
virtual void onFrameAvailable();
|
||||
#else
|
||||
virtual void onFrameAvailable(const ::android::BufferItem& item);
|
||||
virtual void onFrameReplaced(const ::android::BufferItem& item);
|
||||
#endif
|
||||
virtual void onBuffersReleased();
|
||||
virtual void onSidebandStreamChanged();
|
||||
private:
|
||||
// mConsumerListener is a weak reference to the IConsumerListener. This is
|
||||
// the raison d'etre of ProxyConsumerListener.
|
||||
wp<ConsumerListener> mConsumerListener;
|
||||
};
|
||||
|
||||
// GonkBufferQueue manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allocator is used to allocate all the
|
||||
// needed gralloc buffers.
|
||||
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
|
||||
sp<IGonkGraphicBufferConsumer>* outConsumer,
|
||||
const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
|
||||
private:
|
||||
GonkBufferQueue(); // Create through createBufferQueue
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
|
|
@ -1,886 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
|
||||
#define LOG_TAG "GonkBufferQueueProducer"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "GonkBufferItem.h"
|
||||
#include "GonkBufferQueueCore.h"
|
||||
#include "GonkBufferQueueProducer.h"
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
#include "mozilla/layers/GrallocTextureClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
GonkBufferQueueProducer::GonkBufferQueueProducer(const sp<GonkBufferQueueCore>& core) :
|
||||
mCore(core),
|
||||
mSlots(core->mSlots),
|
||||
mConsumerName(),
|
||||
mSynchronousMode(true),
|
||||
mStickyTransform(0) {}
|
||||
|
||||
GonkBufferQueueProducer::~GonkBufferQueueProducer() {}
|
||||
|
||||
status_t GonkBufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("requestBuffer: slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("requestBuffer: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGE("requestBuffer: slot index %d out of range [0, %d)",
|
||||
slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
|
||||
ALOGE("requestBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mSlots[slot].mRequestBufferCalled = true;
|
||||
*buf = mSlots[slot].mGraphicBuffer;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::setBufferCount(int bufferCount) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("setBufferCount: count = %d", bufferCount);
|
||||
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("setBufferCount: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (bufferCount > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGE("setBufferCount: bufferCount %d too large (max %d)",
|
||||
bufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// There must be no dequeued buffers when changing the buffer count.
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == GonkBufferSlot::DEQUEUED) {
|
||||
ALOGE("setBufferCount: buffer owned by producer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferCount == 0) {
|
||||
mCore->mOverrideMaxBufferCount = 0;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false);
|
||||
if (bufferCount < minBufferSlots) {
|
||||
ALOGE("setBufferCount: requested buffer count %d is less than "
|
||||
"minimum %d", bufferCount, minBufferSlots);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Here we are guaranteed that the producer doesn't have any dequeued
|
||||
// buffers and will release all of its buffer references. We don't
|
||||
// clear the queue, however, so that currently queued buffers still
|
||||
// get displayed.
|
||||
mCore->freeAllBuffersLocked();
|
||||
mCore->mOverrideMaxBufferCount = bufferCount;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
listener = mCore->mConsumerListener;
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
|
||||
bool async, int* found, status_t* returnFlags) const {
|
||||
bool tryAgain = true;
|
||||
while (tryAgain) {
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("%s: GonkBufferQueue has been abandoned", caller);
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||
if (async && mCore->mOverrideMaxBufferCount) {
|
||||
// FIXME: Some drivers are manually setting the buffer count
|
||||
// (which they shouldn't), so we do this extra test here to
|
||||
// handle that case. This is TEMPORARY until we get this fixed.
|
||||
if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
|
||||
ALOGE("%s: async mode is invalid with buffer count override",
|
||||
caller);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Free up any buffers that are in slots beyond the max buffer count
|
||||
//for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
// assert(mSlots[s].mBufferState == GonkBufferSlot::FREE);
|
||||
// if (mSlots[s].mGraphicBuffer != NULL) {
|
||||
// mCore->freeBufferLocked(s);
|
||||
// *returnFlags |= RELEASE_ALL_BUFFERS;
|
||||
// }
|
||||
//}
|
||||
|
||||
// Look for a free buffer to give to the client
|
||||
*found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
int dequeuedCount = 0;
|
||||
int acquiredCount = 0;
|
||||
for (int s = 0; s < maxBufferCount; ++s) {
|
||||
switch (mSlots[s].mBufferState) {
|
||||
case GonkBufferSlot::DEQUEUED:
|
||||
++dequeuedCount;
|
||||
break;
|
||||
case GonkBufferSlot::ACQUIRED:
|
||||
++acquiredCount;
|
||||
break;
|
||||
case GonkBufferSlot::FREE:
|
||||
// We return the oldest of the free buffers to avoid
|
||||
// stalling the producer if possible, since the consumer
|
||||
// may still have pending reads of in-flight buffers
|
||||
if (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
|
||||
*found = s;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Producers are not allowed to dequeue more than one buffer if they
|
||||
// did not set a buffer count
|
||||
if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
|
||||
ALOGE("%s: can't dequeue multiple buffers without setting the "
|
||||
"buffer count", caller);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// See whether a buffer has been queued since the last
|
||||
// setBufferCount so we know whether to perform the min undequeued
|
||||
// buffers check below
|
||||
if (mCore->mBufferHasBeenQueued) {
|
||||
// Make sure the producer is not trying to dequeue more buffers
|
||||
// than allowed
|
||||
const int newUndequeuedCount =
|
||||
maxBufferCount - (dequeuedCount + 1);
|
||||
const int minUndequeuedCount =
|
||||
mCore->getMinUndequeuedBufferCountLocked(async);
|
||||
if (newUndequeuedCount < minUndequeuedCount) {
|
||||
ALOGE("%s: min undequeued buffer count (%d) exceeded "
|
||||
"(dequeued=%d undequeued=%d)",
|
||||
caller, minUndequeuedCount,
|
||||
dequeuedCount, newUndequeuedCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
}
|
||||
|
||||
// If we disconnect and reconnect quickly, we can be in a state where
|
||||
// our slots are empty but we have many buffers in the queue. This can
|
||||
// cause us to run out of memory if we outrun the consumer. Wait here if
|
||||
// it looks like we have too many buffers queued up.
|
||||
bool tooManyBuffers = mCore->mQueue.size()
|
||||
> static_cast<size_t>(maxBufferCount);
|
||||
if (tooManyBuffers) {
|
||||
ALOGV("%s: queue size is %zu, waiting", caller,
|
||||
mCore->mQueue.size());
|
||||
}
|
||||
|
||||
// If no buffer is found, or if the queue has too many buffers
|
||||
// outstanding, wait for a buffer to be acquired or released, or for the
|
||||
// max buffer count to change.
|
||||
tryAgain = (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) ||
|
||||
tooManyBuffers;
|
||||
if (tryAgain) {
|
||||
// Return an error if we're in non-blocking mode (producer and
|
||||
// consumer are controlled by the application).
|
||||
// However, the consumer is allowed to briefly acquire an extra
|
||||
// buffer (which could cause us to have to wait here), which is
|
||||
// okay, since it is only used to implement an atomic acquire +
|
||||
// release (e.g., in GLConsumer::updateTexImage())
|
||||
if (mCore->mDequeueBufferCannotBlock &&
|
||||
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
mCore->mDequeueCondition.wait(mCore->mMutex);
|
||||
}
|
||||
} // while (tryAgain)
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::dequeueBuffer(int *outSlot,
|
||||
sp<android::Fence> *outFence, bool async,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
|
||||
ATRACE_CALL();
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mConsumerName = mCore->mConsumerName;
|
||||
} // Autolock scope
|
||||
|
||||
ALOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
|
||||
async ? "true" : "false", width, height, format, usage);
|
||||
|
||||
if ((width && !height) || (!width && height)) {
|
||||
ALOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t returnFlags = NO_ERROR;
|
||||
// Reset slot
|
||||
*outSlot = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
|
||||
bool attachedByConsumer = false;
|
||||
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (format == 0) {
|
||||
format = mCore->mDefaultBufferFormat;
|
||||
}
|
||||
|
||||
// Enable the usage bits the consumer requested
|
||||
usage |= mCore->mConsumerUsageBits;
|
||||
|
||||
int found;
|
||||
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
ALOGE("dequeueBuffer: no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
|
||||
attachedByConsumer = mSlots[found].mAttachedByConsumer;
|
||||
|
||||
const bool useDefaultSize = !width && !height;
|
||||
if (useDefaultSize) {
|
||||
width = mCore->mDefaultWidth;
|
||||
height = mCore->mDefaultHeight;
|
||||
}
|
||||
|
||||
mSlots[found].mBufferState = GonkBufferSlot::DEQUEUED;
|
||||
|
||||
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
|
||||
if ((buffer == NULL) ||
|
||||
(static_cast<uint32_t>(buffer->width) != width) ||
|
||||
(static_cast<uint32_t>(buffer->height) != height) ||
|
||||
(static_cast<uint32_t>(buffer->format) != format) ||
|
||||
((static_cast<uint32_t>(buffer->usage) & usage) != usage))
|
||||
{
|
||||
mSlots[found].mAcquireCalled = false;
|
||||
mSlots[found].mGraphicBuffer = NULL;
|
||||
mSlots[found].mRequestBufferCalled = false;
|
||||
mSlots[found].mFence = Fence::NO_FENCE;
|
||||
|
||||
if (mSlots[found].mTextureClient) {
|
||||
mSlots[found].mTextureClient->ClearRecycleCallback();
|
||||
// release TextureClient in ImageBridge thread
|
||||
RefPtr<TextureClientReleaseTask> task =
|
||||
MakeAndAddRef<TextureClientReleaseTask>(mSlots[found].mTextureClient);
|
||||
mSlots[found].mTextureClient = NULL;
|
||||
ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
returnFlags |= BUFFER_NEEDS_REALLOCATION;
|
||||
}
|
||||
|
||||
if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
|
||||
ALOGE("dequeueBuffer: about to return a NULL fence - "
|
||||
"slot=%d w=%d h=%d format=%u",
|
||||
found, buffer->width, buffer->height, buffer->format);
|
||||
}
|
||||
|
||||
*outFence = mSlots[found].mFence;
|
||||
mSlots[found].mFence = Fence::NO_FENCE;
|
||||
} // Autolock scope
|
||||
|
||||
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
|
||||
RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
|
||||
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
|
||||
GrallocTextureData* texData = GrallocTextureData::Create(IntSize(width,height), format,
|
||||
gfx::BackendType::NONE,
|
||||
usage, allocator);
|
||||
if (!texData) {
|
||||
ALOGE("dequeueBuffer: failed to alloc gralloc buffer");
|
||||
return -ENOMEM;
|
||||
}
|
||||
RefPtr<TextureClient> textureClient = TextureClient::CreateWithData(
|
||||
texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator);
|
||||
|
||||
sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
|
||||
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
mSlots[*outSlot].mFrameNumber = UINT32_MAX;
|
||||
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
|
||||
mSlots[*outSlot].mTextureClient = textureClient;
|
||||
} // Autolock scope
|
||||
}
|
||||
|
||||
if (attachedByConsumer) {
|
||||
returnFlags |= BUFFER_NEEDS_REALLOCATION;
|
||||
}
|
||||
|
||||
ALOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
|
||||
*outSlot,
|
||||
mSlots[*outSlot].mFrameNumber,
|
||||
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
|
||||
|
||||
return returnFlags;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::detachBuffer(int slot) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
ALOGV("detachBuffer(P): slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("detachBuffer(P): GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGE("detachBuffer(P): slot index %d out of range [0, %d)",
|
||||
slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
|
||||
ALOGE("detachBuffer(P): slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
} else if (!mSlots[slot].mRequestBufferCalled) {
|
||||
ALOGE("detachBuffer(P): buffer in slot %d has not been requested",
|
||||
slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->freeBufferLocked(slot);
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||
sp<Fence>* outFence) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outBuffer == NULL) {
|
||||
ALOGE("detachNextBuffer: outBuffer must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (outFence == NULL) {
|
||||
ALOGE("detachNextBuffer: outFence must not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("detachNextBuffer: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
// Find the oldest valid slot
|
||||
int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == GonkBufferSlot::FREE &&
|
||||
mSlots[s].mGraphicBuffer != NULL) {
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
|
||||
found = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
ALOGV("detachNextBuffer detached slot %d", found);
|
||||
|
||||
*outBuffer = mSlots[found].mGraphicBuffer;
|
||||
*outFence = mSlots[found].mFence;
|
||||
mCore->freeBufferLocked(found);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::attachBuffer(int* outSlot,
|
||||
const sp<android::GraphicBuffer>& buffer) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlot == NULL) {
|
||||
ALOGE("attachBuffer(P): outSlot must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (buffer == NULL) {
|
||||
ALOGE("attachBuffer(P): cannot attach NULL buffer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
status_t returnFlags = NO_ERROR;
|
||||
int found;
|
||||
// TODO: Should we provide an async flag to attachBuffer? It seems
|
||||
// unlikely that buffers which we are attaching to a GonkBufferQueue will
|
||||
// be asynchronous (droppable), but it may not be impossible.
|
||||
status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
ALOGE("attachBuffer(P): no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
ATRACE_BUFFER_INDEX(*outSlot);
|
||||
ALOGV("attachBuffer(P): returning slot %d flags=%#x",
|
||||
*outSlot, returnFlags);
|
||||
|
||||
mSlots[*outSlot].mGraphicBuffer = buffer;
|
||||
mSlots[*outSlot].mBufferState = GonkBufferSlot::DEQUEUED;
|
||||
mSlots[*outSlot].mFence = Fence::NO_FENCE;
|
||||
mSlots[*outSlot].mRequestBufferCalled = true;
|
||||
|
||||
return returnFlags;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::setSynchronousMode(bool enabled) {
|
||||
ALOGV("setSynchronousMode: enabled=%d", enabled);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("setSynchronousMode: BufferQueue has been abandoned!");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (mSynchronousMode != enabled) {
|
||||
mSynchronousMode = enabled;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::queueBuffer(int slot,
|
||||
const QueueBufferInput &input, QueueBufferOutput *output) {
|
||||
ATRACE_CALL();
|
||||
|
||||
int64_t timestamp;
|
||||
bool isAutoTimestamp;
|
||||
Rect crop;
|
||||
int scalingMode;
|
||||
uint32_t transform;
|
||||
uint32_t stickyTransform;
|
||||
bool async;
|
||||
sp<Fence> fence;
|
||||
input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
|
||||
&async, &fence, &stickyTransform);
|
||||
|
||||
if (fence == NULL) {
|
||||
ALOGE("queueBuffer: fence is NULL");
|
||||
// Temporary workaround for b/17946343: soldier-on instead of returning an error. This
|
||||
// prevents the client from dying, at the risk of visible corruption due to hwcomposer
|
||||
// reading the buffer before the producer is done rendering it. Unless the buffer is the
|
||||
// last frame of an animation, the corruption will be transient.
|
||||
fence = Fence::NO_FENCE;
|
||||
// return BAD_VALUE;
|
||||
}
|
||||
|
||||
switch (scalingMode) {
|
||||
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
|
||||
case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
|
||||
break;
|
||||
default:
|
||||
ALOGE("queueBuffer: unknown scaling mode %d", scalingMode);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
GonkBufferItem item;
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("queueBuffer: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||
if (async && mCore->mOverrideMaxBufferCount) {
|
||||
// FIXME: Some drivers are manually setting the buffer count
|
||||
// (which they shouldn't), so we do this extra test here to
|
||||
// handle that case. This is TEMPORARY until we get this fixed.
|
||||
if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
|
||||
ALOGE("queueBuffer: async mode is invalid with "
|
||||
"buffer count override");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= maxBufferCount) {
|
||||
ALOGE("queueBuffer: slot index %d out of range [0, %d)",
|
||||
slot, maxBufferCount);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
|
||||
ALOGE("queueBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
} else if (!mSlots[slot].mRequestBufferCalled) {
|
||||
ALOGE("queueBuffer: slot %d was queued without requesting "
|
||||
"a buffer", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ALOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64
|
||||
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
|
||||
slot, mCore->mFrameCounter + 1, timestamp,
|
||||
crop.left, crop.top, crop.right, crop.bottom,
|
||||
transform, GonkBufferItem::scalingModeName(scalingMode));
|
||||
|
||||
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
|
||||
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
|
||||
Rect croppedRect;
|
||||
crop.intersect(bufferRect, &croppedRect);
|
||||
if (croppedRect != crop) {
|
||||
ALOGE("queueBuffer: crop rect is not contained within the "
|
||||
"buffer in slot %d", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mSlots[slot].mFence = fence;
|
||||
mSlots[slot].mBufferState = GonkBufferSlot::QUEUED;
|
||||
++mCore->mFrameCounter;
|
||||
mSlots[slot].mFrameNumber = mCore->mFrameCounter;
|
||||
|
||||
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
|
||||
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
|
||||
item.mCrop = crop;
|
||||
item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
|
||||
item.mTransformToDisplayInverse =
|
||||
bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
|
||||
item.mScalingMode = scalingMode;
|
||||
item.mTimestamp = timestamp;
|
||||
item.mIsAutoTimestamp = isAutoTimestamp;
|
||||
item.mFrameNumber = mCore->mFrameCounter;
|
||||
item.mSlot = slot;
|
||||
item.mFence = fence;
|
||||
item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
|
||||
|
||||
mStickyTransform = stickyTransform;
|
||||
|
||||
if (mCore->mQueue.empty()) {
|
||||
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
|
||||
// and simply queue this buffer
|
||||
mCore->mQueue.push_back(item);
|
||||
listener = mCore->mConsumerListener;
|
||||
} else {
|
||||
// When the queue is not empty, we need to look at the front buffer
|
||||
// state to see if we need to replace it
|
||||
GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
|
||||
if (front->mIsDroppable || !mSynchronousMode) {
|
||||
// If the front queued buffer is still being tracked, we first
|
||||
// mark it as freed
|
||||
if (mCore->stillTracking(front)) {
|
||||
mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE;
|
||||
// Reset the frame number of the freed buffer so that it is
|
||||
// the first in line to be dequeued again
|
||||
mSlots[front->mSlot].mFrameNumber = 0;
|
||||
}
|
||||
// Overwrite the droppable buffer with the incoming one
|
||||
*front = item;
|
||||
listener = mCore->mConsumerListener;
|
||||
} else {
|
||||
mCore->mQueue.push_back(item);
|
||||
listener = mCore->mConsumerListener;
|
||||
}
|
||||
}
|
||||
|
||||
mCore->mBufferHasBeenQueued = true;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
|
||||
mCore->mTransformHint, mCore->mQueue.size());
|
||||
|
||||
item.mGraphicBuffer.clear();
|
||||
item.mSlot = GonkBufferItem::INVALID_BUFFER_SLOT;
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
#if ANDROID_VERSION == 21
|
||||
listener->onFrameAvailable();
|
||||
#else
|
||||
listener->onFrameAvailable(reinterpret_cast<::android::BufferItem&>(item));
|
||||
#endif
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void GonkBufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("cancelBuffer: slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("cancelBuffer: GonkBufferQueue has been abandoned");
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
ALOGE("cancelBuffer: slot index %d out of range [0, %d)",
|
||||
slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return;
|
||||
} else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
|
||||
ALOGE("cancelBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return;
|
||||
} else if (fence == NULL) {
|
||||
ALOGE("cancelBuffer: fence is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mSlots[slot].mBufferState = GonkBufferSlot::FREE;
|
||||
mSlots[slot].mFrameNumber = 0;
|
||||
mSlots[slot].mFence = fence;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
}
|
||||
|
||||
int GonkBufferQueueProducer::query(int what, int *outValue) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (outValue == NULL) {
|
||||
ALOGE("query: outValue was NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("query: GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
int value;
|
||||
switch (what) {
|
||||
case NATIVE_WINDOW_WIDTH:
|
||||
value = mCore->mDefaultWidth;
|
||||
break;
|
||||
case NATIVE_WINDOW_HEIGHT:
|
||||
value = mCore->mDefaultHeight;
|
||||
break;
|
||||
case NATIVE_WINDOW_FORMAT:
|
||||
value = mCore->mDefaultBufferFormat;
|
||||
break;
|
||||
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
|
||||
value = mCore->getMinUndequeuedBufferCountLocked(false);
|
||||
break;
|
||||
case NATIVE_WINDOW_STICKY_TRANSFORM:
|
||||
value = static_cast<int>(mStickyTransform);
|
||||
break;
|
||||
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
|
||||
value = (mCore->mQueue.size() > 1);
|
||||
break;
|
||||
case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
|
||||
value = mCore->mConsumerUsageBits;
|
||||
break;
|
||||
default:
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ALOGV("query: %d? %d", what, value);
|
||||
*outValue = value;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput *output) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mConsumerName = mCore->mConsumerName;
|
||||
ALOGV("connect(P): api=%d producerControlledByApp=%s", api,
|
||||
producerControlledByApp ? "true" : "false");
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
ALOGE("connect(P): GonkBufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (mCore->mConsumerListener == NULL) {
|
||||
ALOGE("connect(P): GonkBufferQueue has no consumer");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (output == NULL) {
|
||||
ALOGE("connect(P): output was NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) {
|
||||
ALOGE("connect(P): already connected (cur=%d req=%d)", mCore->mConnectedApi,
|
||||
api);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int status = NO_ERROR;
|
||||
switch (api) {
|
||||
case NATIVE_WINDOW_API_EGL:
|
||||
case NATIVE_WINDOW_API_CPU:
|
||||
case NATIVE_WINDOW_API_MEDIA:
|
||||
case NATIVE_WINDOW_API_CAMERA:
|
||||
mCore->mConnectedApi = api;
|
||||
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
|
||||
mCore->mTransformHint, mCore->mQueue.size());
|
||||
|
||||
// Set up a death notification so that we can disconnect
|
||||
// automatically if the remote producer dies
|
||||
if (listener != NULL &&
|
||||
listener->asBinder()->remoteBinder() != NULL) {
|
||||
status = listener->asBinder()->linkToDeath(
|
||||
static_cast<IBinder::DeathRecipient*>(this));
|
||||
if (status != NO_ERROR) {
|
||||
ALOGE("connect(P): linkToDeath failed: %s (%d)",
|
||||
strerror(-status), status);
|
||||
}
|
||||
}
|
||||
mCore->mConnectedProducerListener = listener;
|
||||
break;
|
||||
default:
|
||||
ALOGE("connect(P): unknown API %d", api);
|
||||
status = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
mCore->mBufferHasBeenQueued = false;
|
||||
mCore->mDequeueBufferCannotBlock =
|
||||
mCore->mConsumerControlledByApp && producerControlledByApp;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::disconnect(int api) {
|
||||
ATRACE_CALL();
|
||||
ALOGV("disconnect(P): api %d", api);
|
||||
|
||||
int status = NO_ERROR;
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
// It's not really an error to disconnect after the surface has
|
||||
// been abandoned; it should just be a no-op.
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
switch (api) {
|
||||
case NATIVE_WINDOW_API_EGL:
|
||||
case NATIVE_WINDOW_API_CPU:
|
||||
case NATIVE_WINDOW_API_MEDIA:
|
||||
case NATIVE_WINDOW_API_CAMERA:
|
||||
if (mCore->mConnectedApi == api) {
|
||||
mCore->freeAllBuffersLocked();
|
||||
mCore->mConnectedApi = GonkBufferQueueCore::NO_CONNECTED_API;
|
||||
mCore->mSidebandStream.clear();
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
listener = mCore->mConsumerListener;
|
||||
} else {
|
||||
ALOGE("disconnect(P): connected to another API "
|
||||
"(cur=%d req=%d)", mCore->mConnectedApi, api);
|
||||
status = BAD_VALUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ALOGE("disconnect(P): unknown API %d", api);
|
||||
status = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t GonkBufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
void GonkBufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
||||
uint32_t height, uint32_t format, uint32_t usage) {
|
||||
ALOGE("allocateBuffers: no op");
|
||||
}
|
||||
|
||||
void GonkBufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
|
||||
// If we're here, it means that a producer we were connected to died.
|
||||
// We're guaranteed that we are still connected to it because we remove
|
||||
// this callback upon disconnect. It's therefore safe to read mConnectedApi
|
||||
// without synchronization here.
|
||||
int api = mCore->mConnectedApi;
|
||||
disconnect(api);
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,216 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* 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 NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H
|
||||
#define NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H
|
||||
|
||||
#include "GonkBufferQueueDefs.h"
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkBufferQueueProducer : public BnGraphicBufferProducer,
|
||||
private IBinder::DeathRecipient {
|
||||
public:
|
||||
friend class GonkBufferQueue; // Needed to access binderDied
|
||||
|
||||
GonkBufferQueueProducer(const sp<GonkBufferQueueCore>& core);
|
||||
virtual ~GonkBufferQueueProducer();
|
||||
|
||||
// requestBuffer returns the GraphicBuffer for slot N.
|
||||
//
|
||||
// In normal operation, this is called the first time slot N is returned
|
||||
// by dequeueBuffer. It must be called again if dequeueBuffer returns
|
||||
// flags indicating that previously-returned buffers are no longer valid.
|
||||
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
|
||||
|
||||
// setBufferCount updates the number of available buffer slots. If this
|
||||
// method succeeds, buffer slots will be both unallocated and owned by
|
||||
// the GonkBufferQueue object (i.e. they are not owned by the producer or
|
||||
// consumer).
|
||||
//
|
||||
// This will fail if the producer has dequeued any buffers, or if
|
||||
// bufferCount is invalid. bufferCount must generally be a value
|
||||
// between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS
|
||||
// (inclusive). It may also be set to zero (the default) to indicate
|
||||
// that the producer does not wish to set a value. The minimum value
|
||||
// can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
|
||||
// ...).
|
||||
//
|
||||
// This may only be called by the producer. The consumer will be told
|
||||
// to discard buffers through the onBuffersReleased callback.
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
|
||||
// dequeueBuffer gets the next buffer slot index for the producer to use.
|
||||
// If a buffer slot is available then that slot index is written to the
|
||||
// location pointed to by the buf argument and a status of OK is returned.
|
||||
// If no slot is available then a status of -EBUSY is returned and buf is
|
||||
// unmodified.
|
||||
//
|
||||
// The outFence parameter will be updated to hold the fence associated with
|
||||
// the buffer. The contents of the buffer must not be overwritten until the
|
||||
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
|
||||
// written immediately.
|
||||
//
|
||||
// The width and height parameters must be no greater than the minimum of
|
||||
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
|
||||
// An error due to invalid dimensions might not be reported until
|
||||
// updateTexImage() is called. If width and height are both zero, the
|
||||
// default values specified by setDefaultBufferSize() are used instead.
|
||||
//
|
||||
// The pixel formats are enumerated in graphics.h, e.g.
|
||||
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
|
||||
// will be used.
|
||||
//
|
||||
// The usage argument specifies gralloc buffer usage flags. The values
|
||||
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
|
||||
// will be merged with the usage flags specified by setConsumerUsageBits.
|
||||
//
|
||||
// The return value may be a negative error value or a non-negative
|
||||
// collection of flags. If the flags are set, the return values are
|
||||
// valid, but additional actions must be performed.
|
||||
//
|
||||
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
|
||||
// producer must discard cached GraphicBuffer references for the slot
|
||||
// returned in buf.
|
||||
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
|
||||
// must discard cached GraphicBuffer references for all slots.
|
||||
//
|
||||
// In both cases, the producer will need to call requestBuffer to get a
|
||||
// GraphicBuffer handle for the returned slot.
|
||||
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
|
||||
|
||||
// See IGraphicBufferProducer::detachBuffer
|
||||
virtual status_t detachBuffer(int slot);
|
||||
|
||||
// See IGraphicBufferProducer::detachNextBuffer
|
||||
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||
sp<Fence>* outFence);
|
||||
|
||||
// See IGraphicBufferProducer::attachBuffer
|
||||
virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
|
||||
|
||||
// queueBuffer returns a filled buffer to the GonkBufferQueue.
|
||||
//
|
||||
// Additional data is provided in the QueueBufferInput struct. Notably,
|
||||
// a timestamp must be provided for the buffer. The timestamp is in
|
||||
// nanoseconds, and must be monotonically increasing. Its other semantics
|
||||
// (zero point, etc) are producer-specific and should be documented by the
|
||||
// producer.
|
||||
//
|
||||
// The caller may provide a fence that signals when all rendering
|
||||
// operations have completed. Alternatively, NO_FENCE may be used,
|
||||
// indicating that the buffer is ready immediately.
|
||||
//
|
||||
// Some values are returned in the output struct: the current settings
|
||||
// for default width and height, the current transform hint, and the
|
||||
// number of queued buffers.
|
||||
virtual status_t queueBuffer(int slot,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
|
||||
// cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
|
||||
// queue it for use by the consumer.
|
||||
//
|
||||
// The buffer will not be overwritten until the fence signals. The fence
|
||||
// will usually be the one obtained from dequeueBuffer.
|
||||
virtual void cancelBuffer(int slot, const sp<Fence>& fence);
|
||||
|
||||
// Query native window attributes. The "what" values are enumerated in
|
||||
// window.h (e.g. NATIVE_WINDOW_FORMAT).
|
||||
virtual int query(int what, int* outValue);
|
||||
|
||||
// connect attempts to connect a producer API to the GonkBufferQueue. This
|
||||
// must be called before any other IGraphicBufferProducer methods are
|
||||
// called except for getAllocator. A consumer must already be connected.
|
||||
//
|
||||
// This method will fail if connect was previously called on the
|
||||
// GonkBufferQueue and no corresponding disconnect call was made (i.e. if
|
||||
// it's still connected to a producer).
|
||||
//
|
||||
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
|
||||
virtual status_t connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput* output);
|
||||
|
||||
// disconnect attempts to disconnect a producer API from the GonkBufferQueue.
|
||||
// Calling this method will cause any subsequent calls to other
|
||||
// IGraphicBufferProducer methods to fail except for getAllocator and connect.
|
||||
// Successfully calling connect after this will allow the other methods to
|
||||
// succeed again.
|
||||
//
|
||||
// This method will fail if the the GonkBufferQueue is not currently
|
||||
// connected to the specified producer API.
|
||||
virtual status_t disconnect(int api);
|
||||
|
||||
// Attaches a sideband buffer stream to the IGraphicBufferProducer.
|
||||
//
|
||||
// A sideband stream is a device-specific mechanism for passing buffers
|
||||
// from the producer to the consumer without using dequeueBuffer/
|
||||
// queueBuffer. If a sideband stream is present, the consumer can choose
|
||||
// whether to acquire buffers from the sideband stream or from the queued
|
||||
// buffers.
|
||||
//
|
||||
// Passing NULL or a different stream handle will detach the previous
|
||||
// handle if any.
|
||||
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
|
||||
|
||||
// See IGraphicBufferProducer::allocateBuffers
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t usage);
|
||||
|
||||
// setSynchronousMode sets whether dequeueBuffer is synchronous or
|
||||
// asynchronous. In synchronous mode, dequeueBuffer blocks until
|
||||
// a buffer is available, the currently bound buffer can be dequeued and
|
||||
// queued buffers will be acquired in order. In asynchronous mode,
|
||||
// a queued buffer may be replaced by a subsequently queued buffer.
|
||||
//
|
||||
// The default mode is synchronous.
|
||||
// This should be called only during initialization.
|
||||
virtual status_t setSynchronousMode(bool enabled);
|
||||
|
||||
private:
|
||||
// This is required by the IBinder::DeathRecipient interface
|
||||
virtual void binderDied(const wp<IBinder>& who);
|
||||
|
||||
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
|
||||
// block if there are no available slots and we are not in non-blocking
|
||||
// mode (producer and consumer controlled by the application). If it blocks,
|
||||
// it will release mCore->mMutex while blocked so that other operations on
|
||||
// the GonkBufferQueue may succeed.
|
||||
status_t waitForFreeSlotThenRelock(const char* caller, bool async,
|
||||
int* found, status_t* returnFlags) const;
|
||||
|
||||
sp<GonkBufferQueueCore> mCore;
|
||||
|
||||
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
|
||||
GonkBufferQueueDefs::SlotsType& mSlots;
|
||||
|
||||
// This is a cached copy of the name stored in the GonkBufferQueueCore.
|
||||
// It's updated during connect and dequeueBuffer (which should catch
|
||||
// most updates).
|
||||
String8 mConsumerName;
|
||||
|
||||
// mSynchronousMode whether we're in synchronous mode or not
|
||||
bool mSynchronousMode;
|
||||
|
||||
uint32_t mStickyTransform;
|
||||
|
||||
}; // class GonkBufferQueueProducer
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче