diff options
164 files changed, 19054 insertions, 4701 deletions
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp index d650721327..78b1007f5e 100644 --- a/cmds/surfaceflinger/main_surfaceflinger.cpp +++ b/cmds/surfaceflinger/main_surfaceflinger.cpp @@ -1,18 +1,25 @@ -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> -#include <binder/IServiceManager.h> -#include <utils/Log.h> +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <binder/BinderService.h> #include <SurfaceFlinger.h> using namespace android; -int main(int argc, char** argv) -{ - sp<ProcessState> proc(ProcessState::self()); - sp<IServiceManager> sm = defaultServiceManager(); - LOGI("ServiceManager: %p", sm.get()); - SurfaceFlinger::instantiate(); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); +int main(int argc, char** argv) { + SurfaceFlinger::publishAndJoinThreadPool(); + return 0; } diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h new file mode 100644 index 0000000000..2316fef788 --- /dev/null +++ b/include/binder/BinderService.h @@ -0,0 +1,60 @@ +/* + * 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_BINDER_SERVICE_H +#define ANDROID_BINDER_SERVICE_H + +#include <stdint.h> + +#include <utils/Errors.h> +#include <utils/String16.h> + +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +// --------------------------------------------------------------------------- +namespace android { + +template<typename SERVICE> +class BinderService +{ +public: + static status_t publish() { + sp<IServiceManager> sm(defaultServiceManager()); + return sm->addService(String16(SERVICE::getServiceName()), new SERVICE()); + } + + static void publishAndJoinThreadPool() { + sp<ProcessState> proc(ProcessState::self()); + sp<IServiceManager> sm(defaultServiceManager()); + sm->addService(String16(SERVICE::getServiceName()), new SERVICE()); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + } + + static void instantiate() { publish(); } + + static status_t shutdown() { + return NO_ERROR; + } +}; + + +}; // namespace android +// --------------------------------------------------------------------------- +#endif // ANDROID_BINDER_SERVICE_H diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h index 3ab985d785..04e24d29a6 100644 --- a/include/binder/IPCThreadState.h +++ b/include/binder/IPCThreadState.h @@ -40,6 +40,9 @@ public: int getCallingPid(); int getCallingUid(); + + void setStrictModePolicy(int32_t policy); + int32_t getStrictModePolicy() const; int64_t clearCallingIdentity(); void restoreCallingIdentity(int64_t token); @@ -109,8 +112,9 @@ private: status_t mLastError; pid_t mCallingPid; uid_t mCallingUid; + int32_t mStrictModePolicy; }; - + }; // namespace android // --------------------------------------------------------------------------- diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index 66c34b279f..32c9a1d5a9 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -26,11 +26,12 @@ // --------------------------------------------------------------------------- namespace android { +class Flattenable; class IBinder; +class IPCThreadState; class ProcessState; class String8; class TextOutput; -class Flattenable; struct flat_binder_object; // defined in support_p/binder_module.h @@ -56,9 +57,19 @@ public: bool hasFileDescriptors() const; + // Writes the RPC header. status_t writeInterfaceToken(const String16& interface); - bool enforceInterface(const String16& interface) const; - bool checkInterface(IBinder*) const; + + // Parses the RPC header, returning true if the interface name + // in the header matches the expected interface from the caller. + // + // Additionally, enforceInterface does part of the work of + // propagating the StrictMode policy mask, populating the current + // IPCThreadState, which as an optimization may optionally be + // passed in. + bool enforceInterface(const String16& interface, + IPCThreadState* threadState = NULL) const; + bool checkInterface(IBinder*) const; void freeData(); @@ -100,6 +111,11 @@ public: status_t writeObject(const flat_binder_object& val, bool nullMetaData); + // Like Parcel.java's writeNoException(). Just writes a zero int32. + // Currently the native implementation doesn't do any of the StrictMode + // stack gathering and serialization that the Java implementation does. + status_t writeNoException(); + void remove(size_t start, size_t amt); status_t read(void* outData, size_t len) const; @@ -122,7 +138,14 @@ public: sp<IBinder> readStrongBinder() const; wp<IBinder> readWeakBinder() const; status_t read(Flattenable& val) const; - + + // Like Parcel.java's readExceptionCode(). Reads the first int32 + // off of a Parcel's header, returning 0 or the negative error + // code on exceptions, but also deals with skipping over rich + // response headers. Callers should use this to read & parse the + // response headers rather than doing it by hand. + int32_t readExceptionCode() const; + // Retrieve native_handle from the parcel. This returns a copy of the // parcel's native_handle (the caller takes ownership). The caller // must free the native_handle with native_handle_close() and diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h new file mode 100644 index 0000000000..ed4e4cc72f --- /dev/null +++ b/include/gui/ISensorEventConnection.h @@ -0,0 +1,57 @@ +/* + * 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_GUI_ISENSOR_EVENT_CONNECTION_H +#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <binder/IInterface.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class SensorChannel; + +class ISensorEventConnection : public IInterface +{ +public: + DECLARE_META_INTERFACE(SensorEventConnection); + + virtual sp<SensorChannel> getSensorChannel() const = 0; + virtual status_t enableDisable(int handle, bool enabled) = 0; + virtual status_t setEventRate(int handle, nsecs_t ns) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSensorEventConnection : public BnInterface<ISensorEventConnection> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h new file mode 100644 index 0000000000..9c8afc5a08 --- /dev/null +++ b/include/gui/ISensorServer.h @@ -0,0 +1,57 @@ +/* + * 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_GUI_ISENSORSERVER_H +#define ANDROID_GUI_ISENSORSERVER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <binder/IInterface.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class Sensor; +class ISensorEventConnection; + +class ISensorServer : public IInterface +{ +public: + DECLARE_META_INTERFACE(SensorServer); + + virtual Vector<Sensor> getSensorList() = 0; + virtual sp<ISensorEventConnection> createSensorEventConnection() = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSensorServer : public BnInterface<ISensorServer> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_ISENSORSERVER_H diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h new file mode 100644 index 0000000000..2de07b1325 --- /dev/null +++ b/include/gui/Sensor.h @@ -0,0 +1,91 @@ +/* + * 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_GUI_SENSOR_H +#define ANDROID_GUI_SENSOR_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/Flattenable.h> + +#include <hardware/sensors.h> + +#include <android/sensor.h> + +// ---------------------------------------------------------------------------- +// Concrete types for the NDK +struct ASensor { }; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class Parcel; + +// ---------------------------------------------------------------------------- + +class Sensor : public ASensor, public Flattenable +{ +public: + enum { + TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, + TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, + TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, + TYPE_LIGHT = ASENSOR_TYPE_LIGHT, + TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY + }; + + Sensor(); + Sensor(struct sensor_t const* hwSensor); + virtual ~Sensor(); + + const String8& getName() const; + const String8& getVendor() const; + int32_t getHandle() const; + int32_t getType() const; + float getMinValue() const; + float getMaxValue() const; + float getResolution() const; + float getPowerUsage() const; + int32_t getMinDelay() const; + + // Flattenable interface + virtual size_t getFlattenedSize() const; + virtual size_t getFdCount() const; + virtual status_t flatten(void* buffer, size_t size, + int fds[], size_t count) const; + virtual status_t unflatten(void const* buffer, size_t size, + int fds[], size_t count); + +private: + String8 mName; + String8 mVendor; + int32_t mHandle; + int32_t mType; + float mMinValue; + float mMaxValue; + float mResolution; + float mPower; + int32_t mMinDelay; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_SENSOR_H diff --git a/services/surfaceflinger/Tokenizer.h b/include/gui/SensorChannel.h index 6b3057d240..bb546186a6 100644 --- a/services/surfaceflinger/Tokenizer.h +++ b/include/gui/SensorChannel.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * 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. @@ -14,44 +14,40 @@ * limitations under the License. */ -#ifndef ANDROID_TOKENIZER_H -#define ANDROID_TOKENIZER_H +#ifndef ANDROID_GUI_SENSOR_CHANNEL_H +#define ANDROID_GUI_SENSOR_CHANNEL_H + +#include <stdint.h> +#include <sys/types.h> -#include <utils/Vector.h> #include <utils/Errors.h> +#include <utils/RefBase.h> -// ---------------------------------------------------------------------------- namespace android { +// ---------------------------------------------------------------------------- +class Parcel; -class Tokenizer +class SensorChannel : public RefBase { public: - Tokenizer(); - Tokenizer(const Tokenizer& other); - ~Tokenizer(); - - uint32_t acquire(); - status_t reserve(uint32_t token); - status_t release(uint32_t token); - bool isAcquired(uint32_t token) const; - - void dump() const; - - struct run_t { - run_t() {}; - run_t(uint32_t f, uint32_t l) : first(f), length(l) {} - uint32_t first; - uint32_t length; - }; + + SensorChannel(); + SensorChannel(const Parcel& data); + virtual ~SensorChannel(); + + int getFd() const; + ssize_t write(void const* vaddr, size_t size); + ssize_t read(void* vaddr, size_t size); + + status_t writeToParcel(Parcel* reply) const; + private: - ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; - ssize_t _insertTokenAt(uint32_t token, size_t index); - Vector<run_t> mRanges; + int mSendFd; + mutable int mReceiveFd; }; -}; // namespace android - // ---------------------------------------------------------------------------- +}; // namespace android -#endif // ANDROID_TOKENIZER_H +#endif // ANDROID_GUI_SENSOR_CHANNEL_H diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h new file mode 100644 index 0000000000..6581ae3092 --- /dev/null +++ b/include/gui/SensorEventQueue.h @@ -0,0 +1,82 @@ +/* + * 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_SENSOR_EVENT_QUEUE_H +#define ANDROID_SENSOR_EVENT_QUEUE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include <gui/SensorChannel.h> + +// ---------------------------------------------------------------------------- + +struct ALooper; +struct ASensorEvent; + +// Concrete types for the NDK +struct ASensorEventQueue { + ALooper* looper; +}; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class ISensorEventConnection; +class Sensor; +class PollLoop; + +// ---------------------------------------------------------------------------- + +class SensorEventQueue : public ASensorEventQueue, public RefBase +{ +public: + SensorEventQueue(const sp<ISensorEventConnection>& connection); + virtual ~SensorEventQueue(); + virtual void onFirstRef(); + + int getFd() const; + ssize_t write(ASensorEvent const* events, size_t numEvents); + ssize_t read(ASensorEvent* events, size_t numEvents); + + status_t waitForEvent() const; + status_t wake() const; + + status_t enableSensor(Sensor const* sensor) const; + status_t disableSensor(Sensor const* sensor) const; + status_t setEventRate(Sensor const* sensor, nsecs_t ns) const; + + // these are here only to support SensorManager.java + status_t enableSensor(int32_t handle, int32_t us) const; + status_t disableSensor(int32_t handle) const; + +private: + sp<PollLoop> getPollLoop() const; + sp<ISensorEventConnection> mSensorEventConnection; + sp<SensorChannel> mSensorChannel; + mutable Mutex mLock; + mutable sp<PollLoop> mPollLoop; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SENSOR_EVENT_QUEUE_H diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h new file mode 100644 index 0000000000..e1b1a7be45 --- /dev/null +++ b/include/gui/SensorManager.h @@ -0,0 +1,63 @@ +/* + * 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_GUI_SENSOR_MANAGER_H +#define ANDROID_GUI_SENSOR_MANAGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Singleton.h> +#include <utils/Vector.h> + +#include <gui/SensorEventQueue.h> + +// ---------------------------------------------------------------------------- +// Concrete types for the NDK +struct ASensorManager { }; + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class ISensorServer; +class Sensor; +class SensorEventQueue; + +// ---------------------------------------------------------------------------- + +class SensorManager : public ASensorManager, public Singleton<SensorManager> +{ +public: + SensorManager(); + ~SensorManager(); + + ssize_t getSensorList(Sensor const* const** list) const; + Sensor const* getDefaultSensor(int type); + sp<SensorEventQueue> createEventQueue(); + +private: + sp<ISensorServer> mSensorServer; + Sensor const** mSensorList; + Vector<Sensor> mSensors; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_SENSOR_MANAGER_H diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 9b5a1e0f53..d016dfa969 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -43,24 +43,9 @@ namespace android { * unless they are in use by the server, which is only the case for the last * dequeue-able buffer. When these various conditions are not met, the caller * waits until the condition is met. - * - * - * CAVEATS: - * - * In the current implementation there are several limitations: - * - buffers must be locked in the same order they've been dequeued - * - buffers must be enqueued in the same order they've been locked - * - dequeue() is not reentrant - * - no error checks are done on the condition above * */ -// When changing these values, the COMPILE_TIME_ASSERT at the end of this -// file need to be updated. -const unsigned int NUM_LAYERS_MAX = 31; -const unsigned int NUM_BUFFER_MAX = 4; -const unsigned int NUM_DISPLAY_MAX = 4; - // ---------------------------------------------------------------------------- class Region; @@ -69,7 +54,6 @@ class SharedClient; // ---------------------------------------------------------------------------- -// should be 128 bytes (32 longs) class SharedBufferStack { friend class SharedClient; @@ -78,22 +62,44 @@ class SharedBufferStack friend class SharedBufferServer; public: - struct FlatRegion { // 12 bytes - static const unsigned int NUM_RECT_MAX = 1; - uint32_t count; - uint16_t rects[4*NUM_RECT_MAX]; - }; - + // When changing these values, the COMPILE_TIME_ASSERT at the end of this + // file need to be updated. + static const unsigned int NUM_LAYERS_MAX = 31; + static const unsigned int NUM_BUFFER_MAX = 16; + static const unsigned int NUM_BUFFER_MIN = 2; + static const unsigned int NUM_DISPLAY_MAX = 4; + struct Statistics { // 4 longs typedef int32_t usecs_t; usecs_t totalTime; usecs_t reserved[3]; }; + + struct SmallRect { + uint16_t l, t, r, b; + }; + + struct FlatRegion { // 52 bytes = 4 * (1 + 2*N) + static const unsigned int NUM_RECT_MAX = 5; + uint32_t count; + SmallRect rects[NUM_RECT_MAX]; + }; + + struct BufferData { + FlatRegion dirtyRegion; + SmallRect crop; + uint8_t transform; + uint8_t reserved[3]; + }; SharedBufferStack(); void init(int32_t identity); status_t setDirtyRegion(int buffer, const Region& reg); + status_t setCrop(int buffer, const Rect& reg); + status_t setTransform(int buffer, uint8_t transform); Region getDirtyRegion(int buffer) const; + Rect getCrop(int buffer) const; + uint32_t getTransform(int buffer) const; // these attributes are part of the conditions/updates volatile int32_t head; // server's current front buffer @@ -104,24 +110,25 @@ public: // not part of the conditions volatile int32_t reallocMask; + volatile int8_t index[NUM_BUFFER_MAX]; int32_t identity; // surface's identity (const) - int32_t reserved32[9]; + int32_t token; // surface's token (for debugging) + int32_t reserved32[1]; Statistics stats; - FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes + int32_t reserved; + BufferData buffers[NUM_BUFFER_MAX]; // 1024 bytes }; // ---------------------------------------------------------------------------- -// 4 KB max +// 32 KB max class SharedClient { public: SharedClient(); ~SharedClient(); - status_t validate(size_t token) const; - uint32_t getIdentity(size_t token) const; private: friend class SharedBufferBase; @@ -131,7 +138,7 @@ private: // FIXME: this should be replaced by a lock-less primitive Mutex lock; Condition cv; - SharedBufferStack surfaces[ NUM_LAYERS_MAX ]; + SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ]; }; // ============================================================================ @@ -139,18 +146,17 @@ private: class SharedBufferBase { public: - SharedBufferBase(SharedClient* sharedClient, int surface, int num, + SharedBufferBase(SharedClient* sharedClient, int surface, int32_t identity); ~SharedBufferBase(); - uint32_t getIdentity(); status_t getStatus() const; + int32_t getIdentity() const; size_t getFrontBuffer() const; String8 dump(char const* prefix) const; protected: SharedClient* const mSharedClient; SharedBufferStack* const mSharedStack; - const int mNumBuffers; const int mIdentity; friend struct Update; @@ -160,61 +166,22 @@ protected: SharedBufferStack& stack; inline ConditionBase(SharedBufferBase* sbc) : stack(*sbc->mSharedStack) { } + virtual ~ConditionBase() { }; + virtual bool operator()() const = 0; + virtual const char* name() const = 0; }; + status_t waitForCondition(const ConditionBase& condition); struct UpdateBase { SharedBufferStack& stack; inline UpdateBase(SharedBufferBase* sbb) : stack(*sbb->mSharedStack) { } }; - - template <typename T> - status_t waitForCondition(T condition); - template <typename T> status_t updateCondition(T update); }; template <typename T> -status_t SharedBufferBase::waitForCondition(T condition) -{ - const SharedBufferStack& stack( *mSharedStack ); - SharedClient& client( *mSharedClient ); - const nsecs_t TIMEOUT = s2ns(1); - Mutex::Autolock _l(client.lock); - while ((condition()==false) && - (stack.identity == mIdentity) && - (stack.status == NO_ERROR)) - { - status_t err = client.cv.waitRelative(client.lock, TIMEOUT); - - // handle errors and timeouts - if (CC_UNLIKELY(err != NO_ERROR)) { - if (err == TIMED_OUT) { - if (condition()) { - LOGE("waitForCondition(%s) timed out (identity=%d), " - "but condition is true! We recovered but it " - "shouldn't happen." , T::name(), - stack.identity); - break; - } else { - LOGW("waitForCondition(%s) timed out " - "(identity=%d, status=%d). " - "CPU may be pegged. trying again.", T::name(), - stack.identity, stack.status); - } - } else { - LOGE("waitForCondition(%s) error (%s) ", - T::name(), strerror(-err)); - return err; - } - } - } - return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; -} - - -template <typename T> status_t SharedBufferBase::updateCondition(T update) { SharedClient& client( *mSharedClient ); Mutex::Autolock _l(client.lock); @@ -238,13 +205,21 @@ public: status_t queue(int buf); bool needNewBuffer(int buffer) const; status_t setDirtyRegion(int buffer, const Region& reg); - + status_t setCrop(int buffer, const Rect& reg); + status_t setTransform(int buffer, uint32_t transform); + + class SetBufferCountCallback { + friend class SharedBufferClient; + virtual status_t operator()(int bufferCount) const = 0; + protected: + virtual ~SetBufferCountCallback() { } + }; + status_t setBufferCount(int bufferCount, const SetBufferCountCallback& ipc); + private: friend struct Condition; friend struct DequeueCondition; friend struct LockCondition; - - int32_t computeTail() const; struct QueueUpdate : public UpdateBase { inline QueueUpdate(SharedBufferBase* sbb); @@ -260,25 +235,34 @@ private: struct DequeueCondition : public ConditionBase { inline DequeueCondition(SharedBufferClient* sbc); - inline bool operator()(); - static inline const char* name() { return "DequeueCondition"; } + inline bool operator()() const; + inline const char* name() const { return "DequeueCondition"; } }; struct LockCondition : public ConditionBase { int buf; inline LockCondition(SharedBufferClient* sbc, int buf); - inline bool operator()(); - static inline const char* name() { return "LockCondition"; } + inline bool operator()() const; + inline const char* name() const { return "LockCondition"; } }; + int32_t computeTail() const; + + mutable RWLock mLock; + int mNumBuffers; + int32_t tail; + int32_t undoDequeueTail; + int32_t queued_head; // statistics... - nsecs_t mDequeueTime[NUM_BUFFER_MAX]; + nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX]; }; // ---------------------------------------------------------------------------- -class SharedBufferServer : public SharedBufferBase +class SharedBufferServer + : public SharedBufferBase, + public LightRefBase<SharedBufferServer> { public: SharedBufferServer(SharedClient* sharedClient, int surface, int num, @@ -287,16 +271,75 @@ public: ssize_t retireAndLock(); status_t unlock(int buffer); void setStatus(status_t status); - status_t reallocate(); - status_t assertReallocate(int buffer); + status_t reallocateAll(); + status_t reallocateAllExcept(int buffer); int32_t getQueuedCount() const; - Region getDirtyRegion(int buffer) const; + Rect getCrop(int buffer) const; + uint32_t getTransform(int buffer) const; + + status_t resize(int newNumBuffers); SharedBufferStack::Statistics getStats() const; private: + friend class LightRefBase<SharedBufferServer>; + ~SharedBufferServer(); + + /* + * BufferList is basically a fixed-capacity sorted-vector of + * unsigned 5-bits ints using a 32-bits int as storage. + * it has efficient iterators to find items in the list and not in the list. + */ + class BufferList { + size_t mCapacity; + uint32_t mList; + public: + BufferList(size_t c = SharedBufferStack::NUM_BUFFER_MAX) + : mCapacity(c), mList(0) { } + status_t add(int value); + status_t remove(int value); + uint32_t getMask() const { return mList; } + + class const_iterator { + friend class BufferList; + uint32_t mask, curr; + const_iterator(uint32_t mask) : + mask(mask), curr(__builtin_clz(mask)) { + } + public: + inline bool operator == (const const_iterator& rhs) const { + return mask == rhs.mask; + } + inline bool operator != (const const_iterator& rhs) const { + return mask != rhs.mask; + } + inline int operator *() const { return curr; } + inline const const_iterator& operator ++() { + mask &= ~(1<<(31-curr)); + curr = __builtin_clz(mask); + return *this; + } + }; + + inline const_iterator begin() const { + return const_iterator(mList); + } + inline const_iterator end() const { + return const_iterator(0); + } + inline const_iterator free_begin() const { + uint32_t mask = (1 << (32-mCapacity)) - 1; + return const_iterator( ~(mList | mask) ); + } + }; + + // this protects mNumBuffers and mBufferList + mutable RWLock mLock; + int mNumBuffers; + BufferList mBufferList; + struct UnlockUpdate : public UpdateBase { const int lockedBuffer; inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer); @@ -314,13 +357,6 @@ private: inline StatusUpdate(SharedBufferBase* sbb, status_t status); inline ssize_t operator()(); }; - - struct ReallocateCondition : public ConditionBase { - int buf; - inline ReallocateCondition(SharedBufferBase* sbb, int buf); - inline bool operator()(); - static inline const char* name() { return "ReallocateCondition"; } - }; }; // =========================================================================== @@ -344,13 +380,12 @@ struct surface_flinger_cblk_t // 4KB max uint8_t connected; uint8_t reserved[3]; uint32_t pad[7]; - display_cblk_t displays[NUM_DISPLAY_MAX]; + display_cblk_t displays[SharedBufferStack::NUM_DISPLAY_MAX]; }; // --------------------------------------------------------------------------- -COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096) -COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128) +COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768) COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096) // --------------------------------------------------------------------------- diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h index 472f759237..ddbe03dbcc 100644 --- a/include/surfaceflinger/ISurface.h +++ b/include/surfaceflinger/ISurface.h @@ -47,13 +47,30 @@ protected: POST_BUFFER, // one-way transaction CREATE_OVERLAY, REQUEST_BUFFER, + SET_BUFFER_COUNT, }; public: DECLARE_META_INTERFACE(Surface); - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) = 0; + /* + * requests a new buffer for the given index. If w, h, or format are + * null the buffer is created with the parameters assigned to the + * surface it is bound to. Otherwise the buffer's parameters are + * set to those specified. + */ + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0; + + /* + * sets the number of buffers dequeuable for this surface. + */ + virtual status_t setBufferCount(int bufferCount) = 0; + // ------------------------------------------------------------------------ + // Deprecated... + // ------------------------------------------------------------------------ + class BufferHeap { public: enum { diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index d1e7785ccd..dd44aa5afa 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -27,7 +27,7 @@ #include <ui/PixelFormat.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> namespace android { // ---------------------------------------------------------------------------- @@ -85,8 +85,11 @@ public: /* create connection with surface flinger, requires * ACCESS_SURFACE_FLINGER permission */ + virtual sp<ISurfaceComposerClient> createConnection() = 0; - virtual sp<ISurfaceFlingerClient> createConnection() = 0; + /* create a client connection with surface flinger + */ + virtual sp<ISurfaceComposerClient> createClientConnection() = 0; /* retrieve the control block */ virtual sp<IMemoryHeap> getCblk() const = 0; @@ -123,6 +126,7 @@ public: // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, + CREATE_CLIENT_CONNECTION, GET_CBLK, OPEN_GLOBAL_TRANSACTION, CLOSE_GLOBAL_TRANSACTION, diff --git a/include/surfaceflinger/ISurfaceFlingerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h index d257645b65..a1e9e04884 100644 --- a/include/surfaceflinger/ISurfaceFlingerClient.h +++ b/include/surfaceflinger/ISurfaceComposerClient.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_SF_ISURFACE_FLINGER_CLIENT_H -#define ANDROID_SF_ISURFACE_FLINGER_CLIENT_H +#ifndef ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H +#define ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H #include <stdint.h> #include <sys/types.h> @@ -26,7 +26,7 @@ #include <binder/IInterface.h> #include <ui/PixelFormat.h> - + #include <surfaceflinger/ISurface.h> namespace android { @@ -42,10 +42,10 @@ typedef int32_t DisplayID; class layer_state_t; -class ISurfaceFlingerClient : public IInterface +class ISurfaceComposerClient : public IInterface { -public: - DECLARE_META_INTERFACE(SurfaceFlingerClient); +public: + DECLARE_META_INTERFACE(SurfaceComposerClient); struct surface_data_t { int32_t token; @@ -56,26 +56,36 @@ public: status_t readFromParcel(const Parcel& parcel); status_t writeToParcel(Parcel* parcel) const; }; - + virtual sp<IMemoryHeap> getControlBlock() const = 0; + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual sp<ISurface> createSurface( surface_data_t* data, - int pid, + int pid, const String8& name, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) = 0; - + + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual status_t destroySurface(SurfaceID sid) = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual status_t setState(int32_t count, const layer_state_t* states) = 0; }; // ---------------------------------------------------------------------------- -class BnSurfaceFlingerClient : public BnInterface<ISurfaceFlingerClient> +class BnSurfaceComposerClient : public BnInterface<ISurfaceComposerClient> { public: virtual status_t onTransact( uint32_t code, @@ -88,4 +98,4 @@ public: }; // namespace android -#endif // ANDROID_SF_ISURFACE_FLINGER_CLIENT_H +#endif // ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index 0279d84b64..7c5a39b680 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/types.h> +#include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -28,12 +29,15 @@ #include <ui/egl/android_natives.h> #include <surfaceflinger/ISurface.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> + +#define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" namespace android { // --------------------------------------------------------------------------- +class GraphicBuffer; class GraphicBufferMapper; class IOMX; class Rect; @@ -41,6 +45,7 @@ class Surface; class SurfaceComposerClient; class SharedClient; class SharedBufferClient; +class SurfaceClient; // --------------------------------------------------------------------------- @@ -56,7 +61,6 @@ public: static bool isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs); - SurfaceID ID() const { return mToken; } uint32_t getFlags() const { return mFlags; } uint32_t getIdentity() const { return mIdentity; } @@ -104,7 +108,7 @@ private: SurfaceControl( const sp<SurfaceComposerClient>& client, const sp<ISurface>& surface, - const ISurfaceFlingerClient::surface_data_t& data, + const ISurfaceComposerClient::surface_data_t& data, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); ~SurfaceControl(); @@ -128,7 +132,7 @@ private: // --------------------------------------------------------------------------- class Surface - : public EGLNativeBase<android_native_window_t, Surface, RefBase> + : public EGLNativeBase<ANativeWindow, Surface, RefBase> { public: struct SurfaceInfo { @@ -141,17 +145,16 @@ public: uint32_t reserved[2]; }; - Surface(const Parcel& data); + static status_t writeToParcel( + const sp<Surface>& control, Parcel* parcel); + + static sp<Surface> readFromParcel(const Parcel& data); static bool isValid(const sp<Surface>& surface) { return (surface != 0) && surface->isValid(); } - static bool isSameSurface( - const sp<Surface>& lhs, const sp<Surface>& rhs); - bool isValid(); - SurfaceID ID() const { return mToken; } uint32_t getFlags() const { return mFlags; } uint32_t getIdentity() const { return mIdentity; } @@ -163,44 +166,43 @@ public: // setSwapRectangle() is intended to be used by GL ES clients void setSwapRectangle(const Rect& r); -private: - // can't be copied - Surface& operator = (Surface& rhs); - Surface(const Surface& rhs); - - Surface(const sp<SurfaceControl>& control); - void init(); - ~Surface(); - - friend class SurfaceComposerClient; - friend class SurfaceControl; - +private: + /* + * Android frameworks friends + * (eventually this should go away and be replaced by proper APIs) + */ // camera and camcorder need access to the ISurface binder interface for preview friend class Camera; friend class MediaRecorder; - // mediaplayer needs access to ISurface for display + // MediaPlayer needs access to ISurface for display friend class MediaPlayer; friend class IOMX; // this is just to be able to write some unit tests friend class Test; - sp<SurfaceComposerClient> getClient() const; - sp<ISurface> getISurface() const; +private: + friend class SurfaceComposerClient; + friend class SurfaceControl; - status_t getBufferLocked(int index, int usage); - - status_t validate() const; + // can't be copied + Surface& operator = (Surface& rhs); + Surface(const Surface& rhs); - inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } - inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } - - static int setSwapInterval(android_native_window_t* window, int interval); - static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer); - static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int query(android_native_window_t* window, int what, int* value); - static int perform(android_native_window_t* window, int operation, ...); + Surface(const sp<SurfaceControl>& control); + Surface(const Parcel& data, const sp<IBinder>& ref); + ~Surface(); + + + /* + * ANativeWindow hooks + */ + static int setSwapInterval(ANativeWindow* window, int interval); + static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer); + static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int query(ANativeWindow* window, int what, int* value); + static int perform(ANativeWindow* window, int operation, ...); int dequeueBuffer(android_native_buffer_t** buffer); int lockBuffer(android_native_buffer_t* buffer); @@ -208,44 +210,91 @@ private: int query(int what, int* value); int perform(int operation, va_list args); - status_t dequeueBuffer(sp<GraphicBuffer>* buffer); - void dispatch_setUsage(va_list args); int dispatch_connect(va_list args); int dispatch_disconnect(va_list args); + int dispatch_crop(va_list args); + int dispatch_set_buffer_count(va_list args); + int dispatch_set_buffers_geometry(va_list args); + int dispatch_set_buffers_transform(va_list args); void setUsage(uint32_t reqUsage); int connect(int api); int disconnect(int api); + int crop(Rect const* rect); + int setBufferCount(int bufferCount); + int setBuffersGeometry(int w, int h, int format); + int setBuffersTransform(int transform); + + /* + * private stuff... + */ + void init(); + status_t validate() const; + sp<ISurface> getISurface() const; + + inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } + inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } - uint32_t getUsage() const; - int getConnectedApi() const; + status_t getBufferLocked(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + int getBufferIndex(const sp<GraphicBuffer>& buffer) const; + + int getConnectedApi() const; + bool needNewBuffer(int bufIdx, + uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const; + + static void cleanCachedSurfaces(); + + class BufferInfo { + uint32_t mWidth; + uint32_t mHeight; + uint32_t mFormat; + uint32_t mUsage; + mutable uint32_t mDirty; + enum { + GEOMETRY = 0x01 + }; + public: + BufferInfo(); + void set(uint32_t w, uint32_t h, uint32_t format); + void set(uint32_t usage); + void get(uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const; + bool validateBuffer(const sp<GraphicBuffer>& buffer) const; + }; + // constants - sp<SurfaceComposerClient> mClient; + GraphicBufferMapper& mBufferMapper; + SurfaceClient& mClient; + SharedBufferClient* mSharedBufferClient; + status_t mInitCheck; sp<ISurface> mSurface; - SurfaceID mToken; uint32_t mIdentity; PixelFormat mFormat; uint32_t mFlags; - GraphicBufferMapper& mBufferMapper; - SharedBufferClient* mSharedBufferClient; // protected by mSurfaceLock Rect mSwapRectangle; - uint32_t mUsage; int mConnected; + Rect mNextBufferCrop; + uint32_t mNextBufferTransform; + BufferInfo mBufferInfo; // protected by mSurfaceLock. These are also used from lock/unlock // but in that case, they must be called form the same thread. - sp<GraphicBuffer> mBuffers[2]; mutable Region mDirtyRegion; // must be used from the lock/unlock thread sp<GraphicBuffer> mLockedBuffer; sp<GraphicBuffer> mPostedBuffer; mutable Region mOldDirtyRegion; - bool mNeedFullUpdate; + bool mReserved; + + // only used from dequeueBuffer() + Vector< sp<GraphicBuffer> > mBuffers; // query() must be called from dequeueBuffer() thread uint32_t mWidth; @@ -254,6 +303,10 @@ private: // Inherently thread-safe mutable Mutex mSurfaceLock; mutable Mutex mApiLock; + + // A cache of Surface objects that have been deserialized into this process. + static Mutex sCachedSurfacesLock; + static DefaultKeyedVector<wp<IBinder>, wp<Surface> > sCachedSurfaces; }; }; // namespace android diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 9d0f0cbeb8..8773d7133b 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -22,8 +22,9 @@ #include <binder/IBinder.h> -#include <utils/SortedVector.h> #include <utils/RefBase.h> +#include <utils/Singleton.h> +#include <utils/SortedVector.h> #include <utils/threads.h> #include <ui/PixelFormat.h> @@ -39,8 +40,26 @@ class Region; class SharedClient; class ISurfaceComposer; class DisplayInfo; +class surface_flinger_cblk_t; + +// --------------------------------------------------------------------------- + +class ComposerService : public Singleton<ComposerService> +{ + // these are constants + sp<ISurfaceComposer> mComposerService; + sp<IMemoryHeap> mServerCblkMemory; + surface_flinger_cblk_t volatile* mServerCblk; + ComposerService(); + friend class Singleton<ComposerService>; +public: + static sp<ISurfaceComposer> getComposerService(); + static surface_flinger_cblk_t const volatile * getControlBlock(); +}; -class SurfaceComposerClient : virtual public RefBase +// --------------------------------------------------------------------------- + +class SurfaceComposerClient : public RefBase { public: SurfaceComposerClient(); @@ -52,10 +71,6 @@ public: // Return the connection of this client sp<IBinder> connection() const; - // Retrieve a client for an existing connection. - static sp<SurfaceComposerClient> - clientForConnection(const sp<IBinder>& conn); - // Forcibly remove connection before all references have gone away. void dispose(); @@ -123,13 +138,6 @@ public: status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient, void* cookie = NULL, uint32_t flags = 0); -private: - friend class Surface; - friend class SurfaceControl; - - SurfaceComposerClient(const sp<ISurfaceComposer>& sm, - const sp<IBinder>& conn); - status_t hide(SurfaceID id); status_t show(SurfaceID id, int32_t layer = -1); status_t freeze(SurfaceID id); @@ -142,32 +150,26 @@ private: status_t setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy); status_t setPosition(SurfaceID id, int32_t x, int32_t y); status_t setSize(SurfaceID id, uint32_t w, uint32_t h); - - void signalServer(); - status_t destroySurface(SurfaceID sid); - void _init(const sp<ISurfaceComposer>& sm, - const sp<ISurfaceFlingerClient>& conn); - - inline layer_state_t* _get_state_l(SurfaceID id); - layer_state_t* _lockLayerState(SurfaceID id); - inline void _unlockLayerState(); +private: + virtual void onFirstRef(); + inline layer_state_t* get_state_l(SurfaceID id); + layer_state_t* lockLayerState(SurfaceID id); + inline void unlockLayerState(); mutable Mutex mLock; - layer_state_t* mPrebuiltLayerState; SortedVector<layer_state_t> mStates; int32_t mTransactionOpen; + layer_state_t* mPrebuiltLayerState; // these don't need to be protected because they never change // after assignment status_t mStatus; - SharedClient* mControl; - sp<IMemoryHeap> mControlMemory; - sp<ISurfaceFlingerClient> mClient; - sp<ISurfaceComposer> mSignalServer; + sp<ISurfaceComposerClient> mClient; }; +// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index a21bb49a60..3d42856335 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -18,6 +18,7 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H +#include <android/input.h> #include <utils/String8.h> #include <utils/threads.h> #include <utils/Log.h> @@ -27,6 +28,31 @@ #include <linux/input.h> +/* These constants are not defined in linux/input.h but they are part of the multitouch + * input protocol. */ + +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ +#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ +#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ +#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ +#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ +#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device (finger, pen, ...) */ +#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ +#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ + +#define MT_TOOL_FINGER 0 /* Identifies a finger */ +#define MT_TOOL_PEN 1 /* Identifies a pen */ + +#define SYN_MT_REPORT 2 + +/* Convenience constants. */ + +#define BTN_FIRST 0x100 // first button scancode +#define BTN_LAST 0x15f // last button scancode + struct pollfd; namespace android { @@ -34,73 +60,154 @@ namespace android { class KeyLayoutMap; /* - * Grand Central Station for events. With a single call to waitEvent() - * you can wait for: - * - input events from the keypad of a real device - * - input events and meta-events (e.g. "quit") from the simulator - * - synthetic events from the runtime (e.g. "URL fetch completed") - * - real or forged "vsync" events + * A raw event as retrieved from the EventHub. + */ +struct RawEvent { + nsecs_t when; + int32_t deviceId; + int32_t type; + int32_t scanCode; + int32_t keyCode; + int32_t value; + uint32_t flags; +}; + +/* 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 + + inline int32_t getRange() { return maxValue - minValue; } +}; + +/* + * Input device classes. + */ +enum { + /* The input device is a keyboard. */ + 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 (either single-touch or multi-touch). */ + INPUT_DEVICE_CLASS_TOUCHSCREEN = 0x00000004, + + /* The input device is a trackball. */ + INPUT_DEVICE_CLASS_TRACKBALL = 0x00000008, + + /* The input device is a multi-touch touchscreen. */ + INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010, + + /* The input device is a directional pad. */ + INPUT_DEVICE_CLASS_DPAD = 0x00000020, + + /* The input device is a gamepad (implies keyboard). */ + INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + + /* The input device has switches. */ + INPUT_DEVICE_CLASS_SWITCH = 0x00000080, +}; + +/* + * Grand Central Station for events. * - * Do not instantiate this class. Instead, call startUp(). + * 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 provies 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 EventHub : public RefBase +class EventHubInterface : public virtual RefBase { +protected: + EventHubInterface() { } + virtual ~EventHubInterface() { } + +public: + // Synthetic raw event type codes produced when devices are added or removed. + enum { + DEVICE_ADDED = 0x10000000, + DEVICE_REMOVED = 0x20000000 + }; + + virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; + + virtual String8 getDeviceName(int32_t deviceId) const = 0; + + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const = 0; + + virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, + int32_t* outKeycode, uint32_t* outFlags) const = 0; + + // exclude a particular device from opening + // this can be used to ignore input devices for sensors + virtual void addExcludedDevice(const char* deviceName) = 0; + + /* + * Wait for the next event to become available and return it. + * 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). + */ + virtual bool getEvent(RawEvent* outEvent) = 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; + + /* + * 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; +}; + +class EventHub : public EventHubInterface { public: EventHub(); - + status_t errorCheck() const; + + virtual uint32_t getDeviceClasses(int32_t deviceId) const; - // bit fields for classes of devices. - enum { - CLASS_KEYBOARD = 0x00000001, - CLASS_ALPHAKEY = 0x00000002, - CLASS_TOUCHSCREEN = 0x00000004, - CLASS_TRACKBALL = 0x00000008, - CLASS_TOUCHSCREEN_MT= 0x00000010, - CLASS_DPAD = 0x00000020 - }; - uint32_t getDeviceClasses(int32_t deviceId) const; - - String8 getDeviceName(int32_t deviceId) const; - - int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const; - - int getSwitchState(int sw) const; - int getSwitchState(int32_t deviceId, int sw) const; - - int getScancodeState(int key) const; - int getScancodeState(int32_t deviceId, int key) const; - - int getKeycodeState(int key) const; - int getKeycodeState(int32_t deviceId, int key) const; + virtual String8 getDeviceName(int32_t deviceId) const; - status_t scancodeToKeycode(int32_t deviceId, int scancode, + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const; + + virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const; - // exclude a particular device from opening - // this can be used to ignore input devices for sensors - void addExcludedDevice(const char* deviceName); + virtual void addExcludedDevice(const char* deviceName); - // special type codes when devices are added/removed. - enum { - DEVICE_ADDED = 0x10000000, - DEVICE_REMOVED = 0x20000000 - }; - - // examine key input devices for specific framework keycode support - bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags); + 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 bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const; - virtual bool getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen); + virtual bool getEvent(RawEvent* outEvent); protected: virtual ~EventHub(); private: bool openPlatformInput(void); - int32_t convertDeviceKey_TI_P2(int code); int open_device(const char *device); int close_device(const char *device); @@ -127,6 +234,12 @@ private: device_t* getDevice(int32_t deviceId) const; bool hasKeycode(device_t* device, int keycode) const; + int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const; + int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const; + int32_t getSwitchStateLocked(device_t* device, int32_t sw) const; + bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const; + // Protect all internal state. mutable Mutex mLock; @@ -152,8 +265,14 @@ private: // device ids that report particular switches. #ifdef EV_SW - int32_t mSwitches[SW_MAX+1]; + int32_t mSwitches[SW_MAX + 1]; #endif + + static const int INPUT_BUFFER_SIZE = 64; + struct input_event mInputBufferData[INPUT_BUFFER_SIZE]; + int32_t mInputBufferIndex; + int32_t mInputBufferCount; + int32_t mInputDeviceIndex; }; }; // namespace android diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h index 8ea3ab973a..0f4594ffbd 100644 --- a/include/ui/FramebufferNativeWindow.h +++ b/include/ui/FramebufferNativeWindow.h @@ -43,7 +43,7 @@ class NativeBuffer; class FramebufferNativeWindow : public EGLNativeBase< - android_native_window_t, + ANativeWindow, FramebufferNativeWindow, LightRefBase<FramebufferNativeWindow> > { @@ -59,12 +59,12 @@ public: private: friend class LightRefBase<FramebufferNativeWindow>; ~FramebufferNativeWindow(); // this class cannot be overloaded - static int setSwapInterval(android_native_window_t* window, int interval); - static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer); - static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int query(android_native_window_t* window, int what, int* value); - static int perform(android_native_window_t* window, int operation, ...); + static int setSwapInterval(ANativeWindow* window, int interval); + static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer); + static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int query(ANativeWindow* window, int what, int* value); + static int perform(ANativeWindow* window, int operation, ...); framebuffer_device_t* fbDev; alloc_device_t* grDev; diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index e72b6b3b8e..a3e85a96aa 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -93,10 +93,8 @@ public: void setIndex(int index); int getIndex() const; - void setVerticalStride(uint32_t vstride); - uint32_t getVerticalStride() const; -protected: +private: virtual ~GraphicBuffer(); enum { @@ -105,8 +103,12 @@ protected: ownData = 2, }; - inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } - inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } + inline const GraphicBufferMapper& getBufferMapper() const { + return mBufferMapper; + } + inline GraphicBufferMapper& getBufferMapper() { + return mBufferMapper; + } uint8_t mOwner; private: @@ -134,7 +136,6 @@ private: GraphicBufferMapper& mBufferMapper; ssize_t mInitCheck; - uint32_t mVStride; int mIndex; }; diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h index 741d763561..54b8236e2a 100644 --- a/include/ui/GraphicBufferAllocator.h +++ b/include/ui/GraphicBufferAllocator.h @@ -73,9 +73,9 @@ private: struct alloc_rec_t { uint32_t w; uint32_t h; + uint32_t s; PixelFormat format; uint32_t usage; - void* vaddr; size_t size; }; diff --git a/include/ui/Input.h b/include/ui/Input.h new file mode 100644 index 0000000000..2385973e69 --- /dev/null +++ b/include/ui/Input.h @@ -0,0 +1,491 @@ +/* + * 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_H +#define _UI_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> + +/* + * Additional private constants not defined in ndk/ui/input.h. + */ +enum { + /* + * Private control to determine when an app is tracking a key sequence. + */ + AKEY_EVENT_FLAG_START_TRACKING = 0x40000000 +}; + +/* + * Maximum number of pointers supported per motion event. + */ +#define MAX_POINTERS 10 + +/* + * 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 { + +/* + * Flags that flow alongside events in the input dispatch system to help with certain + * policy decisions such as waking from device sleep. + */ +enum { + /* These flags originate in RawEvents and are generally set in the key map. */ + + 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_RAW_MASK = 0x0000ffff, + + /* 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, +}; + +/* + * Describes the basic configuration of input devices that are present. + */ +struct InputConfiguration { + enum { + TOUCHSCREEN_UNDEFINED = 0, + TOUCHSCREEN_NOTOUCH = 1, + TOUCHSCREEN_STYLUS = 2, + TOUCHSCREEN_FINGER = 3 + }; + + enum { + KEYBOARD_UNDEFINED = 0, + KEYBOARD_NOKEYS = 1, + KEYBOARD_QWERTY = 2, + KEYBOARD_12KEY = 3 + }; + + enum { + NAVIGATION_UNDEFINED = 0, + NAVIGATION_NONAV = 1, + NAVIGATION_DPAD = 2, + NAVIGATION_TRACKBALL = 3, + NAVIGATION_WHEEL = 4 + }; + + int32_t touchScreen; + int32_t keyboard; + int32_t navigation; +}; + +/* + * Pointer coordinate data. + */ +struct PointerCoords { + float x; + float y; + float pressure; + float size; + float touchMajor; + float touchMinor; + float toolMajor; + float toolMinor; + float orientation; +}; + +/* + * 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; } + +protected: + void initialize(int32_t deviceId, int32_t source); + void initialize(const InputEvent& from); + +private: + 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 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); + +private: + 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 getEdgeFlags() const { return mEdgeFlags; } + + inline int32_t getMetaState() const { return mMetaState; } + + 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 size_t getPointerCount() const { return mPointerIds.size(); } + + inline int32_t getPointerId(size_t pointerIndex) const { return mPointerIds[pointerIndex]; } + + inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } + + inline float getRawX(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).x; + } + + inline float getRawY(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).y; + } + + inline float getX(size_t pointerIndex) const { + return getRawX(pointerIndex) + mXOffset; + } + + inline float getY(size_t pointerIndex) const { + return getRawY(pointerIndex) + mYOffset; + } + + inline float getPressure(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).pressure; + } + + inline float getSize(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).size; + } + + inline float getTouchMajor(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).touchMajor; + } + + inline float getTouchMinor(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).touchMinor; + } + + inline float getToolMajor(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).toolMajor; + } + + inline float getToolMinor(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).toolMinor; + } + + inline float getOrientation(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).orientation; + } + + inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; } + + inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const { + return mSampleEventTimes[historicalIndex]; + } + + inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).x; + } + + inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).y; + } + + inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset; + } + + inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset; + } + + inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure; + } + + inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).size; + } + + inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMajor; + } + + inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMinor; + } + + inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMajor; + } + + inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMinor; + } + + inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalPointerCoords(pointerIndex, historicalIndex).orientation; + } + + void initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t edgeFlags, + int32_t metaState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const int32_t* pointerIds, + const PointerCoords* pointerCoords); + + void addSample( + nsecs_t eventTime, + const PointerCoords* pointerCoords); + + void offsetLocation(float xOffset, float yOffset); + + // Low-level accessors. + inline const int32_t* getPointerIds() const { return mPointerIds.array(); } + inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } + inline const PointerCoords* getSamplePointerCoords() const { + return mSamplePointerCoords.array(); + } + +private: + int32_t mAction; + int32_t mEdgeFlags; + int32_t mMetaState; + float mXOffset; + float mYOffset; + float mXPrecision; + float mYPrecision; + nsecs_t mDownTime; + Vector<int32_t> mPointerIds; + Vector<nsecs_t> mSampleEventTimes; + Vector<PointerCoords> mSamplePointerCoords; + + inline const PointerCoords& getCurrentPointerCoords(size_t pointerIndex) const { + return mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; + } + + inline const PointerCoords& getHistoricalPointerCoords( + size_t pointerIndex, size_t historicalIndex) const { + return mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; + } +}; + +/* + * 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; +}; + +/* + * Describes the characteristics and capabilities of an input device. + */ +class InputDeviceInfo { +public: + InputDeviceInfo(); + InputDeviceInfo(const InputDeviceInfo& other); + ~InputDeviceInfo(); + + struct MotionRange { + float min; + float max; + float flat; + float fuzz; + }; + + void initialize(int32_t id, const String8& name); + + inline int32_t getId() const { return mId; } + inline const String8 getName() const { return mName; } + inline uint32_t getSources() const { return mSources; } + + const MotionRange* getMotionRange(int32_t rangeType) const; + + void addSource(uint32_t source); + void addMotionRange(int32_t rangeType, float min, float max, float flat, float fuzz); + void addMotionRange(int32_t rangeType, const MotionRange& range); + + inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + inline int32_t getKeyboardType() const { return mKeyboardType; } + +private: + int32_t mId; + String8 mName; + uint32_t mSources; + int32_t mKeyboardType; + + KeyedVector<int32_t, MotionRange> mMotionRanges; +}; + +/* + * Provides remote access to information about an input device. + * + * Note: This is essentially a wrapper for Binder calls into the Window Manager Service. + */ +class InputDeviceProxy : public RefBase, public AInputDevice { +protected: + InputDeviceProxy(); + virtual ~InputDeviceProxy(); + +public: + static void getDeviceIds(Vector<int32_t>& outIds); + + static sp<InputDeviceProxy> getDevice(int32_t id); + + inline const InputDeviceInfo* getInfo() { return & mInfo; } + + // TODO add hasKeys, keymap, etc... + +private: + InputDeviceInfo mInfo; +}; + + +} // namespace android + +#endif // _UI_INPUT_H diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h new file mode 100644 index 0000000000..aed4fa112b --- /dev/null +++ b/include/ui/InputDispatcher.h @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_DISPATCHER_H +#define _UI_INPUT_DISPATCHER_H + +#include <ui/Input.h> +#include <ui/InputTransport.h> +#include <utils/KeyedVector.h> +#include <utils/Vector.h> +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/PollLoop.h> +#include <utils/Pool.h> + +#include <stddef.h> +#include <unistd.h> + + +namespace android { + +/* + * Constants used to report the outcome of input event injection. + */ +enum { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + INPUT_EVENT_INJECTION_PENDING = -1, + + /* Injection succeeded. */ + INPUT_EVENT_INJECTION_SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + INPUT_EVENT_INJECTION_FAILED = 2, + + /* Injection failed due to a timeout. */ + INPUT_EVENT_INJECTION_TIMED_OUT = 3 +}; + +/* + * Constants used to determine the input event injection synchronization mode. + */ +enum { + /* Injection is asynchronous and is assumed always to be successful. */ + INPUT_EVENT_INJECTION_SYNC_NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, +}; + + +/* + * An input target specifies how an input event is to be dispatched to a particular window + * including the window's input channel, control flags, a timeout, and an X / Y offset to + * be added to input event coordinates to compensate for the absolute position of the + * window area. + */ +struct InputTarget { + enum { + /* This flag indicates that subsequent event delivery should be held until the + * current event is delivered to this target or a timeout occurs. */ + FLAG_SYNC = 0x01, + + /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of + * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */ + FLAG_OUTSIDE = 0x02, + + /* This flag indicates that a KeyEvent or MotionEvent is being canceled. + * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set. + * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */ + FLAG_CANCEL = 0x04 + }; + + // The input channel to be targeted. + sp<InputChannel> inputChannel; + + // Flags for the input target. + int32_t flags; + + // The timeout for event delivery to this target in nanoseconds. Or -1 if none. + nsecs_t timeout; + + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset, yOffset; +}; + + +/* + * Input dispatcher policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputDispatcherPolicyInterface : public virtual RefBase { +protected: + InputDispatcherPolicyInterface() { } + virtual ~InputDispatcherPolicyInterface() { } + +public: + /* Notifies the system that a configuration change has occurred. */ + virtual void notifyConfigurationChanged(nsecs_t when) = 0; + + /* Notifies the system that an input channel is unrecoverably broken. */ + virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0; + + /* Notifies the system that an input channel is not responding. + * Returns true and a new timeout value if the dispatcher should keep waiting. + * Otherwise returns false. */ + virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout) = 0; + + /* Notifies the system that an input channel recovered from ANR. */ + virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0; + + /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */ + virtual nsecs_t getKeyRepeatTimeout() = 0; + + /* Waits for key event input targets to become available. + * If the event is being injected, injectorPid and injectorUid should specify the + * process id and used id of the injecting application, otherwise they should both + * be -1. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ + virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, + Vector<InputTarget>& outTargets) = 0; + + /* Waits for motion event targets to become available. + * If the event is being injected, injectorPid and injectorUid should specify the + * process id and used id of the injecting application, otherwise they should both + * be -1. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ + virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, + Vector<InputTarget>& outTargets) = 0; + + /* Gets the maximum suggested event delivery rate per second. + * This value is used to throttle motion event movement actions on a per-device + * basis. It is not intended to be a hard limit. + */ + virtual int32_t getMaxEventsPerSecond() = 0; +}; + + +/* Notifies the system about input events generated by the input reader. + * The dispatcher is expected to be mostly asynchronous. */ +class InputDispatcherInterface : public virtual RefBase { +protected: + InputDispatcherInterface() { } + virtual ~InputDispatcherInterface() { } + +public: + /* Runs a single iteration of the dispatch loop. + * Nominally processes one queued event, a timeout, or a response from an input consumer. + * + * This method should only be called on the input dispatcher thread. + */ + virtual void dispatchOnce() = 0; + + /* Notifies the dispatcher about new events. + * + * These methods should only be called on the input reader thread. + */ + virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0; + virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0; + virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; + virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, + uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, nsecs_t downTime) = 0; + + /* Injects an input event and optionally waits for sync. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0; + + /* Preempts input dispatch in progress by making pending synchronous + * dispatches asynchronous instead. This method is generally called during a focus + * transition from one application to the next so as to enable the new application + * to start receiving input as soon as possible without having to wait for the + * old application to finish up. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void preemptInputDispatch() = 0; + + /* Registers or unregister input channels that may be used as targets for input events. + * + * These methods may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; +}; + +/* Dispatches events to input targets. Some functions of the input dispatcher, such as + * identifying input targets, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy can potentially block or cause re-entrance into the input dispatcher, + * the input dispatcher never calls into the policy while holding its internal locks. + * The implementation is also carefully designed to recover from scenarios such as an + * input channel becoming unregistered while identifying input targets or processing timeouts. + * + * Methods marked 'Locked' must be called with the lock acquired. + * + * Methods marked 'LockedInterruptible' must be called with the lock acquired but + * may during the course of their execution release the lock, call into the policy, and + * then reacquire the lock. The caller is responsible for recovering gracefully. + * + * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. + */ +class InputDispatcher : public InputDispatcherInterface { +protected: + virtual ~InputDispatcher(); + +public: + explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); + + virtual void dispatchOnce(); + + virtual void notifyConfigurationChanged(nsecs_t eventTime); + virtual void notifyAppSwitchComing(nsecs_t eventTime); + virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, nsecs_t downTime); + virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, + uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, nsecs_t downTime); + + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); + + virtual void preemptInputDispatch(); + + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); + +private: + template <typename T> + struct Link { + T* next; + T* prev; + }; + + struct EventEntry : Link<EventEntry> { + enum { + TYPE_SENTINEL, + TYPE_CONFIGURATION_CHANGED, + TYPE_KEY, + TYPE_MOTION + }; + + int32_t refCount; + int32_t type; + nsecs_t eventTime; + + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t injectorPid; // -1 if not injected + int32_t injectorUid; // -1 if not injected + + bool dispatchInProgress; // initially false, set to true while dispatching + int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress + + inline bool isInjected() { return injectorPid >= 0; } + }; + + struct ConfigurationChangedEntry : EventEntry { + }; + + struct KeyEntry : EventEntry { + int32_t deviceId; + int32_t source; + uint32_t policyFlags; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + }; + + struct MotionSample { + MotionSample* next; + + nsecs_t eventTime; + PointerCoords pointerCoords[MAX_POINTERS]; + }; + + struct MotionEntry : EventEntry { + int32_t deviceId; + int32_t source; + uint32_t policyFlags; + int32_t action; + int32_t metaState; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + nsecs_t downTime; + uint32_t pointerCount; + int32_t pointerIds[MAX_POINTERS]; + + // Linked list of motion samples associated with this motion event. + MotionSample firstSample; + MotionSample* lastSample; + + uint32_t countSamples() const; + }; + + // Tracks the progress of dispatching a particular event to a particular connection. + struct DispatchEntry : Link<DispatchEntry> { + EventEntry* eventEntry; // the event to dispatch + int32_t targetFlags; + float xOffset; + float yOffset; + nsecs_t timeout; + + // True if dispatch has started. + bool inProgress; + + // For motion events: + // Pointer to the first motion sample to dispatch in this cycle. + // Usually NULL to indicate that the list of motion samples begins at + // MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous + // cycle and this pointer indicates the location of the first remainining sample + // to dispatch during the current cycle. + MotionSample* headMotionSample; + // Pointer to a motion sample to dispatch in the next cycle if the dispatcher was + // unable to send all motion samples during this cycle. On the next cycle, + // headMotionSample will be initialized to tailMotionSample and tailMotionSample + // will be set to NULL. + MotionSample* tailMotionSample; + + inline bool isSyncTarget() { + return targetFlags & InputTarget::FLAG_SYNC; + } + }; + + // A command entry captures state and behavior for an action to be performed in the + // dispatch loop after the initial processing has taken place. It is essentially + // a kind of continuation used to postpone sensitive policy interactions to a point + // in the dispatch loop where it is safe to release the lock (generally after finishing + // the critical parts of the dispatch cycle). + // + // The special thing about commands is that they can voluntarily release and reacquire + // the dispatcher lock at will. Initially when the command starts running, the + // dispatcher lock is held. However, if the command needs to call into the policy to + // do some work, it can release the lock, do the work, then reacquire the lock again + // before returning. + // + // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch + // never calls into the policy while holding its lock. + // + // Commands are implicitly 'LockedInterruptible'. + struct CommandEntry; + typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); + + class Connection; + struct CommandEntry : Link<CommandEntry> { + CommandEntry(); + ~CommandEntry(); + + Command command; + + // parameters for the command (usage varies by command) + sp<Connection> connection; + }; + + // Generic queue implementation. + template <typename T> + struct Queue { + T head; + T tail; + + inline Queue() { + head.prev = NULL; + head.next = & tail; + tail.prev = & head; + tail.next = NULL; + } + + inline bool isEmpty() { + return head.next == & tail; + } + + inline void enqueueAtTail(T* entry) { + T* last = tail.prev; + last->next = entry; + entry->prev = last; + entry->next = & tail; + tail.prev = entry; + } + + inline void enqueueAtHead(T* entry) { + T* first = head.next; + head.next = entry; + entry->prev = & head; + entry->next = first; + first->prev = entry; + } + + inline void dequeue(T* entry) { + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + } + + inline T* dequeueAtHead() { + T* first = head.next; + dequeue(first); + return first; + } + }; + + /* Allocates queue entries and performs reference counting as needed. */ + class Allocator { + public: + Allocator(); + + ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime); + KeyEntry* obtainKeyEntry(nsecs_t eventTime, + int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime); + MotionEntry* obtainMotionEntry(nsecs_t eventTime, + int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, + int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, + nsecs_t downTime, uint32_t pointerCount, + const int32_t* pointerIds, const PointerCoords* pointerCoords); + DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry); + CommandEntry* obtainCommandEntry(Command command); + + void releaseEventEntry(EventEntry* entry); + void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry); + void releaseKeyEntry(KeyEntry* entry); + void releaseMotionEntry(MotionEntry* entry); + void releaseDispatchEntry(DispatchEntry* entry); + void releaseCommandEntry(CommandEntry* entry); + + void appendMotionSample(MotionEntry* motionEntry, + nsecs_t eventTime, const PointerCoords* pointerCoords); + + private: + Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool; + Pool<KeyEntry> mKeyEntryPool; + Pool<MotionEntry> mMotionEntryPool; + Pool<MotionSample> mMotionSamplePool; + Pool<DispatchEntry> mDispatchEntryPool; + Pool<CommandEntry> mCommandEntryPool; + + void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime); + }; + + /* Manages the dispatch state associated with a single input channel. */ + class Connection : public RefBase { + protected: + virtual ~Connection(); + + public: + enum Status { + // Everything is peachy. + STATUS_NORMAL, + // An unrecoverable communication error has occurred. + STATUS_BROKEN, + // The client is not responding. + STATUS_NOT_RESPONDING, + // The input channel has been unregistered. + STATUS_ZOMBIE + }; + + Status status; + sp<InputChannel> inputChannel; + InputPublisher inputPublisher; + Queue<DispatchEntry> outboundQueue; + nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none) + + nsecs_t lastEventTime; // the time when the event was originally captured + nsecs_t lastDispatchTime; // the time when the last event was dispatched + nsecs_t lastANRTime; // the time when the last ANR was recorded + + explicit Connection(const sp<InputChannel>& inputChannel); + + inline const char* getInputChannelName() const { return inputChannel->getName().string(); } + + const char* getStatusLabel() const; + + // Finds a DispatchEntry in the outbound queue associated with the specified event. + // Returns NULL if not found. + DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const; + + // Determine whether this connection has a pending synchronous dispatch target. + // Since there can only ever be at most one such target at a time, if there is one, + // it must be at the tail because nothing else can be enqueued after it. + inline bool hasPendingSyncTarget() { + return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget(); + } + + // Gets the time since the current event was originally obtained from the input driver. + inline double getEventLatencyMillis(nsecs_t currentTime) { + return (currentTime - lastEventTime) / 1000000.0; + } + + // Gets the time since the current event entered the outbound dispatch queue. + inline double getDispatchLatencyMillis(nsecs_t currentTime) { + return (currentTime - lastDispatchTime) / 1000000.0; + } + + // Gets the time since the current event ANR was declared, if applicable. + inline double getANRLatencyMillis(nsecs_t currentTime) { + return (currentTime - lastANRTime) / 1000000.0; + } + + status_t initialize(); + + void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout); + }; + + sp<InputDispatcherPolicyInterface> mPolicy; + + Mutex mLock; + + Allocator mAllocator; + sp<PollLoop> mPollLoop; + + Queue<EventEntry> mInboundQueue; + Queue<CommandEntry> mCommandQueue; + + // All registered connections mapped by receive pipe file descriptor. + KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; + + ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel); + + // Active connections are connections that have a non-empty outbound queue. + // We don't use a ref-counted pointer here because we explicitly abort connections + // during unregistration which causes the connection's outbound queue to be cleared + // and the connection itself to be deactivated. + Vector<Connection*> mActiveConnections; + + // List of connections that have timed out. Only used by dispatchOnce() + // We don't use a ref-counted pointer here because it is not possible for a connection + // to be unregistered while processing timed out connections since we hold the lock for + // the duration. + Vector<Connection*> mTimedOutConnections; + + // Preallocated key and motion event objects used only to ask the input dispatcher policy + // for the targets of an event that is to be dispatched. + KeyEvent mReusableKeyEvent; + MotionEvent mReusableMotionEvent; + + // The input targets that were most recently identified for dispatch. + // If there is a synchronous event dispatch in progress, the current input targets will + // remain unchanged until the dispatch has completed or been aborted. + Vector<InputTarget> mCurrentInputTargets; + bool mCurrentInputTargetsValid; // false while targets are being recomputed + + // Event injection and synchronization. + Condition mInjectionResultAvailableCondition; + EventEntry* createEntryFromInputEventLocked(const InputEvent* event); + void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); + + Condition mInjectionSyncFinishedCondition; + void decrementPendingSyncDispatchesLocked(EventEntry* entry); + + // Throttling state. + struct ThrottleState { + nsecs_t minTimeBetweenEvents; + + nsecs_t lastEventTime; + int32_t lastDeviceId; + uint32_t lastSource; + + uint32_t originalSampleCount; // only collected during debugging + } mThrottleState; + + // Key repeat tracking. + // XXX Move this up to the input reader instead. + struct KeyRepeatState { + KeyEntry* lastKeyEntry; // or null if no repeat + nsecs_t nextRepeatTime; + } mKeyRepeatState; + + void resetKeyRepeatLocked(); + + // Deferred command processing. + bool runCommandsLockedInterruptible(); + CommandEntry* postCommandLocked(Command command); + + // Process events that have just been dequeued from the head of the input queue. + void processConfigurationChangedLockedInterruptible( + nsecs_t currentTime, ConfigurationChangedEntry* entry); + void processKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout); + void processKeyRepeatLockedInterruptible( + nsecs_t currentTime, nsecs_t keyRepeatTimeout); + void processMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry); + + // Identify input targets for an event and dispatch to them. + void identifyInputTargetsAndDispatchKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry); + void identifyInputTargetsAndDispatchMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry); + void dispatchEventToCurrentInputTargetsLocked( + nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); + + // Manage the dispatch cycle for a single connection. + // These methods are deliberately not Interruptible because doing all of the work + // with the mutex held makes it easier to ensure that connection invariants are maintained. + // If needed, the methods post commands to run later once the critical bits are done. + void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + EventEntry* eventEntry, const InputTarget* inputTarget, + bool resumeWithAppendedMotionSample); + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, nsecs_t newTimeout); + void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + bool broken); + static bool handleReceiveCallback(int receiveFd, int events, void* data); + + // Add or remove a connection to the mActiveConnections vector. + void activateConnectionLocked(Connection* connection); + void deactivateConnectionLocked(Connection* connection); + + // Interesting events that we might like to log or tell the framework about. + void onDispatchCycleStartedLocked( + nsecs_t currentTime, const sp<Connection>& connection); + void onDispatchCycleFinishedLocked( + nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR); + void onDispatchCycleANRLocked( + nsecs_t currentTime, const sp<Connection>& connection); + void onDispatchCycleBrokenLocked( + nsecs_t currentTime, const sp<Connection>& connection); + + // Outbound policy interactions. + void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); + void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry); + void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry); +}; + +/* Enqueues and dispatches input events, endlessly. */ +class InputDispatcherThread : public Thread { +public: + explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher); + ~InputDispatcherThread(); + +private: + virtual bool threadLoop(); + + sp<InputDispatcherInterface> mDispatcher; +}; + +} // namespace android + +#endif // _UI_INPUT_DISPATCHER_PRIV_H diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h new file mode 100644 index 0000000000..4012c69135 --- /dev/null +++ b/include/ui/InputManager.h @@ -0,0 +1,172 @@ +/* + * 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 <ui/EventHub.h> +#include <ui/Input.h> +#include <utils/Errors.h> +#include <utils/Vector.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> + +namespace android { + +class InputChannel; + +class InputReaderInterface; +class InputReaderPolicyInterface; +class InputReaderThread; + +class InputDispatcherInterface; +class InputDispatcherPolicyInterface; +class InputDispatcherThread; + +/* + * 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; + + /* Registers an input channel prior to using it as the target of an event. */ + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; + + /* Unregisters an input channel. */ + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; + + /* Injects an input event and optionally waits for sync. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + */ + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0; + + /* Preempts input dispatch in progress by making pending synchronous + * dispatches asynchronous instead. This method is generally called during a focus + * transition from one application to the next so as to enable the new application + * to start receiving input as soon as possible without having to wait for the + * old application to finish up. + */ + virtual void preemptInputDispatch() = 0; + + /* Gets input device configuration. */ + virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; + + /* Gets information about the specified input device. + * Returns OK if the device information was obtained or NAME_NOT_FOUND if there + * was no such device. + */ + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; + + /* Gets the list of all registered device ids. */ + virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0; + + /* Queries current input state. */ + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) = 0; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) = 0; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw) = 0; + + /* Determines whether physical keys exist for the given framework-domain key codes. */ + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; +}; + +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 status_t registerInputChannel(const sp<InputChannel>& inputChannel); + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); + + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); + + virtual void preemptInputDispatch(); + + virtual void getInputConfiguration(InputConfiguration* outConfiguration); + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); + virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds); + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw); + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + +private: + sp<InputReaderInterface> mReader; + sp<InputReaderThread> mReaderThread; + + sp<InputDispatcherInterface> mDispatcher; + sp<InputDispatcherThread> mDispatcherThread; + + void initialize(); +}; + +} // namespace android + +#endif // _UI_INPUT_MANAGER_H diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h new file mode 100644 index 0000000000..56d2765152 --- /dev/null +++ b/include/ui/InputReader.h @@ -0,0 +1,835 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_READER_H +#define _UI_INPUT_READER_H + +#include <ui/EventHub.h> +#include <ui/Input.h> +#include <ui/InputDispatcher.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/BitSet.h> + +#include <stddef.h> +#include <unistd.h> + +namespace android { + +class InputDevice; +class InputMapper; + + +/* + * Input reader policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputReaderPolicyInterface : public virtual RefBase { +protected: + InputReaderPolicyInterface() { } + virtual ~InputReaderPolicyInterface() { } + +public: + /* Display orientations. */ + enum { + ROTATION_0 = 0, + ROTATION_90 = 1, + ROTATION_180 = 2, + ROTATION_270 = 3 + }; + + /* Actions returned by interceptXXX methods. */ + enum { + // The input dispatcher should do nothing and discard the input unless other + // flags are set. + ACTION_NONE = 0, + + // The input dispatcher should dispatch the input to the application. + ACTION_DISPATCH = 0x00000001, + + // The input dispatcher should perform special filtering in preparation for + // a pending app switch. + ACTION_APP_SWITCH_COMING = 0x00000002, + }; + + /* 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; + }; + + /* Gets information about the display with the specified id. + * Returns true if the display info is available, false otherwise. + */ + virtual bool getDisplayInfo(int32_t displayId, + int32_t* width, int32_t* height, int32_t* orientation) = 0; + + /* Provides feedback for a virtual key down. + */ + virtual void virtualKeyDownFeedback() = 0; + + /* Intercepts a key event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, + bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) = 0; + + /* Intercepts a switch event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * Switches are not dispatched to applications so this method should + * usually return ACTION_NONE. + */ + virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, + uint32_t& policyFlags) = 0; + + /* Intercepts a generic touch, trackball or other event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags) = 0; + + /* Determines whether to turn on some hacks we have to improve the touch interaction with a + * certain device whose screen currently is not all that good. + */ + virtual bool filterTouchEvents() = 0; + + /* Determines whether to turn on some hacks to improve touch interaction with another device + * where touch coordinate data can get corrupted. + */ + virtual bool filterJumpyTouchEvents() = 0; + + /* Gets the configured virtual key definitions for an input device. */ + virtual void getVirtualKeyDefinitions(const String8& deviceName, + Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0; + + /* Gets the excluded device names for the platform. */ + virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0; +}; + + +/* Processes raw input events and sends cooked event data to an input dispatcher. */ +class InputReaderInterface : public virtual RefBase { +protected: + InputReaderInterface() { } + virtual ~InputReaderInterface() { } + +public: + /* Runs a single iteration of the processing loop. + * Nominally reads and processes one incoming message from the EventHub. + * + * This method should be called on the input reader thread. + */ + virtual void loopOnce() = 0; + + /* Gets the current input device configuration. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; + + /* Gets information about the specified input device. + * Returns OK if the device information was obtained or NAME_NOT_FOUND if there + * was no such device. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; + + /* Gets the list of all registered device ids. */ + virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0; + + /* Query current input state. */ + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) = 0; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) = 0; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw) = 0; + + /* Determine whether physical keys exist for the given framework-domain key codes. */ + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; +}; + + +/* Internal interface used by individual input devices to access global input device state + * and parameters maintained by the input reader. + */ +class InputReaderContext { +protected: + InputReaderContext() { } + virtual ~InputReaderContext() { } + +public: + virtual void updateGlobalMetaState() = 0; + virtual int32_t getGlobalMetaState() = 0; + + virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputDispatcherInterface* getDispatcher() = 0; + virtual EventHubInterface* getEventHub() = 0; +}; + + +/* The input reader reads raw event data from the event hub and processes it into input events + * that it sends to the input dispatcher. Some functions of the input reader, such as early + * event filtering in low power states, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy and dispatcher can potentially block or cause re-entrance into + * the input reader, the input reader never calls into other components while holding + * an exclusive internal lock whenever re-entrance can happen. + */ +class InputReader : public InputReaderInterface, private InputReaderContext { +public: + InputReader(const sp<EventHubInterface>& eventHub, + const sp<InputReaderPolicyInterface>& policy, + const sp<InputDispatcherInterface>& dispatcher); + virtual ~InputReader(); + + virtual void loopOnce(); + + virtual void getInputConfiguration(InputConfiguration* outConfiguration); + + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); + virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds); + + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw); + + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + +private: + sp<EventHubInterface> mEventHub; + sp<InputReaderPolicyInterface> mPolicy; + sp<InputDispatcherInterface> mDispatcher; + + virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); } + virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); } + virtual EventHubInterface* getEventHub() { return mEventHub.get(); } + + // This reader/writer lock guards the list of input devices. + // The writer lock must be held whenever the list of input devices is modified + // and then promptly released. + // The reader lock must be held whenever the list of input devices is traversed or an + // input device in the list is accessed. + // This lock only protects the registry and prevents inadvertent deletion of device objects + // that are in use. Individual devices are responsible for guarding their own internal state + // as needed for concurrent operation. + RWLock mDeviceRegistryLock; + KeyedVector<int32_t, InputDevice*> mDevices; + + // low-level input event decoding and device management + void process(const RawEvent* rawEvent); + + void addDevice(nsecs_t when, int32_t deviceId); + void removeDevice(nsecs_t when, int32_t deviceId); + InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes); + void configureExcludedDevices(); + + void consumeEvent(const RawEvent* rawEvent); + + void handleConfigurationChanged(nsecs_t when); + + // state management for all devices + Mutex mStateLock; + + int32_t mGlobalMetaState; + virtual void updateGlobalMetaState(); + virtual int32_t getGlobalMetaState(); + + InputConfiguration mInputConfiguration; + void updateInputConfiguration(); + + // state queries + typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc); + bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); +}; + + +/* Reads raw events from the event hub and processes them, endlessly. */ +class InputReaderThread : public Thread { +public: + InputReaderThread(const sp<InputReaderInterface>& reader); + virtual ~InputReaderThread(); + +private: + sp<InputReaderInterface> mReader; + + virtual bool threadLoop(); +}; + + +/* Represents the state of a single input device. */ +class InputDevice { +public: + InputDevice(InputReaderContext* context, int32_t id, const String8& name); + ~InputDevice(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() { return mId; } + inline const String8& getName() { return mName; } + inline uint32_t getSources() { return mSources; } + + inline bool isIgnored() { return mMappers.isEmpty(); } + + void addMapper(InputMapper* mapper); + void configure(); + void reset(); + void process(const RawEvent* rawEvent); + + void getDeviceInfo(InputDeviceInfo* outDeviceInfo); + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + int32_t getMetaState(); + +private: + InputReaderContext* mContext; + int32_t mId; + + Vector<InputMapper*> mMappers; + + String8 mName; + uint32_t mSources; + + typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); +}; + + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + */ +class InputMapper { +public: + InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const String8 getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void configure(); + virtual void reset(); + virtual void process(const RawEvent* rawEvent) = 0; + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + bool applyStandardPolicyActions(nsecs_t when, int32_t policyActions); +}; + + +class SwitchInputMapper : public InputMapper { +public: + SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + +private: + void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue); +}; + + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources, + int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + +private: + Mutex mLock; + + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + int32_t mAssociatedDisplayId; + uint32_t mSources; + int32_t mKeyboardType; + + struct LockedState { + Vector<KeyDown> keyDowns; // keys that are down + int32_t metaState; + nsecs_t downTime; // time of most recent key down + } mLocked; + + void initializeLocked(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + + void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, + uint32_t policyFlags); + void applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, + bool down, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime); + + ssize_t findKeyDownLocked(int32_t scanCode); +}; + + +class TrackballInputMapper : public InputMapper { +public: + TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~TrackballInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + Mutex mLock; + + int32_t mAssociatedDisplayId; + + struct Accumulator { + enum { + FIELD_BTN_MOUSE = 1, + FIELD_REL_X = 2, + FIELD_REL_Y = 4 + }; + + uint32_t fields; + + bool btnMouse; + int32_t relX; + int32_t relY; + + inline void clear() { + fields = 0; + } + } mAccumulator; + + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + struct LockedState { + bool down; + nsecs_t downTime; + } mLocked; + + void initializeLocked(); + + void sync(nsecs_t when); + void applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction, + PointerCoords* pointerCoords, nsecs_t downTime); +}; + + +class TouchInputMapper : public InputMapper { +public: + TouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void configure(); + virtual void reset(); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + +protected: + /* Maximum pointer id value supported. + * (This is limited by our use of BitSet32 to track pointer assignments.) */ + static const uint32_t MAX_POINTER_ID = 31; + + Mutex mLock; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + struct PointerData { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t size; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + }; + + struct TouchData { + uint32_t pointerCount; + PointerData pointers[MAX_POINTERS]; + BitSet32 idBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + void copyFrom(const TouchData& other) { + pointerCount = other.pointerCount; + idBits = other.idBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + + int id = pointers[i].id; + idToIndex[id] = other.idToIndex[id]; + } + } + + inline void clear() { + pointerCount = 0; + idBits.clear(); + } + }; + + int32_t mAssociatedDisplayId; + + // Immutable configuration parameters. + struct Parameters { + bool useBadTouchFilter; + bool useJumpyTouchFilter; + bool useAveragingTouchFilter; + } mParameters; + + // Raw axis information. + struct Axes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo size; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + } mAxes; + + // Current and previous touch sample data. + TouchData mCurrentTouch; + TouchData mLastTouch; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + struct LockedState { + Vector<VirtualKey> virtualKeys; + + // The surface orientation and width and height set by configureSurfaceLocked(). + int32_t surfaceOrientation; + int32_t surfaceWidth, surfaceHeight; + + // Translation and scaling factors, orientation-independent. + int32_t xOrigin; + float xScale; + float xPrecision; + + int32_t yOrigin; + float yScale; + float yPrecision; + + int32_t pressureOrigin; + float pressureScale; + + int32_t sizeOrigin; + float sizeScale; + + float orientationScale; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + InputDeviceInfo::MotionRange size; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + InputDeviceInfo::MotionRange orientation; + } orientedRanges; + + // Oriented dimensions and precision. + float orientedSurfaceWidth, orientedSurfaceHeight; + float orientedXPrecision, orientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } currentVirtualKey; + } mLocked; + + virtual void configureAxes(); + virtual bool configureSurfaceLocked(); + virtual void configureVirtualKeysLocked(); + + enum TouchResult { + // Dispatch the touch normally. + DISPATCH_TOUCH, + // Do not dispatch the touch, but keep tracking the current stroke. + SKIP_TOUCH, + // Do not dispatch the touch, and drop all information associated with the current stoke + // so the next movement will appear as a new down. + DROP_STROKE + }; + + void syncTouch(nsecs_t when, bool havePointerIds); + +private: + /* Maximum number of historical samples to average. */ + static const uint32_t AVERAGING_HISTORY_SIZE = 5; + + /* Slop distance for jumpy pointer detection. + * The vertical range of the screen divided by this is our epsilon value. */ + static const uint32_t JUMPY_EPSILON_DIVISOR = 212; + + /* Number of jumpy points to drop for touchscreens that need it. */ + static const uint32_t JUMPY_TRANSITION_DROPS = 3; + static const uint32_t JUMPY_DROP_LIMIT = 3; + + /* Maximum squared distance for averaging. + * If moving farther than this, turn of averaging to avoid lag in response. */ + static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75; + + struct AveragingTouchFilterState { + // Individual history tracks are stored by pointer id + uint32_t historyStart[MAX_POINTERS]; + uint32_t historyEnd[MAX_POINTERS]; + struct { + struct { + int32_t x; + int32_t y; + int32_t pressure; + } pointers[MAX_POINTERS]; + } historyData[AVERAGING_HISTORY_SIZE]; + } mAveragingTouchFilter; + + struct JumpyTouchFilterState { + uint32_t jumpyPointsDropped; + } mJumpyTouchFilter; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + void initializeLocked(); + + TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, + BitSet32 idBits, uint32_t changedId, int32_t motionEventAction); + + void applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags, + int32_t keyCode, int32_t scanCode, nsecs_t downTime); + + bool isPointInsideSurfaceLocked(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y); + + bool applyBadTouchFilter(); + bool applyJumpyTouchFilter(); + void applyAveragingTouchFilter(); + void calculatePointerIds(); +}; + + +class SingleTouchInputMapper : public TouchInputMapper { +public: + SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~SingleTouchInputMapper(); + + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void configureAxes(); + +private: + struct Accumulator { + enum { + FIELD_BTN_TOUCH = 1, + FIELD_ABS_X = 2, + FIELD_ABS_Y = 4, + FIELD_ABS_PRESSURE = 8, + FIELD_ABS_TOOL_WIDTH = 16 + }; + + uint32_t fields; + + bool btnTouch; + int32_t absX; + int32_t absY; + int32_t absPressure; + int32_t absToolWidth; + + inline void clear() { + fields = 0; + } + } mAccumulator; + + bool mDown; + int32_t mX; + int32_t mY; + int32_t mPressure; + int32_t mSize; + + void initialize(); + + void sync(nsecs_t when); +}; + + +class MultiTouchInputMapper : public TouchInputMapper { +public: + MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~MultiTouchInputMapper(); + + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void configureAxes(); + +private: + struct Accumulator { + enum { + FIELD_ABS_MT_POSITION_X = 1, + FIELD_ABS_MT_POSITION_Y = 2, + FIELD_ABS_MT_TOUCH_MAJOR = 4, + FIELD_ABS_MT_TOUCH_MINOR = 8, + FIELD_ABS_MT_WIDTH_MAJOR = 16, + FIELD_ABS_MT_WIDTH_MINOR = 32, + FIELD_ABS_MT_ORIENTATION = 64, + FIELD_ABS_MT_TRACKING_ID = 128, + FIELD_ABS_MT_PRESSURE = 256, + }; + + uint32_t pointerCount; + struct Pointer { + uint32_t fields; + + int32_t absMTPositionX; + int32_t absMTPositionY; + int32_t absMTTouchMajor; + int32_t absMTTouchMinor; + int32_t absMTWidthMajor; + int32_t absMTWidthMinor; + int32_t absMTOrientation; + int32_t absMTTrackingId; + int32_t absMTPressure; + + inline void clear() { + fields = 0; + } + } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks + + inline void clear() { + pointerCount = 0; + pointers[0].clear(); + } + } mAccumulator; + + void initialize(); + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUT_READER_H diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h new file mode 100644 index 0000000000..31ec7019a6 --- /dev/null +++ b/include/ui/InputTransport.h @@ -0,0 +1,334 @@ +/* + * 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_TRANSPORT_H +#define _UI_INPUT_TRANSPORT_H + +/** + * Native input transport. + * + * Uses anonymous shared memory as a whiteboard for sending input events from an + * InputPublisher to an InputConsumer and ensuring appropriate synchronization. + * One interesting feature is that published events can be updated in place as long as they + * have not yet been consumed. + * + * The InputPublisher and InputConsumer only take care of transferring event data + * over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue + * build on these abstractions to add multiplexing and queueing. + */ + +#include <semaphore.h> +#include <ui/Input.h> +#include <utils/Errors.h> +#include <utils/PollLoop.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> + +namespace android { + +/* + * An input channel consists of a shared memory buffer and a pair of pipes + * used to send input messages from an InputPublisher to an InputConsumer + * across processes. Each channel has a descriptive name for debugging purposes. + * + * Each endpoint has its own InputChannel object that specifies its own file descriptors. + * + * The input channel is closed when all references to it are released. + */ +class InputChannel : public RefBase { +protected: + virtual ~InputChannel(); + +public: + InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, + int32_t sendPipeFd); + + /* Creates a pair of input channels and their underlying shared memory buffers + * and pipes. + * + * Returns OK on success. + */ + static status_t openInputChannelPair(const String8& name, + sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel); + + inline String8 getName() const { return mName; } + inline int32_t getAshmemFd() const { return mAshmemFd; } + inline int32_t getReceivePipeFd() const { return mReceivePipeFd; } + inline int32_t getSendPipeFd() const { return mSendPipeFd; } + + /* Sends a signal to the other endpoint. + * + * Returns OK on success. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t sendSignal(char signal); + + /* Receives a signal send by the other endpoint. + * (Should only call this after poll() indicates that the receivePipeFd has available input.) + * + * 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 receiveSignal(char* outSignal); + +private: + String8 mName; + int32_t mAshmemFd; + int32_t mReceivePipeFd; + int32_t mSendPipeFd; +}; + +/* + * Private intermediate representation of input events as messages written into an + * ashmem buffer. + */ +struct InputMessage { + /* Semaphore count is set to 1 when the message is published. + * It becomes 0 transiently while the publisher updates the message. + * It becomes 0 permanently when the consumer consumes the message. + */ + sem_t semaphore; + + /* Initialized to false by the publisher. + * Set to true by the consumer when it consumes the message. + */ + bool consumed; + + int32_t type; + + struct SampleData { + nsecs_t eventTime; + PointerCoords coords[0]; // variable length + }; + + int32_t deviceId; + int32_t source; + + union { + struct { + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + nsecs_t eventTime; + } key; + + struct { + int32_t action; + int32_t metaState; + int32_t edgeFlags; + nsecs_t downTime; + float xOffset; + float yOffset; + float xPrecision; + float yPrecision; + size_t pointerCount; + int32_t pointerIds[MAX_POINTERS]; + size_t sampleCount; + SampleData sampleData[0]; // variable length + } motion; + }; + + /* Gets the number of bytes to add to step to the next SampleData object in a motion + * event message for a given number of pointers. + */ + static inline size_t sampleDataStride(size_t pointerCount) { + return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords); + } + + /* Adds the SampleData stride to the given pointer. */ + static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) { + return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride); + } +}; + +/* + * Publishes input events to an anonymous shared memory buffer. + * Uses atomic operations to coordinate shared access with a single concurrent consumer. + */ +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; } + + /* Prepares the publisher for use. Must be called before it is used. + * Returns OK on success. + * + * This method implicitly calls reset(). */ + status_t initialize(); + + /* Resets the publisher to its initial state and unpins its ashmem buffer. + * Returns OK on success. + * + * Should be called after an event has been consumed to release resources used by the + * publisher until the next event is ready to be published. + */ + status_t reset(); + + /* Publishes a key event to the ashmem buffer. + * + * Returns OK on success. + * Returns INVALID_OPERATION if the publisher has not been reset. + */ + status_t publishKeyEvent( + 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 ashmem buffer. + * + * Returns OK on success. + * Returns INVALID_OPERATION if the publisher has not been reset. + * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS. + */ + status_t publishMotionEvent( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t edgeFlags, + int32_t metaState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const int32_t* pointerIds, + const PointerCoords* pointerCoords); + + /* Appends a motion sample to a motion event unless already consumed. + * + * Returns OK on success. + * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event. + * Returns FAILED_TRANSACTION if the current event has already been consumed. + * Returns NO_MEMORY if the buffer is full and no additional samples can be added. + */ + status_t appendMotionSample( + nsecs_t eventTime, + const PointerCoords* pointerCoords); + + /* Sends a dispatch signal to the consumer to inform it that a new message is available. + * + * Returns OK on success. + * Errors probably indicate that the channel is broken. + */ + status_t sendDispatchSignal(); + + /* Receives the finished signal from the consumer in reply to the original dispatch signal. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no signal present. + * Other errors probably indicate that the channel is broken. + */ + status_t receiveFinishedSignal(); + +private: + sp<InputChannel> mChannel; + + size_t mAshmemSize; + InputMessage* mSharedMessage; + bool mPinned; + bool mSemaphoreInitialized; + bool mWasDispatched; + + size_t mMotionEventPointerCount; + InputMessage::SampleData* mMotionEventSampleDataTail; + size_t mMotionEventSampleDataStride; + + status_t publishInputEvent( + int32_t type, + int32_t deviceId, + int32_t source); +}; + +/* + * Consumes input events from an anonymous shared memory buffer. + * Uses atomic operations to coordinate shared access with a single concurrent publisher. + */ +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; } + + /* Prepares the consumer for use. Must be called before it is used. */ + status_t initialize(); + + /* Consumes the input event in the buffer and copies its contents into + * an InputEvent object created using the specified factory. + * This operation will block if the publisher is updating the event. + * + * Returns OK on success. + * Returns INVALID_OPERATION if there is no currently published event. + * Returns NO_MEMORY if the event could not be created. + */ + status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent); + + /* Sends a finished signal to the publisher to inform it that the current message is + * finished processing. + * + * Returns OK on success. + * Errors probably indicate that the channel is broken. + */ + status_t sendFinishedSignal(); + + /* Receives the dispatched signal from the publisher. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no signal present. + * Other errors probably indicate that the channel is broken. + */ + status_t receiveDispatchSignal(); + +private: + sp<InputChannel> mChannel; + + size_t mAshmemSize; + InputMessage* mSharedMessage; + + void populateKeyEvent(KeyEvent* keyEvent) const; + void populateMotionEvent(MotionEvent* motionEvent) const; +}; + +} // namespace android + +#endif // _UI_INPUT_TRANSPORT_H diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index e81d0f9064..c8d6ffcbf8 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -17,6 +17,8 @@ #ifndef _UI_KEYCODE_LABELS_H #define _UI_KEYCODE_LABELS_H +#include <android/keycodes.h> + struct KeycodeLabel { const char *literal; int value; @@ -118,117 +120,28 @@ static const KeycodeLabel KEYCODES[] = { { "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 }, - // NOTE: If you add a new keycode here you must also add it to: - // (enum KeyCode, in this file) - // frameworks/base/core/java/android/view/KeyEvent.java - // tools/puppet_master/PuppetMaster.nav_keys.py - // frameworks/base/core/res/res/values/attrs.xml + // 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 } }; -// These constants need to match the above mappings. -typedef enum KeyCode { - kKeyCodeUnknown = 0, - - kKeyCodeSoftLeft = 1, - kKeyCodeSoftRight = 2, - kKeyCodeHome = 3, - kKeyCodeBack = 4, - kKeyCodeCall = 5, - kKeyCodeEndCall = 6, - kKeyCode0 = 7, - kKeyCode1 = 8, - kKeyCode2 = 9, - kKeyCode3 = 10, - kKeyCode4 = 11, - kKeyCode5 = 12, - kKeyCode6 = 13, - kKeyCode7 = 14, - kKeyCode8 = 15, - kKeyCode9 = 16, - kKeyCodeStar = 17, - kKeyCodePound = 18, - kKeyCodeDpadUp = 19, - kKeyCodeDpadDown = 20, - kKeyCodeDpadLeft = 21, - kKeyCodeDpadRight = 22, - kKeyCodeDpadCenter = 23, - kKeyCodeVolumeUp = 24, - kKeyCodeVolumeDown = 25, - kKeyCodePower = 26, - kKeyCodeCamera = 27, - kKeyCodeClear = 28, - kKeyCodeA = 29, - kKeyCodeB = 30, - kKeyCodeC = 31, - kKeyCodeD = 32, - kKeyCodeE = 33, - kKeyCodeF = 34, - kKeyCodeG = 35, - kKeyCodeH = 36, - kKeyCodeI = 37, - kKeyCodeJ = 38, - kKeyCodeK = 39, - kKeyCodeL = 40, - kKeyCodeM = 41, - kKeyCodeN = 42, - kKeyCodeO = 43, - kKeyCodeP = 44, - kKeyCodeQ = 45, - kKeyCodeR = 46, - kKeyCodeS = 47, - kKeyCodeT = 48, - kKeyCodeU = 49, - kKeyCodeV = 50, - kKeyCodeW = 51, - kKeyCodeX = 52, - kKeyCodeY = 53, - kKeyCodeZ = 54, - kKeyCodeComma = 55, - kKeyCodePeriod = 56, - kKeyCodeAltLeft = 57, - kKeyCodeAltRight = 58, - kKeyCodeShiftLeft = 59, - kKeyCodeShiftRight = 60, - kKeyCodeTab = 61, - kKeyCodeSpace = 62, - kKeyCodeSym = 63, - kKeyCodeExplorer = 64, - kKeyCodeEnvelope = 65, - kKeyCodeNewline = 66, - kKeyCodeDel = 67, - kKeyCodeGrave = 68, - kKeyCodeMinus = 69, - kKeyCodeEquals = 70, - kKeyCodeLeftBracket = 71, - kKeyCodeRightBracket = 72, - kKeyCodeBackslash = 73, - kKeyCodeSemicolon = 74, - kKeyCodeApostrophe = 75, - kKeyCodeSlash = 76, - kKeyCodeAt = 77, - kKeyCodeNum = 78, - kKeyCodeHeadSetHook = 79, - kKeyCodeFocus = 80, - kKeyCodePlus = 81, - kKeyCodeMenu = 82, - kKeyCodeNotification = 83, - kKeyCodeSearch = 84, - kKeyCodePlayPause = 85, - kKeyCodeStop = 86, - kKeyCodeNextSong = 87, - kKeyCodePreviousSong = 88, - kKeyCodeRewind = 89, - kKeyCodeForward = 90, - kKeyCodeMute = 91, - kKeyCodePageUp = 92, - kKeyCodePageDown = 93, - kKeyCodePictSymbols = 94, - kKeyCodeSwitchCharset = 95 -} KeyCode; - static const KeycodeLabel FLAGS[] = { { "WAKE", 0x00000001 }, { "WAKE_DROPPED", 0x00000002 }, diff --git a/include/ui/Rect.h b/include/ui/Rect.h index a213c09309..4e65a2d0b9 100644 --- a/include/ui/Rect.h +++ b/include/ui/Rect.h @@ -20,31 +20,28 @@ #include <utils/TypeHelpers.h> #include <ui/Point.h> +#include <android/rect.h> + namespace android { -class Rect +class Rect : public ARect { public: - int left; - int top; - int right; - int bottom; - - typedef int value_type; + typedef int32_t value_type; // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions inline Rect() { } - inline Rect(int w, int h) - : left(0), top(0), right(w), bottom(h) { + inline Rect(int32_t w, int32_t h) { + left = top = 0; right = w; bottom = h; } - inline Rect(int l, int t, int r, int b) - : left(l), top(t), right(r), bottom(b) { + inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) { + left = l; top = t; right = r; bottom = b; } - inline Rect(const Point& lt, const Point& rb) - : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) { + inline Rect(const Point& lt, const Point& rb) { + left = lt.x; top = lt.y; right = rb.x; bottom = rb.y; } void makeInvalid(); @@ -68,12 +65,12 @@ public: } // rectangle's width - inline int width() const { + inline int32_t width() const { return right-left; } // rectangle's height - inline int height() const { + inline int32_t height() const { return bottom-top; } @@ -136,12 +133,12 @@ public: const Rect operator + (const Point& rhs) const; const Rect operator - (const Point& rhs) const; - void translate(int dx, int dy) { // legacy, don't use. + void translate(int32_t dx, int32_t dy) { // legacy, don't use. offsetBy(dx, dy); } - Rect& offsetTo(int x, int y); - Rect& offsetBy(int x, int y); + Rect& offsetTo(int32_t x, int32_t y); + Rect& offsetBy(int32_t x, int32_t y); bool intersect(const Rect& with, Rect* result) const; }; diff --git a/include/ui/android_native_buffer.h b/include/ui/android_native_buffer.h index 9c92af8845..402843e205 100644 --- a/include/ui/android_native_buffer.h +++ b/include/ui/android_native_buffer.h @@ -33,6 +33,15 @@ typedef struct android_native_buffer_t common.version = sizeof(android_native_buffer_t); memset(common.reserved, 0, sizeof(common.reserved)); } + + // Implement the methods that sp<android_native_buffer_t> expects so that it + // can be used to automatically refcount android_native_buffer_t's. + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } #endif struct android_native_base_t common; diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h index 773fd9301f..d59d72b7e5 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -22,6 +22,8 @@ #include <hardware/gralloc.h> +#include <android/native_window.h> + #ifdef __cplusplus extern "C" { #endif @@ -41,6 +43,14 @@ extern "C" { struct android_native_buffer_t; +typedef struct android_native_rect_t +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} android_native_rect_t; + // --------------------------------------------------------------------------- typedef struct android_native_base_t @@ -63,15 +73,19 @@ typedef struct android_native_base_t /* attributes queriable with query() */ enum { NATIVE_WINDOW_WIDTH = 0, - NATIVE_WINDOW_HEIGHT = 1, - NATIVE_WINDOW_FORMAT = 2, + NATIVE_WINDOW_HEIGHT, + NATIVE_WINDOW_FORMAT, }; /* valid operations for the (*perform)() hook */ enum { NATIVE_WINDOW_SET_USAGE = 0, - NATIVE_WINDOW_CONNECT = 1, - NATIVE_WINDOW_DISCONNECT = 2 + NATIVE_WINDOW_CONNECT, + NATIVE_WINDOW_DISCONNECT, + NATIVE_WINDOW_SET_CROP, + NATIVE_WINDOW_SET_BUFFER_COUNT, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, }; /* parameter for NATIVE_WINDOW_[DIS]CONNECT */ @@ -79,16 +93,39 @@ enum { NATIVE_WINDOW_API_EGL = 1 }; -typedef struct android_native_window_t +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ +enum { + /* flip source image horizontally */ + NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , + /* flip source image vertically */ + NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, + /* rotate source image 90 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, + /* rotate source image 180 degrees */ + NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, + /* rotate source image 270 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, +}; + +struct ANativeWindow { #ifdef __cplusplus - android_native_window_t() + ANativeWindow() : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) { common.magic = ANDROID_NATIVE_WINDOW_MAGIC; - common.version = sizeof(android_native_window_t); + common.version = sizeof(ANativeWindow); memset(common.reserved, 0, sizeof(common.reserved)); } + + // Implement the methods that sp<ANativeWindow> expects so that it + // can be used to automatically refcount ANativeWindow's. + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } #endif struct android_native_base_t common; @@ -115,7 +152,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*setSwapInterval)(struct android_native_window_t* window, + int (*setSwapInterval)(struct ANativeWindow* window, int interval); /* @@ -125,7 +162,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*dequeueBuffer)(struct android_native_window_t* window, + int (*dequeueBuffer)(struct ANativeWindow* window, struct android_native_buffer_t** buffer); /* @@ -135,7 +172,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*lockBuffer)(struct android_native_window_t* window, + int (*lockBuffer)(struct ANativeWindow* window, struct android_native_buffer_t* buffer); /* * hook called by EGL when modifications to the render buffer are done. @@ -145,7 +182,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*queueBuffer)(struct android_native_window_t* window, + int (*queueBuffer)(struct ANativeWindow* window, struct android_native_buffer_t* buffer); /* @@ -153,13 +190,13 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*query)(struct android_native_window_t* window, + int (*query)(struct ANativeWindow* window, int what, int* value); /* * hook used to perform various operations on the surface. * (*perform)() is a generic mechanism to add functionality to - * android_native_window_t while keeping backward binary compatibility. + * ANativeWindow while keeping backward binary compatibility. * * This hook should not be called directly, instead use the helper functions * defined below. @@ -171,19 +208,26 @@ typedef struct android_native_window_t * NATIVE_WINDOW_SET_USAGE * NATIVE_WINDOW_CONNECT * NATIVE_WINDOW_DISCONNECT + * NATIVE_WINDOW_SET_CROP + * NATIVE_WINDOW_SET_BUFFER_COUNT + * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY + * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM * */ - int (*perform)(struct android_native_window_t* window, + int (*perform)(struct ANativeWindow* window, int operation, ... ); void* reserved_proc[3]; -} android_native_window_t; +}; +// Backwards compatibility... please switch to ANativeWindow. +typedef struct ANativeWindow android_native_window_t; /* - * native_window_set_usage() sets the intended usage flags for the next - * buffers acquired with (*lockBuffer)() and on. + * native_window_set_usage(..., usage) + * Sets the intended usage flags for the next buffers + * acquired with (*lockBuffer)() and on. * By default (if this function is never called), a usage of * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE * is assumed. @@ -192,35 +236,96 @@ typedef struct android_native_window_t */ static inline int native_window_set_usage( - android_native_window_t* window, int usage) + ANativeWindow* window, int usage) { return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage); } /* - * native_window_connect(..., NATIVE_WINDOW_API_EGL) must be called - * by EGL when the window is made current. + * native_window_connect(..., NATIVE_WINDOW_API_EGL) + * Must be called by EGL when the window is made current. * Returns -EINVAL if for some reason the window cannot be connected, which * can happen if it's connected to some other API. */ static inline int native_window_connect( - android_native_window_t* window, int api) + ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_CONNECT, api); } /* - * native_window_disconnect(..., NATIVE_WINDOW_API_EGL) must be called - * by EGL when the window is made not current. + * native_window_disconnect(..., NATIVE_WINDOW_API_EGL) + * Must be called by EGL when the window is made not current. * An error is returned if for instance the window wasn't connected in the * first place. */ static inline int native_window_disconnect( - android_native_window_t* window, int api) + ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_DISCONNECT, api); } +/* + * native_window_set_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * A buffer's crop region is scaled to match the surface's size. + * + * The specified crop region applies to all buffers queued after it is called. + * + * if 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, + * out of the buffer's bound or if the window is invalid. + */ +static inline int native_window_set_crop( + ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); +} + +/* + * native_window_set_buffer_count(..., count) + * Sets the number of buffers associated with this native window. + */ +static inline int native_window_set_buffer_count( + ANativeWindow* window, + size_t bufferCount) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); +} + +/* + * native_window_set_buffers_geometry(..., int w, int h, int format) + * All buffers dequeued after this call will have the geometry specified. + * In particular, all buffers will have a fixed-size, independent form the + * native-window size. They will be appropriately scaled to the window-size + * upon composition. + * + * If all parameters are 0, the normal behavior is restored. That is, + * dequeued buffers following this call will be sized to the window's size. + * + */ +static inline int native_window_set_buffers_geometry( + ANativeWindow* window, + int w, int h, int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + w, h, format); +} + +/* + * native_window_set_buffers_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified. + */ +static inline int native_window_set_buffers_transform( + ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + transform); +} // --------------------------------------------------------------------------- @@ -263,6 +368,15 @@ namespace android { template <typename NATIVE_TYPE, typename TYPE, typename REF> class EGLNativeBase : public NATIVE_TYPE, public REF { +public: + // Disambiguate between the incStrong in REF and NATIVE_TYPE + void incStrong(const void* id) const { + REF::incStrong(id); + } + void decStrong(const void* id) const { + REF::decStrong(id); + } + protected: typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE; EGLNativeBase() : NATIVE_TYPE(), REF() { diff --git a/include/utils/Asset.h b/include/utils/Asset.h index 5908bccedd..2a09095bde 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -61,15 +61,6 @@ public: ACCESS_BUFFER, } AccessMode; - enum { - /* data larger than this does not get uncompressed into a buffer */ -#ifdef HAVE_ANDROID_OS - UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 -#else - UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 -#endif - }; - /* * Read data from the current offset. Returns the actual number of * bytes read, 0 on EOF, or -1 on error. @@ -317,6 +308,8 @@ private: FileMap* mMap; // for memory-mapped input int mFd; // for file input + class StreamingZipInflater* mZipInflater; // for streaming large compressed assets + unsigned char* mBuf; // for getBuffer() }; diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index d8994e05a0..9e2bf37e8f 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -29,6 +29,24 @@ #include <utils/ZipFileRO.h> #include <utils/threads.h> +/* + * Native-app access is via the opaque typedef struct AAssetManager in the C namespace. + */ +#ifdef __cplusplus +extern "C" { +#endif + +struct AAssetManager { }; + +#ifdef __cplusplus +}; +#endif + + +/* + * Now the proper C++ android-namespace definitions + */ + namespace android { class Asset; // fwd decl for things that include Asset.h first @@ -48,7 +66,7 @@ struct ResTable_config; * The asset hierarchy may be examined like a filesystem, using * AssetDir objects to peruse a single directory. */ -class AssetManager { +class AssetManager : public AAssetManager { public: typedef enum CacheMode { CACHE_UNKNOWN = 0, @@ -111,6 +129,8 @@ public: */ void setConfiguration(const ResTable_config& config, const char* locale = NULL); + void getConfiguration(ResTable_config* outConfig) const; + typedef Asset::AccessMode AccessMode; // typing shortcut /* diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h new file mode 100644 index 0000000000..19c8bf0930 --- /dev/null +++ b/include/utils/BitSet.h @@ -0,0 +1,67 @@ +/* + * 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_BITSET_H +#define UTILS_BITSET_H + +#include <stdint.h> + +/* + * Contains some bit manipulation helpers. + */ + +namespace android { + +// A simple set of 32 bits that can be individually marked or cleared. +struct BitSet32 { + uint32_t value; + + inline BitSet32() : value(0) { } + explicit inline BitSet32(uint32_t value) : value(value) { } + + // Gets the value associated with a particular bit index. + static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } + + // Clears the bit set. + inline void clear() { value = 0; } + + // Returns true if the bit set does not contain any marked bits. + inline bool isEmpty() const { return ! value; } + + // Returns true if the specified bit is marked. + inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } + + // Marks the specified bit. + inline void markBit(uint32_t n) { value |= valueForBit(n); } + + // Clears the specified bit. + inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } + + // Finds the first marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } + + // Finds the first unmarked bit in the set. + // Result is undefined if all bits are marked. + inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + + inline bool operator== (const BitSet32& other) const { return value == other.value; } + inline bool operator!= (const BitSet32& other) const { return value != other.value; } +}; + +} // namespace android + +#endif // UTILS_BITSET_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h deleted file mode 100644 index 8e22b0f216..0000000000 --- a/include/utils/Buffer.h +++ /dev/null @@ -1,107 +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 __UTILS_BUFFER_H__ -#define __UTILS_BUFFER_H__ 1 - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -namespace android { - -class Buffer -{ -private: - char *buf; - int bufsiz; - int used; - void ensureCapacity(int len); - - void - makeRoomFor(int len) - { - if (len + used >= bufsiz) { - bufsiz = (len + used) * 3/2 + 2; - char *blah = new char[bufsiz]; - - memcpy(blah, buf, used); - delete[] buf; - buf = blah; - } - } - -public: - Buffer() - { - bufsiz = 16; - buf = new char[bufsiz]; - clear(); - } - - ~Buffer() - { - delete[] buf; - } - - void - clear() - { - buf[0] = '\0'; - used = 0; - } - - int - length() - { - return used; - } - - void - append(const char c) - { - makeRoomFor(1); - buf[used] = c; - used++; - buf[used] = '\0'; - } - - void - append(const char *s, int len) - { - makeRoomFor(len); - - memcpy(buf + used, s, len); - used += len; - buf[used] = '\0'; - } - - void - append(const char *s) - { - append(s, strlen(s)); - } - - char * - getBytes() - { - return buf; - } -}; - -}; // namespace android - -#endif diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h new file mode 100644 index 0000000000..5243f50051 --- /dev/null +++ b/include/utils/ObbFile.h @@ -0,0 +1,118 @@ +/* + * 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 OBBFILE_H_ +#define OBBFILE_H_ + +#include <stdint.h> +#include <strings.h> + +#include <utils/RefBase.h> +#include <utils/String8.h> + +namespace android { + +// OBB flags (bit 0) +#define OBB_OVERLAY (1 << 0) + +class ObbFile : public RefBase { +protected: + virtual ~ObbFile(); + +public: + ObbFile(); + + bool readFrom(const char* filename); + bool readFrom(int fd); + bool writeTo(const char* filename); + bool writeTo(int fd); + bool removeFrom(const char* filename); + bool removeFrom(int fd); + + const char* getFileName() const { + return mFileName; + } + + const String8 getPackageName() const { + return mPackageName; + } + + void setPackageName(String8 packageName) { + mPackageName = packageName; + } + + int32_t getVersion() const { + return mVersion; + } + + void setVersion(int32_t version) { + mVersion = version; + } + + int32_t getFlags() const { + return mFlags; + } + + void setFlags(int32_t flags) { + mFlags = flags; + } + + bool isOverlay() { + return (mFlags & OBB_OVERLAY) == OBB_OVERLAY; + } + + void setOverlay(bool overlay) { + if (overlay) { + mFlags |= OBB_OVERLAY; + } else { + mFlags &= ~OBB_OVERLAY; + } + } + + static inline uint32_t get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + + static inline void put4LE(unsigned char* buf, uint32_t val) { + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + buf[2] = (val >> 16) & 0xFF; + buf[3] = (val >> 24) & 0xFF; + } + +private: + /* Package name this ObbFile is associated with */ + String8 mPackageName; + + /* Package version this ObbFile is associated with */ + int32_t mVersion; + + /* Flags for this OBB type. */ + int32_t mFlags; + + const char* mFileName; + + size_t mFileSize; + + size_t mFooterStart; + + unsigned char* mReadBuf; + + bool parseObbFile(int fd); +}; + +} +#endif /* OBBFILE_H_ */ diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h new file mode 100644 index 0000000000..81230e84dd --- /dev/null +++ b/include/utils/PollLoop.h @@ -0,0 +1,197 @@ +/* + * 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_POLL_LOOP_H +#define UTILS_POLL_LOOP_H + +#include <utils/Vector.h> +#include <utils/threads.h> + +#include <sys/poll.h> + +#include <android/looper.h> + +struct ALooper : public android::RefBase { +protected: + virtual ~ALooper() { } + +public: + ALooper() { } +}; + +namespace android { + +/** + * A basic file descriptor polling loop based on poll() with callbacks. + */ +class PollLoop : public ALooper { +protected: + virtual ~PollLoop(); + +public: + PollLoop(bool allowNonCallbacks); + + /** + * A callback that it to be invoked when an event occurs on a file descriptor. + * Specifies the events that were triggered and the user data provided when the + * callback was set. + * + * Returns true if the callback should be kept, false if it should be removed automatically + * after the callback returns. + */ + typedef bool (*Callback)(int fd, int events, void* data); + + enum { + POLL_CALLBACK = ALOOPER_POLL_CALLBACK, + POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, + POLL_ERROR = ALOOPER_POLL_ERROR, + }; + + /** + * Performs a single call to poll() with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until awoken. + * + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method must only be called on the thread owning the PollLoop. + * This method blocks until either a file descriptor is signalled, a timeout occurs, + * or wake() is called. + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ + int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); + + /** + * Wakes the loop asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. + */ + void wake(); + + /** + * Control whether this PollLoop instance allows using IDs instead + * of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** + * Sets the callback for a file descriptor, replacing the existing one, if any. + * It is an error to call this method with events == 0 or callback == NULL. + * + * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events + * even if it is not explicitly requested when registered. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll loop. + */ + void setCallback(int fd, int events, Callback callback, void* data = NULL); + + /** + * Like setCallback(), but for the NDK callback function. + */ + void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data); + + /** + * Removes the callback for a file descriptor, if one exists. + * + * When this method returns, it is safe to close the file descriptor since the poll loop + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning false or calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll loop. + * + * Returns true if a callback was actually removed, false if none was registered. + */ + bool removeCallback(int fd); + + /** + * Set the given PollLoop to be associated with the + * calling thread. There must be a 1:1 relationship between + * PollLoop and thread. + */ + static void setForThread(const sp<PollLoop>& pollLoop); + + /** + * Return the PollLoop associated with the calling thread. + */ + static sp<PollLoop> getForThread(); + +private: + struct RequestedCallback { + Callback callback; + ALooper_callbackFunc* looperCallback; + void* data; + }; + + struct PendingCallback { + int fd; + int events; + Callback callback; + ALooper_callbackFunc* looperCallback; + void* data; + }; + + const bool mAllowNonCallbacks; + + Mutex mLock; + bool mPolling; + uint32_t mWaiters; + Condition mAwake; + Condition mResume; + + int mWakeReadPipeFd; + int mWakeWritePipeFd; + + Vector<struct pollfd> mRequestedFds; + Vector<RequestedCallback> mRequestedCallbacks; + + Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce + Vector<PendingCallback> mPendingFds; // used privately by pollOnce + size_t mPendingFdsPos; + + void openWakePipe(); + void closeWakePipe(); + + void setCallbackCommon(int fd, int events, Callback callback, + ALooper_callbackFunc* looperCallback, void* data); + ssize_t getRequestIndexLocked(int fd); + void wakeAndLock(); + static void threadDestructor(void *st); +}; + +} // namespace android + +#endif // UTILS_POLL_LOOP_H diff --git a/include/utils/Pool.h b/include/utils/Pool.h new file mode 100644 index 0000000000..2ee768eef1 --- /dev/null +++ b/include/utils/Pool.h @@ -0,0 +1,71 @@ +/* + * 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_POOL_H +#define UTILS_POOL_H + +#include <utils/TypeHelpers.h> + +namespace android { + +class PoolImpl { +public: + PoolImpl(size_t objSize); + ~PoolImpl(); + + void* allocImpl(); + void freeImpl(void* obj); + +private: + size_t mObjSize; +}; + +/* + * A homogeneous typed memory pool for fixed size objects. + * Not intended to be thread-safe. + */ +template<typename T> +class Pool : private PoolImpl { +public: + /* Creates an initially empty pool. */ + Pool() : PoolImpl(sizeof(T)) { } + + /* Destroys the pool. + * Assumes that the pool is empty. */ + ~Pool() { } + + /* Allocates an object from the pool, growing the pool if needed. */ + inline T* alloc() { + void* mem = allocImpl(); + if (! traits<T>::has_trivial_ctor) { + return new (mem) T(); + } else { + return static_cast<T*>(mem); + } + } + + /* Frees an object from the pool. */ + inline void free(T* obj) { + if (! traits<T>::has_trivial_dtor) { + obj->~T(); + } + freeImpl(obj); + } +}; + +} // namespace android + +#endif // UTILS_POOL_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index bd7f28cae3..9c64ac0443 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -333,9 +333,10 @@ sp<T>::~sp() template<typename T> sp<T>& sp<T>::operator = (const sp<T>& other) { - if (other.m_ptr) other.m_ptr->incStrong(this); + T* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; + m_ptr = otherPtr; return *this; } @@ -351,9 +352,10 @@ sp<T>& sp<T>::operator = (T* other) template<typename T> template<typename U> sp<T>& sp<T>::operator = (const sp<U>& other) { - if (other.m_ptr) other.m_ptr->incStrong(this); + U* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; + m_ptr = otherPtr; return *this; } @@ -466,10 +468,12 @@ wp<T>& wp<T>::operator = (T* other) template<typename T> wp<T>& wp<T>::operator = (const wp<T>& other) { - if (other.m_ptr) other.m_refs->incWeak(this); + weakref_type* otherRefs(other.m_refs); + T* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; + m_ptr = otherPtr; + m_refs = otherRefs; return *this; } @@ -478,8 +482,9 @@ wp<T>& wp<T>::operator = (const sp<T>& other) { weakref_type* newRefs = other != NULL ? other->createWeak(this) : 0; + T* otherPtr(other.m_ptr); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); + m_ptr = otherPtr; m_refs = newRefs; return *this; } @@ -498,10 +503,12 @@ wp<T>& wp<T>::operator = (U* other) template<typename T> template<typename U> wp<T>& wp<T>::operator = (const wp<U>& other) { - if (other.m_ptr) other.m_refs->incWeak(this); + weakref_type* otherRefs(other.m_refs); + U* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; + m_ptr = otherPtr; + m_refs = otherRefs; return *this; } @@ -510,8 +517,9 @@ wp<T>& wp<T>::operator = (const sp<U>& other) { weakref_type* newRefs = other != NULL ? other->createWeak(this) : 0; + U* otherPtr(other.m_ptr); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); + m_ptr = otherPtr; m_refs = newRefs; return *this; } diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index b701ce74d6..da86da4100 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -31,6 +31,8 @@ #include <stdint.h> #include <sys/types.h> +#include <android/configuration.h> + namespace android { /** ******************************************************************** @@ -822,25 +824,25 @@ struct ResTable_config }; enum { - ORIENTATION_ANY = 0x0000, - ORIENTATION_PORT = 0x0001, - ORIENTATION_LAND = 0x0002, - ORIENTATION_SQUARE = 0x0003, + ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY, + ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT, + ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND, + ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE, }; enum { - TOUCHSCREEN_ANY = 0x0000, - TOUCHSCREEN_NOTOUCH = 0x0001, - TOUCHSCREEN_STYLUS = 0x0002, - TOUCHSCREEN_FINGER = 0x0003, + TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY, + TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH, + TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS, + TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER, }; enum { - DENSITY_DEFAULT = 0, - DENSITY_LOW = 120, - DENSITY_MEDIUM = 160, - DENSITY_HIGH = 240, - DENSITY_NONE = 0xffff + DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT, + DENSITY_LOW = ACONFIGURATION_DENSITY_LOW, + DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, + DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, + DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; union { @@ -853,33 +855,34 @@ struct ResTable_config }; enum { - KEYBOARD_ANY = 0x0000, - KEYBOARD_NOKEYS = 0x0001, - KEYBOARD_QWERTY = 0x0002, - KEYBOARD_12KEY = 0x0003, + KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY, + KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS, + KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY, + KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY, }; enum { - NAVIGATION_ANY = 0x0000, - NAVIGATION_NONAV = 0x0001, - NAVIGATION_DPAD = 0x0002, - NAVIGATION_TRACKBALL = 0x0003, - NAVIGATION_WHEEL = 0x0004, + NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY, + NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV, + NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD, + NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL, + NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL, }; enum { MASK_KEYSHIDDEN = 0x0003, - KEYSHIDDEN_ANY = 0x0000, - KEYSHIDDEN_NO = 0x0001, - KEYSHIDDEN_YES = 0x0002, - KEYSHIDDEN_SOFT = 0x0003, + KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY, + KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO, + KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES, + KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT, }; enum { MASK_NAVHIDDEN = 0x000c, - NAVHIDDEN_ANY = 0x0000, - NAVHIDDEN_NO = 0x0004, - NAVHIDDEN_YES = 0x0008, + SHIFT_NAVHIDDEN = 2, + NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN, + NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN, + NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN, }; union { @@ -929,31 +932,34 @@ struct ResTable_config enum { // screenLayout bits for screen size class. MASK_SCREENSIZE = 0x0f, - SCREENSIZE_ANY = 0x00, - SCREENSIZE_SMALL = 0x01, - SCREENSIZE_NORMAL = 0x02, - SCREENSIZE_LARGE = 0x03, + SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY, + SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL, + SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL, + SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE, + SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE, // screenLayout bits for wide/long screen variation. MASK_SCREENLONG = 0x30, - SCREENLONG_ANY = 0x00, - SCREENLONG_NO = 0x10, - SCREENLONG_YES = 0x20, + SHIFT_SCREENLONG = 4, + SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG, + SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG, + SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG, }; enum { // uiMode bits for the mode type. MASK_UI_MODE_TYPE = 0x0f, - UI_MODE_TYPE_ANY = 0x00, - UI_MODE_TYPE_NORMAL = 0x01, - UI_MODE_TYPE_DESK = 0x02, - UI_MODE_TYPE_CAR = 0x03, + UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY, + UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, + UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, + UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, - UI_MODE_NIGHT_ANY = 0x00, - UI_MODE_NIGHT_NO = 0x10, - UI_MODE_NIGHT_YES = 0x20, + SHIFT_UI_MODE_NIGHT = 4, + UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT, }; union { @@ -1022,19 +1028,19 @@ struct ResTable_config // match the corresponding ones in android.content.pm.ActivityInfo and // attrs_manifest.xml. enum { - CONFIG_MCC = 0x0001, - CONFIG_MNC = 0x0002, - CONFIG_LOCALE = 0x0004, - CONFIG_TOUCHSCREEN = 0x0008, - CONFIG_KEYBOARD = 0x0010, - CONFIG_KEYBOARD_HIDDEN = 0x0020, - CONFIG_NAVIGATION = 0x0040, - CONFIG_ORIENTATION = 0x0080, - CONFIG_DENSITY = 0x0100, - CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400, - CONFIG_SCREEN_LAYOUT = 0x0800, - CONFIG_UI_MODE = 0x1000 + CONFIG_MCC = ACONFIGURATION_MCC, + CONFIG_MNC = ACONFIGURATION_MCC, + CONFIG_LOCALE = ACONFIGURATION_LOCALE, + CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN, + CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD, + CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN, + CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION, + CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION, + CONFIG_DENSITY = ACONFIGURATION_DENSITY, + CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE, + CONFIG_VERSION = ACONFIGURATION_VERSION, + CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT, + CONFIG_UI_MODE = ACONFIGURATION_UI_MODE }; // Compare two configuration, returning CONFIG_* flags set for each value @@ -1208,7 +1214,28 @@ struct ResTable_config if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 && (requested->screenLayout & MASK_SCREENSIZE)) { - return (screenLayout & MASK_SCREENSIZE); + // A little backwards compatibility here: undefined is + // considered equivalent to normal. But only if the + // requested size is at least normal; otherwise, small + // is better than the default. + int mySL = (screenLayout & MASK_SCREENSIZE); + int oSL = (o.screenLayout & MASK_SCREENSIZE); + int fixedMySL = mySL; + int fixedOSL = oSL; + if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { + if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; + if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; + } + // For screen size, the best match is the one that is + // closest to the requested screen size, but not over + // (the not over part is dealt with in match() below). + if (fixedMySL == fixedOSL) { + // If the two are the same, but 'this' is actually + // undefined, then the other is really a better match. + if (mySL == 0) return false; + return true; + } + return fixedMySL >= fixedOSL; } if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 && (requested->screenLayout & MASK_SCREENLONG)) { @@ -1370,8 +1397,11 @@ struct ResTable_config if (screenConfig != 0) { const int screenSize = screenLayout&MASK_SCREENSIZE; const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - if (setScreenSize != 0 && screenSize != 0 - && screenSize != setScreenSize) { + // Any screen sizes for larger screens than the setting do not + // match. + if ((setScreenSize != 0 && screenSize != 0 + && screenSize > setScreenSize) || + (setScreenSize == 0 && screenSize != 0)) { return false; } diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index bc7626a821..3b975b4c44 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -54,11 +54,13 @@ private: * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes, * and avoid to have a copy of them in each compilation units Singleton<TYPE> * is used. + * NOTE: we use a version of Mutex ctor that takes a parameter, because + * for some unknown reason using the default ctor doesn't emit the variable! */ -#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ - template class Singleton< TYPE >; \ - template< class TYPE > Mutex Singleton< TYPE >::sLock; \ +#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ + template class Singleton< TYPE >; \ + template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \ template<> TYPE* Singleton< TYPE >::sInstance(0); diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h index cc0bebc404..693dd3ccfc 100644 --- a/include/utils/StopWatch.h +++ b/include/utils/StopWatch.h @@ -37,6 +37,8 @@ public: const char* name() const; nsecs_t lap(); nsecs_t elapsedTime() const; + + void reset(); private: const char* mName; diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h new file mode 100644 index 0000000000..16867d8db2 --- /dev/null +++ b/include/utils/StreamingZipInflater.h @@ -0,0 +1,82 @@ +/* + * 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 __LIBS_STREAMINGZIPINFLATER_H +#define __LIBS_STREAMINGZIPINFLATER_H + +#include <unistd.h> +#include <inttypes.h> +#include <zlib.h> + +namespace android { + +class StreamingZipInflater { +public: + static const size_t INPUT_CHUNK_SIZE = 64 * 1024; + static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024; + + // Flavor that pages in the compressed data from a fd + StreamingZipInflater(int fd, off_t compDataStart, size_t uncompSize, size_t compSize); + + // Flavor that gets the compressed data from an in-memory buffer + StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); + + ~StreamingZipInflater(); + + // read 'count' bytes of uncompressed data from the current position. outBuf may + // be NULL, in which case the data is consumed and discarded. + ssize_t read(void* outBuf, size_t count); + + // seeking backwards requires uncompressing fom the beginning, so is very + // expensive. seeking forwards only requires uncompressing from the current + // position to the destination. + off_t seekAbsolute(off_t absoluteInputPosition); + +private: + void initInflateState(); + int readNextChunk(); + + // where to find the uncompressed data + int mFd; + off_t mInFileStart; // where the compressed data lives in the file + class FileMap* mDataMap; + + z_stream mInflateState; + bool mStreamNeedsInit; + + // output invariants for this asset + uint8_t* mOutBuf; // output buf for decompressed bytes + size_t mOutBufSize; // allocated size of mOutBuf + size_t mOutTotalSize; // total uncompressed size of the blob + + // current output state bookkeeping + off_t mOutCurPosition; // current position in total offset + size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf + size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf + + // input invariants + uint8_t* mInBuf; + size_t mInBufSize; // allocated size of mInBuf; + size_t mInTotalSize; // total size of compressed data for this blob + + // input state bookkeeping + size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies + // the z_stream contains state about input block consumption +}; + +} + +#endif diff --git a/include/utils/String8.h b/include/utils/String8.h index c4b18a4caa..ef0b51a443 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -171,6 +171,9 @@ public: status_t append(const char* other); status_t append(const char* other, size_t numChars); + status_t appendFormat(const char* fmt, ...) + __attribute__((format (printf, 2, 3))); + // Note that this function takes O(N) time to calculate the value. // No cache value is stored. size_t getUtf32Length() const; @@ -372,7 +375,7 @@ inline String8& String8::operator+=(const String8& other) inline String8 String8::operator+(const String8& other) const { - String8 tmp; + String8 tmp(*this); tmp += other; return tmp; } @@ -385,7 +388,7 @@ inline String8& String8::operator+=(const char* other) inline String8 String8::operator+(const char* other) const { - String8 tmp; + String8 tmp(*this); tmp += other; return tmp; } diff --git a/include/utils/Vector.h b/include/utils/Vector.h index ad59fd63e1..ec851bd0b5 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -114,13 +114,19 @@ public: ssize_t appendVector(const Vector<TYPE>& vector); + //! insert an array at a given index + ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length); + + //! append an array at the end of this vector + ssize_t appendArray(const TYPE* array, size_t length); + /*! * add/insert/replace items */ //! insert one or several items initialized with their default constructor inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert on onr several items initialized from a prototype item + //! insert one or several items initialized from a prototype item ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); //! pop the top of the stack (removes the last element). No-op if the stack's empty inline void pop(); @@ -259,6 +265,16 @@ ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { } template<class TYPE> inline +ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) { + return VectorImpl::insertArrayAt(array, index, length); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) { + return VectorImpl::appendArray(array, length); +} + +template<class TYPE> inline ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { return VectorImpl::insertAt(&item, index, numItems); } diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 49b03f10bd..c4ec2ff97f 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -65,9 +65,11 @@ public: size_t capacity() const; ssize_t setCapacity(size_t size); - /*! append/insert another vector */ + /*! append/insert another vector or array */ ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); /*! add/insert/replace items */ ssize_t insertAt(size_t where, size_t numItems = 1); @@ -184,6 +186,8 @@ private: void push(const void* item); ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); ssize_t insertAt(size_t where, size_t numItems = 1); ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); ssize_t replaceAt(size_t index); diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h index 30e00368e9..e38bf669d7 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/utils/ZipFileCRO.h @@ -47,8 +47,8 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, const char* fileName); extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 51c4f2fb6a..97d31f4db4 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -58,14 +58,19 @@ typedef void* ZipEntryRO; class ZipFileRO { public: ZipFileRO() - : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + : mFd(-1), mFileName(NULL), mFileLength(-1), + mDirectoryMap(NULL), + mNumEntries(-1), mDirectoryOffset(-1), + mHashTableSize(-1), mHashTable(NULL) {} ~ZipFileRO() { free(mHashTable); - if (mFileMap) - mFileMap->release(); + if (mDirectoryMap) + mDirectoryMap->release(); if (mFd >= 0) close(mFd); + if (mFileName) + free(mFileName); } /* @@ -118,8 +123,8 @@ public: * Returns "false" if "entry" is bogus or if the data in the Zip file * appears to be bad. */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; /* * Create a new FileMap object that maps a subset of the archive. For @@ -155,13 +160,13 @@ public: * Utility function: uncompress deflated data, buffer to buffer. */ static bool inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Utility function: uncompress deflated data, buffer to fd. */ static bool inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Some basic functions for raw data manipulation. "LE" means @@ -179,6 +184,9 @@ private: ZipFileRO(const ZipFileRO& src); ZipFileRO& operator=(const ZipFileRO& src); + /* locate and parse the central directory */ + bool mapCentralDirectory(void); + /* parse the archive, prepping internal structures */ bool parseZipArchive(void); @@ -203,12 +211,21 @@ private: /* open Zip archive */ int mFd; + /* zip file name */ + char* mFileName; + + /* length of file */ + size_t mFileLength; + /* mapped file */ - FileMap* mFileMap; + FileMap* mDirectoryMap; /* number of entries in the Zip archive */ int mNumEntries; + /* CD directory offset in the Zip archive */ + off_t mDirectoryOffset; + /* * We know how many entries are in the Zip archive, so we have a * fixed-size hash table. We probe for an empty slot. diff --git a/include/utils/threads.h b/include/utils/threads.h index 5ac0c5ee19..1bcfaede67 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -295,6 +295,96 @@ typedef Mutex::Autolock AutoMutex; /*****************************************************************************/ +#if defined(HAVE_PTHREADS) + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class RWLock { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + RWLock(); + RWLock(const char* name); + RWLock(int type, const char* name = NULL); + ~RWLock(); + + status_t readLock(); + status_t tryReadLock(); + status_t writeLock(); + status_t tryWriteLock(); + void unlock(); + + class AutoRLock { + public: + inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } + inline ~AutoRLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + + class AutoWLock { + public: + inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } + inline ~AutoWLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + +private: + // A RWLock cannot be copied + RWLock(const RWLock&); + RWLock& operator = (const RWLock&); + + pthread_rwlock_t mRWLock; +}; + +inline RWLock::RWLock() { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(const char* name) { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(int type, const char* name) { + if (type == SHARED) { + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(&mRWLock, &attr); + pthread_rwlockattr_destroy(&attr); + } else { + pthread_rwlock_init(&mRWLock, NULL); + } +} +inline RWLock::~RWLock() { + pthread_rwlock_destroy(&mRWLock); +} +inline status_t RWLock::readLock() { + return -pthread_rwlock_rdlock(&mRWLock); +} +inline status_t RWLock::tryReadLock() { + return -pthread_rwlock_tryrdlock(&mRWLock); +} +inline status_t RWLock::writeLock() { + return -pthread_rwlock_wrlock(&mRWLock); +} +inline status_t RWLock::tryWriteLock() { + return -pthread_rwlock_trywrlock(&mRWLock); +} +inline void RWLock::unlock() { + pthread_rwlock_unlock(&mRWLock); +} + +#endif // HAVE_PTHREADS + +/*****************************************************************************/ + /* * Condition variable class. The implementation is system-dependent. * diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 0016503186..f6582e6aa2 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -367,6 +367,16 @@ int64_t IPCThreadState::clearCallingIdentity() return token; } +void IPCThreadState::setStrictModePolicy(int32_t policy) +{ + mStrictModePolicy = policy; +} + +int32_t IPCThreadState::getStrictModePolicy() const +{ + return mStrictModePolicy; +} + void IPCThreadState::restoreCallingIdentity(int64_t token) { mCallingUid = (int)(token>>32); @@ -588,7 +598,8 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) } IPCThreadState::IPCThreadState() - : mProcess(ProcessState::self()), mMyThreadId(androidGetTid()) + : mProcess(ProcessState::self()), mMyThreadId(androidGetTid()), + mStrictModePolicy(0) { pthread_setspecific(gTLS, this); clearCaller(); diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp index bff4c9bd7d..e13036ff42 100644 --- a/libs/binder/IPermissionController.cpp +++ b/libs/binder/IPermissionController.cpp @@ -36,7 +36,7 @@ public: : BpInterface<IPermissionController>(impl) { } - + virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) { Parcel data, reply; @@ -46,7 +46,7 @@ public: data.writeInt32(uid); remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); // fail on exception - if (reply.readInt32() != 0) return 0; + if (reply.readExceptionCode() != 0) return 0; return reply.readInt32() != 0; } }; @@ -66,8 +66,7 @@ status_t BnPermissionController::onTransact( int32_t pid = data.readInt32(); int32_t uid = data.readInt32(); bool res = checkPermission(permission, pid, uid); - // write exception - reply->writeInt32(0); + reply->writeNoException(); reply->writeInt32(res ? 1 : 0); return NO_ERROR; } break; @@ -77,4 +76,3 @@ status_t BnPermissionController::onTransact( } }; // namespace android - diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 0cf4158609..1fa4c3579c 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -129,19 +129,19 @@ public: : BpInterface<IServiceManager>(impl) { } - + virtual sp<IBinder> getService(const String16& name) const { unsigned n; for (n = 0; n < 5; n++){ sp<IBinder> svc = checkService(name); if (svc != NULL) return svc; - LOGI("Waiting for sevice %s...\n", String8(name).string()); + LOGI("Waiting for service %s...\n", String8(name).string()); sleep(1); } return NULL; } - + virtual sp<IBinder> checkService( const String16& name) const { Parcel data, reply; @@ -158,7 +158,7 @@ public: data.writeString16(name); data.writeStrongBinder(service); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); - return err == NO_ERROR ? reply.readInt32() : err; + return err == NO_ERROR ? reply.readExceptionCode() : err; } virtual Vector<String16> listServices() @@ -226,4 +226,3 @@ status_t BnServiceManager::onTransact( } }; // namespace android - diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 00d2210ca9..18f75df2ea 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -19,6 +19,7 @@ #include <binder/Parcel.h> +#include <binder/IPCThreadState.h> #include <binder/Binder.h> #include <binder/BpBinder.h> #include <utils/Debug.h> @@ -47,6 +48,12 @@ #define PAD_SIZE(s) (((s)+3)&~3) +// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER +#define STRICT_MODE_PENALTY_GATHER 0x100 + +// Note: must be kept in sync with android/os/Parcel.java's EX_HAS_REPLY_HEADER +#define EX_HAS_REPLY_HEADER -128 + // XXX This can be made public if we want to provide // support for typed data. struct small_flat_data @@ -436,19 +443,28 @@ bool Parcel::hasFileDescriptors() const return mHasFds; } +// Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { + writeInt32(IPCThreadState::self()->getStrictModePolicy() | + STRICT_MODE_PENALTY_GATHER); // currently the interface identification token is just its name as a string return writeString16(interface); } bool Parcel::checkInterface(IBinder* binder) const { - return enforceInterface(binder->getInterfaceDescriptor()); + return enforceInterface(binder->getInterfaceDescriptor()); } -bool Parcel::enforceInterface(const String16& interface) const +bool Parcel::enforceInterface(const String16& interface, + IPCThreadState* threadState) const { + int32_t strictPolicy = readInt32(); + if (threadState == NULL) { + threadState = IPCThreadState::self(); + } + threadState->setStrictModePolicy(strictPolicy); const String16 str(readString16()); if (str == interface) { return true; @@ -457,7 +473,7 @@ bool Parcel::enforceInterface(const String16& interface) const String8(interface).string(), String8(str).string()); return false; } -} +} const size_t* Parcel::objects() const { @@ -750,6 +766,11 @@ restart_write: goto restart_write; } +status_t Parcel::writeNoException() +{ + return writeInt32(0); +} + void Parcel::remove(size_t start, size_t amt) { LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); @@ -938,6 +959,20 @@ wp<IBinder> Parcel::readWeakBinder() const return val; } +int32_t Parcel::readExceptionCode() const +{ + int32_t exception_code = readAligned<int32_t>(); + if (exception_code == EX_HAS_REPLY_HEADER) { + int32_t header_size = readAligned<int32_t>(); + // Skip over fat responses headers. Not used (or propagated) in + // native code + setDataPosition(dataPosition() + header_size); + // And fat response headers are currently only used when there are no + // exceptions, so return no error: + return 0; + } + return exception_code; +} native_handle* Parcel::readNativeHandle() const { diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk new file mode 100644 index 0000000000..249558ac86 --- /dev/null +++ b/libs/gui/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + ISensorEventConnection.cpp \ + ISensorServer.cpp \ + Sensor.cpp \ + SensorChannel.cpp \ + SensorEventQueue.cpp \ + SensorManager.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libhardware \ + libhardware_legacy + +LOCAL_MODULE:= libgui + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -lpthread +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp new file mode 100644 index 0000000000..a5083fed0a --- /dev/null +++ b/libs/gui/ISensorEventConnection.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/ISensorEventConnection.h> +#include <gui/SensorChannel.h> + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, + ENABLE_DISABLE, + SET_EVENT_RATE +}; + +class BpSensorEventConnection : public BpInterface<ISensorEventConnection> +{ +public: + BpSensorEventConnection(const sp<IBinder>& impl) + : BpInterface<ISensorEventConnection>(impl) + { + } + + virtual sp<SensorChannel> getSensorChannel() const + { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + remote()->transact(GET_SENSOR_CHANNEL, data, &reply); + return new SensorChannel(reply); + } + + virtual status_t enableDisable(int handle, bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + data.writeInt32(handle); + data.writeInt32(enabled); + remote()->transact(ENABLE_DISABLE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setEventRate(int handle, nsecs_t ns) + { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + data.writeInt32(handle); + data.writeInt64(ns); + remote()->transact(SET_EVENT_RATE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection"); + +// ---------------------------------------------------------------------------- + +status_t BnSensorEventConnection::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_SENSOR_CHANNEL: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + sp<SensorChannel> channel(getSensorChannel()); + channel->writeToParcel(reply); + return NO_ERROR; + } break; + case ENABLE_DISABLE: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + int handle = data.readInt32(); + int enabled = data.readInt32(); + status_t result = enableDisable(handle, enabled); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_EVENT_RATE: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + int handle = data.readInt32(); + int ns = data.readInt64(); + status_t result = setEventRate(handle, ns); + reply->writeInt32(result); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp new file mode 100644 index 0000000000..71110925d9 --- /dev/null +++ b/libs/gui/ISensorServer.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> +#include <utils/Timers.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/Sensor.h> +#include <gui/ISensorServer.h> +#include <gui/ISensorEventConnection.h> + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION, + CREATE_SENSOR_EVENT_CONNECTION, +}; + +class BpSensorServer : public BpInterface<ISensorServer> +{ +public: + BpSensorServer(const sp<IBinder>& impl) + : BpInterface<ISensorServer>(impl) + { + } + + virtual Vector<Sensor> getSensorList() + { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + remote()->transact(GET_SENSOR_LIST, data, &reply); + Sensor s; + Vector<Sensor> v; + int32_t n = reply.readInt32(); + v.setCapacity(n); + while (n--) { + reply.read(static_cast<Flattenable&>(s)); + v.add(s); + } + return v; + } + + virtual sp<ISensorEventConnection> createSensorEventConnection() + { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply); + return interface_cast<ISensorEventConnection>(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer"); + +// ---------------------------------------------------------------------- + +status_t BnSensorServer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_SENSOR_LIST: { + CHECK_INTERFACE(ISensorServer, data, reply); + Vector<Sensor> v(getSensorList()); + size_t n = v.size(); + reply->writeInt32(n); + for (size_t i=0 ; i<n ; i++) { + reply->write(static_cast<const Flattenable&>(v[i])); + } + return NO_ERROR; + } break; + case CREATE_SENSOR_EVENT_CONNECTION: { + CHECK_INTERFACE(ISensorServer, data, reply); + sp<ISensorEventConnection> connection(createSensorEventConnection()); + reply->writeStrongBinder(connection->asBinder()); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp new file mode 100644 index 0000000000..b1f37ffed2 --- /dev/null +++ b/libs/gui/Sensor.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/Flattenable.h> + +#include <hardware/sensors.h> + +#include <gui/Sensor.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +Sensor::Sensor() + : mHandle(0), mType(0), + mMinValue(0), mMaxValue(0), mResolution(0), + mPower(0), mMinDelay(0) +{ +} + +Sensor::Sensor(struct sensor_t const* hwSensor) +{ + mName = hwSensor->name; + mVendor = hwSensor->vendor; + mHandle = hwSensor->handle; + mType = hwSensor->type; + mMinValue = 0; // FIXME: minValue + mMaxValue = hwSensor->maxRange; // FIXME: maxValue + mResolution = hwSensor->resolution; + mPower = hwSensor->power; + mMinDelay = hwSensor->minDelay; +} + +Sensor::~Sensor() +{ +} + +const String8& Sensor::getName() const { + return mName; +} + +const String8& Sensor::getVendor() const { + return mVendor; +} + +int32_t Sensor::getHandle() const { + return mHandle; +} + +int32_t Sensor::getType() const { + return mType; +} + +float Sensor::getMinValue() const { + return mMinValue; +} + +float Sensor::getMaxValue() const { + return mMaxValue; +} + +float Sensor::getResolution() const { + return mResolution; +} + +float Sensor::getPowerUsage() const { + return mPower; +} + +int32_t Sensor::getMinDelay() const { + return mMinDelay; +} + +size_t Sensor::getFlattenedSize() const +{ + return sizeof(int32_t) + ((mName.length() + 3) & ~3) + + sizeof(int32_t) + ((mVendor.length() + 3) & ~3) + + sizeof(int32_t) * 2 + + sizeof(float) * 4 + + sizeof(int32_t); +} + +size_t Sensor::getFdCount() const +{ + return 0; +} + +static inline +size_t write(void* buffer, size_t offset, const String8& value) { + memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length()); + return (value.length() + 3) & ~3; +} + +static inline +size_t write(void* buffer, size_t offset, float value) { + *reinterpret_cast<float*>(static_cast<char*>(buffer) + offset) = value; + return sizeof(float); +} + +static inline +size_t write(void* buffer, size_t offset, int32_t value) { + *reinterpret_cast<int32_t*>(static_cast<char*>(buffer) + offset) = value; + return sizeof(int32_t); +} + +status_t Sensor::flatten(void* buffer, size_t size, + int fds[], size_t count) const +{ + if (size < Sensor::getFlattenedSize()) + return -ENOMEM; + + size_t offset = 0; + offset += write(buffer, offset, int32_t(mName.length())); + offset += write(buffer, offset, mName); + offset += write(buffer, offset, int32_t(mVendor.length())); + offset += write(buffer, offset, mVendor); + offset += write(buffer, offset, mHandle); + offset += write(buffer, offset, mType); + offset += write(buffer, offset, mMinValue); + offset += write(buffer, offset, mMaxValue); + offset += write(buffer, offset, mResolution); + offset += write(buffer, offset, mPower); + offset += write(buffer, offset, mMinDelay); + + return NO_ERROR; +} + +static inline +size_t read(void const* buffer, size_t offset, String8* value, int32_t len) { + value->setTo(static_cast<char const*>(buffer) + offset, len); + return (len + 3) & ~3; +} + +static inline +size_t read(void const* buffer, size_t offset, float* value) { + *value = *reinterpret_cast<float const*>(static_cast<char const*>(buffer) + offset); + return sizeof(float); +} + +static inline +size_t read(void const* buffer, size_t offset, int32_t* value) { + *value = *reinterpret_cast<int32_t const*>(static_cast<char const*>(buffer) + offset); + return sizeof(int32_t); +} + +status_t Sensor::unflatten(void const* buffer, size_t size, + int fds[], size_t count) +{ + int32_t len; + size_t offset = 0; + offset += read(buffer, offset, &len); + offset += read(buffer, offset, &mName, len); + offset += read(buffer, offset, &len); + offset += read(buffer, offset, &mVendor, len); + offset += read(buffer, offset, &mHandle); + offset += read(buffer, offset, &mType); + offset += read(buffer, offset, &mMinValue); + offset += read(buffer, offset, &mMaxValue); + offset += read(buffer, offset, &mResolution); + offset += read(buffer, offset, &mPower); + offset += read(buffer, offset, &mMinDelay); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/SensorChannel.cpp b/libs/gui/SensorChannel.cpp new file mode 100644 index 0000000000..147e1c2bb4 --- /dev/null +++ b/libs/gui/SensorChannel.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <unistd.h> +#include <fcntl.h> + +#include <utils/Errors.h> + +#include <binder/Parcel.h> + +#include <gui/SensorChannel.h> + +namespace android { +// ---------------------------------------------------------------------------- + +SensorChannel::SensorChannel() + : mSendFd(-1), mReceiveFd(-1) +{ + int fds[2]; + if (pipe(fds) == 0) { + mReceiveFd = fds[0]; + mSendFd = fds[1]; + fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); + fcntl(mSendFd, F_SETFL, O_NONBLOCK); + } +} + +SensorChannel::SensorChannel(const Parcel& data) + : mSendFd(-1), mReceiveFd(-1) +{ + mReceiveFd = dup(data.readFileDescriptor()); + fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); +} + +SensorChannel::~SensorChannel() +{ + if (mSendFd >= 0) + close(mSendFd); + + if (mReceiveFd >= 0) + close(mReceiveFd); +} + +int SensorChannel::getFd() const +{ + return mReceiveFd; +} + +ssize_t SensorChannel::write(void const* vaddr, size_t size) +{ + ssize_t len = ::write(mSendFd, vaddr, size); + if (len < 0) + return -errno; + return len; +} + +ssize_t SensorChannel::read(void* vaddr, size_t size) +{ + ssize_t len = ::read(mReceiveFd, vaddr, size); + if (len < 0) + return -errno; + return len; +} + +status_t SensorChannel::writeToParcel(Parcel* reply) const +{ + if (mReceiveFd < 0) + return -EINVAL; + + status_t result = reply->writeDupFileDescriptor(mReceiveFd); + close(mReceiveFd); + mReceiveFd = -1; + return result; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp new file mode 100644 index 0000000000..3396f25e71 --- /dev/null +++ b/libs/gui/SensorEventQueue.cpp @@ -0,0 +1,135 @@ +/* + * 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 "Sensors" + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/PollLoop.h> + +#include <gui/Sensor.h> +#include <gui/SensorChannel.h> +#include <gui/SensorEventQueue.h> +#include <gui/ISensorEventConnection.h> + +#include <android/sensor.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) + : mSensorEventConnection(connection) +{ +} + +SensorEventQueue::~SensorEventQueue() +{ +} + +void SensorEventQueue::onFirstRef() +{ + mSensorChannel = mSensorEventConnection->getSensorChannel(); +} + +int SensorEventQueue::getFd() const +{ + return mSensorChannel->getFd(); +} + +ssize_t SensorEventQueue::write(ASensorEvent const* events, size_t numEvents) +{ + ssize_t size = mSensorChannel->write(events, numEvents * sizeof(events[0])); + if (size >= 0) { + if (size % sizeof(events[0])) { + // partial write!!! should never happen. + return -EINVAL; + } + // returns number of events written + size /= sizeof(events[0]); + } + return size; +} + +ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) +{ + ssize_t size = mSensorChannel->read(events, numEvents*sizeof(events[0])); + if (size >= 0) { + if (size % sizeof(events[0])) { + // partial read!!! should never happen. + return -EINVAL; + } + // returns number of events read + size /= sizeof(events[0]); + } + return size; +} + +sp<PollLoop> SensorEventQueue::getPollLoop() const +{ + Mutex::Autolock _l(mLock); + if (mPollLoop == 0) { + mPollLoop = new PollLoop(true); + mPollLoop->setCallback(getFd(), POLLIN, NULL, NULL); + } + return mPollLoop; +} + +status_t SensorEventQueue::waitForEvent() const +{ + const int fd = getFd(); + sp<PollLoop> pollLoop(getPollLoop()); + int32_t result = pollLoop->pollOnce(-1, NULL, NULL); + return (result == fd) ? NO_ERROR : -1; +} + +status_t SensorEventQueue::wake() const +{ + sp<PollLoop> pollLoop(getPollLoop()); + pollLoop->wake(); + return NO_ERROR; +} + +status_t SensorEventQueue::enableSensor(Sensor const* sensor) const { + return mSensorEventConnection->enableDisable(sensor->getHandle(), true); +} + +status_t SensorEventQueue::disableSensor(Sensor const* sensor) const { + return mSensorEventConnection->enableDisable(sensor->getHandle(), false); +} + +status_t SensorEventQueue::enableSensor(int32_t handle, int32_t us) const { + status_t err = mSensorEventConnection->enableDisable(handle, true); + if (err == NO_ERROR) { + mSensorEventConnection->setEventRate(handle, us2ns(us)); + } + return err; +} + +status_t SensorEventQueue::disableSensor(int32_t handle) const { + return mSensorEventConnection->enableDisable(handle, false); +} + +status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const { + return mSensorEventConnection->setEventRate(sensor->getHandle(), ns); +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp new file mode 100644 index 0000000000..d719efb5f1 --- /dev/null +++ b/libs/gui/SensorManager.cpp @@ -0,0 +1,87 @@ +/* + * 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 "Sensors" + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Singleton.h> + +#include <binder/IServiceManager.h> + +#include <gui/ISensorServer.h> +#include <gui/ISensorEventConnection.h> +#include <gui/Sensor.h> +#include <gui/SensorManager.h> +#include <gui/SensorEventQueue.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE(SensorManager) + +SensorManager::SensorManager() + : mSensorList(0) +{ + const String16 name("sensorservice"); + while (getService(name, &mSensorServer) != NO_ERROR) { + usleep(250000); + } + + mSensors = mSensorServer->getSensorList(); + size_t count = mSensors.size(); + mSensorList = (Sensor const**)malloc(count * sizeof(Sensor*)); + for (size_t i=0 ; i<count ; i++) { + mSensorList[i] = mSensors.array() + i; + } +} + +SensorManager::~SensorManager() +{ + free(mSensorList); +} + +ssize_t SensorManager::getSensorList(Sensor const* const** list) const +{ + *list = mSensorList; + return mSensors.size(); +} + +Sensor const* SensorManager::getDefaultSensor(int type) +{ + // For now we just return the first sensor of that type we find. + // in the future it will make sense to let the SensorService make + // that decision. + for (size_t i=0 ; i<mSensors.size() ; i++) { + if (mSensorList[i]->getType() == type) + return mSensorList[i]; + } + return NULL; +} + +sp<SensorEventQueue> SensorManager::createEventQueue() +{ + sp<SensorEventQueue> result = new SensorEventQueue( + mSensorServer->createSensorEventConnection()); + return result; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk index fe85b34986..ce3c71a7d9 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ISurfaceComposer.cpp \ ISurface.cpp \ - ISurfaceFlingerClient.cpp \ + ISurfaceComposerClient.cpp \ LayerState.cpp \ SharedBufferStack.cpp \ Surface.cpp \ diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index bb86199876..7049d9e42d 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -71,11 +71,15 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); data.writeInt32(bufferIdx); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); data.writeInt32(usage); remote()->transact(REQUEST_BUFFER, data, &reply); sp<GraphicBuffer> buffer = new GraphicBuffer(); @@ -83,6 +87,16 @@ public: return buffer; } + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; @@ -140,12 +154,22 @@ status_t BnSurface::onTransact( case REQUEST_BUFFER: { CHECK_INTERFACE(ISurface, data, reply); int bufferIdx = data.readInt32(); - int usage = data.readInt32(); - sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage)); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage)); if (buffer == NULL) return BAD_VALUE; return reply->write(*buffer); } + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferCount = data.readInt32(); + status_t err = setBufferCount(bufferCount); + reply->writeInt32(err); + return NO_ERROR; + } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); BufferHeap buffer; diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index b6f4e24a2a..5c111f64c7 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -46,13 +46,22 @@ public: { } - virtual sp<ISurfaceFlingerClient> createConnection() + virtual sp<ISurfaceComposerClient> createConnection() { uint32_t n; Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); - return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder()); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); + } + + virtual sp<ISurfaceComposerClient> createClientConnection() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } virtual sp<IMemoryHeap> getCblk() const @@ -136,6 +145,11 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); } break; + case CREATE_CLIENT_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createClientConnection()->asBinder(); + reply->writeStrongBinder(b); + } break; case OPEN_GLOBAL_TRANSACTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); openGlobalTransaction(); diff --git a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp index def96d70ff..2cc1f8e343 100644 --- a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp @@ -30,7 +30,7 @@ #include <ui/Rect.h> #include <surfaceflinger/ISurface.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> // --------------------------------------------------------------------------- @@ -51,27 +51,37 @@ namespace android { enum { GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + GET_TOKEN, CREATE_SURFACE, DESTROY_SURFACE, SET_STATE }; -class BpSurfaceFlingerClient : public BpInterface<ISurfaceFlingerClient> +class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> { public: - BpSurfaceFlingerClient(const sp<IBinder>& impl) - : BpInterface<ISurfaceFlingerClient>(impl) + BpSurfaceComposerClient(const sp<IBinder>& impl) + : BpInterface<ISurfaceComposerClient>(impl) { } virtual sp<IMemoryHeap> getControlBlock() const { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); remote()->transact(GET_CBLK, data, &reply); return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); + data.writeStrongBinder(sur->asBinder()); + remote()->transact(GET_TOKEN, data, &reply); + return reply.readInt32(); + } + virtual sp<ISurface> createSurface( surface_data_t* params, int pid, const String8& name, @@ -82,7 +92,7 @@ public: uint32_t flags) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(pid); data.writeString8(name); data.writeInt32(display); @@ -94,11 +104,11 @@ public: params->readFromParcel(reply); return interface_cast<ISurface>(reply.readStrongBinder()); } - + virtual status_t destroySurface(SurfaceID sid) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(sid); remote()->transact(DESTROY_SURFACE, data, &reply); return reply.readInt32(); @@ -107,7 +117,7 @@ public: virtual status_t setState(int32_t count, const layer_state_t* states) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(count); for (int i=0 ; i<count ; i++) states[i].write(data); @@ -116,26 +126,33 @@ public: } }; -IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient"); +IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); // ---------------------------------------------------------------------- -status_t BnSurfaceFlingerClient::onTransact( +status_t BnSurfaceComposerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // codes that don't require permission check switch(code) { case GET_CBLK: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); sp<IMemoryHeap> ctl(getControlBlock()); reply->writeStrongBinder(ctl->asBinder()); return NO_ERROR; } break; + case GET_TOKEN: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder()); + ssize_t token = getTokenForSurface(sur); + reply->writeInt32(token); + return NO_ERROR; + } break; } // these must be checked - + IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); @@ -150,10 +167,10 @@ status_t BnSurfaceFlingerClient::onTransact( return PERMISSION_DENIED; } } - + switch(code) { case CREATE_SURFACE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); surface_data_t params; int32_t pid = data.readInt32(); String8 name = data.readString8(); @@ -169,12 +186,12 @@ status_t BnSurfaceFlingerClient::onTransact( return NO_ERROR; } break; case DESTROY_SURFACE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); reply->writeInt32( destroySurface( data.readInt32() ) ); return NO_ERROR; } break; case SET_STATE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); int32_t count = data.readInt32(); layer_state_t* states = new layer_state_t[count]; for (int i=0 ; i<count ; i++) @@ -191,7 +208,7 @@ status_t BnSurfaceFlingerClient::onTransact( // ---------------------------------------------------------------------- -status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel) +status_t ISurfaceComposerClient::surface_data_t::readFromParcel(const Parcel& parcel) { token = parcel.readInt32(); identity = parcel.readInt32(); @@ -201,7 +218,7 @@ status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& par return NO_ERROR; } -status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) const +status_t ISurfaceComposerClient::surface_data_t::writeToParcel(Parcel* parcel) const { parcel->writeInt32(token); parcel->writeInt32(identity); diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index a17e8acc57..4ad9f861ca 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -44,15 +44,11 @@ SharedClient::~SharedClient() { // these functions are used by the clients status_t SharedClient::validate(size_t i) const { - if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX)) + if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX)) return BAD_INDEX; return surfaces[i].status; } -uint32_t SharedClient::getIdentity(size_t token) const { - return uint32_t(surfaces[token].identity); -} - // ---------------------------------------------------------------------------- @@ -62,24 +58,60 @@ SharedBufferStack::SharedBufferStack() void SharedBufferStack::init(int32_t i) { - inUse = -1; + inUse = -2; status = NO_ERROR; identity = i; } +status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + + buffers[buffer].crop.l = uint16_t(crop.left); + buffers[buffer].crop.t = uint16_t(crop.top); + buffers[buffer].crop.r = uint16_t(crop.right); + buffers[buffer].crop.b = uint16_t(crop.bottom); + return NO_ERROR; +} + +status_t SharedBufferStack::setTransform(int buffer, uint8_t transform) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + buffers[buffer].transform = transform; + return NO_ERROR; +} + status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) { if (uint32_t(buffer) >= NUM_BUFFER_MAX) return BAD_INDEX; - // in the current implementation we only send a single rectangle - const Rect bounds(dirty.getBounds()); - FlatRegion& reg(dirtyRegion[buffer]); - reg.count = 1; - reg.rects[0] = uint16_t(bounds.left); - reg.rects[1] = uint16_t(bounds.top); - reg.rects[2] = uint16_t(bounds.right); - reg.rects[3] = uint16_t(bounds.bottom); + FlatRegion& reg(buffers[buffer].dirtyRegion); + if (dirty.isEmpty()) { + reg.count = 0; + return NO_ERROR; + } + + size_t count; + Rect const* r = dirty.getArray(&count); + if (count > FlatRegion::NUM_RECT_MAX) { + const Rect bounds(dirty.getBounds()); + reg.count = 1; + reg.rects[0].l = uint16_t(bounds.left); + reg.rects[0].t = uint16_t(bounds.top); + reg.rects[0].r = uint16_t(bounds.right); + reg.rects[0].b = uint16_t(bounds.bottom); + } else { + reg.count = count; + for (size_t i=0 ; i<count ; i++) { + reg.rects[i].l = uint16_t(r[i].left); + reg.rects[i].t = uint16_t(r[i].top); + reg.rects[i].r = uint16_t(r[i].right); + reg.rects[i].b = uint16_t(r[i].bottom); + } + } return NO_ERROR; } @@ -89,18 +121,57 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const if (uint32_t(buffer) >= NUM_BUFFER_MAX) return res; - const FlatRegion& reg(dirtyRegion[buffer]); - res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3])); + const FlatRegion& reg(buffers[buffer].dirtyRegion); + if (reg.count > FlatRegion::NUM_RECT_MAX) + return res; + + if (reg.count == 1) { + const Rect r( + reg.rects[0].l, + reg.rects[0].t, + reg.rects[0].r, + reg.rects[0].b); + res.set(r); + } else { + for (size_t i=0 ; i<reg.count ; i++) { + const Rect r( + reg.rects[i].l, + reg.rects[i].t, + reg.rects[i].r, + reg.rects[i].b); + res.orSelf(r); + } + } return res; } +Rect SharedBufferStack::getCrop(int buffer) const +{ + Rect res(-1, -1); + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return res; + res.left = buffers[buffer].crop.l; + res.top = buffers[buffer].crop.t; + res.right = buffers[buffer].crop.r; + res.bottom = buffers[buffer].crop.b; + return res; +} + +uint32_t SharedBufferStack::getTransform(int buffer) const +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return 0; + return buffers[buffer].transform; +} + + // ---------------------------------------------------------------------------- SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, - int surface, int num, int32_t identity) + int surface, int32_t identity) : mSharedClient(sharedClient), mSharedStack(sharedClient->surfaces + surface), - mNumBuffers(num), mIdentity(identity) + mIdentity(identity) { } @@ -108,16 +179,16 @@ SharedBufferBase::~SharedBufferBase() { } -uint32_t SharedBufferBase::getIdentity() +status_t SharedBufferBase::getStatus() const { SharedBufferStack& stack( *mSharedStack ); - return stack.identity; + return stack.status; } -status_t SharedBufferBase::getStatus() const +int32_t SharedBufferBase::getIdentity() const { SharedBufferStack& stack( *mSharedStack ); - return stack.status; + return stack.identity; } size_t SharedBufferBase::getFrontBuffer() const @@ -132,16 +203,52 @@ String8 SharedBufferBase::dump(char const* prefix) const char buffer[SIZE]; String8 result; SharedBufferStack& stack( *mSharedStack ); - int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; snprintf(buffer, SIZE, - "%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] " - "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n", - prefix, stack.head, stack.available, stack.queued, tail, + "%s[ head=%2d, available=%2d, queued=%2d ] " + "reallocMask=%08x, inUse=%2d, identity=%d, status=%d", + prefix, stack.head, stack.available, stack.queued, stack.reallocMask, stack.inUse, stack.identity, stack.status); result.append(buffer); + result.append("\n"); return result; } +status_t SharedBufferBase::waitForCondition(const ConditionBase& condition) +{ + const SharedBufferStack& stack( *mSharedStack ); + SharedClient& client( *mSharedClient ); + const nsecs_t TIMEOUT = s2ns(1); + const int identity = mIdentity; + + Mutex::Autolock _l(client.lock); + while ((condition()==false) && + (stack.identity == identity) && + (stack.status == NO_ERROR)) + { + status_t err = client.cv.waitRelative(client.lock, TIMEOUT); + // handle errors and timeouts + if (CC_UNLIKELY(err != NO_ERROR)) { + if (err == TIMED_OUT) { + if (condition()) { + LOGE("waitForCondition(%s) timed out (identity=%d), " + "but condition is true! We recovered but it " + "shouldn't happen." , condition.name(), stack.identity); + break; + } else { + LOGW("waitForCondition(%s) timed out " + "(identity=%d, status=%d). " + "CPU may be pegged. trying again.", condition.name(), + stack.identity, stack.status); + } + } else { + LOGE("waitForCondition(%s) error (%s) ", + condition.name(), strerror(-err)); + return err; + } + } + } + return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; +} // ============================================================================ // conditions and updates // ============================================================================ @@ -149,26 +256,21 @@ String8 SharedBufferBase::dump(char const* prefix) const SharedBufferClient::DequeueCondition::DequeueCondition( SharedBufferClient* sbc) : ConditionBase(sbc) { } -bool SharedBufferClient::DequeueCondition::operator()() { +bool SharedBufferClient::DequeueCondition::operator()() const { return stack.available > 0; } SharedBufferClient::LockCondition::LockCondition( SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { } -bool SharedBufferClient::LockCondition::operator()() { - return (buf != stack.head || +bool SharedBufferClient::LockCondition::operator()() const { + // NOTE: if stack.head is messed up, we could crash the client + // or cause some drawing artifacts. This is okay, as long as it is + // limited to the client. + return (buf != stack.index[stack.head] || (stack.queued > 0 && stack.inUse != buf)); } -SharedBufferServer::ReallocateCondition::ReallocateCondition( - SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) { -} -bool SharedBufferServer::ReallocateCondition::operator()() { - // TODO: we should also check that buf has been dequeued - return (buf != stack.head); -} - // ---------------------------------------------------------------------------- SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) @@ -193,8 +295,10 @@ SharedBufferServer::UnlockUpdate::UnlockUpdate( } ssize_t SharedBufferServer::UnlockUpdate::operator()() { if (stack.inUse != lockedBuffer) { - LOGE("unlocking %d, but currently locked buffer is %d", - lockedBuffer, stack.inUse); + LOGE("unlocking %d, but currently locked buffer is %d " + "(identity=%d, token=%d)", + lockedBuffer, stack.inUse, + stack.identity, stack.token); return BAD_VALUE; } android_atomic_write(-1, &stack.inUse); @@ -206,11 +310,12 @@ SharedBufferServer::RetireUpdate::RetireUpdate( : UpdateBase(sbb), numBuffers(numBuffers) { } ssize_t SharedBufferServer::RetireUpdate::operator()() { - // head is only written in this function, which is single-thread. int32_t head = stack.head; + if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; // Preventively lock the current buffer before updating queued. - android_atomic_write(head, &stack.inUse); + android_atomic_write(stack.index[head], &stack.inUse); // Decrement the number of queued buffers int32_t queued; @@ -221,16 +326,15 @@ ssize_t SharedBufferServer::RetireUpdate::operator()() { } } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); - // update the head pointer - head = ((head+1 >= numBuffers) ? 0 : head+1); - // lock the buffer before advancing head, which automatically unlocks // the buffer we preventively locked upon entering this function - android_atomic_write(head, &stack.inUse); - // advance head + head = (head + 1) % numBuffers; + android_atomic_write(stack.index[head], &stack.inUse); + + // head is only modified here, so we don't need to use cmpxchg android_atomic_write(head, &stack.head); - + // now that head has moved, we can increment the number of available buffers android_atomic_inc(&stack.available); return head; @@ -250,41 +354,31 @@ ssize_t SharedBufferServer::StatusUpdate::operator()() { SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, num, identity), tail(0) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num), tail(0), undoDequeueTail(0) { + SharedBufferStack& stack( *mSharedStack ); tail = computeTail(); + queued_head = stack.head; } int32_t SharedBufferClient::computeTail() const { SharedBufferStack& stack( *mSharedStack ); - // we need to make sure we read available and head coherently, - // w.r.t RetireUpdate. - int32_t newTail; - int32_t avail; - int32_t head; - do { - avail = stack.available; - head = stack.head; - } while (stack.available != avail); - newTail = head - avail + 1; - if (newTail < 0) { - newTail += mNumBuffers; - } else if (newTail >= mNumBuffers) { - newTail -= mNumBuffers; - } - return newTail; + return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; } ssize_t SharedBufferClient::dequeue() { SharedBufferStack& stack( *mSharedStack ); - if (stack.head == tail && stack.available == 2) { + if (stack.head == tail && stack.available == mNumBuffers) { LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", tail, stack.head, stack.available, stack.queued); } - + + RWLock::AutoRLock _rd(mLock); + const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); //LOGD("[%d] about to dequeue a buffer", @@ -301,9 +395,10 @@ ssize_t SharedBufferClient::dequeue() LOGW("dequeue probably called from multiple threads!"); } - int dequeued = tail; + undoDequeueTail = tail; + int dequeued = stack.index[tail]; tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); - LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s", + LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", dequeued, tail, dump("").string()); mDequeueTime[dequeued] = dequeueTime; @@ -313,16 +408,23 @@ ssize_t SharedBufferClient::dequeue() status_t SharedBufferClient::undoDequeue(int buf) { + RWLock::AutoRLock _rd(mLock); + + // TODO: we can only undo the previous dequeue, we should + // enforce that in the api UndoDequeueUpdate update(this); status_t err = updateCondition( update ); if (err == NO_ERROR) { - tail = computeTail(); + tail = undoDequeueTail; } return err; } status_t SharedBufferClient::lock(int buf) { + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); LockCondition condition(this, buf); status_t err = waitForCondition(condition); return err; @@ -330,53 +432,111 @@ status_t SharedBufferClient::lock(int buf) status_t SharedBufferClient::queue(int buf) { + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); + + queued_head = (queued_head + 1) % mNumBuffers; + stack.index[queued_head] = buf; + QueueUpdate update(this); status_t err = updateCondition( update ); LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); - SharedBufferStack& stack( *mSharedStack ); + const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); return err; } -bool SharedBufferClient::needNewBuffer(int buffer) const +bool SharedBufferClient::needNewBuffer(int buf) const { SharedBufferStack& stack( *mSharedStack ); - const uint32_t mask = 1<<buffer; + const uint32_t mask = 1<<(31-buf); return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; } -status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg) +status_t SharedBufferClient::setCrop(int buf, const Rect& crop) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setCrop(buf, crop); +} + +status_t SharedBufferClient::setTransform(int buf, uint32_t transform) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setTransform(buf, uint8_t(transform)); +} + +status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setDirtyRegion(buf, reg); +} + +status_t SharedBufferClient::setBufferCount( + int bufferCount, const SetBufferCountCallback& ipc) { SharedBufferStack& stack( *mSharedStack ); - return stack.setDirtyRegion(buffer, reg); + if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN) + return BAD_VALUE; + + RWLock::AutoWLock _wr(mLock); + + status_t err = ipc(bufferCount); + if (err == NO_ERROR) { + mNumBuffers = bufferCount; + queued_head = (stack.head + stack.queued) % mNumBuffers; + } + return err; } // ---------------------------------------------------------------------------- SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, num, identity) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num) { mSharedStack->init(identity); + mSharedStack->token = surface; mSharedStack->head = num-1; mSharedStack->available = num; mSharedStack->queued = 0; mSharedStack->reallocMask = 0; - memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion)); + memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); + for (int i=0 ; i<num ; i++) { + mBufferList.add(i); + mSharedStack->index[i] = i; + } +} + +SharedBufferServer::~SharedBufferServer() +{ } ssize_t SharedBufferServer::retireAndLock() { + RWLock::AutoRLock _l(mLock); + RetireUpdate update(this, mNumBuffers); ssize_t buf = updateCondition( update ); - LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string()); + if (buf >= 0) { + if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + SharedBufferStack& stack( *mSharedStack ); + buf = stack.index[buf]; + LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", + int(buf), dump("").string()); + } return buf; } -status_t SharedBufferServer::unlock(int buffer) +status_t SharedBufferServer::unlock(int buf) { - UnlockUpdate update(this, buffer); + UnlockUpdate update(this, buf); status_t err = updateCondition( update ); return err; } @@ -389,11 +549,25 @@ void SharedBufferServer::setStatus(status_t status) } } -status_t SharedBufferServer::reallocate() +status_t SharedBufferServer::reallocateAll() { + RWLock::AutoRLock _l(mLock); + SharedBufferStack& stack( *mSharedStack ); - uint32_t mask = (1<<mNumBuffers)-1; - android_atomic_or(mask, &stack.reallocMask); + uint32_t mask = mBufferList.getMask(); + android_atomic_or(mask, &stack.reallocMask); + return NO_ERROR; +} + +status_t SharedBufferServer::reallocateAllExcept(int buffer) +{ + RWLock::AutoRLock _l(mLock); + + SharedBufferStack& stack( *mSharedStack ); + BufferList temp(mBufferList); + temp.remove(buffer); + uint32_t mask = temp.getMask(); + android_atomic_or(mask, &stack.reallocMask); return NO_ERROR; } @@ -403,17 +577,74 @@ int32_t SharedBufferServer::getQueuedCount() const return stack.queued; } -status_t SharedBufferServer::assertReallocate(int buffer) +Region SharedBufferServer::getDirtyRegion(int buf) const { - ReallocateCondition condition(this, buffer); - status_t err = waitForCondition(condition); - return err; + SharedBufferStack& stack( *mSharedStack ); + return stack.getDirtyRegion(buf); } -Region SharedBufferServer::getDirtyRegion(int buffer) const +Rect SharedBufferServer::getCrop(int buf) const { SharedBufferStack& stack( *mSharedStack ); - return stack.getDirtyRegion(buffer); + return stack.getCrop(buf); +} + +uint32_t SharedBufferServer::getTransform(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getTransform(buf); +} + +/* + * NOTE: this is not thread-safe on the server-side, meaning + * 'head' cannot move during this operation. The client-side + * can safely operate an usual. + * + */ +status_t SharedBufferServer::resize(int newNumBuffers) +{ + if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + RWLock::AutoWLock _l(mLock); + + // for now we're not supporting shrinking + const int numBuffers = mNumBuffers; + if (newNumBuffers < numBuffers) + return BAD_VALUE; + + SharedBufferStack& stack( *mSharedStack ); + const int extra = newNumBuffers - numBuffers; + + // read the head, make sure it's valid + int32_t head = stack.head; + if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + int base = numBuffers; + int32_t avail = stack.available; + int tail = head - avail + 1; + + if (tail >= 0) { + int8_t* const index = const_cast<int8_t*>(stack.index); + const int nb = numBuffers - head; + memmove(&index[head + extra], &index[head], nb); + base = head; + // move head 'extra' ahead, this doesn't impact stack.index[head]; + stack.head = head + extra; + } + stack.available += extra; + + // fill the new free space with unused buffers + BufferList::const_iterator curr(mBufferList.free_begin()); + for (int i=0 ; i<extra ; i++) { + stack.index[base+i] = *curr; + mBufferList.add(*curr); + ++curr; + } + + mNumBuffers = newNumBuffers; + return NO_ERROR; } SharedBufferStack::Statistics SharedBufferServer::getStats() const @@ -422,6 +653,29 @@ SharedBufferStack::Statistics SharedBufferServer::getStats() const return stack.stats; } +// --------------------------------------------------------------------------- +status_t SharedBufferServer::BufferList::add(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (mList & mask) + return ALREADY_EXISTS; + mList |= mask; + return NO_ERROR; +} + +status_t SharedBufferServer::BufferList::remove(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (!(mList & mask)) + return NAME_NOT_FOUND; + mList &= ~mask; + return NO_ERROR; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 5dd75c3213..cb76091763 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -17,8 +17,6 @@ #define LOG_TAG "Surface" #include <stdint.h> -#include <unistd.h> -#include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> @@ -28,8 +26,6 @@ #include <utils/CallStack.h> #include <utils/Log.h> -#include <pixelflinger/pixelflinger.h> - #include <binder/IPCThreadState.h> #include <binder/IMemory.h> @@ -55,6 +51,8 @@ static status_t copyBlt( const sp<GraphicBuffer>& src, const Region& reg) { + // src and dst with, height and format must be identical. no verification + // is done here. status_t err; uint8_t const * src_bits = NULL; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); @@ -67,7 +65,6 @@ static status_t copyBlt( Region::const_iterator head(reg.begin()); Region::const_iterator tail(reg.end()); if (head != tail && src_bits && dst_bits) { - // NOTE: dst and src must be the same format const size_t bpp = bytesPerPixel(src->format); const size_t dbpr = dst->stride * bpp; const size_t sbpr = src->stride * bpp; @@ -107,7 +104,7 @@ static status_t copyBlt( SurfaceControl::SurfaceControl( const sp<SurfaceComposerClient>& client, const sp<ISurface>& surface, - const ISurfaceFlingerClient::surface_data_t& data, + const ISurfaceComposerClient::surface_data_t& data, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) : mClient(client), mSurface(surface), mToken(data.token), mIdentity(data.identity), @@ -154,75 +151,75 @@ bool SurfaceControl::isSameSurface( } status_t SurfaceControl::setLayer(int32_t layer) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setLayer(mToken, layer); } status_t SurfaceControl::setPosition(int32_t x, int32_t y) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setPosition(mToken, x, y); } status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setSize(mToken, w, h); } status_t SurfaceControl::hide() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->hide(mToken); } status_t SurfaceControl::show(int32_t layer) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->show(mToken, layer); } status_t SurfaceControl::freeze() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->freeze(mToken); } status_t SurfaceControl::unfreeze() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->unfreeze(mToken); } status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setFlags(mToken, flags, mask); } status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setTransparentRegionHint(mToken, transparent); } status_t SurfaceControl::setAlpha(float alpha) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setAlpha(mToken, alpha); } status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); } status_t SurfaceControl::setFreezeTint(uint32_t tint) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setFreezeTint(mToken, tint); } @@ -233,50 +230,27 @@ status_t SurfaceControl::validate() const mToken, mIdentity, mClient.get()); return NO_INIT; } - SharedClient const* cblk = mClient->mControl; - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); - return NO_INIT; - } - status_t err = cblk->validate(mToken); - if (err != NO_ERROR) { - LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", - mToken, mIdentity, err, strerror(-err)); - return err; - } - uint32_t identity = cblk->getIdentity(mToken); - if (mIdentity != identity) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - mToken, mIdentity, identity); - return NO_INIT; - } return NO_ERROR; } status_t SurfaceControl::writeSurfaceToParcel( const sp<SurfaceControl>& control, Parcel* parcel) { - uint32_t flags = 0; - uint32_t format = 0; - SurfaceID token = -1; + sp<ISurface> sur; uint32_t identity = 0; uint32_t width = 0; uint32_t height = 0; - sp<SurfaceComposerClient> client; - sp<ISurface> sur; + uint32_t format = 0; + uint32_t flags = 0; if (SurfaceControl::isValid(control)) { - token = control->mToken; - identity = control->mIdentity; - client = control->mClient; sur = control->mSurface; + identity = control->mIdentity; width = control->mWidth; height = control->mHeight; format = control->mFormat; flags = control->mFlags; } - parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); - parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); - parcel->writeInt32(token); + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); parcel->writeInt32(identity); parcel->writeInt32(width); parcel->writeInt32(height); @@ -298,70 +272,180 @@ sp<Surface> SurfaceControl::getSurface() const // Surface // ============================================================================ +class SurfaceClient : public Singleton<SurfaceClient> +{ + // all these attributes are constants + sp<ISurfaceComposer> mComposerService; + sp<ISurfaceComposerClient> mClient; + status_t mStatus; + SharedClient* mControl; + sp<IMemoryHeap> mControlMemory; + + SurfaceClient() + : Singleton<SurfaceClient>(), mStatus(NO_INIT) + { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + mComposerService = sf; + mClient = sf->createClientConnection(); + if (mClient != NULL) { + mControlMemory = mClient->getControlBlock(); + if (mControlMemory != NULL) { + mControl = static_cast<SharedClient *>( + mControlMemory->getBase()); + if (mControl) { + mStatus = NO_ERROR; + } + } + } + } + friend class Singleton<SurfaceClient>; +public: + status_t initCheck() const { + return mStatus; + } + SharedClient* getSharedClient() const { + return mControl; + } + ssize_t getTokenForSurface(const sp<ISurface>& sur) const { + // TODO: we could cache a few tokens here to avoid an IPC + return mClient->getTokenForSurface(sur); + } + void signalServer() const { + mComposerService->signal(); + } +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient); + +// --------------------------------------------------------------------------- + Surface::Surface(const sp<SurfaceControl>& surface) - : mClient(surface->mClient), mSurface(surface->mSurface), - mToken(surface->mToken), mIdentity(surface->mIdentity), + : mBufferMapper(GraphicBufferMapper::get()), + mClient(SurfaceClient::getInstance()), + mSharedBufferClient(NULL), + mInitCheck(NO_INIT), + mSurface(surface->mSurface), + mIdentity(surface->mIdentity), mFormat(surface->mFormat), mFlags(surface->mFlags), - mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL), mWidth(surface->mWidth), mHeight(surface->mHeight) { - mSharedBufferClient = new SharedBufferClient( - mClient->mControl, mToken, 2, mIdentity); - init(); } -Surface::Surface(const Parcel& parcel) - : mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL) +Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) + : mBufferMapper(GraphicBufferMapper::get()), + mClient(SurfaceClient::getInstance()), + mSharedBufferClient(NULL), + mInitCheck(NO_INIT) { - sp<IBinder> clientBinder = parcel.readStrongBinder(); - mSurface = interface_cast<ISurface>(parcel.readStrongBinder()); - mToken = parcel.readInt32(); + mSurface = interface_cast<ISurface>(ref); mIdentity = parcel.readInt32(); mWidth = parcel.readInt32(); mHeight = parcel.readInt32(); mFormat = parcel.readInt32(); mFlags = parcel.readInt32(); + init(); +} + +status_t Surface::writeToParcel( + const sp<Surface>& surface, Parcel* parcel) +{ + sp<ISurface> sur; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t format = 0; + uint32_t flags = 0; + if (Surface::isValid(surface)) { + sur = surface->mSurface; + identity = surface->mIdentity; + width = surface->mWidth; + height = surface->mHeight; + format = surface->mFormat; + flags = surface->mFlags; + } + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; + +} + - // FIXME: what does that mean if clientBinder is NULL here? - if (clientBinder != NULL) { - mClient = SurfaceComposerClient::clientForConnection(clientBinder); +Mutex Surface::sCachedSurfacesLock; +DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces(wp<Surface>(0)); - mSharedBufferClient = new SharedBufferClient( - mClient->mControl, mToken, 2, mIdentity); +sp<Surface> Surface::readFromParcel(const Parcel& data) { + Mutex::Autolock _l(sCachedSurfacesLock); + sp<IBinder> binder(data.readStrongBinder()); + sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote(); + if (surface == 0) { + surface = new Surface(data, binder); + sCachedSurfaces.add(binder, surface); } + if (surface->mSurface == 0) { + surface = 0; + } + cleanCachedSurfaces(); + return surface; +} - init(); +// Remove the stale entries from the surface cache. This should only be called +// with sCachedSurfacesLock held. +void Surface::cleanCachedSurfaces() { + for (int i = sCachedSurfaces.size()-1; i >= 0; --i) { + wp<Surface> s(sCachedSurfaces.valueAt(i)); + if (s == 0 || s.promote() == 0) { + sCachedSurfaces.removeItemsAt(i); + } + } } void Surface::init() { - android_native_window_t::setSwapInterval = setSwapInterval; - android_native_window_t::dequeueBuffer = dequeueBuffer; - android_native_window_t::lockBuffer = lockBuffer; - android_native_window_t::queueBuffer = queueBuffer; - android_native_window_t::query = query; - android_native_window_t::perform = perform; - mSwapRectangle.makeInvalid(); + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; + DisplayInfo dinfo; SurfaceComposerClient::getDisplayInfo(0, &dinfo); - const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; - const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi; + const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; + const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; // FIXME: set real values here - const_cast<int&>(android_native_window_t::minSwapInterval) = 1; - const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; - const_cast<uint32_t&>(android_native_window_t::flags) = 0; - // be default we request a hardware surface - mUsage = GRALLOC_USAGE_HW_RENDER; + const_cast<int&>(ANativeWindow::minSwapInterval) = 1; + const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; + const_cast<uint32_t&>(ANativeWindow::flags) = 0; + + mNextBufferTransform = 0; mConnected = 0; - mNeedFullUpdate = false; + mSwapRectangle.makeInvalid(); + mNextBufferCrop = Rect(0,0); + // two buffers by default + mBuffers.setCapacity(2); + mBuffers.insertAt(0, 2); + + if (mSurface != 0 && mClient.initCheck() == NO_ERROR) { + int32_t token = mClient.getTokenForSurface(mSurface); + if (token >= 0) { + mSharedBufferClient = new SharedBufferClient( + mClient.getSharedClient(), token, 2, mIdentity); + mInitCheck = mClient.getSharedClient()->validate(token); + } + } } Surface::~Surface() { // this is a client-side operation, the surface is destroyed, unmap // its buffers in this process. - for (int i=0 ; i<2 ; i++) { + size_t size = mBuffers.size(); + for (size_t i=0 ; i<size ; i++) { if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) { getBufferMapper().unregisterBuffer(mBuffers[i]->handle); } @@ -369,93 +453,88 @@ Surface::~Surface() // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. - mClient.clear(); + mBuffers.clear(); mSurface.clear(); delete mSharedBufferClient; IPCThreadState::self()->flushCommands(); } -sp<SurfaceComposerClient> Surface::getClient() const { - return mClient; -} - -sp<ISurface> Surface::getISurface() const { - return mSurface; -} - bool Surface::isValid() { - return mToken>=0 && mClient!=0; + return mInitCheck == NO_ERROR; } status_t Surface::validate() const { - sp<SurfaceComposerClient> client(getClient()); - if (mToken<0 || mClient==0) { - LOGE("invalid token (%d, identity=%u) or client (%p)", - mToken, mIdentity, client.get()); - return NO_INIT; + // check that we initialized ourself properly + if (mInitCheck != NO_ERROR) { + LOGE("invalid token (identity=%u)", mIdentity); + return mInitCheck; + } + + // verify the identity of this surface + uint32_t identity = mSharedBufferClient->getIdentity(); + + // this is a bit of a (temporary) special case, identity==0 means that + // no operation are allowed from the client (eg: dequeue/queue), this + // is used with PUSH_BUFFER surfaces for instance + if (identity == 0) { + LOGE("[Surface] invalid operation (identity=%u)", mIdentity); + return INVALID_OPERATION; } - SharedClient const* cblk = mClient->mControl; - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + + if (mIdentity != identity) { + LOGE("[Surface] using an invalid surface, " + "identity=%u should be %d", + mIdentity, identity); return NO_INIT; } - status_t err = cblk->validate(mToken); + + // check the surface didn't become invalid + status_t err = mSharedBufferClient->getStatus(); if (err != NO_ERROR) { - LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", - mToken, mIdentity, err, strerror(-err)); + LOGE("surface (identity=%u) is invalid, err=%d (%s)", + mIdentity, err, strerror(-err)); return err; } - uint32_t identity = cblk->getIdentity(mToken); - if (mIdentity != identity) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - mToken, mIdentity, identity); - return NO_INIT; - } + return NO_ERROR; } - -bool Surface::isSameSurface( - const sp<Surface>& lhs, const sp<Surface>& rhs) -{ - if (lhs == 0 || rhs == 0) - return false; - - return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); +sp<ISurface> Surface::getISurface() const { + return mSurface; } // ---------------------------------------------------------------------------- -int Surface::setSwapInterval(android_native_window_t* window, int interval) { +int Surface::setSwapInterval(ANativeWindow* window, int interval) { return 0; } -int Surface::dequeueBuffer(android_native_window_t* window, +int Surface::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { Surface* self = getSelf(window); return self->dequeueBuffer(buffer); } -int Surface::lockBuffer(android_native_window_t* window, +int Surface::lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { Surface* self = getSelf(window); return self->lockBuffer(buffer); } -int Surface::queueBuffer(android_native_window_t* window, +int Surface::queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { Surface* self = getSelf(window); return self->queueBuffer(buffer); } -int Surface::query(android_native_window_t* window, +int Surface::query(ANativeWindow* window, int what, int* value) { Surface* self = getSelf(window); return self->query(what, value); } -int Surface::perform(android_native_window_t* window, +int Surface::perform(ANativeWindow* window, int operation, ...) { va_list args; va_start(args, operation); @@ -467,21 +546,24 @@ int Surface::perform(android_native_window_t* window, // ---------------------------------------------------------------------------- -status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) { - android_native_buffer_t* out; - status_t err = dequeueBuffer(&out); - if (err == NO_ERROR) { - *buffer = GraphicBuffer::getSelf(out); +bool Surface::needNewBuffer(int bufIdx, + uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const +{ + Mutex::Autolock _l(mSurfaceLock); + + // Always call needNewBuffer(), since it clears the needed buffers flags + bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); + bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); + bool newNeewBuffer = needNewBuffer || !validBuffer; + if (newNeewBuffer) { + mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); } - return err; + return newNeewBuffer; } -// ---------------------------------------------------------------------------- - - int Surface::dequeueBuffer(android_native_buffer_t** buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); if (err != NO_ERROR) return err; @@ -492,24 +574,28 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) return bufIdx; } - // below we make sure we AT LEAST have the usage flags we want - const uint32_t usage(getUsage()); - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - if (backBuffer == 0 || - ((uint32_t(backBuffer->usage) & usage) != usage) || - mSharedBufferClient->needNewBuffer(bufIdx)) - { - err = getBufferLocked(bufIdx, usage); - LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)", - bufIdx, usage, strerror(-err)); + // grow the buffer array if needed + const size_t size = mBuffers.size(); + const size_t needed = bufIdx+1; + if (size < needed) { + mBuffers.insertAt(size, needed-size); + } + + uint32_t w, h, format, usage; + if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { + err = getBufferLocked(bufIdx, w, h, format, usage); + LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)", + bufIdx, w, h, format, usage, strerror(-err)); if (err == NO_ERROR) { // reset the width/height with the what we get from the buffer + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); mWidth = uint32_t(backBuffer->width); mHeight = uint32_t(backBuffer->height); } } // if we still don't have a buffer here, we probably ran out of memory + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); if (!err && backBuffer==0) { err = NO_MEMORY; } @@ -526,12 +612,11 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) int Surface::lockBuffer(android_native_buffer_t* buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); if (err != NO_ERROR) return err; - int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); err = mSharedBufferClient->lock(bufIdx); LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); return err; @@ -539,7 +624,6 @@ int Surface::lockBuffer(android_native_buffer_t* buffer) int Surface::queueBuffer(android_native_buffer_t* buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); if (err != NO_ERROR) return err; @@ -548,14 +632,16 @@ int Surface::queueBuffer(android_native_buffer_t* buffer) mDirtyRegion.set(mSwapRectangle); } - int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); + mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); err = mSharedBufferClient->queue(bufIdx); LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); if (err == NO_ERROR) { - // FIXME: can we avoid this IPC if we know there is one pending? - client->signalServer(); + // TODO: can we avoid this IPC if we know there is one pending? + mClient.signalServer(); } return err; } @@ -578,6 +664,10 @@ int Surface::query(int what, int* value) int Surface::perform(int operation, va_list args) { + status_t err = validate(); + if (err != NO_ERROR) + return err; + int res = NO_ERROR; switch (operation) { case NATIVE_WINDOW_SET_USAGE: @@ -589,6 +679,18 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_DISCONNECT: res = dispatch_disconnect( args ); break; + case NATIVE_WINDOW_SET_CROP: + res = dispatch_crop( args ); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatch_set_buffer_count( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatch_set_buffers_geometry( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatch_set_buffers_transform( args ); + break; default: res = NAME_NOT_FOUND; break; @@ -608,12 +710,30 @@ int Surface::dispatch_disconnect(va_list args) { int api = va_arg(args, int); return disconnect( api ); } +int Surface::dispatch_crop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return crop( reinterpret_cast<Rect const*>(rect) ); +} +int Surface::dispatch_set_buffer_count(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} +int Surface::dispatch_set_buffers_geometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + return setBuffersGeometry(w, h, f); +} +int Surface::dispatch_set_buffers_transform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} void Surface::setUsage(uint32_t reqUsage) { Mutex::Autolock _l(mSurfaceLock); - mUsage = reqUsage; + mBufferInfo.set(reqUsage); } int Surface::connect(int api) @@ -654,19 +774,66 @@ int Surface::disconnect(int api) return err; } -uint32_t Surface::getUsage() const +int Surface::crop(Rect const* rect) { + // empty/invalid rects are not allowed + if (rect->isEmpty()) + return BAD_VALUE; + Mutex::Autolock _l(mSurfaceLock); - return mUsage; + // TODO: validate rect size + mNextBufferCrop = *rect; + return NO_ERROR; } +int Surface::setBufferCount(int bufferCount) +{ + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { + sp<ISurface> surface; + virtual status_t operator()(int bufferCount) const { + return surface->setBufferCount(bufferCount); + } + public: + SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { } + } ipc(s); + + status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); + LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + return err; +} + +int Surface::setBuffersGeometry(int w, int h, int format) +{ + if (w<0 || h<0 || format<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock _l(mSurfaceLock); + mBufferInfo.set(w, h, format); + return NO_ERROR; +} + +int Surface::setBuffersTransform(int transform) +{ + Mutex::Autolock _l(mSurfaceLock); + mNextBufferTransform = transform; + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + int Surface::getConnectedApi() const { Mutex::Autolock _l(mSurfaceLock); return mConnected; } - // ---------------------------------------------------------------------------- status_t Surface::lock(SurfaceInfo* info, bool blocking) { @@ -677,7 +844,7 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) { if (getConnectedApi()) { LOGE("Surface::lock(%p) failed. Already connected to another API", - (android_native_window_t*)this); + (ANativeWindow*)this); CallStack stack; stack.update(); stack.dump(""); @@ -703,45 +870,47 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) // we're intending to do software rendering from this point setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> backBuffer; - status_t err = dequeueBuffer(&backBuffer); + android_native_buffer_t* out; + status_t err = dequeueBuffer(&out); LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { + sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); err = lockBuffer(backBuffer.get()); LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", - backBuffer->getIndex(), strerror(-err)); + getBufferIndex(backBuffer), strerror(-err)); if (err == NO_ERROR) { - // we handle copy-back here... - const Rect bounds(backBuffer->width, backBuffer->height); - Region scratch(bounds); + const Region boundsRegion(bounds); + Region scratch(boundsRegion); Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + newDirtyRegion &= boundsRegion; - if (mNeedFullUpdate) { - // reset newDirtyRegion to bounds when a buffer is reallocated - // it would be better if this information was associated with - // the buffer and made available to outside of Surface. - // This will do for now though. - mNeedFullUpdate = false; - newDirtyRegion.set(bounds); - } else { - newDirtyRegion.andSelf(bounds); - } - + // figure out if we can copy the frontbuffer back const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - if (frontBuffer !=0 && - backBuffer->width == frontBuffer->width && - backBuffer->height == frontBuffer->height && - !(mFlags & ISurfaceComposer::eDestroyBackbuffer)) - { + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format && + !(mFlags & ISurfaceComposer::eDestroyBackbuffer)); + + // the dirty region we report to surfaceflinger is the one + // given by the user (as opposed to the one *we* return to the + // user). + mDirtyRegion = newDirtyRegion; + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); - if (!copyback.isEmpty() && frontBuffer!=0) { - // copy front to back + if (!copyback.isEmpty()) copyBlt(backBuffer, frontBuffer, copyback); - } + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion = boundsRegion; } - mDirtyRegion = newDirtyRegion; + // keep track of the are of the buffer that is "clean" + // (ie: that will be redrawn) mOldDirtyRegion = newDirtyRegion; void* vaddr; @@ -777,7 +946,7 @@ status_t Surface::unlockAndPost() err = queueBuffer(mLockedBuffer.get()); LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", - mLockedBuffer->getIndex(), strerror(-err)); + getBufferIndex(mLockedBuffer), strerror(-err)); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; @@ -789,7 +958,13 @@ void Surface::setSwapRectangle(const Rect& r) { mSwapRectangle = r; } -status_t Surface::getBufferLocked(int index, int usage) +int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const +{ + return buffer->getIndex(); +} + +status_t Surface::getBufferLocked(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { sp<ISurface> s(mSurface); if (s == 0) return NO_INIT; @@ -797,20 +972,21 @@ status_t Surface::getBufferLocked(int index, int usage) status_t err = NO_MEMORY; // free the current buffer - sp<GraphicBuffer>& currentBuffer(mBuffers[index]); + sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); if (currentBuffer != 0) { getBufferMapper().unregisterBuffer(currentBuffer->handle); currentBuffer.clear(); } - sp<GraphicBuffer> buffer = s->requestBuffer(index, usage); + sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); LOGE_IF(buffer==0, "ISurface::getBuffer(%d, %08x) returned NULL", index, usage); if (buffer != 0) { // this should never happen by construction LOGE_IF(buffer->handle == NULL, - "Surface (identity=%d) requestBuffer(%d, %08x) returned" - "a buffer with a null handle", mIdentity, index, usage); + "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " + "returned a buffer with a null handle", + mIdentity, index, w, h, format, usage); err = mSharedBufferClient->getStatus(); LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); if (!err && buffer->handle != NULL) { @@ -820,14 +996,51 @@ status_t Surface::getBufferLocked(int index, int usage) if (err == NO_ERROR) { currentBuffer = buffer; currentBuffer->setIndex(index); - mNeedFullUpdate = true; } } else { - err = err<0 ? err : NO_MEMORY; + err = err<0 ? err : status_t(NO_MEMORY); } } return err; } -}; // namespace android +// ---------------------------------------------------------------------------- +Surface::BufferInfo::BufferInfo() + : mWidth(0), mHeight(0), mFormat(0), + mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0) +{ +} + +void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) { + if ((mWidth != w) || (mHeight != h) || (mFormat != format)) { + mWidth = w; + mHeight = h; + mFormat = format; + mDirty |= GEOMETRY; + } +} + +void Surface::BufferInfo::set(uint32_t usage) { + mUsage = usage; +} + +void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const { + *pWidth = mWidth; + *pHeight = mHeight; + *pFormat = mFormat; + *pUsage = mUsage; +} +bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { + // make sure we AT LEAST have the usage flags we want + if (mDirty || buffer==0 || + ((buffer->usage & mUsage) != mUsage)) { + mDirty = 0; + return false; + } + return true; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp index 85167da17c..4096ac6854 100644 --- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp @@ -17,98 +17,137 @@ #define LOG_TAG "SurfaceComposerClient" #include <stdint.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> #include <sys/types.h> -#include <sys/stat.h> -#include <cutils/memory.h> - -#include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> #include <utils/Log.h> +#include <utils/Singleton.h> #include <binder/IServiceManager.h> #include <binder/IMemory.h> #include <ui/DisplayInfo.h> -#include <ui/Rect.h> #include <surfaceflinger/ISurfaceComposer.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <surfaceflinger/ISurface.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> #include <private/surfaceflinger/SharedBufferStack.h> -#define VERBOSE(...) ((void)0) -//#define VERBOSE LOGD - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); + +ComposerService::ComposerService() +: Singleton<ComposerService>() { + const String16 name("SurfaceFlinger"); + while (getService(name, &mComposerService) != NO_ERROR) { + usleep(250000); + } + mServerCblkMemory = mComposerService->getCblk(); + mServerCblk = static_cast<surface_flinger_cblk_t volatile *>( + mServerCblkMemory->getBase()); +} + +sp<ISurfaceComposer> ComposerService::getComposerService() { + return ComposerService::getInstance().mComposerService; +} + +surface_flinger_cblk_t const volatile * ComposerService::getControlBlock() { + return ComposerService::getInstance().mServerCblk; +} + +static inline sp<ISurfaceComposer> getComposerService() { + return ComposerService::getComposerService(); +} + +static inline surface_flinger_cblk_t const volatile * get_cblk() { + return ComposerService::getControlBlock(); +} // --------------------------------------------------------------------------- -// Must not be holding SurfaceComposerClient::mLock when acquiring gLock here. -static Mutex gLock; -static sp<ISurfaceComposer> gSurfaceManager; -static DefaultKeyedVector< sp<IBinder>, wp<SurfaceComposerClient> > gActiveConnections; -static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions; -static sp<IMemoryHeap> gServerCblkMemory; -static volatile surface_flinger_cblk_t* gServerCblk; - -static sp<ISurfaceComposer> getComposerService() -{ - sp<ISurfaceComposer> sc; - Mutex::Autolock _l(gLock); - if (gSurfaceManager != 0) { - sc = gSurfaceManager; - } else { - // release the lock while we're waiting... - gLock.unlock(); - - sp<IBinder> binder; - sp<IServiceManager> sm = defaultServiceManager(); - do { - binder = sm->getService(String16("SurfaceFlinger")); - if (binder == 0) { - LOGW("SurfaceFlinger not published, waiting..."); - usleep(500000); // 0.5 s +class Composer : public Singleton<Composer> +{ + Mutex mLock; + SortedVector< wp<SurfaceComposerClient> > mActiveConnections; + SortedVector<sp<SurfaceComposerClient> > mOpenTransactions; + + Composer() : Singleton<Composer>() { + } + + void addClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.add(client); + } + + void removeClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.remove(client); + } + + void openGlobalTransactionImpl() + { + Mutex::Autolock _l(mLock); + if (mOpenTransactions.size()) { + LOGE("openGlobalTransaction() called more than once. skipping."); + return; + } + const size_t N = mActiveConnections.size(); + for (size_t i=0; i<N; i++) { + sp<SurfaceComposerClient> client(mActiveConnections[i].promote()); + if (client != 0 && mOpenTransactions.indexOf(client) < 0) { + if (client->openTransaction() == NO_ERROR) { + mOpenTransactions.add(client); + } else { + LOGE("openTransaction on client %p failed", client.get()); + // let it go, it'll fail later when the user + // tries to do something with the transaction + } } - } while(binder == 0); - - // grab the lock again for updating gSurfaceManager - gLock.lock(); - if (gSurfaceManager == 0) { - sc = interface_cast<ISurfaceComposer>(binder); - gSurfaceManager = sc; - } else { - sc = gSurfaceManager; } } - return sc; -} -static volatile surface_flinger_cblk_t const * get_cblk() -{ - if (gServerCblk == 0) { + void closeGlobalTransactionImpl() + { + mLock.lock(); + SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions); + mOpenTransactions.clear(); + mLock.unlock(); + sp<ISurfaceComposer> sm(getComposerService()); - Mutex::Autolock _l(gLock); - if (gServerCblk == 0) { - gServerCblkMemory = sm->getCblk(); - LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); - gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase(); - LOGE_IF(gServerCblk==0, "Can't get server control block address"); - } + sm->openGlobalTransaction(); + const size_t N = clients.size(); + for (size_t i=0; i<N; i++) { + clients[i]->closeTransaction(); + } + sm->closeGlobalTransaction(); } - return gServerCblk; -} + + friend class Singleton<Composer>; + +public: + static void addClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().addClientImpl(client); + } + static void removeClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().removeClientImpl(client); + } + static void openGlobalTransaction() { + Composer::getInstance().openGlobalTransactionImpl(); + } + static void closeGlobalTransaction() { + Composer::getInstance().closeGlobalTransactionImpl(); + } +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- @@ -120,61 +159,27 @@ static inline int compare_type( const layer_state_t& lhs, } SurfaceComposerClient::SurfaceComposerClient() + : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT) { - sp<ISurfaceComposer> sm(getComposerService()); - if (sm == 0) { - _init(0, 0); - return; - } - - _init(sm, sm->createConnection()); - - if (mClient != 0) { - Mutex::Autolock _l(gLock); - VERBOSE("Adding client %p to map", this); - gActiveConnections.add(mClient->asBinder(), this); - } } -SurfaceComposerClient::SurfaceComposerClient( - const sp<ISurfaceComposer>& sm, const sp<IBinder>& conn) -{ - _init(sm, interface_cast<ISurfaceFlingerClient>(conn)); -} - - -status_t SurfaceComposerClient::linkToComposerDeath( - const sp<IBinder::DeathRecipient>& recipient, - void* cookie, uint32_t flags) +void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(getComposerService()); - return sm->asBinder()->linkToDeath(recipient, cookie, flags); -} - -void SurfaceComposerClient::_init( - const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn) -{ - VERBOSE("Creating client %p, conn %p", this, conn.get()); - - mPrebuiltLayerState = 0; - mTransactionOpen = 0; - mStatus = NO_ERROR; - mControl = 0; - - mClient = conn; - if (mClient == 0) { - mStatus = NO_INIT; - return; + if (sm != 0) { + sp<ISurfaceComposerClient> conn = sm->createConnection(); + if (conn != 0) { + mClient = conn; + Composer::addClient(this); + mPrebuiltLayerState = new layer_state_t; + mStatus = NO_ERROR; + } } - - mControlMemory = mClient->getControlBlock(); - mSignalServer = sm; - mControl = static_cast<SharedClient *>(mControlMemory->getBase()); } SurfaceComposerClient::~SurfaceComposerClient() { - VERBOSE("Destroying client %p, conn %p", this, mClient.get()); + delete mPrebuiltLayerState; dispose(); } @@ -188,69 +193,31 @@ sp<IBinder> SurfaceComposerClient::connection() const return (mClient != 0) ? mClient->asBinder() : 0; } -sp<SurfaceComposerClient> -SurfaceComposerClient::clientForConnection(const sp<IBinder>& conn) +status_t SurfaceComposerClient::linkToComposerDeath( + const sp<IBinder::DeathRecipient>& recipient, + void* cookie, uint32_t flags) { - sp<SurfaceComposerClient> client; - - { // scope for lock - Mutex::Autolock _l(gLock); - client = gActiveConnections.valueFor(conn).promote(); - } - - if (client == 0) { - // Need to make a new client. - sp<ISurfaceComposer> sm(getComposerService()); - client = new SurfaceComposerClient(sm, conn); - if (client != 0 && client->initCheck() == NO_ERROR) { - Mutex::Autolock _l(gLock); - gActiveConnections.add(conn, client); - //LOGD("we have %d connections", gActiveConnections.size()); - } else { - client.clear(); - } - } - - return client; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->asBinder()->linkToDeath(recipient, cookie, flags); } void SurfaceComposerClient::dispose() { // this can be called more than once. - - sp<IMemoryHeap> controlMemory; - sp<ISurfaceFlingerClient> client; - - { - Mutex::Autolock _lg(gLock); - Mutex::Autolock _lm(mLock); - - mSignalServer = 0; - - if (mClient != 0) { - client = mClient; - mClient.clear(); - - ssize_t i = gActiveConnections.indexOfKey(client->asBinder()); - if (i >= 0 && gActiveConnections.valueAt(i) == this) { - VERBOSE("Removing client %p from map at %d", this, int(i)); - gActiveConnections.removeItemsAt(i); - } - } - - delete mPrebuiltLayerState; - mPrebuiltLayerState = 0; - controlMemory = mControlMemory; - mControlMemory.clear(); - mControl = 0; - mStatus = NO_INIT; + sp<ISurfaceComposerClient> client; + Mutex::Autolock _lm(mLock); + if (mClient != 0) { + Composer::removeClient(this); + client = mClient; // hold ref while lock is held + mClient.clear(); } + mStatus = NO_INIT; } status_t SurfaceComposerClient::getDisplayInfo( DisplayID dpy, DisplayInfo* info) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); @@ -268,7 +235,7 @@ status_t SurfaceComposerClient::getDisplayInfo( ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -277,7 +244,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -286,7 +253,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -305,12 +272,6 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays() return n; } - -void SurfaceComposerClient::signalServer() -{ - mSignalServer->signal(); -} - sp<SurfaceControl> SurfaceComposerClient::createSurface( int pid, DisplayID display, @@ -327,7 +288,6 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( return SurfaceComposerClient::createSurface(pid, name, display, w, h, format, flags); - } sp<SurfaceControl> SurfaceComposerClient::createSurface( @@ -341,13 +301,11 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( { sp<SurfaceControl> result; if (mStatus == NO_ERROR) { - ISurfaceFlingerClient::surface_data_t data; + ISurfaceComposerClient::surface_data_t data; sp<ISurface> surface = mClient->createSurface(&data, pid, name, display, w, h, format, flags); if (surface != 0) { - if (uint32_t(data.token) < NUM_LAYERS_MAX) { - result = new SurfaceControl(this, surface, data, w, h, format, flags); - } + result = new SurfaceControl(this, surface, data, w, h, format, flags); } } return result; @@ -373,56 +331,14 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid) void SurfaceComposerClient::openGlobalTransaction() { - Mutex::Autolock _l(gLock); - - if (gOpenTransactions.size()) { - LOGE("openGlobalTransaction() called more than once. skipping."); - return; - } - - const size_t N = gActiveConnections.size(); - VERBOSE("openGlobalTransaction (%ld clients)", N); - for (size_t i=0; i<N; i++) { - sp<SurfaceComposerClient> client(gActiveConnections.valueAt(i).promote()); - if (client != 0 && gOpenTransactions.indexOf(client) < 0) { - if (client->openTransaction() == NO_ERROR) { - if (gOpenTransactions.add(client) < 0) { - // Ooops! - LOGE( "Unable to add a SurfaceComposerClient " - "to the global transaction set (out of memory?)"); - client->closeTransaction(); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } else { - LOGE("openTransaction on client %p failed", client.get()); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } - } + Composer::openGlobalTransaction(); } void SurfaceComposerClient::closeGlobalTransaction() { - gLock.lock(); - SortedVector< sp<SurfaceComposerClient> > clients(gOpenTransactions); - gOpenTransactions.clear(); - gLock.unlock(); - - const size_t N = clients.size(); - VERBOSE("closeGlobalTransaction (%ld clients)", N); - - sp<ISurfaceComposer> sm(getComposerService()); - sm->openGlobalTransaction(); - for (size_t i=0; i<N; i++) { - clients[i]->closeTransaction(); - } - sm->closeGlobalTransaction(); - + Composer::closeGlobalTransaction(); } - status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) { sp<ISurfaceComposer> sm(getComposerService()); @@ -447,26 +363,16 @@ status_t SurfaceComposerClient::openTransaction() if (mStatus != NO_ERROR) return mStatus; Mutex::Autolock _l(mLock); - VERBOSE( "openTransaction (client %p, mTransactionOpen=%d)", - this, mTransactionOpen); mTransactionOpen++; - if (mPrebuiltLayerState == 0) { - mPrebuiltLayerState = new layer_state_t; - } return NO_ERROR; } - status_t SurfaceComposerClient::closeTransaction() { if (mStatus != NO_ERROR) return mStatus; Mutex::Autolock _l(mLock); - - VERBOSE( "closeTransaction (client %p, mTransactionOpen=%d)", - this, mTransactionOpen); - if (mTransactionOpen <= 0) { LOGE( "closeTransaction (client %p, mTransactionOpen=%d) " "called more times than openTransaction()", @@ -488,7 +394,7 @@ status_t SurfaceComposerClient::closeTransaction() return NO_ERROR; } -layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) +layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index) { // API usage error, do nothing. if (mTransactionOpen<=0) { @@ -498,7 +404,7 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) } // use mPrebuiltLayerState just to find out if we already have it - layer_state_t& dummy = *mPrebuiltLayerState; + layer_state_t& dummy(*mPrebuiltLayerState); dummy.surface = index; ssize_t i = mStates.indexOf(dummy); if (i < 0) { @@ -508,49 +414,49 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) return mStates.editArray() + i; } -layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id) +layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id) { layer_state_t* s; mLock.lock(); - s = _get_state_l(id); + s = get_state_l(id); if (!s) mLock.unlock(); return s; } -void SurfaceComposerClient::_unlockLayerState() +void SurfaceComposerClient::unlockLayerState() { mLock.unlock(); } status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::ePositionChanged; s->x = x; s->y = y; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eSizeChanged; s->w = w; s->h = h; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eLayerChanged; s->z = z; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } @@ -579,34 +485,34 @@ status_t SurfaceComposerClient::unfreeze(SurfaceID id) status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, uint32_t mask) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eVisibilityChanged; s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setTransparentRegionHint( SurfaceID id, const Region& transparentRegion) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eTransparentRegionChanged; s->transparentRegion = transparentRegion; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eAlphaChanged; s->alpha = alpha; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } @@ -615,7 +521,7 @@ status_t SurfaceComposerClient::setMatrix( float dsdx, float dtdx, float dsdy, float dtdy ) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eMatrixChanged; layer_state_t::matrix22_t matrix; @@ -624,19 +530,20 @@ status_t SurfaceComposerClient::setMatrix( matrix.dsdy = dsdy; matrix.dtdy = dtdy; s->matrix = matrix; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eFreezeTintChanged; s->tint = tint; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } +// ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk new file mode 100644 index 0000000000..5053e7d643 --- /dev/null +++ b/libs/surfaceflinger_client/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk new file mode 100644 index 0000000000..d3dfe04285 --- /dev/null +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + SharedBufferStackTest.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-sharedbufferstack + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp new file mode 100644 index 0000000000..f409f48289 --- /dev/null +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp @@ -0,0 +1,279 @@ +/* + * 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. + */ + +#undef NDEBUG + +#include <assert.h> +#include <cutils/memory.h> +#include <cutils/log.h> +#include <utils/Errors.h> +#include <private/surfaceflinger/SharedBufferStack.h> + +using namespace android; + +void log(const char* prefix, int *b, size_t num); +void test0(SharedBufferServer& s, SharedBufferClient& c, size_t num, int* list); + +// ---------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + SharedClient client; + SharedBufferServer s(&client, 0, 4, 0); + SharedBufferClient c(&client, 0, 4, 0); + + printf("basic test 0\n"); + int list0[4] = {0, 1, 2, 3}; + test0(s, c, 4, list0); + + printf("basic test 1\n"); + int list1[4] = {2, 1, 0, 3}; + test0(s, c, 4, list1); + + int b = c.dequeue(); + c.lock(b); + c.queue(b); + s.retireAndLock(); + + printf("basic test 2\n"); + int list2[4] = {1, 2, 3, 0}; + test0(s, c, 4, list2); + + + printf("resize test\n"); + class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { + SharedBufferServer& s; + virtual status_t operator()(int bufferCount) const { + return s.resize(bufferCount); + } + public: + SetBufferCountIPC(SharedBufferServer& s) : s(s) { } + } resize(s); + + c.setBufferCount(6, resize); + int list3[6] = {3, 2, 1, 4, 5, 0}; + test0(s, c, 6, list3); + + return 0; +} + +void log(const char* prefix, int *b, size_t num) +{ + printf("%s: ", prefix); + for (size_t i=0 ; i<num ; i++) { + printf("%d ", b[i]); + } + printf("\n"); +} + +// ---------------------------------------------------------------------------- + +void test0( + SharedBufferServer& s, + SharedBufferClient& c, + size_t num, + int* list) +{ + status_t err; + int b[num], u[num], r[num]; + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==list[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==list[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(b[num-1]); + assert(err == 0); + log("LK", b+num-1, 1); + + err = c.queue(b[num-1]); + assert(err == 0); + log(" Q", b+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==list[num-1]); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==list[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + u[i] = b[num-2-i]; + } + u[num-1] = b[num-1]; + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(u[i]); + assert(err==0); + } + log(" Q", u, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(b[num-1]); + assert(err == 0); + log("LK", b+num-1, 1); + + err = c.queue(b[num-1]); + assert(err == 0); + log(" Q", b+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==list[num-1]); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==u[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(u[num-1]); + assert(err == 0); + log("LK", u+num-1, 1); + + err = c.queue(u[num-1]); + assert(err == 0); + log(" Q", u+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==u[num-1]); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + b[0] = c.dequeue(); + assert(b[0]==u[0]); + log("DQ", b, 1); + + c.undoDequeue(b[0]); + assert(err == 0); + log("UDQ", b, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==u[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(u[num-1]); + assert(err == 0); + log("LK", u+num-1, 1); + + err = c.queue(u[num-1]); + assert(err == 0); + log(" Q", u+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==u[num-1]); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + printf("\n"); +} diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index f7acd973ff..9f493483bf 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -11,6 +11,11 @@ LOCAL_SRC_FILES:= \ GraphicBufferMapper.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ + Input.cpp \ + InputDispatcher.cpp \ + InputManager.cpp \ + InputReader.cpp \ + InputTransport.cpp \ IOverlay.cpp \ Overlay.cpp \ PixelFormat.cpp \ @@ -33,3 +38,13 @@ ifeq ($(TARGET_SIMULATOR),true) endif include $(BUILD_SHARED_LIBRARY) + + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 2d4c2e213d..891661d690 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -54,6 +54,9 @@ */ #define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) +/* this macro computes the number of bytes needed to represent a bit array of the specified size */ +#define sizeof_bit_array(bits) ((bits + 7) / 8) + #define ID_MASK 0x0000ffff #define SEQ_MASK 0x7fff0000 #define SEQ_SHIFT 16 @@ -96,6 +99,7 @@ EventHub::EventHub(void) , mDevicesById(0), mNumDevicesById(0) , mOpeningDevices(0), mClosingDevices(0) , mDevices(0), mFDs(0), mFDCount(0), mOpened(false) + , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); #ifdef EV_SW @@ -133,9 +137,14 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const return device->classes; } -int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const -{ +status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const { + outAxisInfo->valid = false; + outAxisInfo->minValue = 0; + outAxisInfo->maxValue = 0; + outAxisInfo->flat = 0; + outAxisInfo->fuzz = 0; + AutoMutex _l(mLock); device_t* device = getDevice(deviceId); if (device == NULL) return -1; @@ -143,87 +152,58 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - LOGE("Error reading absolute controller %d for device %s fd %d\n", + LOGW("Error reading absolute controller %d for device %s fd %d\n", axis, device->name.string(), device->fd); - return -1; + return -errno; } - *outMinValue = info.minimum; - *outMaxValue = info.maximum; - *outFlat = info.flat; - *outFuzz = info.fuzz; - return 0; -} - -int EventHub::getSwitchState(int sw) const -{ -#ifdef EV_SW - if (sw >= 0 && sw <= SW_MAX) { - int32_t devid = mSwitches[sw]; - if (devid != 0) { - return getSwitchState(devid, sw); - } + if (info.minimum != info.maximum) { + outAxisInfo->valid = true; + outAxisInfo->minValue = info.minimum; + outAxisInfo->maxValue = info.maximum; + outAxisInfo->flat = info.flat; + outAxisInfo->fuzz = info.fuzz; } -#endif - return -1; + return OK; } -int EventHub::getSwitchState(int32_t deviceId, int sw) const -{ -#ifdef EV_SW - AutoMutex _l(mLock); - device_t* device = getDevice(deviceId); - if (device == NULL) return -1; - - if (sw >= 0 && sw <= SW_MAX) { - uint8_t sw_bitmask[(SW_MAX+7)/8]; - memset(sw_bitmask, 0, sizeof(sw_bitmask)); - if (ioctl(device->fd, EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) { - return test_bit(sw, sw_bitmask) ? 1 : 0; +int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { + if (scanCode >= 0 && scanCode <= KEY_MAX) { + AutoMutex _l(mLock); + + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getScanCodeStateLocked(device, scanCode); } } -#endif - - return -1; + return AKEY_STATE_UNKNOWN; } -int EventHub::getScancodeState(int code) const -{ - return getScancodeState(mFirstKeyboardId, code); +int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const { + uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(device->fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + return AKEY_STATE_UNKNOWN; } -int EventHub::getScancodeState(int32_t deviceId, int code) const -{ +int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); - if (device == NULL) return -1; - - if (code >= 0 && code <= KEY_MAX) { - uint8_t key_bitmask[(KEY_MAX+7)/8]; - memset(key_bitmask, 0, sizeof(key_bitmask)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { - return test_bit(code, key_bitmask) ? 1 : 0; - } + if (device != NULL) { + return getKeyCodeStateLocked(device, keyCode); } - - return -1; + return AKEY_STATE_UNKNOWN; } -int EventHub::getKeycodeState(int code) const -{ - return getKeycodeState(mFirstKeyboardId, code); -} - -int EventHub::getKeycodeState(int32_t deviceId, int code) const -{ - AutoMutex _l(mLock); - device_t* device = getDevice(deviceId); - if (device == NULL || device->layoutMap == NULL) return -1; - +int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const { Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(code, &scanCodes); - - uint8_t key_bitmask[(KEY_MAX+7)/8]; + device->layoutMap->findScancodes(keyCode, &scanCodes); + + uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { #if 0 @@ -236,12 +216,72 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const int32_t sc = scanCodes.itemAt(i); //LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask)); if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) { - return 1; + return AKEY_STATE_DOWN; } } + return AKEY_STATE_UP; } - - return 0; + return AKEY_STATE_UNKNOWN; +} + +int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { +#ifdef EV_SW + if (sw >= 0 && sw <= SW_MAX) { + AutoMutex _l(mLock); + + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getSwitchStateLocked(device, sw); + } + } +#endif + return AKEY_STATE_UNKNOWN; +} + +int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const { + uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + if (ioctl(device->fd, + EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) { + return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + return AKEY_STATE_UNKNOWN; +} + +bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + AutoMutex _l(mLock); + + device_t* device = getDevice(deviceId); + if (device != NULL) { + return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags); + } + return false; +} + +bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + if (device->layoutMap == NULL || device->keyBitmask == NULL) { + return false; + } + + Vector<int32_t> scanCodes; + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + scanCodes.clear(); + + status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (! err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], device->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + return true; } status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, @@ -292,27 +332,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const return NULL; } -bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen) +bool EventHub::getEvent(RawEvent* outEvent) { - *outDeviceId = 0; - *outType = 0; - *outScancode = 0; - *outKeycode = 0; - *outFlags = 0; - *outValue = 0; - *outWhen = 0; - - status_t err; - - fd_set readfds; - int maxFd = -1; - int cc; - int i; - int res; - int pollres; - struct input_event iev; + outEvent->deviceId = 0; + outEvent->type = 0; + outEvent->scanCode = 0; + outEvent->keyCode = 0; + outEvent->flags = 0; + outEvent->value = 0; + outEvent->when = 0; // Note that we only allow one caller to getEvent(), so don't need // to do locking here... only when adding/removing devices. @@ -322,93 +350,127 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, mOpened = true; } - while(1) { - - // First, report any devices that had last been added/removed. + for (;;) { + // Report any devices that had last been added/removed. if (mClosingDevices != NULL) { device_t* device = mClosingDevices; LOGV("Reporting device closed: id=0x%x, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_REMOVED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_REMOVED; delete device; return true; } + if (mOpeningDevices != NULL) { device_t* device = mOpeningDevices; LOGV("Reporting device opened: id=0x%x, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_ADDED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_ADDED; return true; } - release_wake_lock(WAKE_LOCK_ID); - - pollres = poll(mFDs, mFDCount, -1); + // Grab the next input event. + for (;;) { + // Consume buffered input events, if any. + if (mInputBufferIndex < mInputBufferCount) { + const struct input_event& iev = mInputBufferData[mInputBufferIndex++]; + const device_t* device = mDevices[mInputDeviceIndex]; + + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = iev.type; + outEvent->scanCode = iev.code; + if (iev.type == EV_KEY) { + status_t err = device->layoutMap->map(iev.code, + & outEvent->keyCode, & outEvent->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, outEvent->keyCode, outEvent->flags, err); + if (err != 0) { + outEvent->keyCode = AKEYCODE_UNKNOWN; + outEvent->flags = 0; + } + } else { + outEvent->keyCode = iev.code; + } + outEvent->value = iev.value; - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + // Use an event timestamp in the same timebase as + // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() + // as expected by the rest of the system. + outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); + return true; + } - if (pollres <= 0) { - if (errno != EINTR) { - LOGW("select failed (errno=%d)\n", errno); - usleep(100000); + // Finish reading all events from devices identified in previous poll(). + // This code assumes that mInputDeviceIndex is initially 0 and that the + // revents member of pollfd is initialized to 0 when the device is first added. + // Since mFDs[0] is used for inotify, we process regular events starting at index 1. + mInputDeviceIndex += 1; + if (mInputDeviceIndex >= mFDCount) { + mInputDeviceIndex = 0; + break; } - continue; - } - //printf("poll %d, returned %d\n", mFDCount, pollres); - - // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] - for(i = 1; i < mFDCount; i++) { - if(mFDs[i].revents) { - LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); - if(mFDs[i].revents & POLLIN) { - res = read(mFDs[i].fd, &iev, sizeof(iev)); - if (res == sizeof(iev)) { - LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", - mDevices[i]->path.string(), - (int) iev.time.tv_sec, (int) iev.time.tv_usec, - iev.type, iev.code, iev.value); - *outDeviceId = mDevices[i]->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = iev.type; - *outScancode = iev.code; - if (iev.type == EV_KEY) { - err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); - LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", - iev.code, *outKeycode, *outFlags, err); - if (err != 0) { - *outKeycode = 0; - *outFlags = 0; - } - } else { - *outKeycode = iev.code; - } - *outValue = iev.value; - *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); - return true; - } else { - if (res<0) { - LOGW("could not get event (errno=%d)", errno); - } else { - LOGE("could not get event (wrong size: %d)", res); - } - continue; + const struct pollfd &pfd = mFDs[mInputDeviceIndex]; + if (pfd.revents & POLLIN) { + int32_t readSize = read(pfd.fd, mInputBufferData, + sizeof(struct input_event) * INPUT_BUFFER_SIZE); + if (readSize < 0) { + if (errno != EAGAIN && errno != EINTR) { + LOGW("could not get event (errno=%d)", errno); } + } else if ((readSize % sizeof(struct input_event)) != 0) { + LOGE("could not get event (wrong size: %d)", readSize); + } else { + mInputBufferCount = readSize / sizeof(struct input_event); + mInputBufferIndex = 0; } } } - + // read_notify() will modify mFDs and mFDCount, so this must be done after // processing all other events. if(mFDs[0].revents & POLLIN) { read_notify(mFDs[0].fd); } + + // Poll for events. Mind the wake lock dance! + // We hold a wake lock at all times except during poll(). This works due to some + // subtle choreography. When a device driver has pending (unread) events, it acquires + // a kernel wake lock. However, once the last pending event has been read, the device + // driver will release the kernel wake lock. To prevent the system from going to sleep + // when this happens, the EventHub holds onto its own user wake lock while the client + // is processing events. Thus the system can only sleep if there are no events + // pending or currently being processed. + release_wake_lock(WAKE_LOCK_ID); + + int pollResult = poll(mFDs, mFDCount, -1); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + if (pollResult <= 0) { + if (errno != EINTR) { + LOGW("select failed (errno=%d)\n", errno); + usleep(100000); + } + } } } @@ -426,6 +488,7 @@ bool EventHub::openPlatformInput(void) mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); mFDs[0].events = POLLIN; + mFDs[0].revents = 0; mDevices[0] = NULL; #ifdef HAVE_INOTIFY mFDs[0].fd = inotify_init(); @@ -450,37 +513,27 @@ bool EventHub::openPlatformInput(void) return true; } -/* - * Inspect the known devices to determine whether physical keys exist for the given - * framework-domain key codes. - */ -bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) { - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - outFlags[codeIndex] = 0; - - // check each available hardware device for support for this keycode - Vector<int32_t> scanCodes; - for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { - if (mDevices[n]) { - status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { - outFlags[codeIndex] = 1; - break; - } - } - } - } +// ---------------------------------------------------------------------------- + +static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { + const uint8_t* end = array + endIndex; + array += startIndex; + while (array != end) { + if (*(array++) != 0) { + return true; } } - - return true; + return false; } -// ---------------------------------------------------------------------------- +static const int32_t GAMEPAD_KEYCODES[] = { + AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, + AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, + AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, + AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, + AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE +}; int EventHub::open_device(const char *deviceName) { @@ -541,6 +594,12 @@ int EventHub::open_device(const char *deviceName) idstr[0] = '\0'; } + if (fcntl(fd, F_SETFL, O_NONBLOCK)) { + LOGE("Error %d making device file descriptor non-blocking.", errno); + close(fd); + return -1; + } + int devid = 0; while (devid < mNumDevicesById) { if (mDevicesById[devid].device == NULL) { @@ -598,28 +657,29 @@ int EventHub::open_device(const char *deviceName) device->fd = fd; mFDs[mFDCount].fd = fd; mFDs[mFDCount].events = POLLIN; + mFDs[mFDCount].revents = 0; - // figure out the kinds of events the device reports + // Figure out the kinds of events the device reports. - // See if this is a keyboard, and classify it. Note that we only - // consider up through the function keys; we don't want to include - // ones after that (play cd etc) so we don't mistakenly consider a - // controller to be a keyboard. - uint8_t key_bitmask[(KEY_MAX+7)/8]; + uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); + LOGV("Getting keys..."); if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { //LOGI("MAP\n"); - //for (int i=0; i<((KEY_MAX+7)/8); i++) { + //for (int i = 0; i < sizeof(key_bitmask); i++) { // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); //} - for (int i=0; i<((BTN_MISC+7)/8); i++) { - if (key_bitmask[i] != 0) { - device->classes |= CLASS_KEYBOARD; - break; - } - } - if ((device->classes & CLASS_KEYBOARD) != 0) { + + // See if this is a keyboard. Ignore everything in the button range except for + // gamepads which are also considered keyboards. + if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) + || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD), + sizeof_bit_array(BTN_DIGI)) + || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), + sizeof_bit_array(KEY_MAX + 1))) { + device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; if (device->keyBitmask != NULL) { memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); @@ -631,53 +691,58 @@ int EventHub::open_device(const char *deviceName) } } - // See if this is a trackball. + // See if this is a trackball (or mouse). if (test_bit(BTN_MOUSE, key_bitmask)) { - uint8_t rel_bitmask[(REL_MAX+7)/8]; + uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)]; memset(rel_bitmask, 0, sizeof(rel_bitmask)); LOGV("Getting relative controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) - { + if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) { if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { - device->classes |= CLASS_TRACKBALL; + device->classes |= INPUT_DEVICE_CLASS_TRACKBALL; } } } - - uint8_t abs_bitmask[(ABS_MAX+7)/8]; + + // See if this is a touch pad. + uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; memset(abs_bitmask, 0, sizeof(abs_bitmask)); LOGV("Getting absolute controllers..."); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); - - // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) - && test_bit(ABS_MT_POSITION_X, abs_bitmask) - && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { - device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT; - - // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, key_bitmask) - && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { - device->classes |= CLASS_TOUCHSCREEN; + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) { + // Is this a new modern multi-touch driver? + if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) + && test_bit(ABS_MT_POSITION_X, abs_bitmask) + && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; + + // Is this an old style single-touch driver? + } else if (test_bit(BTN_TOUCH, key_bitmask) + && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN; + } } #ifdef EV_SW // figure out the switches this device reports - uint8_t sw_bitmask[(SW_MAX+7)/8]; + uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; memset(sw_bitmask, 0, sizeof(sw_bitmask)); + bool hasSwitches = false; if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { for (int i=0; i<EV_SW; i++) { //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); if (test_bit(i, sw_bitmask)) { + hasSwitches = true; if (mSwitches[i] == 0) { mSwitches[i] = device->id; } } } } + if (hasSwitches) { + device->classes |= INPUT_DEVICE_CLASS_SWITCH; + } #endif - if ((device->classes&CLASS_KEYBOARD) != 0) { + if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { char tmpfn[sizeof(name)]; char keylayoutFilename[300]; @@ -699,7 +764,10 @@ int EventHub::open_device(const char *deviceName) "%s/usr/keylayout/%s", root, "qwerty.kl"); defaultKeymap = true; } - device->layoutMap->load(keylayoutFilename); + status_t status = device->layoutMap->load(keylayoutFilename); + if (status) { + LOGE("Error %d loading key layout.", status); + } // tell the world about the devname (the descriptive name) if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) { @@ -719,19 +787,27 @@ int EventHub::open_device(const char *deviceName) property_set(propName, name); // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (hasKeycode(device, kKeyCodeQ)) { - device->classes |= CLASS_ALPHAKEY; + if (hasKeycode(device, AKEYCODE_Q)) { + device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } - // See if this has a DPAD. - if (hasKeycode(device, kKeyCodeDpadUp) && - hasKeycode(device, kKeyCodeDpadDown) && - hasKeycode(device, kKeyCodeDpadLeft) && - hasKeycode(device, kKeyCodeDpadRight) && - hasKeycode(device, kKeyCodeDpadCenter)) { - device->classes |= CLASS_DPAD; + // See if this device has a DPAD. + if (hasKeycode(device, AKEYCODE_DPAD_UP) && + hasKeycode(device, AKEYCODE_DPAD_DOWN) && + hasKeycode(device, AKEYCODE_DPAD_LEFT) && + hasKeycode(device, AKEYCODE_DPAD_RIGHT) && + hasKeycode(device, AKEYCODE_DPAD_CENTER)) { + device->classes |= INPUT_DEVICE_CLASS_DPAD; } + // See if this device has a gamepad. + for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) { + if (hasKeycode(device, GAMEPAD_KEYCODES[i])) { + device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; + break; + } + } + LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n", device->id, name, propName, keylayoutFilename); } diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 52380a077c..6f8948d70e 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -67,7 +67,7 @@ private: * This implements the (main) framebuffer management. This class is used * mostly by SurfaceFlinger, but also by command line GL application. * - * In fact this is an implementation of android_native_window_t on top of + * In fact this is an implementation of ANativeWindow on top of * the framebuffer. * * Currently it is pretty simple, it manages only two buffers (the front and @@ -117,23 +117,23 @@ FramebufferNativeWindow::FramebufferNativeWindow() LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", fbDev->width, fbDev->height, strerror(-err)); - const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; - const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi; - const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi; - const_cast<int&>(android_native_window_t::minSwapInterval) = + const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; + const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; + const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi; + const_cast<int&>(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval; - const_cast<int&>(android_native_window_t::maxSwapInterval) = + const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval; } else { LOGE("Couldn't get gralloc module"); } - android_native_window_t::setSwapInterval = setSwapInterval; - android_native_window_t::dequeueBuffer = dequeueBuffer; - android_native_window_t::lockBuffer = lockBuffer; - android_native_window_t::queueBuffer = queueBuffer; - android_native_window_t::query = query; - android_native_window_t::perform = perform; + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; } FramebufferNativeWindow::~FramebufferNativeWindow() @@ -168,13 +168,13 @@ status_t FramebufferNativeWindow::compositionComplete() } int FramebufferNativeWindow::setSwapInterval( - android_native_window_t* window, int interval) + ANativeWindow* window, int interval) { framebuffer_device_t* fb = getSelf(window)->fbDev; return fb->setSwapInterval(fb, interval); } -int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, +int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -196,7 +196,7 @@ int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, return 0; } -int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, +int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -210,7 +210,7 @@ int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, return NO_ERROR; } -int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, +int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -224,7 +224,7 @@ int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, return res; } -int FramebufferNativeWindow::query(android_native_window_t* window, +int FramebufferNativeWindow::query(ANativeWindow* window, int what, int* value) { FramebufferNativeWindow* self = getSelf(window); @@ -245,7 +245,7 @@ int FramebufferNativeWindow::query(android_native_window_t* window, return BAD_VALUE; } -int FramebufferNativeWindow::perform(android_native_window_t* window, +int FramebufferNativeWindow::perform(ANativeWindow* window, int operation, ...) { switch (operation) { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index ba1fd9c9dc..519c277c60 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -38,7 +38,7 @@ namespace android { GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) + mInitCheck(NO_ERROR), mIndex(-1) { width = height = @@ -51,7 +51,7 @@ GraphicBuffer::GraphicBuffer() GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, PixelFormat reqFormat, uint32_t reqUsage) : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) + mInitCheck(NO_ERROR), mIndex(-1) { width = height = @@ -67,7 +67,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, uint32_t inStride, native_handle_t* inHandle, bool keepOwnership) : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) + mInitCheck(NO_ERROR), mIndex(-1) { width = w; height = h; @@ -111,6 +111,9 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f, if (mOwner != ownData) return INVALID_OPERATION; + if (handle && w==width && h==height && f==format && reqUsage==usage) + return NO_ERROR; + if (handle) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); @@ -122,9 +125,6 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f, status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format, uint32_t reqUsage) { - if (format == PIXEL_FORMAT_RGBX_8888) - format = PIXEL_FORMAT_RGBA_8888; - GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride); if (err == NO_ERROR) { @@ -132,7 +132,6 @@ status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format, this->height = h; this->format = format; this->usage = reqUsage; - mVStride = 0; } return err; } @@ -173,7 +172,6 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) sur->height = height; sur->stride = stride; sur->format = format; - sur->vstride = mVStride; sur->data = static_cast<GGLubyte*>(vaddr); } return res; @@ -267,14 +265,6 @@ int GraphicBuffer::getIndex() const { return mIndex; } -void GraphicBuffer::setVerticalStride(uint32_t vstride) { - mVStride = vstride; -} - -uint32_t GraphicBuffer::getVerticalStride() const { - return mVStride; -} - // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 6ae7e741b6..d51664dded 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -15,6 +15,8 @@ ** limitations under the License. */ +#define LOG_TAG "GraphicBufferAllocator" + #include <cutils/log.h> #include <utils/Singleton.h> @@ -61,9 +63,9 @@ void GraphicBufferAllocator::dump(String8& result) const const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n", + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %2d | 0x%08x\n", list.keyAt(i), rec.size/1024.0f, - rec.w, rec.h, rec.format, rec.usage); + rec.w, rec.s, rec.h, rec.format, rec.usage); result.append(buffer); total += rec.size; } @@ -71,16 +73,13 @@ void GraphicBufferAllocator::dump(String8& result) const result.append(buffer); } -static inline uint32_t clamp(uint32_t c) { - return c>0 ? c : 1; -} - status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride) { - // make sure to not allocate a 0 x 0 buffer - w = clamp(w); - h = clamp(h); + // make sure to not allocate a N x 0 or 0 x N buffer, since this is + // allowed from an API stand-point allocate a 1x1 buffer instead. + if (!w || !h) + w = h = 1; // we have a h/w allocator and h/w buffer is requested status_t err; @@ -100,9 +99,9 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma alloc_rec_t rec; rec.w = w; rec.h = h; + rec.s = *stride; rec.format = format; rec.usage = usage; - rec.vaddr = 0; rec.size = h * stride[0] * bytesPerPixel(format); list.add(*handle, rec); } else { diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp new file mode 100644 index 0000000000..5fbaf0961b --- /dev/null +++ b/libs/ui/Input.cpp @@ -0,0 +1,230 @@ +// +// Copyright 2010 The Android Open Source Project +// +// Provides a pipe-based transport for native events in the NDK. +// +#define LOG_TAG "Input" + +//#define LOG_NDEBUG 0 + +#include <ui/Input.h> + +namespace android { + +// class 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; +} + +// class 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_POWER: + case AKEYCODE_CAMERA: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MENU: + case AKEYCODE_NOTIFICATION: + case AKEYCODE_FOCUS: + case AKEYCODE_SEARCH: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MUTE: + 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_MUTE: + case AKEYCODE_POWER: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_CAMERA: + case AKEYCODE_FOCUS: + case AKEYCODE_SEARCH: + 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; +} + +// class MotionEvent + +void MotionEvent::initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t edgeFlags, + int32_t metaState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const int32_t* pointerIds, + const PointerCoords* pointerCoords) { + InputEvent::initialize(deviceId, source); + mAction = action; + mEdgeFlags = edgeFlags; + mMetaState = metaState; + mXOffset = xOffset; + mYOffset = yOffset; + mXPrecision = xPrecision; + mYPrecision = yPrecision; + mDownTime = downTime; + mPointerIds.clear(); + mPointerIds.appendArray(pointerIds, pointerCount); + mSampleEventTimes.clear(); + mSamplePointerCoords.clear(); + addSample(eventTime, pointerCoords); +} + +void MotionEvent::addSample( + int64_t eventTime, + const PointerCoords* pointerCoords) { + mSampleEventTimes.push(eventTime); + mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); +} + +void MotionEvent::offsetLocation(float xOffset, float yOffset) { + mXOffset += xOffset; + mYOffset += yOffset; +} + +// class InputDeviceInfo + +InputDeviceInfo::InputDeviceInfo() { + initialize(-1, String8("uninitialized device info")); +} + +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : + mId(other.mId), mName(other.mName), mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mMotionRanges(other.mMotionRanges) { +} + +InputDeviceInfo::~InputDeviceInfo() { +} + +void InputDeviceInfo::initialize(int32_t id, const String8& name) { + mId = id; + mName = name; + mSources = 0; + mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; + mMotionRanges.clear(); +} + +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { + ssize_t index = mMotionRanges.indexOfKey(rangeType); + return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; +} + +void InputDeviceInfo::addSource(uint32_t source) { + mSources |= source; +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, + float flat, float fuzz) { + MotionRange range = { min, max, flat, fuzz }; + addMotionRange(rangeType, range); +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { + mMotionRanges.add(rangeType, range); +} + +// class InputDeviceProxy + +InputDeviceProxy::InputDeviceProxy() { +} + +InputDeviceProxy::~InputDeviceProxy() { +} + +void InputDeviceProxy::getDeviceIds(Vector<int32_t>& outIds) { + // TODO use Binder +} + +sp<InputDeviceProxy> InputDeviceProxy::getDevice(int32_t id) { + // TODO use Binder + return NULL; +} + +} // namespace android diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp new file mode 100644 index 0000000000..e35050cb6e --- /dev/null +++ b/libs/ui/InputDispatcher.cpp @@ -0,0 +1,1857 @@ +// +// Copyright 2010 The Android Open Source Project +// +// The input dispatcher. +// +#define LOG_TAG "InputDispatcher" + +//#define LOG_NDEBUG 0 + +// Log detailed debug messages about each inbound event notification to the dispatcher. +#define DEBUG_INBOUND_EVENT_DETAILS 0 + +// Log detailed debug messages about each outbound event processed by the dispatcher. +#define DEBUG_OUTBOUND_EVENT_DETAILS 0 + +// Log debug messages about batching. +#define DEBUG_BATCHING 0 + +// Log debug messages about the dispatch cycle. +#define DEBUG_DISPATCH_CYCLE 0 + +// Log debug messages about registrations. +#define DEBUG_REGISTRATION 0 + +// Log debug messages about performance statistics. +#define DEBUG_PERFORMANCE_STATISTICS 0 + +// Log debug messages about input event injection. +#define DEBUG_INJECTION 0 + +// Log debug messages about input event throttling. +#define DEBUG_THROTTLING 0 + +#include <cutils/log.h> +#include <ui/InputDispatcher.h> + +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> + +namespace android { + +// TODO, this needs to be somewhere else, perhaps in the policy +static inline bool isMovementKey(int32_t keyCode) { + return keyCode == AKEYCODE_DPAD_UP + || keyCode == AKEYCODE_DPAD_DOWN + || keyCode == AKEYCODE_DPAD_LEFT + || keyCode == AKEYCODE_DPAD_RIGHT; +} + +static inline nsecs_t now() { + return systemTime(SYSTEM_TIME_MONOTONIC); +} + +// --- InputDispatcher --- + +InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : + mPolicy(policy) { + mPollLoop = new PollLoop(false); + + mInboundQueue.head.refCount = -1; + mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.head.eventTime = LONG_LONG_MIN; + + mInboundQueue.tail.refCount = -1; + mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.tail.eventTime = LONG_LONG_MAX; + + mKeyRepeatState.lastKeyEntry = NULL; + + int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond(); + mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond; + mThrottleState.lastDeviceId = -1; + +#if DEBUG_THROTTLING + mThrottleState.originalSampleCount = 0; + LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); +#endif + + mCurrentInputTargetsValid = false; +} + +InputDispatcher::~InputDispatcher() { + resetKeyRepeatLocked(); + + while (mConnectionsByReceiveFd.size() != 0) { + unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); + } + + for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) { + EventEntry* next = entry->next; + mAllocator.releaseEventEntry(next); + entry = next; + } +} + +void InputDispatcher::dispatchOnce() { + nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); + + bool skipPoll = false; + nsecs_t currentTime; + nsecs_t nextWakeupTime = LONG_LONG_MAX; + { // acquire lock + AutoMutex _l(mLock); + currentTime = now(); + + // Reset the key repeat timer whenever we disallow key events, even if the next event + // is not a key. This is to ensure that we abort a key repeat if the device is just coming + // out of sleep. + // XXX we should handle resetting input state coming out of sleep more generally elsewhere + if (keyRepeatTimeout < 0) { + resetKeyRepeatLocked(); + } + + // Detect and process timeouts for all connections and determine if there are any + // synchronous event dispatches pending. This step is entirely non-interruptible. + bool hasPendingSyncTarget = false; + size_t activeConnectionCount = mActiveConnections.size(); + for (size_t i = 0; i < activeConnectionCount; i++) { + Connection* connection = mActiveConnections.itemAt(i); + + if (connection->hasPendingSyncTarget()) { + hasPendingSyncTarget = true; + } + + nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; + if (connectionTimeoutTime <= currentTime) { + mTimedOutConnections.add(connection); + } else if (connectionTimeoutTime < nextWakeupTime) { + nextWakeupTime = connectionTimeoutTime; + } + } + + size_t timedOutConnectionCount = mTimedOutConnections.size(); + for (size_t i = 0; i < timedOutConnectionCount; i++) { + Connection* connection = mTimedOutConnections.itemAt(i); + timeoutDispatchCycleLocked(currentTime, connection); + skipPoll = true; + } + mTimedOutConnections.clear(); + + // If we don't have a pending sync target, then we can begin delivering a new event. + // (Otherwise we wait for dispatch to complete for that target.) + if (! hasPendingSyncTarget) { + if (mInboundQueue.isEmpty()) { + if (mKeyRepeatState.lastKeyEntry) { + if (currentTime >= mKeyRepeatState.nextRepeatTime) { + processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout); + skipPoll = true; + } else { + if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) { + nextWakeupTime = mKeyRepeatState.nextRepeatTime; + } + } + } + } else { + // Inbound queue has at least one entry. + EventEntry* entry = mInboundQueue.head.next; + + // Consider throttling the entry if it is a move event and there are no + // other events behind it in the queue. Due to movement batching, additional + // samples may be appended to this event by the time the throttling timeout + // expires. + // TODO Make this smarter and consider throttling per device independently. + if (entry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + int32_t deviceId = motionEntry->deviceId; + uint32_t source = motionEntry->source; + if (motionEntry->next == & mInboundQueue.tail + && motionEntry->action == AMOTION_EVENT_ACTION_MOVE + && deviceId == mThrottleState.lastDeviceId + && source == mThrottleState.lastSource) { + nsecs_t nextTime = mThrottleState.lastEventTime + + mThrottleState.minTimeBetweenEvents; + if (currentTime < nextTime) { + // Throttle it! +#if DEBUG_THROTTLING + LOGD("Throttling - Delaying motion event for " + "device 0x%x, source 0x%08x by up to %0.3fms.", + deviceId, source, (nextTime - currentTime) * 0.000001); +#endif + if (nextTime < nextWakeupTime) { + nextWakeupTime = nextTime; + } + if (mThrottleState.originalSampleCount == 0) { + mThrottleState.originalSampleCount = + motionEntry->countSamples(); + } + goto Throttle; + } + } + +#if DEBUG_THROTTLING + if (mThrottleState.originalSampleCount != 0) { + uint32_t count = motionEntry->countSamples(); + LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", + count - mThrottleState.originalSampleCount, + mThrottleState.originalSampleCount, count); + mThrottleState.originalSampleCount = 0; + } +#endif + + mThrottleState.lastEventTime = entry->eventTime < currentTime + ? entry->eventTime : currentTime; + mThrottleState.lastDeviceId = deviceId; + mThrottleState.lastSource = source; + } + + // Start processing the entry but leave it on the queue until later so that the + // input reader can keep appending samples onto a motion event between the + // time we started processing it and the time we finally enqueue dispatch + // entries for it. + switch (entry->type) { + case EventEntry::TYPE_CONFIGURATION_CHANGED: { + ConfigurationChangedEntry* typedEntry = + static_cast<ConfigurationChangedEntry*>(entry); + processConfigurationChangedLockedInterruptible(currentTime, typedEntry); + break; + } + + case EventEntry::TYPE_KEY: { + KeyEntry* typedEntry = static_cast<KeyEntry*>(entry); + processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* typedEntry = static_cast<MotionEntry*>(entry); + processMotionLockedInterruptible(currentTime, typedEntry); + break; + } + + default: + assert(false); + break; + } + + // Dequeue and release the event entry that we just processed. + mInboundQueue.dequeue(entry); + mAllocator.releaseEventEntry(entry); + skipPoll = true; + + Throttle: ; + } + } + + // Run any deferred commands. + skipPoll |= runCommandsLockedInterruptible(); + } // release lock + + // If we dispatched anything, don't poll just now. Wait for the next iteration. + // Contents may have shifted during flight. + if (skipPoll) { + return; + } + + // Wait for callback or timeout or wake. (make sure we round up, not down) + nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL; + int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; + mPollLoop->pollOnce(timeoutMillis); +} + +bool InputDispatcher::runCommandsLockedInterruptible() { + if (mCommandQueue.isEmpty()) { + return false; + } + + do { + CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); + + Command command = commandEntry->command; + (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' + + commandEntry->connection.clear(); + mAllocator.releaseCommandEntry(commandEntry); + } while (! mCommandQueue.isEmpty()); + return true; +} + +InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { + CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command); + mCommandQueue.enqueueAtTail(commandEntry); + return commandEntry; +} + +void InputDispatcher::processConfigurationChangedLockedInterruptible( + nsecs_t currentTime, ConfigurationChangedEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); +#endif + + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); + + mLock.unlock(); + + mPolicy->notifyConfigurationChanged(entry->eventTime); + + mLock.lock(); +} + +void InputDispatcher::processKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " + "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", + entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, + entry->flags, entry->keyCode, entry->scanCode, entry->metaState, + entry->downTime); +#endif + + if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) { + if (mKeyRepeatState.lastKeyEntry + && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + // We have seen two identical key downs in a row which indicates that the device + // driver is automatically generating key repeats itself. We take note of the + // repeat here, but we disable our own next key repeat timer since it is clear that + // we will not need to synthesize key repeats ourselves. + entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves + } else { + // Not a repeat. Save key down state in case we do see a repeat later. + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; + } + mKeyRepeatState.lastKeyEntry = entry; + entry->refCount += 1; + } else { + resetKeyRepeatLocked(); + } + + identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); +} + +void InputDispatcher::processKeyRepeatLockedInterruptible( + nsecs_t currentTime, nsecs_t keyRepeatTimeout) { + KeyEntry* entry = mKeyRepeatState.lastKeyEntry; + + // Search the inbound queue for a key up corresponding to this device. + // It doesn't make sense to generate a key repeat event if the key is already up. + for (EventEntry* queuedEntry = mInboundQueue.head.next; + queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) { + if (queuedEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry); + if (queuedKeyEntry->deviceId == entry->deviceId + && entry->action == AKEY_EVENT_ACTION_UP) { + resetKeyRepeatLocked(); + return; + } + } + } + + // Synthesize a key repeat after the repeat timeout expired. + // Reuse the repeated key entry if it is otherwise unreferenced. + uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; + if (entry->refCount == 1) { + entry->eventTime = currentTime; + entry->policyFlags = policyFlags; + entry->repeatCount += 1; + } else { + KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime, + entry->deviceId, entry->source, policyFlags, + entry->action, entry->flags, entry->keyCode, entry->scanCode, + entry->metaState, entry->repeatCount + 1, entry->downTime); + + mKeyRepeatState.lastKeyEntry = newEntry; + mAllocator.releaseKeyEntry(entry); + + entry = newEntry; + } + + if (entry->repeatCount == 1) { + entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; + } + + mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout; + +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " + "repeatCount=%d, downTime=%lld", + entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, + entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, + entry->repeatCount, entry->downTime); +#endif + + identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); +} + +void InputDispatcher::processMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " + "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", + entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, + entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, + entry->downTime); + + // Print the most recent sample that we have available, this may change due to batching. + size_t sampleCount = 1; + MotionSample* sample = & entry->firstSample; + for (; sample->next != NULL; sample = sample->next) { + sampleCount += 1; + } + for (uint32_t i = 0; i < entry->pointerCount; i++) { + LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", + i, entry->pointerIds[i], + sample->pointerCoords[i].x, + sample->pointerCoords[i].y, + sample->pointerCoords[i].pressure, + sample->pointerCoords[i].size); + } + + // Keep in mind that due to batching, it is possible for the number of samples actually + // dispatched to change before the application finally consumed them. + if (entry->action == AMOTION_EVENT_ACTION_MOVE) { + LOGD(" ... Total movement samples currently batched %d ...", sampleCount); + } +#endif + + identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry); +} + +void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( + nsecs_t currentTime, KeyEntry* entry) { +#if DEBUG_DISPATCH_CYCLE + LOGD("identifyInputTargetsAndDispatchKey"); +#endif + + entry->dispatchInProgress = true; + mCurrentInputTargetsValid = false; + mLock.unlock(); + + mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, + entry->downTime, entry->eventTime); + + mCurrentInputTargets.clear(); + int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent, + entry->policyFlags, entry->injectorPid, entry->injectorUid, + mCurrentInputTargets); + + mLock.lock(); + mCurrentInputTargetsValid = true; + + setInjectionResultLocked(entry, injectionResult); + + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + } +} + +void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( + nsecs_t currentTime, MotionEntry* entry) { +#if DEBUG_DISPATCH_CYCLE + LOGD("identifyInputTargetsAndDispatchMotion"); +#endif + + entry->dispatchInProgress = true; + mCurrentInputTargetsValid = false; + mLock.unlock(); + + mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, + entry->edgeFlags, entry->metaState, + 0, 0, entry->xPrecision, entry->yPrecision, + entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds, + entry->firstSample.pointerCoords); + + mCurrentInputTargets.clear(); + int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent, + entry->policyFlags, entry->injectorPid, entry->injectorUid, + mCurrentInputTargets); + + mLock.lock(); + mCurrentInputTargetsValid = true; + + setInjectionResultLocked(entry, injectionResult); + + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + } +} + +void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, + EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { +#if DEBUG_DISPATCH_CYCLE + LOGD("dispatchEventToCurrentInputTargets - " + "resumeWithAppendedMotionSample=%s", + resumeWithAppendedMotionSample ? "true" : "false"); +#endif + + assert(eventEntry->dispatchInProgress); // should already have been set to true + + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { + const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + + ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, + resumeWithAppendedMotionSample); + } else { + LOGW("Framework requested delivery of an input event to channel '%s' but it " + "is not registered with the input dispatcher.", + inputTarget.inputChannel->getName().string()); + } + } +} + +void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, + bool resumeWithAppendedMotionSample) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, " + "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", + connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, + inputTarget->xOffset, inputTarget->yOffset, + resumeWithAppendedMotionSample ? "true" : "false"); +#endif + + // Skip this event if the connection status is not normal. + // We don't want to queue outbound events at all if the connection is broken or + // not responding. + if (connection->status != Connection::STATUS_NORMAL) { + LOGV("channel '%s' ~ Dropping event because the channel status is %s", + connection->getStatusLabel()); + return; + } + + // Resume the dispatch cycle with a freshly appended motion sample. + // First we check that the last dispatch entry in the outbound queue is for the same + // motion event to which we appended the motion sample. If we find such a dispatch + // entry, and if it is currently in progress then we try to stream the new sample. + bool wasEmpty = connection->outboundQueue.isEmpty(); + + if (! wasEmpty && resumeWithAppendedMotionSample) { + DispatchEntry* motionEventDispatchEntry = + connection->findQueuedDispatchEntryForEvent(eventEntry); + if (motionEventDispatchEntry) { + // If the dispatch entry is not in progress, then we must be busy dispatching an + // earlier event. Not a problem, the motion event is on the outbound queue and will + // be dispatched later. + if (! motionEventDispatchEntry->inProgress) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Not streaming because the motion event has " + "not yet been dispatched. " + "(Waiting for earlier events to be consumed.)", + connection->getInputChannelName()); +#endif + return; + } + + // If the dispatch entry is in progress but it already has a tail of pending + // motion samples, then it must mean that the shared memory buffer filled up. + // Not a problem, when this dispatch cycle is finished, we will eventually start + // a new dispatch cycle to process the tail and that tail includes the newly + // appended motion sample. + if (motionEventDispatchEntry->tailMotionSample) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Not streaming because no new samples can " + "be appended to the motion event in this dispatch cycle. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName()); +#endif + return; + } + + // The dispatch entry is in progress and is still potentially open for streaming. + // Try to stream the new motion sample. This might fail if the consumer has already + // consumed the motion event (or if the channel is broken). + MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample; + status_t status = connection->inputPublisher.appendMotionSample( + appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); + if (status == OK) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Successfully streamed new motion sample.", + connection->getInputChannelName()); +#endif + return; + } + +#if DEBUG_BATCHING + if (status == NO_MEMORY) { + LOGD("channel '%s' ~ Could not append motion sample to currently " + "dispatched move event because the shared memory buffer is full. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName()); + } else if (status == status_t(FAILED_TRANSACTION)) { + LOGD("channel '%s' ~ Could not append motion sample to currently " + "dispatched move event because the event has already been consumed. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName()); + } else { + LOGD("channel '%s' ~ Could not append motion sample to currently " + "dispatched move event due to an error, status=%d. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName(), status); + } +#endif + // Failed to stream. Start a new tail of pending motion samples to dispatch + // in the next cycle. + motionEventDispatchEntry->tailMotionSample = appendedMotionSample; + return; + } + } + + // This is a new event. + // Enqueue a new dispatch entry onto the outbound queue for this connection. + DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref + dispatchEntry->targetFlags = inputTarget->flags; + dispatchEntry->xOffset = inputTarget->xOffset; + dispatchEntry->yOffset = inputTarget->yOffset; + dispatchEntry->timeout = inputTarget->timeout; + dispatchEntry->inProgress = false; + dispatchEntry->headMotionSample = NULL; + dispatchEntry->tailMotionSample = NULL; + + if (dispatchEntry->isSyncTarget()) { + eventEntry->pendingSyncDispatches += 1; + } + + // Handle the case where we could not stream a new motion sample because the consumer has + // already consumed the motion event (otherwise the corresponding dispatch entry would + // still be in the outbound queue for this connection). We set the head motion sample + // to the list starting with the newly appended motion sample. + if (resumeWithAppendedMotionSample) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples " + "that cannot be streamed because the motion event has already been consumed.", + connection->getInputChannelName()); +#endif + MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample; + dispatchEntry->headMotionSample = appendedMotionSample; + } + + // Enqueue the dispatch entry. + connection->outboundQueue.enqueueAtTail(dispatchEntry); + + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty) { + activateConnectionLocked(connection.get()); + startDispatchCycleLocked(currentTime, connection); + } +} + +void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ startDispatchCycle", + connection->getInputChannelName()); +#endif + + assert(connection->status == Connection::STATUS_NORMAL); + assert(! connection->outboundQueue.isEmpty()); + + DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + assert(! dispatchEntry->inProgress); + + // TODO throttle successive ACTION_MOVE motion events for the same device + // possible implementation could set a brief poll timeout here and resume starting the + // dispatch cycle when elapsed + + // Publish the event. + status_t status; + switch (dispatchEntry->eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); + + // Apply target flags. + int32_t action = keyEntry->action; + int32_t flags = keyEntry->flags; + if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) { + flags |= AKEY_EVENT_FLAG_CANCELED; + } + + // Publish the key event. + status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source, + action, flags, keyEntry->keyCode, keyEntry->scanCode, + keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); + + if (status) { + LOGE("channel '%s' ~ Could not publish key event, " + "status=%d", connection->getInputChannelName(), status); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + return; + } + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); + + // Apply target flags. + int32_t action = motionEntry->action; + if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) { + action = AMOTION_EVENT_ACTION_OUTSIDE; + } + if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) { + action = AMOTION_EVENT_ACTION_CANCEL; + } + + // If headMotionSample is non-NULL, then it points to the first new sample that we + // were unable to dispatch during the previous cycle so we resume dispatching from + // that point in the list of motion samples. + // Otherwise, we just start from the first sample of the motion event. + MotionSample* firstMotionSample = dispatchEntry->headMotionSample; + if (! firstMotionSample) { + firstMotionSample = & motionEntry->firstSample; + } + + // Set the X and Y offset depending on the input source. + float xOffset, yOffset; + if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { + xOffset = dispatchEntry->xOffset; + yOffset = dispatchEntry->yOffset; + } else { + xOffset = 0.0f; + yOffset = 0.0f; + } + + // Publish the motion event and the first motion sample. + status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, + motionEntry->source, action, motionEntry->edgeFlags, motionEntry->metaState, + xOffset, yOffset, + motionEntry->xPrecision, motionEntry->yPrecision, + motionEntry->downTime, firstMotionSample->eventTime, + motionEntry->pointerCount, motionEntry->pointerIds, + firstMotionSample->pointerCoords); + + if (status) { + LOGE("channel '%s' ~ Could not publish motion event, " + "status=%d", connection->getInputChannelName(), status); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + return; + } + + // Append additional motion samples. + MotionSample* nextMotionSample = firstMotionSample->next; + for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { + status = connection->inputPublisher.appendMotionSample( + nextMotionSample->eventTime, nextMotionSample->pointerCoords); + if (status == NO_MEMORY) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will " + "be sent in the next dispatch cycle.", + connection->getInputChannelName()); +#endif + break; + } + if (status != OK) { + LOGE("channel '%s' ~ Could not append motion sample " + "for a reason other than out of memory, status=%d", + connection->getInputChannelName(), status); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + return; + } + } + + // Remember the next motion sample that we could not dispatch, in case we ran out + // of space in the shared memory buffer. + dispatchEntry->tailMotionSample = nextMotionSample; + break; + } + + default: { + assert(false); + } + } + + // Send the dispatch signal. + status = connection->inputPublisher.sendDispatchSignal(); + if (status) { + LOGE("channel '%s' ~ Could not send dispatch signal, status=%d", + connection->getInputChannelName(), status); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + return; + } + + // Record information about the newly started dispatch cycle. + dispatchEntry->inProgress = true; + + connection->lastEventTime = dispatchEntry->eventEntry->eventTime; + connection->lastDispatchTime = currentTime; + + nsecs_t timeout = dispatchEntry->timeout; + connection->setNextTimeoutTime(currentTime, timeout); + + // Notify other system components. + onDispatchCycleStartedLocked(currentTime, connection); +} + +void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " + "%01.1fms since dispatch", + connection->getInputChannelName(), + connection->getEventLatencyMillis(currentTime), + connection->getDispatchLatencyMillis(currentTime)); +#endif + + if (connection->status == Connection::STATUS_BROKEN + || connection->status == Connection::STATUS_ZOMBIE) { + return; + } + + // Clear the pending timeout. + connection->nextTimeoutTime = LONG_LONG_MAX; + + if (connection->status == Connection::STATUS_NOT_RESPONDING) { + // Recovering from an ANR. + connection->status = Connection::STATUS_NORMAL; + + // Notify other system components. + onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/); + } else { + // Normal finish. Not much to do here. + + // Notify other system components. + onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/); + } + + // Reset the publisher since the event has been consumed. + // We do this now so that the publisher can release some of its internal resources + // while waiting for the next dispatch cycle to begin. + status_t status = connection->inputPublisher.reset(); + if (status) { + LOGE("channel '%s' ~ Could not reset publisher, status=%d", + connection->getInputChannelName(), status); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + return; + } + + // Start the next dispatch cycle for this connection. + while (! connection->outboundQueue.isEmpty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + if (dispatchEntry->inProgress) { + // Finish or resume current event in progress. + if (dispatchEntry->tailMotionSample) { + // We have a tail of undispatched motion samples. + // Reuse the same DispatchEntry and start a new cycle. + dispatchEntry->inProgress = false; + dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; + dispatchEntry->tailMotionSample = NULL; + startDispatchCycleLocked(currentTime, connection); + return; + } + // Finished. + connection->outboundQueue.dequeueAtHead(); + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } + mAllocator.releaseDispatchEntry(dispatchEntry); + } else { + // If the head is not in progress, then we must have already dequeued the in + // progress event, which means we actually aborted it (due to ANR). + // So just start the next event for this connection. + startDispatchCycleLocked(currentTime, connection); + return; + } + } + + // Outbound queue is empty, deactivate the connection. + deactivateConnectionLocked(connection.get()); +} + +void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ timeoutDispatchCycle", + connection->getInputChannelName()); +#endif + + if (connection->status != Connection::STATUS_NORMAL) { + return; + } + + // Enter the not responding state. + connection->status = Connection::STATUS_NOT_RESPONDING; + connection->lastANRTime = currentTime; + + // Notify other system components. + // This enqueues a command which will eventually either call + // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked. + onDispatchCycleANRLocked(currentTime, connection); +} + +void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, nsecs_t newTimeout) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked", + connection->getInputChannelName()); +#endif + + if (connection->status != Connection::STATUS_NOT_RESPONDING) { + return; + } + + // Resume normal dispatch. + connection->status = Connection::STATUS_NORMAL; + connection->setNextTimeoutTime(currentTime, newTimeout); +} + +void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, bool broken) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", + connection->getInputChannelName(), broken ? "true" : "false"); +#endif + + // Clear the pending timeout. + connection->nextTimeoutTime = LONG_LONG_MAX; + + // Clear the outbound queue. + if (! connection->outboundQueue.isEmpty()) { + do { + DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } + mAllocator.releaseDispatchEntry(dispatchEntry); + } while (! connection->outboundQueue.isEmpty()); + + deactivateConnectionLocked(connection.get()); + } + + // Handle the case where the connection appears to be unrecoverably broken. + // Ignore already broken or zombie connections. + if (broken) { + if (connection->status == Connection::STATUS_NORMAL + || connection->status == Connection::STATUS_NOT_RESPONDING) { + connection->status = Connection::STATUS_BROKEN; + + // Notify other system components. + onDispatchCycleBrokenLocked(currentTime, connection); + } + } +} + +bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { + InputDispatcher* d = static_cast<InputDispatcher*>(data); + + { // acquire lock + AutoMutex _l(d->mLock); + + ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd); + if (connectionIndex < 0) { + LOGE("Received spurious receive callback for unknown input channel. " + "fd=%d, events=0x%x", receiveFd, events); + return false; // remove the callback + } + + nsecs_t currentTime = now(); + + sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); + if (events & (POLLERR | POLLHUP | POLLNVAL)) { + LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " + "events=0x%x", connection->getInputChannelName(), events); + d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + d->runCommandsLockedInterruptible(); + return false; // remove the callback + } + + if (! (events & POLLIN)) { + LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", connection->getInputChannelName(), events); + return true; + } + + status_t status = connection->inputPublisher.receiveFinishedSignal(); + if (status) { + LOGE("channel '%s' ~ Failed to receive finished signal. status=%d", + connection->getInputChannelName(), status); + d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + d->runCommandsLockedInterruptible(); + return false; // remove the callback + } + + d->finishDispatchCycleLocked(currentTime, connection); + d->runCommandsLockedInterruptible(); + return true; + } // release lock +} + +void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); +#endif + + bool wasEmpty; + { // acquire lock + AutoMutex _l(mLock); + + ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); + + wasEmpty = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(newEntry); + } // release lock + + if (wasEmpty) { + mPollLoop->wake(); + } +} + +void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime); +#endif + + // Remove movement keys from the queue from most recent to least recent, stopping at the + // first non-movement key. + // TODO: Include a detailed description of why we do this... + + { // acquire lock + AutoMutex _l(mLock); + + for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) { + EventEntry* prev = entry->prev; + + if (entry->type == EventEntry::TYPE_KEY) { + KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); + if (isMovementKey(keyEntry->keyCode)) { + LOGV("Dropping movement key during app switch: keyCode=%d, action=%d", + keyEntry->keyCode, keyEntry->action); + mInboundQueue.dequeue(keyEntry); + + setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); + + mAllocator.releaseKeyEntry(keyEntry); + } else { + // stop at last non-movement key + break; + } + } + + entry = prev; + } + } // release lock +} + +void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " + "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", + eventTime, deviceId, source, policyFlags, action, flags, + keyCode, scanCode, metaState, downTime); +#endif + + bool wasEmpty; + { // acquire lock + AutoMutex _l(mLock); + + int32_t repeatCount = 0; + KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime, + deviceId, source, policyFlags, action, flags, keyCode, scanCode, + metaState, repeatCount, downTime); + + wasEmpty = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(newEntry); + } // release lock + + if (wasEmpty) { + mPollLoop->wake(); + } +} + +void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, + uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags, + uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, nsecs_t downTime) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + "action=0x%x, metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, " + "downTime=%lld", + eventTime, deviceId, source, policyFlags, action, metaState, edgeFlags, + xPrecision, yPrecision, downTime); + for (uint32_t i = 0; i < pointerCount; i++) { + LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", + i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, + pointerCoords[i].pressure, pointerCoords[i].size); + } +#endif + + bool wasEmpty; + { // acquire lock + AutoMutex _l(mLock); + + // Attempt batching and streaming of move events. + if (action == AMOTION_EVENT_ACTION_MOVE) { + // BATCHING CASE + // + // Try to append a move sample to the tail of the inbound queue for this device. + // Give up if we encounter a non-move motion event for this device since that + // means we cannot append any new samples until a new motion event has started. + for (EventEntry* entry = mInboundQueue.tail.prev; + entry != & mInboundQueue.head; entry = entry->prev) { + if (entry->type != EventEntry::TYPE_MOTION) { + // Keep looking for motion events. + continue; + } + + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + if (motionEntry->deviceId != deviceId) { + // Keep looking for this device. + continue; + } + + if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE + || motionEntry->pointerCount != pointerCount + || motionEntry->isInjected()) { + // Last motion event in the queue for this device is not compatible for + // appending new samples. Stop here. + goto NoBatchingOrStreaming; + } + + // The last motion event is a move and is compatible for appending. + // Do the batching magic. + mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); +#if DEBUG_BATCHING + LOGD("Appended motion sample onto batch for most recent " + "motion event for this device in the inbound queue."); +#endif + + // Sanity check for special case because dispatch is interruptible. + // The dispatch logic is partially interruptible and releases its lock while + // identifying targets. However, as soon as the targets have been identified, + // the dispatcher proceeds to write a dispatch entry into all relevant outbound + // queues and then promptly removes the motion entry from the queue. + // + // Consequently, we should never observe the case where the inbound queue contains + // an in-progress motion entry unless the current input targets are invalid + // (currently being computed). Check for this! + assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid)); + + return; // done! + } + + // STREAMING CASE + // + // There is no pending motion event (of any kind) for this device in the inbound queue. + // Search the outbound queues for a synchronously dispatched motion event for this + // device. If found, then we append the new sample to that event and then try to + // push it out to all current targets. It is possible that some targets will already + // have consumed the motion event. This case is automatically handled by the + // logic in prepareDispatchCycleLocked by tracking where resumption takes place. + // + // The reason we look for a synchronously dispatched motion event is because we + // want to be sure that no other motion events have been dispatched since the move. + // It's also convenient because it means that the input targets are still valid. + // This code could be improved to support streaming of asynchronously dispatched + // motion events (which might be significantly more efficient) but it may become + // a little more complicated as a result. + // + // Note: This code crucially depends on the invariant that an outbound queue always + // contains at most one synchronous event and it is always last (but it might + // not be first!). + if (mCurrentInputTargetsValid) { + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections.itemAt(i); + if (! connection->outboundQueue.isEmpty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; + if (dispatchEntry->isSyncTarget()) { + if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { + goto NoBatchingOrStreaming; + } + + MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>( + dispatchEntry->eventEntry); + if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE + || syncedMotionEntry->deviceId != deviceId + || syncedMotionEntry->pointerCount != pointerCount + || syncedMotionEntry->isInjected()) { + goto NoBatchingOrStreaming; + } + + // Found synced move entry. Append sample and resume dispatch. + mAllocator.appendMotionSample(syncedMotionEntry, eventTime, + pointerCoords); + #if DEBUG_BATCHING + LOGD("Appended motion sample onto batch for most recent synchronously " + "dispatched motion event for this device in the outbound queues."); + #endif + nsecs_t currentTime = now(); + dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, + true /*resumeWithAppendedMotionSample*/); + + runCommandsLockedInterruptible(); + return; // done! + } + } + } + } + +NoBatchingOrStreaming:; + } + + // Just enqueue a new motion event. + MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, + deviceId, source, policyFlags, action, metaState, edgeFlags, + xPrecision, yPrecision, downTime, + pointerCount, pointerIds, pointerCoords); + + wasEmpty = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(newEntry); + } // release lock + + if (wasEmpty) { + mPollLoop->wake(); + } +} + +int32_t InputDispatcher::injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " + "syncMode=%d, timeoutMillis=%d", + event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis); +#endif + + nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); + + EventEntry* injectedEntry; + bool wasEmpty; + { // acquire lock + AutoMutex _l(mLock); + + injectedEntry = createEntryFromInputEventLocked(event); + injectedEntry->refCount += 1; + injectedEntry->injectorPid = injectorPid; + injectedEntry->injectorUid = injectorUid; + + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectedEntry->injectionIsAsync = true; + } + + wasEmpty = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(injectedEntry); + + } // release lock + + if (wasEmpty) { + mPollLoop->wake(); + } + + int32_t injectionResult; + { // acquire lock + AutoMutex _l(mLock); + + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + } else { + for (;;) { + injectionResult = injectedEntry->injectionResult; + if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + break; + } + + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Timed out waiting for injection result " + "to become available."); +#endif + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); + } + + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED + && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { + while (injectedEntry->pendingSyncDispatches != 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.", + injectedEntry->pendingSyncDispatches); +#endif + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Timed out waiting for pending synchronous " + "dispatches to finish."); +#endif + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout); + } + } + } + + mAllocator.releaseEventEntry(injectedEntry); + } // release lock + +#if DEBUG_INJECTION + LOGD("injectInputEvent - Finished with result %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, injectorPid, injectorUid); +#endif + + return injectionResult; +} + +void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { + if (entry->isInjected()) { +#if DEBUG_INJECTION + LOGD("Setting input event injection result to %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, entry->injectorPid, entry->injectorUid); +#endif + + if (entry->injectionIsAsync) { + // Log the outcome since the injector did not wait for the injection result. + switch (injectionResult) { + case INPUT_EVENT_INJECTION_SUCCEEDED: + LOGV("Asynchronous input event injection succeeded."); + break; + case INPUT_EVENT_INJECTION_FAILED: + LOGW("Asynchronous input event injection failed."); + break; + case INPUT_EVENT_INJECTION_PERMISSION_DENIED: + LOGW("Asynchronous input event injection permission denied."); + break; + case INPUT_EVENT_INJECTION_TIMED_OUT: + LOGW("Asynchronous input event injection timed out."); + break; + } + } + + entry->injectionResult = injectionResult; + mInjectionResultAvailableCondition.broadcast(); + } +} + +void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) { + entry->pendingSyncDispatches -= 1; + + if (entry->isInjected() && entry->pendingSyncDispatches == 0) { + mInjectionSyncFinishedCondition.broadcast(); + } +} + +InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( + const InputEvent* event) { + switch (event->getType()) { + case AINPUT_EVENT_TYPE_KEY: { + const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); + uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events + + KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), + keyEvent->getDeviceId(), keyEvent->getSource(), policyFlags, + keyEvent->getAction(), keyEvent->getFlags(), + keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), + keyEvent->getRepeatCount(), keyEvent->getDownTime()); + return keyEntry; + } + + case AINPUT_EVENT_TYPE_MOTION: { + const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); + uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events + + const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); + const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); + size_t pointerCount = motionEvent->getPointerCount(); + + MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, + motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), + motionEvent->getDownTime(), uint32_t(pointerCount), + motionEvent->getPointerIds(), samplePointerCoords); + for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { + sampleEventTimes += 1; + samplePointerCoords += pointerCount; + mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords); + } + return motionEntry; + } + + default: + assert(false); + return NULL; + } +} + +void InputDispatcher::resetKeyRepeatLocked() { + if (mKeyRepeatState.lastKeyEntry) { + mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); + mKeyRepeatState.lastKeyEntry = NULL; + } +} + +void InputDispatcher::preemptInputDispatch() { +#if DEBUG_DISPATCH_CYCLE + LOGD("preemptInputDispatch"); +#endif + + bool preemptedOne = false; + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections[i]; + if (connection->hasPendingSyncTarget()) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Preempted pending synchronous dispatch", + connection->getInputChannelName()); +#endif + connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC; + preemptedOne = true; + } + } + } // release lock + + if (preemptedOne) { + // Wake up the poll loop so it can get a head start dispatching the next event. + mPollLoop->wake(); + } +} + +status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { +#if DEBUG_REGISTRATION + LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + if (getConnectionIndex(inputChannel) >= 0) { + LOGW("Attempted to register already registered input channel '%s'", + inputChannel->getName().string()); + return BAD_VALUE; + } + + sp<Connection> connection = new Connection(inputChannel); + status_t status = connection->initialize(); + if (status) { + LOGE("Failed to initialize input publisher for input channel '%s', status=%d", + inputChannel->getName().string(), status); + return status; + } + + int32_t receiveFd = inputChannel->getReceivePipeFd(); + mConnectionsByReceiveFd.add(receiveFd, connection); + + mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); + + runCommandsLockedInterruptible(); + } // release lock + return OK; +} + +status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { +#if DEBUG_REGISTRATION + LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + ssize_t connectionIndex = getConnectionIndex(inputChannel); + if (connectionIndex < 0) { + LOGW("Attempted to unregister already unregistered input channel '%s'", + inputChannel->getName().string()); + return BAD_VALUE; + } + + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + mConnectionsByReceiveFd.removeItemsAt(connectionIndex); + + connection->status = Connection::STATUS_ZOMBIE; + + mPollLoop->removeCallback(inputChannel->getReceivePipeFd()); + + nsecs_t currentTime = now(); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); + + runCommandsLockedInterruptible(); + } // release lock + + // Wake the poll loop because removing the connection may have changed the current + // synchronization state. + mPollLoop->wake(); + return OK; +} + +ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) { + ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd()); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + if (connection->inputChannel.get() == inputChannel.get()) { + return connectionIndex; + } + } + + return -1; +} + +void InputDispatcher::activateConnectionLocked(Connection* connection) { + for (size_t i = 0; i < mActiveConnections.size(); i++) { + if (mActiveConnections.itemAt(i) == connection) { + return; + } + } + mActiveConnections.add(connection); +} + +void InputDispatcher::deactivateConnectionLocked(Connection* connection) { + for (size_t i = 0; i < mActiveConnections.size(); i++) { + if (mActiveConnections.itemAt(i) == connection) { + mActiveConnections.removeAt(i); + return; + } + } +} + +void InputDispatcher::onDispatchCycleStartedLocked( + nsecs_t currentTime, const sp<Connection>& connection) { +} + +void InputDispatcher::onDispatchCycleFinishedLocked( + nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) { + if (recoveredFromANR) { + LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, " + "%01.1fms since dispatch, %01.1fms since ANR", + connection->getInputChannelName(), + connection->getEventLatencyMillis(currentTime), + connection->getDispatchLatencyMillis(currentTime), + connection->getANRLatencyMillis(currentTime)); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible); + commandEntry->connection = connection; + } +} + +void InputDispatcher::onDispatchCycleANRLocked( + nsecs_t currentTime, const sp<Connection>& connection) { + LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch", + connection->getInputChannelName(), + connection->getEventLatencyMillis(currentTime), + connection->getDispatchLatencyMillis(currentTime)); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelANRLockedInterruptible); + commandEntry->connection = connection; +} + +void InputDispatcher::onDispatchCycleBrokenLocked( + nsecs_t currentTime, const sp<Connection>& connection) { + LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", + connection->getInputChannelName()); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + commandEntry->connection = connection; +} + +void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( + CommandEntry* commandEntry) { + sp<Connection> connection = commandEntry->connection; + + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); + + mPolicy->notifyInputChannelBroken(connection->inputChannel); + + mLock.lock(); + } +} + +void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( + CommandEntry* commandEntry) { + sp<Connection> connection = commandEntry->connection; + + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); + + nsecs_t newTimeout; + bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout); + + mLock.lock(); + + nsecs_t currentTime = now(); + if (resume) { + resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); + } else { + abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); + } + } +} + +void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( + CommandEntry* commandEntry) { + sp<Connection> connection = commandEntry->connection; + + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); + + mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel); + + mLock.lock(); + } +} + + +// --- InputDispatcher::Allocator --- + +InputDispatcher::Allocator::Allocator() { +} + +void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type, + nsecs_t eventTime) { + entry->type = type; + entry->refCount = 1; + entry->dispatchInProgress = false; + entry->eventTime = eventTime; + entry->injectionResult = INPUT_EVENT_INJECTION_PENDING; + entry->injectionIsAsync = false; + entry->injectorPid = -1; + entry->injectorUid = -1; + entry->pendingSyncDispatches = 0; +} + +InputDispatcher::ConfigurationChangedEntry* +InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) { + ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); + initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime); + return entry; +} + +InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime, + int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime) { + KeyEntry* entry = mKeyEntryPool.alloc(); + initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime); + + entry->deviceId = deviceId; + entry->source = source; + entry->policyFlags = policyFlags; + entry->action = action; + entry->flags = flags; + entry->keyCode = keyCode; + entry->scanCode = scanCode; + entry->metaState = metaState; + entry->repeatCount = repeatCount; + entry->downTime = downTime; + return entry; +} + +InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime, + int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, + int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, + nsecs_t downTime, uint32_t pointerCount, + const int32_t* pointerIds, const PointerCoords* pointerCoords) { + MotionEntry* entry = mMotionEntryPool.alloc(); + initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime); + + entry->eventTime = eventTime; + entry->deviceId = deviceId; + entry->source = source; + entry->policyFlags = policyFlags; + entry->action = action; + entry->metaState = metaState; + entry->edgeFlags = edgeFlags; + entry->xPrecision = xPrecision; + entry->yPrecision = yPrecision; + entry->downTime = downTime; + entry->pointerCount = pointerCount; + entry->firstSample.eventTime = eventTime; + entry->firstSample.next = NULL; + entry->lastSample = & entry->firstSample; + for (uint32_t i = 0; i < pointerCount; i++) { + entry->pointerIds[i] = pointerIds[i]; + entry->firstSample.pointerCoords[i] = pointerCoords[i]; + } + return entry; +} + +InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( + EventEntry* eventEntry) { + DispatchEntry* entry = mDispatchEntryPool.alloc(); + entry->eventEntry = eventEntry; + eventEntry->refCount += 1; + return entry; +} + +InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) { + CommandEntry* entry = mCommandEntryPool.alloc(); + entry->command = command; + return entry; +} + +void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) { + switch (entry->type) { + case EventEntry::TYPE_CONFIGURATION_CHANGED: + releaseConfigurationChangedEntry(static_cast<ConfigurationChangedEntry*>(entry)); + break; + case EventEntry::TYPE_KEY: + releaseKeyEntry(static_cast<KeyEntry*>(entry)); + break; + case EventEntry::TYPE_MOTION: + releaseMotionEntry(static_cast<MotionEntry*>(entry)); + break; + default: + assert(false); + break; + } +} + +void InputDispatcher::Allocator::releaseConfigurationChangedEntry( + ConfigurationChangedEntry* entry) { + entry->refCount -= 1; + if (entry->refCount == 0) { + mConfigurationChangeEntryPool.free(entry); + } else { + assert(entry->refCount > 0); + } +} + +void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { + entry->refCount -= 1; + if (entry->refCount == 0) { + mKeyEntryPool.free(entry); + } else { + assert(entry->refCount > 0); + } +} + +void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { + entry->refCount -= 1; + if (entry->refCount == 0) { + for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) { + MotionSample* next = sample->next; + mMotionSamplePool.free(sample); + sample = next; + } + mMotionEntryPool.free(entry); + } else { + assert(entry->refCount > 0); + } +} + +void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) { + releaseEventEntry(entry->eventEntry); + mDispatchEntryPool.free(entry); +} + +void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) { + mCommandEntryPool.free(entry); +} + +void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, + nsecs_t eventTime, const PointerCoords* pointerCoords) { + MotionSample* sample = mMotionSamplePool.alloc(); + sample->eventTime = eventTime; + uint32_t pointerCount = motionEntry->pointerCount; + for (uint32_t i = 0; i < pointerCount; i++) { + sample->pointerCoords[i] = pointerCoords[i]; + } + + sample->next = NULL; + motionEntry->lastSample->next = sample; + motionEntry->lastSample = sample; +} + +// --- InputDispatcher::MotionEntry --- + +uint32_t InputDispatcher::MotionEntry::countSamples() const { + uint32_t count = 1; + for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) { + count += 1; + } + return count; +} + +// --- InputDispatcher::Connection --- + +InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : + status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel), + nextTimeoutTime(LONG_LONG_MAX), + lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX), + lastANRTime(LONG_LONG_MAX) { +} + +InputDispatcher::Connection::~Connection() { +} + +status_t InputDispatcher::Connection::initialize() { + return inputPublisher.initialize(); +} + +void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) { + nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; +} + +const char* InputDispatcher::Connection::getStatusLabel() const { + switch (status) { + case STATUS_NORMAL: + return "NORMAL"; + + case STATUS_BROKEN: + return "BROKEN"; + + case STATUS_NOT_RESPONDING: + return "NOT_RESPONDING"; + + case STATUS_ZOMBIE: + return "ZOMBIE"; + + default: + return "UNKNOWN"; + } +} + +InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( + const EventEntry* eventEntry) const { + for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev; + dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) { + if (dispatchEntry->eventEntry == eventEntry) { + return dispatchEntry; + } + } + return NULL; +} + +// --- InputDispatcher::CommandEntry --- + +InputDispatcher::CommandEntry::CommandEntry() { +} + +InputDispatcher::CommandEntry::~CommandEntry() { +} + + +// --- InputDispatcherThread --- + +InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : + Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { +} + +InputDispatcherThread::~InputDispatcherThread() { +} + +bool InputDispatcherThread::threadLoop() { + mDispatcher->dispatchOnce(); + return true; +} + +} // namespace android diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp new file mode 100644 index 0000000000..ed4f07b277 --- /dev/null +++ b/libs/ui/InputManager.cpp @@ -0,0 +1,123 @@ +// +// Copyright 2010 The Android Open Source Project +// +// The input manager. +// +#define LOG_TAG "InputManager" + +//#define LOG_NDEBUG 0 + +#include <cutils/log.h> +#include <ui/InputManager.h> +#include <ui/InputReader.h> +#include <ui/InputDispatcher.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) { + LOGE("Could not start InputDispatcher thread due to error %d.", result); + return result; + } + + result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + if (result) { + LOGE("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) { + LOGW("Could not stop InputReader thread due to error %d.", result); + } + + result = mDispatcherThread->requestExitAndWait(); + if (result) { + LOGW("Could not stop InputDispatcher thread due to error %d.", result); + } + + return OK; +} + +status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) { + return mDispatcher->registerInputChannel(inputChannel); +} + +status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) { + return mDispatcher->unregisterInputChannel(inputChannel); +} + +int32_t InputManager::injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { + return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); +} + +void InputManager::preemptInputDispatch() { + mDispatcher->preemptInputDispatch(); +} + +void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) { + mReader->getInputConfiguration(outConfiguration); +} + +status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + return mReader->getInputDeviceInfo(deviceId, outDeviceInfo); +} + +void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { + mReader->getInputDeviceIds(outDeviceIds); +} + +int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return mReader->getScanCodeState(deviceId, sourceMask, scanCode); +} + +int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return mReader->getKeyCodeState(deviceId, sourceMask, keyCode); +} + +int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { + return mReader->getSwitchState(deviceId, sourceMask, sw); +} + +bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} + +} // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp new file mode 100644 index 0000000000..6f042ec7e8 --- /dev/null +++ b/libs/ui/InputReader.cpp @@ -0,0 +1,2738 @@ +// +// Copyright 2010 The Android Open Source Project +// +// The input reader. +// +#define LOG_TAG "InputReader" + +//#define LOG_NDEBUG 0 + +// Log debug messages for each raw event received from the EventHub. +#define DEBUG_RAW_EVENTS 0 + +// Log debug messages about touch screen filtering hacks. +#define DEBUG_HACKS 0 + +// Log debug messages about virtual key processing. +#define DEBUG_VIRTUAL_KEYS 0 + +// Log debug messages about pointers. +#define DEBUG_POINTERS 0 + +// Log debug messages about pointer assignment calculations. +#define DEBUG_POINTER_ASSIGNMENT 0 + +#include <cutils/log.h> +#include <ui/InputReader.h> + +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <math.h> + +namespace android { + +// --- Static Functions --- + +template<typename T> +inline static T abs(const T& value) { + return value < 0 ? - value : value; +} + +template<typename T> +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +template<typename T> +inline static void swap(T& a, T& b) { + T temp = a; + a = b; + b = temp; +} + + +int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { + int32_t mask; + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + mask = AMETA_ALT_LEFT_ON; + break; + case AKEYCODE_ALT_RIGHT: + mask = AMETA_ALT_RIGHT_ON; + break; + case AKEYCODE_SHIFT_LEFT: + mask = AMETA_SHIFT_LEFT_ON; + break; + case AKEYCODE_SHIFT_RIGHT: + mask = AMETA_SHIFT_RIGHT_ON; + break; + case AKEYCODE_SYM: + mask = AMETA_SYM_ON; + break; + default: + return oldMetaState; + } + + int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask + & ~ (AMETA_ALT_ON | AMETA_SHIFT_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; + } + + return newMetaState; +} + +static const int32_t keyCodeRotationMap[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT }, + { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN }, + { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT }, + { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP }, +}; +static const int keyCodeRotationMapSize = + sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); + +int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + if (orientation != InputReaderPolicyInterface::ROTATION_0) { + for (int i = 0; i < keyCodeRotationMapSize; i++) { + if (keyCode == keyCodeRotationMap[i][0]) { + return keyCodeRotationMap[i][orientation]; + } + } + } + return keyCode; +} + +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; +} + + +// --- InputReader --- + +InputReader::InputReader(const sp<EventHubInterface>& eventHub, + const sp<InputReaderPolicyInterface>& policy, + const sp<InputDispatcherInterface>& dispatcher) : + mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), + mGlobalMetaState(0) { + configureExcludedDevices(); + updateGlobalMetaState(); + updateInputConfiguration(); +} + +InputReader::~InputReader() { + for (size_t i = 0; i < mDevices.size(); i++) { + delete mDevices.valueAt(i); + } +} + +void InputReader::loopOnce() { + RawEvent rawEvent; + mEventHub->getEvent(& rawEvent); + +#if DEBUG_RAW_EVENTS + LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", + rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, + rawEvent.value); +#endif + + process(& rawEvent); +} + +void InputReader::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EventHubInterface::DEVICE_ADDED: + addDevice(rawEvent->when, rawEvent->deviceId); + break; + + case EventHubInterface::DEVICE_REMOVED: + removeDevice(rawEvent->when, rawEvent->deviceId); + break; + + default: + consumeEvent(rawEvent); + break; + } +} + +void InputReader::addDevice(nsecs_t when, int32_t deviceId) { + String8 name = mEventHub->getDeviceName(deviceId); + uint32_t classes = mEventHub->getDeviceClasses(deviceId); + + InputDevice* device = createDevice(deviceId, name, classes); + device->configure(); + + bool added = false; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + mDevices.add(deviceId, device); + added = true; + } + } // release device registry writer lock + + if (! added) { + LOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + delete device; + return; + } + + if (device->isIgnored()) { + LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", + deviceId, name.string()); + } else { + LOGI("Device added: id=0x%x, name=%s, sources=%08x", + deviceId, name.string(), device->getSources()); + } + + handleConfigurationChanged(when); +} + +void InputReader::removeDevice(nsecs_t when, int32_t deviceId) { + bool removed = false; + InputDevice* device = NULL; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + removed = true; + } + } // release device registry writer lock + + if (! removed) { + LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); + return; + } + + device->reset(); + + if (device->isIgnored()) { + LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", + device->getId(), device->getName().string()); + } else { + LOGI("Device removed: id=0x%x, name=%s, sources=%08x", + device->getId(), device->getName().string(), device->getSources()); + } + + delete device; + + handleConfigurationChanged(when); +} + +InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) { + InputDevice* device = new InputDevice(this, deviceId, name); + + const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices + + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + device->addMapper(new SwitchInputMapper(device)); + } + + // Keyboard-like devices. + uint32_t keyboardSources = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSources |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSources |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSources != 0) { + device->addMapper(new KeyboardInputMapper(device, + associatedDisplayId, keyboardSources, keyboardType)); + } + + // Trackball-like devices. + if (classes & INPUT_DEVICE_CLASS_TRACKBALL) { + device->addMapper(new TrackballInputMapper(device, associatedDisplayId)); + } + + // Touchscreen-like devices. + if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) { + device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) { + device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId)); + } + + return device; +} + +void InputReader::consumeEvent(const RawEvent* rawEvent) { + int32_t deviceId = rawEvent->deviceId; + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + LOGW("Discarding event for unknown deviceId %d.", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + //LOGD("Discarding event for ignored deviceId %d.", deviceId); + return; + } + + device->process(rawEvent); + } // release device registry reader lock +} + +void InputReader::handleConfigurationChanged(nsecs_t when) { + // Reset global meta state because it depends on the list of all configured devices. + updateGlobalMetaState(); + + // Update input configuration. + updateInputConfiguration(); + + // Enqueue configuration changed. + mDispatcher->notifyConfigurationChanged(when); +} + +void InputReader::configureExcludedDevices() { + Vector<String8> excludedDeviceNames; + mPolicy->getExcludedDeviceNames(excludedDeviceNames); + + for (size_t i = 0; i < excludedDeviceNames.size(); i++) { + mEventHub->addExcludedDevice(excludedDeviceNames[i]); + } +} + +void InputReader::updateGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + mGlobalMetaState = 0; + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + mGlobalMetaState |= device->getMetaState(); + } + } // release device registry reader lock + } // release state lock +} + +int32_t InputReader::getGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + return mGlobalMetaState; + } // release state lock +} + +void InputReader::updateInputConfiguration() { + { // acquire state lock + AutoMutex _l(mStateLock); + + int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; + int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; + int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + InputDeviceInfo deviceInfo; + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->getDeviceInfo(& deviceInfo); + uint32_t sources = deviceInfo.getSources(); + + if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) { + touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; + } + if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) { + navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; + } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) { + navigationConfig = InputConfiguration::NAVIGATION_DPAD; + } + if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) { + keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; + } + } + } // release device registry reader lock + + mInputConfiguration.touchScreen = touchScreenConfig; + mInputConfiguration.keyboard = keyboardConfig; + mInputConfiguration.navigation = navigationConfig; + } // release state lock +} + +void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { + { // acquire state lock + AutoMutex _l(mStateLock); + + *outConfiguration = mInputConfiguration; + } // release state lock +} + +status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + return NAME_NOT_FOUND; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + return NAME_NOT_FOUND; + } + + device->getDeviceInfo(outDeviceInfo); + return OK; + } // release device registy reader lock +} + +void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { + outDeviceIds.clear(); + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored()) { + outDeviceIds.add(device->getId()); + } + } + } // release device registy reader lock +} + +int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState); +} + +int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState); +} + +int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { + return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState); +} + +int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + int32_t result = AKEY_STATE_UNKNOWN; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } + } + } + } + return result; + } // release device registy reader lock +} + +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + memset(outFlags, 0, numCodes); + return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} + +bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + bool result = false; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result |= device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } + } + return result; + } // release device registy reader lock +} + + +// --- InputReaderThread --- + +InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : + Thread(/*canCallJava*/ true), mReader(reader) { +} + +InputReaderThread::~InputReaderThread() { +} + +bool InputReaderThread::threadLoop() { + mReader->loopOnce(); + return true; +} + + +// --- InputDevice --- + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) : + mContext(context), mId(id), mName(name), mSources(0) { +} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; + } + mMappers.clear(); +} + +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.add(mapper); +} + +void InputDevice::configure() { + mSources = 0; + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->configure(); + mSources |= mapper->getSources(); + } +} + +void InputDevice::reset() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->reset(); + } +} + +void InputDevice::process(const RawEvent* rawEvent) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->process(rawEvent); + } +} + +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mName); + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->populateDeviceInfo(outDeviceInfo); + } +} + +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); +} + +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); +} + +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, & InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result = (mapper->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } + } + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + return result; +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + result |= mapper->getMetaState(); + } + return result; +} + + +// --- InputMapper --- + +InputMapper::InputMapper(InputDevice* device) : + mDevice(device), mContext(device->getContext()) { +} + +InputMapper::~InputMapper() { +} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::configure() { +} + +void InputMapper::reset() { +} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +int32_t InputMapper::getMetaState() { + return 0; +} + +bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) { + if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { + getDispatcher()->notifyAppSwitchComing(when); + } + + return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; +} + + +// --- SwitchInputMapper --- + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) : + InputMapper(device) { +} + +SwitchInputMapper::~SwitchInputMapper() { +} + +uint32_t SwitchInputMapper::getSources() { + return 0; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value); + break; + } +} + +void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) { + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptSwitch( + when, switchCode, switchValue, policyFlags); + + applyStandardPolicyActions(when, policyActions); +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, + uint32_t sources, int32_t keyboardType) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), + mKeyboardType(keyboardType) { + initializeLocked(); +} + +KeyboardInputMapper::~KeyboardInputMapper() { +} + +void KeyboardInputMapper::initializeLocked() { + mLocked.metaState = AMETA_NONE; + mLocked.downTime = 0; +} + +uint32_t KeyboardInputMapper::getSources() { + return mSources; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); +} + +void KeyboardInputMapper::reset() { + for (;;) { + int32_t keyCode, scanCode; + { // acquire lock + AutoMutex _l(mLock); + + // Synthesize key up event on reset if keys are currently down. + if (mLocked.keyDowns.isEmpty()) { + initializeLocked(); + break; // done + } + + const KeyDown& keyDown = mLocked.keyDowns.top(); + keyCode = keyDown.keyCode; + scanCode = keyDown.scanCode; + } // release lock + + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + processKey(when, false, keyCode, scanCode, 0); + } + + InputMapper::reset(); + getContext()->updateGlobalMetaState(); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->scanCode; + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode, + rawEvent->flags); + } + break; + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE + || scanCode >= KEY_OK + || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, + int32_t scanCode, uint32_t policyFlags) { + int32_t newMetaState; + nsecs_t downTime; + bool metaStateChanged = false; + + { // acquire lock + AutoMutex _l(mLock); + + if (down) { + // Rotate key codes according to orientation if needed. + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. + if (mAssociatedDisplayId >= 0) { + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + keyCode = rotateKeyCode(keyCode, orientation); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDownLocked(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mLocked.keyDowns.top().keyCode; + } else { + // key down + mLocked.keyDowns.push(); + KeyDown& keyDown = mLocked.keyDowns.editTop(); + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + } + + mLocked.downTime = when; + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDownLocked(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mLocked.keyDowns.top().keyCode; + mLocked.keyDowns.removeAt(size_t(keyDownIndex)); + } else { + // key was not actually down + LOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().string(), keyCode, scanCode); + return; + } + } + + int32_t oldMetaState = mLocked.metaState; + newMetaState = updateMetaState(keyCode, down, oldMetaState); + if (oldMetaState != newMetaState) { + mLocked.metaState = newMetaState; + metaStateChanged = true; + } + + downTime = mLocked.downTime; + } // release lock + + if (metaStateChanged) { + getContext()->updateGlobalMetaState(); + } + + applyPolicyAndDispatch(when, policyFlags, down, keyCode, scanCode, newMetaState, downTime); +} + +void KeyboardInputMapper::applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, bool down, + int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { + int32_t policyActions = getPolicy()->interceptKey(when, + getDeviceId(), down, keyCode, scanCode, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + return; // event dropped + } + + int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP; + int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM; + if (policyFlags & POLICY_FLAG_WOKE_HERE) { + keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE; + } + + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, + keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); +} + +ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) { + size_t n = mLocked.keyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mLocked.keyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} + +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} + +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} + +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + { // acquire lock + AutoMutex _l(mLock); + return mLocked.metaState; + } // release lock +} + + +// --- TrackballInputMapper --- + +TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + + initializeLocked(); +} + +TrackballInputMapper::~TrackballInputMapper() { +} + +uint32_t TrackballInputMapper::getSources() { + return AINPUT_SOURCE_TRACKBALL; +} + +void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); +} + +void TrackballInputMapper::initializeLocked() { + mAccumulator.clear(); + + mLocked.down = false; + mLocked.downTime = 0; +} + +void TrackballInputMapper::reset() { + for (;;) { + { // acquire lock + AutoMutex _l(mLock); + + if (! mLocked.down) { + initializeLocked(); + break; // done + } + } // release lock + + // Synthesize trackball button up event on reset. + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = false; + sync(when); + } + + InputMapper::reset(); +} + +void TrackballInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_MOUSE: + mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = rawEvent->value != 0; + // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and + // we need to ensure that we report the up/down promptly. + sync(rawEvent->when); + break; + } + break; + + case EV_REL: + switch (rawEvent->scanCode) { + case REL_X: + mAccumulator.fields |= Accumulator::FIELD_REL_X; + mAccumulator.relX = rawEvent->value; + break; + case REL_Y: + mAccumulator.fields |= Accumulator::FIELD_REL_Y; + mAccumulator.relY = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + sync(rawEvent->when); + break; + } + break; + } +} + +void TrackballInputMapper::sync(nsecs_t when) { + uint32_t fields = mAccumulator.fields; + if (fields == 0) { + return; // no new state changes, so nothing to do + } + + int motionEventAction; + PointerCoords pointerCoords; + nsecs_t downTime; + { // acquire lock + AutoMutex _l(mLock); + + bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; + + if (downChanged) { + if (mAccumulator.btnMouse) { + mLocked.down = true; + mLocked.downTime = when; + } else { + mLocked.down = false; + } + } + + downTime = mLocked.downTime; + float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f; + float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f; + + if (downChanged) { + motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } + + pointerCoords.x = x; + pointerCoords.y = y; + pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f; + pointerCoords.size = 0; + pointerCoords.touchMajor = 0; + pointerCoords.touchMinor = 0; + pointerCoords.toolMajor = 0; + pointerCoords.toolMinor = 0; + pointerCoords.orientation = 0; + + if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) { + // Rotate motion based on display orientation if needed. + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + float temp; + switch (orientation) { + case InputReaderPolicyInterface::ROTATION_90: + temp = pointerCoords.x; + pointerCoords.x = pointerCoords.y; + pointerCoords.y = - temp; + break; + + case InputReaderPolicyInterface::ROTATION_180: + pointerCoords.x = - pointerCoords.x; + pointerCoords.y = - pointerCoords.y; + break; + + case InputReaderPolicyInterface::ROTATION_270: + temp = pointerCoords.x; + pointerCoords.x = - pointerCoords.y; + pointerCoords.y = temp; + break; + } + } + } // release lock + + applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime); + + mAccumulator.clear(); +} + +void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction, + PointerCoords* pointerCoords, nsecs_t downTime) { + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + return; // event dropped + } + + int32_t metaState = mContext->getGlobalMetaState(); + int32_t pointerId = 0; + + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags, + motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime); +} + +int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { + mLocked.surfaceOrientation = -1; + mLocked.surfaceWidth = -1; + mLocked.surfaceHeight = -1; + + initializeLocked(); +} + +TouchInputMapper::~TouchInputMapper() { +} + +uint32_t TouchInputMapper::getSources() { + return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + { // acquire lock + AutoMutex _l(mLock); + + // Ensure surface information is up to date so that orientation changes are + // noticed immediately. + configureSurfaceLocked(); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y); + info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mLocked.orientedRanges.pressure); + info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mLocked.orientedRanges.size); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mLocked.orientedRanges.touchMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mLocked.orientedRanges.touchMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mLocked.orientedRanges.toolMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mLocked.orientedRanges.toolMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mLocked.orientedRanges.orientation); + } // release lock +} + +void TouchInputMapper::initializeLocked() { + mCurrentTouch.clear(); + mLastTouch.clear(); + mDownTime = 0; + + for (uint32_t i = 0; i < MAX_POINTERS; i++) { + mAveragingTouchFilter.historyStart[i] = 0; + mAveragingTouchFilter.historyEnd[i] = 0; + } + + mJumpyTouchFilter.jumpyPointsDropped = 0; + + mLocked.currentVirtualKey.down = false; +} + +void TouchInputMapper::configure() { + InputMapper::configure(); + + // Configure basic parameters. + mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); + + // Configure absolute axis information. + configureAxes(); + + { // acquire lock + AutoMutex _l(mLock); + + // Configure pressure factors. + if (mAxes.pressure.valid) { + mLocked.pressureOrigin = mAxes.pressure.minValue; + mLocked.pressureScale = 1.0f / mAxes.pressure.getRange(); + } else { + mLocked.pressureOrigin = 0; + mLocked.pressureScale = 1.0f; + } + + mLocked.orientedRanges.pressure.min = 0.0f; + mLocked.orientedRanges.pressure.max = 1.0f; + mLocked.orientedRanges.pressure.flat = 0.0f; + mLocked.orientedRanges.pressure.fuzz = mLocked.pressureScale; + + // Configure size factors. + if (mAxes.size.valid) { + mLocked.sizeOrigin = mAxes.size.minValue; + mLocked.sizeScale = 1.0f / mAxes.size.getRange(); + } else { + mLocked.sizeOrigin = 0; + mLocked.sizeScale = 1.0f; + } + + mLocked.orientedRanges.size.min = 0.0f; + mLocked.orientedRanges.size.max = 1.0f; + mLocked.orientedRanges.size.flat = 0.0f; + mLocked.orientedRanges.size.fuzz = mLocked.sizeScale; + + // Configure orientation factors. + if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { + mLocked.orientationScale = float(M_PI_2) / mAxes.orientation.maxValue; + } else { + mLocked.orientationScale = 0.0f; + } + + mLocked.orientedRanges.orientation.min = - M_PI_2; + mLocked.orientedRanges.orientation.max = M_PI_2; + mLocked.orientedRanges.orientation.flat = 0; + mLocked.orientedRanges.orientation.fuzz = mLocked.orientationScale; + + // Configure surface dimensions and orientation. + configureSurfaceLocked(); + } // release lock +} + +void TouchInputMapper::configureAxes() { + mAxes.x.valid = false; + mAxes.y.valid = false; + mAxes.pressure.valid = false; + mAxes.size.valid = false; + mAxes.touchMajor.valid = false; + mAxes.touchMinor.valid = false; + mAxes.toolMajor.valid = false; + mAxes.toolMinor.valid = false; + mAxes.orientation.valid = false; +} + +bool TouchInputMapper::configureSurfaceLocked() { + // Update orientation and dimensions if needed. + int32_t orientation; + int32_t width, height; + if (mAssociatedDisplayId >= 0) { + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { + return false; + } + } else { + orientation = InputReaderPolicyInterface::ROTATION_0; + width = mAxes.x.getRange(); + height = mAxes.y.getRange(); + } + + bool orientationChanged = mLocked.surfaceOrientation != orientation; + if (orientationChanged) { + mLocked.surfaceOrientation = orientation; + } + + bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; + if (sizeChanged) { + mLocked.surfaceWidth = width; + mLocked.surfaceHeight = height; + + // Compute size-dependent translation and scaling factors and place virtual keys. + if (mAxes.x.valid && mAxes.y.valid) { + mLocked.xOrigin = mAxes.x.minValue; + mLocked.yOrigin = mAxes.y.minValue; + + LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + getDeviceId(), getDeviceName().string()); + + mLocked.xScale = float(width) / mAxes.x.getRange(); + mLocked.yScale = float(height) / mAxes.y.getRange(); + mLocked.xPrecision = 1.0f / mLocked.xScale; + mLocked.yPrecision = 1.0f / mLocked.yScale; + + configureVirtualKeysLocked(); + } else { + mLocked.xOrigin = 0; + mLocked.yOrigin = 0; + mLocked.xScale = 1.0f; + mLocked.yScale = 1.0f; + mLocked.xPrecision = 1.0f; + mLocked.yPrecision = 1.0f; + } + + // Configure touch and tool area ranges. + float diagonal = sqrt(float(width * width + height * height)); + float diagonalFuzz = sqrt(mLocked.xScale * mLocked.xScale + + mLocked.yScale * mLocked.yScale); + + InputDeviceInfo::MotionRange area; + area.min = 0.0f; + area.max = diagonal; + area.flat = 0.0f; + area.fuzz = diagonalFuzz; + + mLocked.orientedRanges.touchMajor = area; + mLocked.orientedRanges.touchMinor = area; + + mLocked.orientedRanges.toolMajor = area; + mLocked.orientedRanges.toolMinor = area; + } + + if (orientationChanged || sizeChanged) { + // Compute oriented surface dimensions, precision, and scales. + float orientedXScale, orientedYScale; + switch (mLocked.surfaceOrientation) { + case InputReaderPolicyInterface::ROTATION_90: + case InputReaderPolicyInterface::ROTATION_270: + mLocked.orientedSurfaceWidth = mLocked.surfaceHeight; + mLocked.orientedSurfaceHeight = mLocked.surfaceWidth; + mLocked.orientedXPrecision = mLocked.yPrecision; + mLocked.orientedYPrecision = mLocked.xPrecision; + orientedXScale = mLocked.yScale; + orientedYScale = mLocked.xScale; + break; + default: + mLocked.orientedSurfaceWidth = mLocked.surfaceWidth; + mLocked.orientedSurfaceHeight = mLocked.surfaceHeight; + mLocked.orientedXPrecision = mLocked.xPrecision; + mLocked.orientedYPrecision = mLocked.yPrecision; + orientedXScale = mLocked.xScale; + orientedYScale = mLocked.yScale; + break; + } + + // Configure position ranges. + mLocked.orientedRanges.x.min = 0; + mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth; + mLocked.orientedRanges.x.flat = 0; + mLocked.orientedRanges.x.fuzz = orientedXScale; + + mLocked.orientedRanges.y.min = 0; + mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight; + mLocked.orientedRanges.y.flat = 0; + mLocked.orientedRanges.y.fuzz = orientedYScale; + } + + return true; +} + +void TouchInputMapper::configureVirtualKeysLocked() { + assert(mAxes.x.valid && mAxes.y.valid); + + // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock. + Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; + getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); + + mLocked.virtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size()); + + int32_t touchScreenLeft = mAxes.x.minValue; + int32_t touchScreenTop = mAxes.y.minValue; + int32_t touchScreenWidth = mAxes.x.getRange(); + int32_t touchScreenHeight = mAxes.y.getRange(); + + for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { + const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + virtualKeyDefinitions[i]; + + mLocked.virtualKeys.add(); + VirtualKey& virtualKey = mLocked.virtualKeys.editTop(); + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + uint32_t flags; + if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, + & keyCode, & flags)) { + LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + mLocked.virtualKeys.pop(); // drop the key + continue; + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) + * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; + virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) + * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) + * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) + * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; + + LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", + virtualKey.scanCode, virtualKey.keyCode, + virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } +} + +void TouchInputMapper::reset() { + // Synthesize touch up event if touch is currently down. + // This will also take care of finishing virtual key processing if needed. + if (mLastTouch.pointerCount != 0) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mCurrentTouch.clear(); + syncTouch(when, true); + } + + { // acquire lock + AutoMutex _l(mLock); + initializeLocked(); + } // release lock + + InputMapper::reset(); +} + +void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { + // Apply generic policy actions. + + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + mLastTouch.clear(); + return; // event dropped + } + + // Preprocess pointer data. + + if (mParameters.useBadTouchFilter) { + if (applyBadTouchFilter()) { + havePointerIds = false; + } + } + + if (mParameters.useJumpyTouchFilter) { + if (applyJumpyTouchFilter()) { + havePointerIds = false; + } + } + + if (! havePointerIds) { + calculatePointerIds(); + } + + TouchData temp; + TouchData* savedTouch; + if (mParameters.useAveragingTouchFilter) { + temp.copyFrom(mCurrentTouch); + savedTouch = & temp; + + applyAveragingTouchFilter(); + } else { + savedTouch = & mCurrentTouch; + } + + // Process touches and virtual keys. + + TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); + if (touchResult == DISPATCH_TOUCH) { + dispatchTouches(when, policyFlags); + } + + // Copy current touch to last touch in preparation for the next cycle. + + if (touchResult == DROP_STROKE) { + mLastTouch.clear(); + } else { + mLastTouch.copyFrom(*savedTouch); + } +} + +TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( + nsecs_t when, uint32_t policyFlags) { + int32_t keyEventAction, keyEventFlags; + int32_t keyCode, scanCode, downTime; + TouchResult touchResult; + + { // acquire lock + AutoMutex _l(mLock); + + // Update surface size and orientation, including virtual key positions. + if (! configureSurfaceLocked()) { + return DROP_STROKE; + } + + // Check for virtual key press. + if (mLocked.currentVirtualKey.down) { + if (mCurrentTouch.pointerCount == 0) { + // Pointer went up while virtual key was down. + mLocked.currentVirtualKey.down = false; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } + + if (mCurrentTouch.pointerCount == 1) { + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); + if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return SKIP_TOUCH; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation and drop the stroke so subsequent motions will be + // considered fresh downs. This is useful when the user swipes away from the + // virtual key area into the main display surface. + mLocked.currentVirtualKey.down = false; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY + | AKEY_EVENT_FLAG_CANCELED; + touchResult = DROP_STROKE; + goto DispatchVirtualKey; + } else { + if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) { + // Pointer just went down. Handle off-screen touches, if needed. + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + if (! isPointInsideSurfaceLocked(x, y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentTouch.pointerCount == 1) { + const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); + if (virtualKey) { + mLocked.currentVirtualKey.down = true; + mLocked.currentVirtualKey.downTime = when; + mLocked.currentVirtualKey.keyCode = virtualKey->keyCode; + mLocked.currentVirtualKey.scanCode = virtualKey->scanCode; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_DOWN; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM + | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } + } + return DROP_STROKE; + } + } + return DISPATCH_TOUCH; + } + + DispatchVirtualKey: + // Collect remaining state needed to dispatch virtual key. + keyCode = mLocked.currentVirtualKey.keyCode; + scanCode = mLocked.currentVirtualKey.scanCode; + downTime = mLocked.currentVirtualKey.downTime; + } // release lock + + // Dispatch virtual key. + applyPolicyAndDispatchVirtualKey(when, policyFlags, keyEventAction, keyEventFlags, + keyCode, scanCode, downTime); + return touchResult; +} + +void TouchInputMapper::applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags, + int32_t keyCode, int32_t scanCode, nsecs_t downTime) { + int32_t metaState = mContext->getGlobalMetaState(); + + if (keyEventAction == AKEY_EVENT_ACTION_DOWN) { + getPolicy()->virtualKeyDownFeedback(); + } + + int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(), + keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags); + + if (applyStandardPolicyActions(when, policyActions)) { + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, + keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); + } +} + +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; + if (currentPointerCount == 0 && lastPointerCount == 0) { + return; // nothing to do! + } + + BitSet32 currentIdBits = mCurrentTouch.idBits; + BitSet32 lastIdBits = mLastTouch.idBits; + + if (currentIdBits == lastIdBits) { + // No pointer id changes so this is a move event. + // The dispatcher takes care of batching moves so we don't have to deal with that here. + int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; + dispatchTouch(when, policyFlags, & mCurrentTouch, + currentIdBits, -1, motionEventAction); + } else { + // There may be pointers going up and pointers going down at the same time when pointer + // ids are reported by the device driver. + BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); + BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); + BitSet32 activeIdBits(lastIdBits.value); + + while (! upIdBits.isEmpty()) { + uint32_t upId = upIdBits.firstMarkedBit(); + upIdBits.clearBit(upId); + BitSet32 oldActiveIdBits = activeIdBits; + activeIdBits.clearBit(upId); + + int32_t motionEventAction; + if (activeIdBits.isEmpty()) { + motionEventAction = AMOTION_EVENT_ACTION_UP; + } else { + motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; + } + + dispatchTouch(when, policyFlags, & mLastTouch, + oldActiveIdBits, upId, motionEventAction); + } + + while (! downIdBits.isEmpty()) { + uint32_t downId = downIdBits.firstMarkedBit(); + downIdBits.clearBit(downId); + BitSet32 oldActiveIdBits = activeIdBits; + activeIdBits.markBit(downId); + + int32_t motionEventAction; + if (oldActiveIdBits.isEmpty()) { + motionEventAction = AMOTION_EVENT_ACTION_DOWN; + mDownTime = when; + } else { + motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; + } + + dispatchTouch(when, policyFlags, & mCurrentTouch, + activeIdBits, downId, motionEventAction); + } + } +} + +void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, + TouchData* touch, BitSet32 idBits, uint32_t changedId, + int32_t motionEventAction) { + uint32_t pointerCount = 0; + int32_t pointerIds[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + int32_t motionEventEdgeFlags = 0; + float xPrecision, yPrecision; + + { // acquire lock + AutoMutex _l(mLock); + + // Walk through the the active pointers and map touch screen coordinates (TouchData) into + // display coordinates (PointerCoords) and adjust for display orientation. + while (! idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = touch->idToIndex[id]; + + float x = float(touch->pointers[index].x - mLocked.xOrigin) * mLocked.xScale; + float y = float(touch->pointers[index].y - mLocked.yOrigin) * mLocked.yScale; + float pressure = float(touch->pointers[index].pressure - mLocked.pressureOrigin) + * mLocked.pressureScale; + float size = float(touch->pointers[index].size - mLocked.sizeOrigin) + * mLocked.sizeScale; + + float orientation = float(touch->pointers[index].orientation) + * mLocked.orientationScale; + + float touchMajor, touchMinor, toolMajor, toolMinor; + if (abs(orientation) <= M_PI_4) { + // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. + touchMajor = float(touch->pointers[index].touchMajor) * mLocked.yScale; + touchMinor = float(touch->pointers[index].touchMinor) * mLocked.xScale; + toolMajor = float(touch->pointers[index].toolMajor) * mLocked.yScale; + toolMinor = float(touch->pointers[index].toolMinor) * mLocked.xScale; + } else { + // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. + touchMajor = float(touch->pointers[index].touchMajor) * mLocked.xScale; + touchMinor = float(touch->pointers[index].touchMinor) * mLocked.yScale; + toolMajor = float(touch->pointers[index].toolMajor) * mLocked.xScale; + toolMinor = float(touch->pointers[index].toolMinor) * mLocked.yScale; + } + + switch (mLocked.surfaceOrientation) { + case InputReaderPolicyInterface::ROTATION_90: { + float xTemp = x; + x = y; + y = mLocked.surfaceWidth - xTemp; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; + } + break; + } + case InputReaderPolicyInterface::ROTATION_180: { + x = mLocked.surfaceWidth - x; + y = mLocked.surfaceHeight - y; + orientation = - orientation; + break; + } + case InputReaderPolicyInterface::ROTATION_270: { + float xTemp = x; + x = mLocked.surfaceHeight - y; + y = xTemp; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; + } + break; + } + } + + pointerIds[pointerCount] = int32_t(id); + + pointerCoords[pointerCount].x = x; + pointerCoords[pointerCount].y = y; + pointerCoords[pointerCount].pressure = pressure; + pointerCoords[pointerCount].size = size; + pointerCoords[pointerCount].touchMajor = touchMajor; + pointerCoords[pointerCount].touchMinor = touchMinor; + pointerCoords[pointerCount].toolMajor = toolMajor; + pointerCoords[pointerCount].toolMinor = toolMinor; + pointerCoords[pointerCount].orientation = orientation; + + if (id == changedId) { + motionEventAction |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + // Check edge flags by looking only at the first pointer since the flags are + // global to the event. + if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { + if (pointerCoords[0].x <= 0) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; + } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; + } + if (pointerCoords[0].y <= 0) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; + } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; + } + } + + xPrecision = mLocked.orientedXPrecision; + yPrecision = mLocked.orientedYPrecision; + } // release lock + + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags, + motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags, + pointerCount, pointerIds, pointerCoords, + xPrecision, yPrecision, mDownTime); +} + +bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { + if (mAxes.x.valid && mAxes.y.valid) { + return x >= mAxes.x.minValue && x <= mAxes.x.maxValue + && y >= mAxes.y.minValue && y <= mAxes.y.maxValue; + } + return true; +} + +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked( + int32_t x, int32_t y) { + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; + +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, + virtualKey.keyCode, virtualKey.scanCode, + virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return & virtualKey; + } + } + + return NULL; +} + +void TouchInputMapper::calculatePointerIds() { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; + + if (currentPointerCount == 0) { + // No pointers to assign. + mCurrentTouch.idBits.clear(); + } else if (lastPointerCount == 0) { + // All pointers are new. + mCurrentTouch.idBits.clear(); + for (uint32_t i = 0; i < currentPointerCount; i++) { + mCurrentTouch.pointers[i].id = i; + mCurrentTouch.idToIndex[i] = i; + mCurrentTouch.idBits.markBit(i); + } + } else if (currentPointerCount == 1 && lastPointerCount == 1) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = mLastTouch.pointers[0].id; + mCurrentTouch.pointers[0].id = id; + mCurrentTouch.idToIndex[id] = 0; + mCurrentTouch.idBits.value = BitSet32::valueForBit(id); + } else { + // General case. + // We build a heap of squared euclidean distances between current and last pointers + // associated with the current and last pointer indices. Then, we find the best + // match (by distance) for each current pointer. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x + - mLastTouch.pointers[lastPointerIndex].x; + int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y + - mLastTouch.pointers[lastPointerIndex].y; + + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + + // Insert new element into the heap (sift up). + heap[heapSize].currentPointerIndex = currentPointerIndex; + heap[heapSize].lastPointerIndex = lastPointerIndex; + heap[heapSize].distance = distance; + heapSize += 1; + } + } + + // Heapify + for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { + startIndex -= 1; + for (uint32_t parentIndex = startIndex; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + + // Pull matches out by increasing order of distance. + // To avoid reassigning pointers that have already been matched, the loop keeps track + // of which last and current pointers have been matched using the matchedXXXBits variables. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { + for (;;) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heapSize -= 1; + assert(heapSize > 0); + + // Sift down. + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + } + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = mLastTouch.pointers[lastPointerIndex].id; + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to new pointers. + if (currentPointerCount > lastPointerCount) { + for (uint32_t i = currentPointerCount - lastPointerCount; ;) { + uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); + uint32_t id = usedIdBits.firstUnmarkedBit(); + + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - assigned: cur=%d, id=%d", + currentPointerIndex, id); +#endif + + if (--i == 0) break; // done + matchedCurrentBits.markBit(currentPointerIndex); + } + } + + // Fix id bits. + mCurrentTouch.idBits = usedIdBits; + } +} + +/* Special hack for devices that have bad screen data: if one of the + * points has moved more than a screen height from the last position, + * then drop it. */ +bool TouchInputMapper::applyBadTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; + } + + uint32_t pointerCount = mCurrentTouch.pointerCount; + + // Nothing to do if there are no points. + if (pointerCount == 0) { + return false; + } + + // Don't do anything if a finger is going down or up. We run + // here before assigning pointer IDs, so there isn't a good + // way to do per-finger matching. + if (pointerCount != mLastTouch.pointerCount) { + return false; + } + + // We consider a single movement across more than a 7/16 of + // the long size of the screen to be bad. This was a magic value + // determined by looking at the maximum distance it is feasible + // to actually move in one sample. + int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16; + + // XXX The original code in InputDevice.java included commented out + // code for testing the X axis. Note that when we drop a point + // we don't actually restore the old X either. Strange. + // The old code also tries to track when bad points were previously + // detected but it turns out that due to the placement of a "break" + // at the end of the loop, we never set mDroppedBadPoint to true + // so it is effectively dead code. + // Need to figure out if the old code is busted or just overcomplicated + // but working as intended. + + // Look through all new points and see if any are farther than + // acceptable from all previous points. + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t y = mCurrentTouch.pointers[i].y; + int32_t closestY = INT_MAX; + int32_t closestDeltaY = 0; + +#if DEBUG_HACKS + LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); +#endif + + for (uint32_t j = pointerCount; j-- > 0; ) { + int32_t lastY = mLastTouch.pointers[j].y; + int32_t deltaY = abs(y - lastY); + +#if DEBUG_HACKS + LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", + j, lastY, deltaY); +#endif + + if (deltaY < maxDeltaY) { + goto SkipSufficientlyClosePoint; + } + if (deltaY < closestDeltaY) { + closestDeltaY = deltaY; + closestY = lastY; + } + } + + // Must not have found a close enough match. +#if DEBUG_HACKS + LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", + i, y, closestY, closestDeltaY, maxDeltaY); +#endif + + mCurrentTouch.pointers[i].y = closestY; + return true; // XXX original code only corrects one point + + SkipSufficientlyClosePoint: ; + } + + // No change. + return false; +} + +/* Special hack for devices that have bad screen data: drop points where + * the coordinate value for one axis has jumped to the other pointer's location. + */ +bool TouchInputMapper::applyJumpyTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; + } + + uint32_t pointerCount = mCurrentTouch.pointerCount; + if (mLastTouch.pointerCount != pointerCount) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Different pointer count %d -> %d", + mLastTouch.pointerCount, pointerCount); + for (uint32_t i = 0; i < pointerCount; i++) { + LOGD(" Pointer %d (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif + + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { + if (mLastTouch.pointerCount == 1 && pointerCount == 2) { + // Just drop the first few events going from 1 to 2 pointers. + // They're bad often enough that they're not worth considering. + mCurrentTouch.pointerCount = 1; + mJumpyTouchFilter.jumpyPointsDropped += 1; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Pointer 2 dropped"); +#endif + return true; + } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) { + // The event when we go from 2 -> 1 tends to be messed up too + mCurrentTouch.pointerCount = 2; + mCurrentTouch.pointers[0] = mLastTouch.pointers[0]; + mCurrentTouch.pointers[1] = mLastTouch.pointers[1]; + mJumpyTouchFilter.jumpyPointsDropped += 1; + +#if DEBUG_HACKS + for (int32_t i = 0; i < 2; i++) { + LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif + return true; + } + } + // Reset jumpy points dropped on other transitions or if limit exceeded. + mJumpyTouchFilter.jumpyPointsDropped = 0; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Transition - drop limit reset"); +#endif + return false; + } + + // We have the same number of pointers as last time. + // A 'jumpy' point is one where the coordinate value for one axis + // has jumped to the other pointer's location. No need to do anything + // else if we only have one pointer. + if (pointerCount < 2) { + return false; + } + + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { + int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; + + // We only replace the single worst jumpy point as characterized by pointer distance + // in a single axis. + int32_t badPointerIndex = -1; + int32_t badPointerReplacementIndex = -1; + int32_t badPointerDistance = INT_MIN; // distance to be corrected + + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t x = mCurrentTouch.pointers[i].x; + int32_t y = mCurrentTouch.pointers[i].y; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); +#endif + + // Check if a touch point is too close to another's coordinates + bool dropX = false, dropY = false; + for (uint32_t j = 0; j < pointerCount; j++) { + if (i == j) { + continue; + } + + if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) { + dropX = true; + break; + } + + if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) { + dropY = true; + break; + } + } + if (! dropX && ! dropY) { + continue; // not jumpy + } + + // Find a replacement candidate by comparing with older points on the + // complementary (non-jumpy) axis. + int32_t distance = INT_MIN; // distance to be corrected + int32_t replacementIndex = -1; + + if (dropX) { + // X looks too close. Find an older replacement point with a close Y. + int32_t smallestDeltaY = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaY = abs(y - mLastTouch.pointers[j].y); + if (deltaY < smallestDeltaY) { + smallestDeltaY = deltaY; + replacementIndex = j; + } + } + distance = abs(x - mLastTouch.pointers[replacementIndex].x); + } else { + // Y looks too close. Find an older replacement point with a close X. + int32_t smallestDeltaX = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaX = abs(x - mLastTouch.pointers[j].x); + if (deltaX < smallestDeltaX) { + smallestDeltaX = deltaX; + replacementIndex = j; + } + } + distance = abs(y - mLastTouch.pointers[replacementIndex].y); + } + + // If replacing this pointer would correct a worse error than the previous ones + // considered, then use this replacement instead. + if (distance > badPointerDistance) { + badPointerIndex = i; + badPointerReplacementIndex = replacementIndex; + badPointerDistance = distance; + } + } + + // Correct the jumpy pointer if one was found. + if (badPointerIndex >= 0) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", + badPointerIndex, + mLastTouch.pointers[badPointerReplacementIndex].x, + mLastTouch.pointers[badPointerReplacementIndex].y); +#endif + + mCurrentTouch.pointers[badPointerIndex].x = + mLastTouch.pointers[badPointerReplacementIndex].x; + mCurrentTouch.pointers[badPointerIndex].y = + mLastTouch.pointers[badPointerReplacementIndex].y; + mJumpyTouchFilter.jumpyPointsDropped += 1; + return true; + } + } + + mJumpyTouchFilter.jumpyPointsDropped = 0; + return false; +} + +/* Special hack for devices that have bad screen data: aggregate and + * compute averages of the coordinate data, to reduce the amount of + * jitter seen by applications. */ +void TouchInputMapper::applyAveragingTouchFilter() { + for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) { + uint32_t id = mCurrentTouch.pointers[currentIndex].id; + int32_t x = mCurrentTouch.pointers[currentIndex].x; + int32_t y = mCurrentTouch.pointers[currentIndex].y; + int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure; + + if (mLastTouch.idBits.hasBit(id)) { + // Pointer was down before and is still down now. + // Compute average over history trace. + uint32_t start = mAveragingTouchFilter.historyStart[id]; + uint32_t end = mAveragingTouchFilter.historyEnd[id]; + + int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x; + int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y; + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", + id, distance); +#endif + + if (distance < AVERAGING_DISTANCE_LIMIT) { + // Increment end index in preparation for recording new historical data. + end += 1; + if (end > AVERAGING_HISTORY_SIZE) { + end = 0; + } + + // If the end index has looped back to the start index then we have filled + // the historical trace up to the desired size so we drop the historical + // data at the start of the trace. + if (end == start) { + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } + + // Add the raw data to the historical trace. + mAveragingTouchFilter.historyStart[id] = start; + mAveragingTouchFilter.historyEnd[id] = end; + mAveragingTouchFilter.historyData[end].pointers[id].x = x; + mAveragingTouchFilter.historyData[end].pointers[id].y = y; + mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure; + + // Average over all historical positions in the trace by total pressure. + int32_t averagedX = 0; + int32_t averagedY = 0; + int32_t totalPressure = 0; + for (;;) { + int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x; + int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y; + int32_t historicalPressure = mAveragingTouchFilter.historyData[start] + .pointers[id].pressure; + + averagedX += historicalX * historicalPressure; + averagedY += historicalY * historicalPressure; + totalPressure += historicalPressure; + + if (start == end) { + break; + } + + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } + + averagedX /= totalPressure; + averagedY /= totalPressure; + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - " + "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, + averagedX, averagedY); +#endif + + mCurrentTouch.pointers[currentIndex].x = averagedX; + mCurrentTouch.pointers[currentIndex].y = averagedY; + } else { +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); +#endif + } + } else { +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); +#endif + } + + // Reset pointer history. + mAveragingTouchFilter.historyStart[id] = 0; + mAveragingTouchFilter.historyEnd[id] = 0; + mAveragingTouchFilter.historyData[0].pointers[id].x = x; + mAveragingTouchFilter.historyData[0].pointers[id].y = y; + mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure; + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + } // release lock + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + } // release lock + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire lock + AutoMutex _l(mLock); + + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; + + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + } // release lock + + return true; +} + + +// --- SingleTouchInputMapper --- + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); +} + +SingleTouchInputMapper::~SingleTouchInputMapper() { +} + +void SingleTouchInputMapper::initialize() { + mAccumulator.clear(); + + mDown = false; + mX = 0; + mY = 0; + mPressure = 1; // default to 1 for devices that don't report pressure + mSize = 0; // default to 0 for devices that don't report size +} + +void SingleTouchInputMapper::reset() { + TouchInputMapper::reset(); + + initialize(); + } + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_TOUCH: + mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; + mAccumulator.btnTouch = rawEvent->value != 0; + // Don't sync immediately. Wait until the next SYN_REPORT since we might + // not have received valid position information yet. This logic assumes that + // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. + break; + } + break; + + case EV_ABS: + switch (rawEvent->scanCode) { + case ABS_X: + mAccumulator.fields |= Accumulator::FIELD_ABS_X; + mAccumulator.absX = rawEvent->value; + break; + case ABS_Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_Y; + mAccumulator.absY = rawEvent->value; + break; + case ABS_PRESSURE: + mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE; + mAccumulator.absPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH; + mAccumulator.absToolWidth = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + sync(rawEvent->when); + break; + } + break; + } +} + +void SingleTouchInputMapper::sync(nsecs_t when) { + uint32_t fields = mAccumulator.fields; + if (fields == 0) { + return; // no new state changes, so nothing to do + } + + if (fields & Accumulator::FIELD_BTN_TOUCH) { + mDown = mAccumulator.btnTouch; + } + + if (fields & Accumulator::FIELD_ABS_X) { + mX = mAccumulator.absX; + } + + if (fields & Accumulator::FIELD_ABS_Y) { + mY = mAccumulator.absY; + } + + if (fields & Accumulator::FIELD_ABS_PRESSURE) { + mPressure = mAccumulator.absPressure; + } + + if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { + mSize = mAccumulator.absToolWidth; + } + + mCurrentTouch.clear(); + + if (mDown) { + mCurrentTouch.pointerCount = 1; + mCurrentTouch.pointers[0].id = 0; + mCurrentTouch.pointers[0].x = mX; + mCurrentTouch.pointers[0].y = mY; + mCurrentTouch.pointers[0].pressure = mPressure; + mCurrentTouch.pointers[0].size = mSize; + mCurrentTouch.pointers[0].touchMajor = mSize; + mCurrentTouch.pointers[0].touchMinor = mSize; + mCurrentTouch.pointers[0].toolMajor = mSize; + mCurrentTouch.pointers[0].toolMinor = mSize; + mCurrentTouch.pointers[0].orientation = 0; + mCurrentTouch.idToIndex[0] = 0; + mCurrentTouch.idBits.markBit(0); + } + + syncTouch(when, true); + + mAccumulator.clear(); +} + +void SingleTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); + + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size); + + mAxes.touchMajor = mAxes.size; + mAxes.touchMinor = mAxes.size; + mAxes.toolMajor = mAxes.size; + mAxes.toolMinor = mAxes.size; +} + + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); +} + +MultiTouchInputMapper::~MultiTouchInputMapper() { +} + +void MultiTouchInputMapper::initialize() { + mAccumulator.clear(); +} + +void MultiTouchInputMapper::reset() { + TouchInputMapper::reset(); + + initialize(); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + uint32_t pointerIndex = mAccumulator.pointerCount; + Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; + + switch (rawEvent->scanCode) { + case ABS_MT_POSITION_X: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; + pointer->absMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; + pointer->absMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; + pointer->absMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; + pointer->absMTTouchMinor = rawEvent->value; + break; + case ABS_MT_WIDTH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; + pointer->absMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; + pointer->absMTWidthMinor = rawEvent->value; + break; + case ABS_MT_ORIENTATION: + pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; + pointer->absMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; + pointer->absMTTrackingId = rawEvent->value; + break; + } + break; + } + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_MT_REPORT: { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + uint32_t pointerIndex = mAccumulator.pointerCount; + + if (mAccumulator.pointers[pointerIndex].fields) { + if (pointerIndex == MAX_POINTERS) { + LOGW("MultiTouch device driver returned more than maximum of %d pointers.", + MAX_POINTERS); + } else { + pointerIndex += 1; + mAccumulator.pointerCount = pointerIndex; + } + } + + mAccumulator.pointers[pointerIndex].clear(); + break; + } + + case SYN_REPORT: + sync(rawEvent->when); + break; + } + break; + } +} + +void MultiTouchInputMapper::sync(nsecs_t when) { + static const uint32_t REQUIRED_FIELDS = + Accumulator::FIELD_ABS_MT_POSITION_X + | Accumulator::FIELD_ABS_MT_POSITION_Y; + + uint32_t inCount = mAccumulator.pointerCount; + uint32_t outCount = 0; + bool havePointerIds = true; + + mCurrentTouch.clear(); + + for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { + const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex]; + uint32_t fields = inPointer.fields; + + if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { + // Some drivers send empty MT sync packets without X / Y to indicate a pointer up. + // Drop this finger. + continue; + } + + PointerData& outPointer = mCurrentTouch.pointers[outCount]; + outPointer.x = inPointer.absMTPositionX; + outPointer.y = inPointer.absMTPositionY; + + if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) { + int32_t value = inPointer.absMTTouchMajor; + if (value <= 0) { + // Some devices send sync packets with X / Y but with a 0 touch major to indicate + // a pointer up. Drop this finger. + continue; + } + outPointer.touchMajor = inPointer.absMTTouchMajor; + } else { + outPointer.touchMajor = 0; + } + + if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) { + outPointer.touchMinor = inPointer.absMTTouchMinor; + } else { + outPointer.touchMinor = outPointer.touchMajor; + } + + if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) { + outPointer.toolMajor = inPointer.absMTWidthMajor; + } else { + outPointer.toolMajor = outPointer.touchMajor; + } + + if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) { + outPointer.toolMinor = inPointer.absMTWidthMinor; + } else { + outPointer.toolMinor = outPointer.toolMajor; + } + + if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) { + outPointer.orientation = inPointer.absMTOrientation; + } else { + outPointer.orientation = 0; + } + + if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { + outPointer.pressure = inPointer.absMTPressure; + } else { + // Derive an approximation of pressure. + // FIXME Traditionally we have just passed a normalized value based on + // ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not + // very meaningful, particularly on large displays. We should probably let + // pressure = touch_major / tool_major but it is unclear whether that will + // break applications. + outPointer.pressure = outPointer.touchMajor; + } + + // Size is an alias for a normalized tool width. + // FIXME Normalized tool width doesn't actually make much sense since it literally + // means the approaching contact major axis is divided by its full range as + // reported by the driver. On a large display this could produce very small values. + outPointer.size = outPointer.toolMajor; + + if (havePointerIds) { + if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { + uint32_t id = uint32_t(inPointer.absMTTrackingId); + + if (id > MAX_POINTER_ID) { +#if DEBUG_POINTERS + LOGD("Pointers: Ignoring driver provided pointer id %d because " + "it is larger than max supported id %d for optimizations", + id, MAX_POINTER_ID); +#endif + havePointerIds = false; + } + else { + outPointer.id = id; + mCurrentTouch.idToIndex[id] = outCount; + mCurrentTouch.idBits.markBit(id); + } + } else { + havePointerIds = false; + } + } + + outCount += 1; + } + + mCurrentTouch.pointerCount = outCount; + + syncTouch(when, havePointerIds); + + mAccumulator.clear(); +} + +void MultiTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); + + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure); + + if (! mAxes.touchMinor.valid) { + mAxes.touchMinor = mAxes.touchMajor; + } + + if (! mAxes.toolMinor.valid) { + mAxes.toolMinor = mAxes.toolMajor; + } + + if (! mAxes.pressure.valid) { + mAxes.pressure = mAxes.touchMajor; + } + + mAxes.size = mAxes.toolMajor; +} + + +} // namespace android diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp new file mode 100644 index 0000000000..cf0f63e247 --- /dev/null +++ b/libs/ui/InputTransport.cpp @@ -0,0 +1,692 @@ +// +// 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 signalling (send signal, receive signal) +#define DEBUG_CHANNEL_SIGNALS 0 + +// Log debug messages whenever InputChannel objects are created/destroyed +#define DEBUG_CHANNEL_LIFECYCLE 0 + +// Log debug messages about transport actions (initialize, reset, publish, ...) +#define DEBUG_TRANSPORT_ACTIONS 0 + + +#include <cutils/ashmem.h> +#include <cutils/log.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <ui/InputTransport.h> +#include <unistd.h> + +namespace android { + +// Must be at least sizeof(InputMessage) + sufficient space for pointer data +static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; + +// Signal sent by the producer to the consumer to inform it that a new message is +// available to be consumed in the shared memory buffer. +static const char INPUT_SIGNAL_DISPATCH = 'D'; + +// Signal sent by the consumer to the producer to inform it that it has finished +// consuming the most recent message. +static const char INPUT_SIGNAL_FINISHED = 'f'; + + +// --- InputChannel --- + +InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, + int32_t sendPipeFd) : + mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) { +#if DEBUG_CHANNEL_LIFECYCLE + LOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", + mName.string(), ashmemFd, receivePipeFd, sendPipeFd); +#endif + + int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe " + "non-blocking. errno=%d", mName.string(), errno); + + result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe " + "non-blocking. errno=%d", mName.string(), errno); +} + +InputChannel::~InputChannel() { +#if DEBUG_CHANNEL_LIFECYCLE + LOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", + mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd); +#endif + + ::close(mAshmemFd); + ::close(mReceivePipeFd); + ::close(mSendPipeFd); +} + +status_t InputChannel::openInputChannelPair(const String8& name, + sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { + status_t result; + + int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE); + if (serverAshmemFd < 0) { + result = -errno; + LOGE("channel '%s' ~ Could not create shared memory region. errno=%d", + name.string(), errno); + } else { + result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE); + if (result < 0) { + LOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.", + name.string(), result, serverAshmemFd); + } else { + // Dup the file descriptor because the server and client input channel objects that + // are returned may have different lifetimes but they share the same shared memory region. + int clientAshmemFd; + clientAshmemFd = dup(serverAshmemFd); + if (clientAshmemFd < 0) { + result = -errno; + LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d", + name.string(), errno); + } else { + int forward[2]; + if (pipe(forward)) { + result = -errno; + LOGE("channel '%s' ~ Could not create forward pipe. errno=%d", + name.string(), errno); + } else { + int reverse[2]; + if (pipe(reverse)) { + result = -errno; + LOGE("channel '%s' ~ Could not create reverse pipe. errno=%d", + name.string(), errno); + } else { + String8 serverChannelName = name; + serverChannelName.append(" (server)"); + outServerChannel = new InputChannel(serverChannelName, + serverAshmemFd, reverse[0], forward[1]); + + String8 clientChannelName = name; + clientChannelName.append(" (client)"); + outClientChannel = new InputChannel(clientChannelName, + clientAshmemFd, forward[0], reverse[1]); + return OK; + } + ::close(forward[0]); + ::close(forward[1]); + } + ::close(clientAshmemFd); + } + } + ::close(serverAshmemFd); + } + + outServerChannel.clear(); + outClientChannel.clear(); + return result; +} + +status_t InputChannel::sendSignal(char signal) { + ssize_t nWrite = ::write(mSendPipeFd, & signal, 1); + + if (nWrite == 1) { +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal); +#endif + return OK; + } + +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno); +#endif + return -errno; +} + +status_t InputChannel::receiveSignal(char* outSignal) { + ssize_t nRead = ::read(mReceivePipeFd, outSignal, 1); + if (nRead == 1) { +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal); +#endif + return OK; + } + + if (nRead == 0) { // check for EOF +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string()); +#endif + return DEAD_OBJECT; + } + + if (errno == EAGAIN) { +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string()); +#endif + return WOULD_BLOCK; + } + +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno); +#endif + return -errno; +} + + +// --- InputPublisher --- + +InputPublisher::InputPublisher(const sp<InputChannel>& channel) : + mChannel(channel), mSharedMessage(NULL), + mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false), + mMotionEventSampleDataTail(NULL) { +} + +InputPublisher::~InputPublisher() { + reset(); + + if (mSharedMessage) { + munmap(mSharedMessage, mAshmemSize); + } +} + +status_t InputPublisher::initialize() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ initialize", + mChannel->getName().string()); +#endif + + int ashmemFd = mChannel->getAshmemFd(); + int result = ashmem_get_size_region(ashmemFd); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.", + mChannel->getName().string(), result, ashmemFd); + return UNKNOWN_ERROR; + } + mAshmemSize = (size_t) result; + + mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); + if (! mSharedMessage) { + LOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.", + mChannel->getName().string(), ashmemFd); + return NO_MEMORY; + } + + mPinned = true; + mSharedMessage->consumed = false; + + return reset(); +} + +status_t InputPublisher::reset() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ reset", + mChannel->getName().string()); +#endif + + if (mPinned) { + // Destroy the semaphore since we are about to unpin the memory region that contains it. + int result; + if (mSemaphoreInitialized) { + if (mSharedMessage->consumed) { + result = sem_post(& mSharedMessage->semaphore); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d in sem_post.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + } + + result = sem_destroy(& mSharedMessage->semaphore); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d in sem_destroy.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + + mSemaphoreInitialized = false; + } + + // Unpin the region since we no longer care about its contents. + int ashmemFd = mChannel->getAshmemFd(); + result = ashmem_unpin_region(ashmemFd, 0, 0); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.", + mChannel->getName().string(), result, ashmemFd); + return UNKNOWN_ERROR; + } + + mPinned = false; + } + + mMotionEventSampleDataTail = NULL; + mWasDispatched = false; + return OK; +} + +status_t InputPublisher::publishInputEvent( + int32_t type, + int32_t deviceId, + int32_t source) { + if (mPinned) { + LOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has " + "not yet been reset.", mChannel->getName().string()); + return INVALID_OPERATION; + } + + // Pin the region. + // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous + // contents of the buffer so it does not matter whether it was purged in the meantime. + int ashmemFd = mChannel->getAshmemFd(); + int result = ashmem_pin_region(ashmemFd, 0, 0); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.", + mChannel->getName().string(), result, ashmemFd); + return UNKNOWN_ERROR; + } + + mPinned = true; + + result = sem_init(& mSharedMessage->semaphore, 1, 1); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d in sem_init.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + + mSemaphoreInitialized = true; + + mSharedMessage->consumed = false; + mSharedMessage->type = type; + mSharedMessage->deviceId = deviceId; + mSharedMessage->source = source; + return OK; +} + +status_t InputPublisher::publishKeyEvent( + 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 + LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=%d, " + "action=%d, flags=%d, keyCode=%d, scanCode=%d, metaState=%d, repeatCount=%d," + "downTime=%lld, eventTime=%lld", + mChannel->getName().string(), + deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, + downTime, eventTime); +#endif + + status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source); + if (result < 0) { + return result; + } + + mSharedMessage->key.action = action; + mSharedMessage->key.flags = flags; + mSharedMessage->key.keyCode = keyCode; + mSharedMessage->key.scanCode = scanCode; + mSharedMessage->key.metaState = metaState; + mSharedMessage->key.repeatCount = repeatCount; + mSharedMessage->key.downTime = downTime; + mSharedMessage->key.eventTime = eventTime; + return OK; +} + +status_t InputPublisher::publishMotionEvent( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t edgeFlags, + int32_t metaState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const int32_t* pointerIds, + const PointerCoords* pointerCoords) { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=%d, " + "action=%d, edgeFlags=%d, metaState=%d, xOffset=%f, yOffset=%f, " + "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " + "pointerCount=%d", + mChannel->getName().string(), + deviceId, source, action, edgeFlags, metaState, xOffset, yOffset, + xPrecision, yPrecision, downTime, eventTime, pointerCount); +#endif + + if (pointerCount > MAX_POINTERS || pointerCount < 1) { + LOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", + mChannel->getName().string(), pointerCount); + return BAD_VALUE; + } + + status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source); + if (result < 0) { + return result; + } + + mSharedMessage->motion.action = action; + mSharedMessage->motion.edgeFlags = edgeFlags; + mSharedMessage->motion.metaState = metaState; + mSharedMessage->motion.xOffset = xOffset; + mSharedMessage->motion.yOffset = yOffset; + mSharedMessage->motion.xPrecision = xPrecision; + mSharedMessage->motion.yPrecision = yPrecision; + mSharedMessage->motion.downTime = downTime; + mSharedMessage->motion.pointerCount = pointerCount; + + mSharedMessage->motion.sampleCount = 1; + mSharedMessage->motion.sampleData[0].eventTime = eventTime; + + for (size_t i = 0; i < pointerCount; i++) { + mSharedMessage->motion.pointerIds[i] = pointerIds[i]; + mSharedMessage->motion.sampleData[0].coords[i] = pointerCoords[i]; + } + + // Cache essential information about the motion event to ensure that a malicious consumer + // cannot confuse the publisher by modifying the contents of the shared memory buffer while + // it is being updated. + if (action == AMOTION_EVENT_ACTION_MOVE) { + mMotionEventPointerCount = pointerCount; + mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount); + mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement( + mSharedMessage->motion.sampleData, mMotionEventSampleDataStride); + } else { + mMotionEventSampleDataTail = NULL; + } + return OK; +} + +status_t InputPublisher::appendMotionSample( + nsecs_t eventTime, + const PointerCoords* pointerCoords) { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld", + mChannel->getName().string(), eventTime); +#endif + + if (! mPinned || ! mMotionEventSampleDataTail) { + LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current " + "AMOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string()); + return INVALID_OPERATION; + } + + InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement( + mMotionEventSampleDataTail, mMotionEventSampleDataStride); + size_t newBytesUsed = reinterpret_cast<char*>(newTail) - + reinterpret_cast<char*>(mSharedMessage); + + if (newBytesUsed > mAshmemSize) { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory " + "buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d", + mChannel->getName().string(), + mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount); +#endif + return NO_MEMORY; + } + + int result; + if (mWasDispatched) { + result = sem_trywait(& mSharedMessage->semaphore); + if (result < 0) { + if (errno == EAGAIN) { + // Only possible source of contention is the consumer having consumed (or being in the + // process of consuming) the message and left the semaphore count at 0. +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has " + "already been consumed.", mChannel->getName().string()); +#endif + return FAILED_TRANSACTION; + } else { + LOGE("channel '%s' publisher ~ Error %d in sem_trywait.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + } + } + + mMotionEventSampleDataTail->eventTime = eventTime; + for (size_t i = 0; i < mMotionEventPointerCount; i++) { + mMotionEventSampleDataTail->coords[i] = pointerCoords[i]; + } + mMotionEventSampleDataTail = newTail; + + mSharedMessage->motion.sampleCount += 1; + + if (mWasDispatched) { + result = sem_post(& mSharedMessage->semaphore); + if (result < 0) { + LOGE("channel '%s' publisher ~ Error %d in sem_post.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + } + return OK; +} + +status_t InputPublisher::sendDispatchSignal() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ sendDispatchSignal", + mChannel->getName().string()); +#endif + + mWasDispatched = true; + return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH); +} + +status_t InputPublisher::receiveFinishedSignal() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' publisher ~ receiveFinishedSignal", + mChannel->getName().string()); +#endif + + char signal; + status_t result = mChannel->receiveSignal(& signal); + if (result) { + return result; + } + if (signal != INPUT_SIGNAL_FINISHED) { + LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer", + mChannel->getName().string(), signal); + return UNKNOWN_ERROR; + } + return OK; +} + +// --- InputConsumer --- + +InputConsumer::InputConsumer(const sp<InputChannel>& channel) : + mChannel(channel), mSharedMessage(NULL) { +} + +InputConsumer::~InputConsumer() { + if (mSharedMessage) { + munmap(mSharedMessage, mAshmemSize); + } +} + +status_t InputConsumer::initialize() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' consumer ~ initialize", + mChannel->getName().string()); +#endif + + int ashmemFd = mChannel->getAshmemFd(); + int result = ashmem_get_size_region(ashmemFd); + if (result < 0) { + LOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.", + mChannel->getName().string(), result, ashmemFd); + return UNKNOWN_ERROR; + } + + mAshmemSize = (size_t) result; + + mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); + if (! mSharedMessage) { + LOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.", + mChannel->getName().string(), ashmemFd); + return NO_MEMORY; + } + + return OK; +} + +status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' consumer ~ consume", + mChannel->getName().string()); +#endif + + *outEvent = NULL; + + int ashmemFd = mChannel->getAshmemFd(); + int result = ashmem_pin_region(ashmemFd, 0, 0); + if (result != ASHMEM_NOT_PURGED) { + if (result == ASHMEM_WAS_PURGED) { + LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged " + "which probably indicates that the publisher and consumer are out of sync.", + mChannel->getName().string(), result, ashmemFd); + return INVALID_OPERATION; + } + + LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.", + mChannel->getName().string(), result, ashmemFd); + return UNKNOWN_ERROR; + } + + if (mSharedMessage->consumed) { + LOGE("channel '%s' consumer ~ The current message has already been consumed.", + mChannel->getName().string()); + return INVALID_OPERATION; + } + + // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal + // to the publisher that the message has been consumed (or is in the process of being + // consumed). Eventually the publisher will reinitialize the semaphore for the next message. + result = sem_wait(& mSharedMessage->semaphore); + if (result < 0) { + LOGE("channel '%s' consumer ~ Error %d in sem_wait.", + mChannel->getName().string(), errno); + return UNKNOWN_ERROR; + } + + mSharedMessage->consumed = true; + + switch (mSharedMessage->type) { + case AINPUT_EVENT_TYPE_KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (! keyEvent) return NO_MEMORY; + + populateKeyEvent(keyEvent); + + *outEvent = keyEvent; + break; + } + + case AINPUT_EVENT_TYPE_MOTION: { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + populateMotionEvent(motionEvent); + + *outEvent = motionEvent; + break; + } + + default: + LOGE("channel '%s' consumer ~ Received message of unknown type %d", + mChannel->getName().string(), mSharedMessage->type); + return UNKNOWN_ERROR; + } + + return OK; +} + +status_t InputConsumer::sendFinishedSignal() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' consumer ~ sendFinishedSignal", + mChannel->getName().string()); +#endif + + return mChannel->sendSignal(INPUT_SIGNAL_FINISHED); +} + +status_t InputConsumer::receiveDispatchSignal() { +#if DEBUG_TRANSPORT_ACTIONS + LOGD("channel '%s' consumer ~ receiveDispatchSignal", + mChannel->getName().string()); +#endif + + char signal; + status_t result = mChannel->receiveSignal(& signal); + if (result) { + return result; + } + if (signal != INPUT_SIGNAL_DISPATCH) { + LOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher", + mChannel->getName().string(), signal); + return UNKNOWN_ERROR; + } + return OK; +} + +void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const { + keyEvent->initialize( + mSharedMessage->deviceId, + mSharedMessage->source, + mSharedMessage->key.action, + mSharedMessage->key.flags, + mSharedMessage->key.keyCode, + mSharedMessage->key.scanCode, + mSharedMessage->key.metaState, + mSharedMessage->key.repeatCount, + mSharedMessage->key.downTime, + mSharedMessage->key.eventTime); +} + +void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { + motionEvent->initialize( + mSharedMessage->deviceId, + mSharedMessage->source, + mSharedMessage->motion.action, + mSharedMessage->motion.edgeFlags, + mSharedMessage->motion.metaState, + mSharedMessage->motion.xOffset, + mSharedMessage->motion.yOffset, + mSharedMessage->motion.xPrecision, + mSharedMessage->motion.yPrecision, + mSharedMessage->motion.downTime, + mSharedMessage->motion.sampleData[0].eventTime, + mSharedMessage->motion.pointerCount, + mSharedMessage->motion.pointerIds, + mSharedMessage->motion.sampleData[0].coords); + + size_t sampleCount = mSharedMessage->motion.sampleCount; + if (sampleCount > 1) { + InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData; + size_t sampleDataStride = InputMessage::sampleDataStride( + mSharedMessage->motion.pointerCount); + + while (--sampleCount > 0) { + sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride); + motionEvent->addSample(sampleData->eventTime, sampleData->coords); + } + } +} + +} // namespace android diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index 9b41804eaf..ee186c84de 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -59,19 +59,11 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) // YUV format from the HAL are handled here switch (format) { case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCrCb_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_CbYCrY_422_I: info->bitsPerPixel = 16; goto done; - case HAL_PIXEL_FORMAT_YCbCr_420_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: - case HAL_PIXEL_FORMAT_YCrCb_420_SP_TILED: - case HAL_PIXEL_FORMAT_YCbCr_420_P: - case HAL_PIXEL_FORMAT_YCbCr_420_I: - case HAL_PIXEL_FORMAT_CbYCrY_420_I: + case HAL_PIXEL_FORMAT_YV12: info->bitsPerPixel = 12; done: info->format = format; diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index 66b95762d0..5694e00002 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -18,11 +18,11 @@ namespace android { -static inline int min(int a, int b) { +static inline int32_t min(int32_t a, int32_t b) { return (a<b) ? a : b; } -static inline int max(int a, int b) { +static inline int32_t max(int32_t a, int32_t b) { return (a>b) ? a : b; } @@ -53,7 +53,7 @@ bool Rect::operator < (const Rect& rhs) const return false; } -Rect& Rect::offsetTo(int x, int y) +Rect& Rect::offsetTo(int32_t x, int32_t y) { right -= left - x; bottom -= top - y; @@ -62,7 +62,7 @@ Rect& Rect::offsetTo(int x, int y) return *this; } -Rect& Rect::offsetBy(int x, int y) +Rect& Rect::offsetBy(int32_t x, int32_t y) { left += x; top += y; diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 6cc4a5ab63..62f824fc72 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -1,16 +1,50 @@ +# Build the unit tests. LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ - region.cpp +ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES := \ +# Build the unit tests. +test_src_files := \ + InputChannel_test.cpp \ + InputDispatcher_test.cpp \ + InputPublisherAndConsumer_test.cpp + +shared_libraries := \ libcutils \ libutils \ - libui + libEGL \ + libbinder \ + libpixelflinger \ + libhardware \ + libhardware_legacy \ + libui \ + libstlport + +static_libraries := \ + libgtest \ + libgtest_main + +c_includes := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport + +module_tags := eng tests -LOCAL_MODULE:= test-region +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) -LOCAL_MODULE_TAGS := tests +# Build the manual test programs. +include $(call all-subdir-makefiles) -include $(BUILD_EXECUTABLE) +endif
\ No newline at end of file diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp new file mode 100644 index 0000000000..6cec1c02ea --- /dev/null +++ b/libs/ui/tests/InputChannel_test.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <ui/InputTransport.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <gtest/gtest.h> +#include <unistd.h> +#include <time.h> +#include <sys/mman.h> +#include <cutils/ashmem.h> + +#include "../../utils/tests/TestHelpers.h" + +namespace android { + +class InputChannelTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + + +TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { + // Our purpose here is to verify that the input channel destructor closes the + // file descriptors provided to it. One easy way is to provide it with one end + // of a pipe and to check for EPIPE on the other end after the channel is destroyed. + Pipe fakeAshmem, sendPipe, receivePipe; + + sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), + fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd); + + EXPECT_STREQ("channel name", inputChannel->getName().string()) + << "channel should have provided name"; + EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd()) + << "channel should have provided ashmem fd"; + EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd()) + << "channel should have provided receive pipe fd"; + EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd()) + << "channel should have provided send pipe fd"; + + inputChannel.clear(); // destroys input channel + + EXPECT_EQ(-EPIPE, fakeAshmem.readSignal()) + << "channel should have closed ashmem fd when destroyed"; + EXPECT_EQ(-EPIPE, receivePipe.writeSignal()) + << "channel should have closed receive pipe fd when destroyed"; + EXPECT_EQ(-EPIPE, sendPipe.readSignal()) + << "channel should have closed send pipe fd when destroyed"; + + // clean up fds of Pipe endpoints that were closed so we don't try to close them again + fakeAshmem.sendFd = -1; + receivePipe.receiveFd = -1; + sendPipe.sendFd = -1; +} + +TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + // Name + EXPECT_STREQ("channel name (server)", serverChannel->getName().string()) + << "server channel should have suffixed name"; + EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) + << "client channel should have suffixed name"; + + // Ashmem uniqueness + EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd()) + << "server and client channel should have different ashmem fds because it was dup'd"; + + // Ashmem usability + ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd()); + ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd()); + uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0)); + uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0)); + ASSERT_TRUE(serverAshmem != NULL) + << "server channel ashmem should be mappable"; + ASSERT_TRUE(clientAshmem != NULL) + << "client channel ashmem should be mappable"; + *serverAshmem = 0xf00dd00d; + EXPECT_EQ(0xf00dd00d, *clientAshmem) + << "ashmem buffer should be shared by client and server"; + munmap(serverAshmem, serverAshmemSize); + munmap(clientAshmem, clientAshmemSize); + + // Server->Client communication + EXPECT_EQ(OK, serverChannel->sendSignal('S')) + << "server channel should be able to send signal to client channel"; + char signal; + EXPECT_EQ(OK, clientChannel->receiveSignal(& signal)) + << "client channel should be able to receive signal from server channel"; + EXPECT_EQ('S', signal) + << "client channel should receive the correct signal from server channel"; + + // Client->Server communication + EXPECT_EQ(OK, clientChannel->sendSignal('c')) + << "client channel should be able to send signal to server channel"; + EXPECT_EQ(OK, serverChannel->receiveSignal(& signal)) + << "server channel should be able to receive signal from client channel"; + EXPECT_EQ('c', signal) + << "server channel should receive the correct signal from client channel"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + char signal; + EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal)) + << "receiveSignal should have returned WOULD_BLOCK"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + char signal; + EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal)) + << "receiveSignal should have returned DEAD_OBJECT"; +} + +TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S')) + << "sendSignal should have returned DEAD_OBJECT"; +} + + +} // namespace android diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp new file mode 100644 index 0000000000..1dc6e46a45 --- /dev/null +++ b/libs/ui/tests/InputDispatcher_test.cpp @@ -0,0 +1,18 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <ui/InputDispatcher.h> +#include <gtest/gtest.h> + +namespace android { + +class InputDispatcherTest : public testing::Test { +public: +}; + +TEST_F(InputDispatcherTest, Dummy) { + // TODO +} + +} // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp new file mode 100644 index 0000000000..3bc21fa296 --- /dev/null +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -0,0 +1,469 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <ui/InputTransport.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <gtest/gtest.h> +#include <unistd.h> +#include <time.h> +#include <sys/mman.h> +#include <cutils/ashmem.h> + +#include "../../utils/tests/TestHelpers.h" + +namespace android { + +class InputPublisherAndConsumerTest : public testing::Test { +protected: + sp<InputChannel> serverChannel, clientChannel; + InputPublisher* mPublisher; + InputConsumer* mConsumer; + PreallocatedInputEventFactory mEventFactory; + + virtual void SetUp() { + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + mPublisher = new InputPublisher(serverChannel); + mConsumer = new InputConsumer(clientChannel); + } + + virtual void TearDown() { + if (mPublisher) { + delete mPublisher; + mPublisher = NULL; + } + + if (mConsumer) { + delete mConsumer; + mConsumer = NULL; + } + + serverChannel.clear(); + clientChannel.clear(); + } + + void Initialize(); + void PublishAndConsumeKeyEvent(); + void PublishAndConsumeMotionEvent( + size_t samplesToAppendBeforeDispatch = 0, + size_t samplesToAppendAfterDispatch = 0); +}; + +TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { + EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); + EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); +} + +void InputPublisherAndConsumerTest::Initialize() { + status_t status; + + status = mPublisher->initialize(); + ASSERT_EQ(OK, status) + << "publisher initialize should return OK"; + + status = mConsumer->initialize(); + ASSERT_EQ(OK, status) + << "consumer initialize should return OK"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { + status_t status; + + const int32_t deviceId = 1; + const int32_t source = AINPUT_SOURCE_KEYBOARD; + const int32_t action = AKEY_EVENT_ACTION_DOWN; + const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + const int32_t keyCode = AKEYCODE_ENTER; + const int32_t scanCode = 13; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t repeatCount = 1; + const nsecs_t downTime = 3; + const nsecs_t eventTime = 4; + + status = mPublisher->publishKeyEvent(deviceId, source, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + ASSERT_EQ(OK, status) + << "publisher publishKeyEvent should return OK"; + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status) + << "publisher sendDispatchSignal should return OK"; + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status) + << "consumer receiveDispatchSignal should return OK"; + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) + << "consumer should have returned a key event"; + + KeyEvent* keyEvent = static_cast<KeyEvent*>(event); + EXPECT_EQ(deviceId, keyEvent->getDeviceId()); + EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(action, keyEvent->getAction()); + EXPECT_EQ(flags, keyEvent->getFlags()); + EXPECT_EQ(keyCode, keyEvent->getKeyCode()); + EXPECT_EQ(scanCode, keyEvent->getScanCode()); + EXPECT_EQ(metaState, keyEvent->getMetaState()); + EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); + EXPECT_EQ(downTime, keyEvent->getDownTime()); + EXPECT_EQ(eventTime, keyEvent->getEventTime()); + + status = mConsumer->sendFinishedSignal(); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + status = mPublisher->receiveFinishedSignal(); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + + status = mPublisher->reset(); + ASSERT_EQ(OK, status) + << "publisher reset should return OK"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( + size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) { + status_t status; + + const int32_t deviceId = 1; + const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t action = AMOTION_EVENT_ACTION_MOVE; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const float xOffset = -10; + const float yOffset = -20; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const nsecs_t downTime = 3; + const size_t pointerCount = 3; + const int32_t pointerIds[pointerCount] = { 2, 0, 1 }; + + Vector<nsecs_t> sampleEventTimes; + Vector<PointerCoords> samplePointerCoords; + + for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) { + sampleEventTimes.push(i + 10); + for (size_t j = 0; j < pointerCount; j++) { + samplePointerCoords.push(); + samplePointerCoords.editTop().x = 100 * i + j; + samplePointerCoords.editTop().y = 200 * i + j; + samplePointerCoords.editTop().pressure = 0.5 * i + j; + samplePointerCoords.editTop().size = 0.7 * i + j; + samplePointerCoords.editTop().touchMajor = 1.5 * i + j; + samplePointerCoords.editTop().touchMinor = 1.7 * i + j; + samplePointerCoords.editTop().toolMajor = 2.5 * i + j; + samplePointerCoords.editTop().toolMinor = 2.7 * i + j; + samplePointerCoords.editTop().orientation = 3.5 * i + j; + } + } + + status = mPublisher->publishMotionEvent(deviceId, source, action, edgeFlags, + metaState, xOffset, yOffset, xPrecision, yPrecision, + downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array()); + ASSERT_EQ(OK, status) + << "publisher publishMotionEvent should return OK"; + + for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) { + size_t sampleIndex = i + 1; + status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], + samplePointerCoords.array() + sampleIndex * pointerCount); + ASSERT_EQ(OK, status) + << "publisher appendMotionEvent should return OK"; + } + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status) + << "publisher sendDispatchSignal should return OK"; + + for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) { + size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch; + status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], + samplePointerCoords.array() + sampleIndex * pointerCount); + ASSERT_EQ(OK, status) + << "publisher appendMotionEvent should return OK"; + } + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status) + << "consumer receiveDispatchSignal should return OK"; + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) + << "consumer should have returned a motion event"; + + size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch; + + MotionEvent* motionEvent = static_cast<MotionEvent*>(event); + EXPECT_EQ(deviceId, motionEvent->getDeviceId()); + EXPECT_EQ(source, motionEvent->getSource()); + EXPECT_EQ(action, motionEvent->getAction()); + EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); + EXPECT_EQ(metaState, motionEvent->getMetaState()); + EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); + EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(downTime, motionEvent->getDownTime()); + EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); + EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); + EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize()); + + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(i)); + } + + for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) { + SCOPED_TRACE(sampleIndex); + EXPECT_EQ(sampleEventTimes[sampleIndex], + motionEvent->getHistoricalEventTime(sampleIndex)); + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + size_t offset = sampleIndex * pointerCount + i; + EXPECT_EQ(samplePointerCoords[offset].x, + motionEvent->getHistoricalRawX(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].y, + motionEvent->getHistoricalRawY(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].x + xOffset, + motionEvent->getHistoricalX(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].y + yOffset, + motionEvent->getHistoricalY(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].pressure, + motionEvent->getHistoricalPressure(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].size, + motionEvent->getHistoricalSize(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].touchMajor, + motionEvent->getHistoricalTouchMajor(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].touchMinor, + motionEvent->getHistoricalTouchMinor(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].toolMajor, + motionEvent->getHistoricalToolMajor(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].toolMinor, + motionEvent->getHistoricalToolMinor(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].orientation, + motionEvent->getHistoricalOrientation(i, sampleIndex)); + } + } + + SCOPED_TRACE(lastSampleIndex); + EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + size_t offset = lastSampleIndex * pointerCount + i; + EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i)); + EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i)); + EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i)); + EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i)); + EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i)); + EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i)); + EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i)); + } + + status = mConsumer->sendFinishedSignal(); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + status = mPublisher->receiveFinishedSignal(); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + + status = mPublisher->reset(); + ASSERT_EQ(OK, status) + << "publisher reset should return OK"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ASSERT_EQ(OK, status) + << "publisher publishKeyEvent should return OK first time"; + + status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher publishKeyEvent should return INVALID_OPERATION because " + "the publisher was not reset"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = 1; + int32_t pointerIds[pointerCount] = { 0 }; + PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status) + << "publisher publishMotionEvent should return OK"; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher publishMotionEvent should return INVALID_OPERATION because "; + "the publisher was not reset"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = 0; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS + 1; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0)); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4)); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + PointerCoords pointerCoords[1]; + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher appendMotionSample should return INVALID_OPERATION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher appendMotionSample should return INVALID_OPERATION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status); + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status); + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status); + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(status_t(FAILED_TRANSACTION), status) + << "publisher appendMotionSample should return FAILED_TRANSACTION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + for (int count = 1;; count++) { + ASSERT_LT(count, 100000) << "should eventually reach OOM"; + + status = mPublisher->appendMotionSample(0, pointerCoords); + if (status != OK) { + ASSERT_GT(count, 12) << "should be able to add at least a dozen samples"; + ASSERT_EQ(NO_MEMORY, status) + << "publisher appendMotionSample should return NO_MEMORY when buffer is full"; + break; + } + } + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(NO_MEMORY, status) + << "publisher appendMotionSample should return NO_MEMORY persistently until reset"; +} + +} // namespace android diff --git a/libs/ui/tests/region/Android.mk b/libs/ui/tests/region/Android.mk new file mode 100644 index 0000000000..6cc4a5ab63 --- /dev/null +++ b/libs/ui/tests/region/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + region.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-region + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region/region.cpp index ef15de9747..ef15de9747 100644 --- a/libs/ui/tests/region.cpp +++ b/libs/ui/tests/region/region.cpp diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index afecdcb0ce..2e20268b3e 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,11 +26,14 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + ObbFile.cpp \ + Pool.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ Static.cpp \ StopWatch.cpp \ + StreamingZipInflater.cpp \ String8.cpp \ String16.cpp \ StringArray.cpp \ @@ -39,7 +42,7 @@ commonSources:= \ Threads.cpp \ Timers.cpp \ VectorImpl.cpp \ - ZipFileCRO.cpp \ + ZipFileCRO.cpp \ ZipFileRO.cpp \ ZipUtils.cpp \ misc.cpp @@ -64,6 +67,11 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif +ifeq ($(HOST_OS),darwin) +# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. +LOCAL_CFLAGS += -DOFF_T_IS_64_BIT +endif + include $(BUILD_HOST_STATIC_LIBRARY) @@ -76,8 +84,9 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp + BackupData.cpp \ + BackupHelpers.cpp \ + PollLoop.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl @@ -114,3 +123,13 @@ LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp include $(BUILD_STATIC_LIBRARY) endif endif + + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 42951237d6..cef7db492e 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -24,6 +24,7 @@ #include <utils/Asset.h> #include <utils/Atomic.h> #include <utils/FileMap.h> +#include <utils/StreamingZipInflater.h> #include <utils/ZipUtils.h> #include <utils/ZipFileRO.h> #include <utils/Log.h> @@ -659,7 +660,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map) */ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mBuf(NULL) + mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) { } @@ -698,6 +699,10 @@ status_t _CompressedAsset::openChunk(int fd, off_t offset, mFd = fd; assert(mBuf == NULL); + if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { + mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen); + } + return NO_ERROR; } @@ -724,6 +729,9 @@ status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, mUncompressedLen = uncompressedLen; assert(mOffset == 0); + if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { + mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); + } return NO_ERROR; } @@ -739,26 +747,29 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) assert(mOffset >= 0 && mOffset <= mUncompressedLen); - // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + /* If we're relying on a streaming inflater, go through that */ + if (mZipInflater) { + actual = mZipInflater->read(buf, count); + } else { + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); - if (mBuf == NULL) { - if (getBuffer(false) == NULL) - return -1; - } - assert(mBuf != NULL); + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; - /* adjust count if we're near EOF */ - maxLen = mUncompressedLen - mOffset; - if (count > maxLen) - count = maxLen; + if (!count) + return 0; - if (!count) - return 0; - - /* copy from buffer */ - //printf("comp buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + } mOffset += actual; return actual; @@ -780,6 +791,9 @@ off_t _CompressedAsset::seek(off_t offset, int whence) if (newPosn == (off_t) -1) return newPosn; + if (mZipInflater) { + mZipInflater->seekAbsolute(newPosn); + } mOffset = newPosn; return mOffset; } @@ -793,10 +807,12 @@ void _CompressedAsset::close(void) mMap->release(); mMap = NULL; } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } + + delete[] mBuf; + mBuf = NULL; + + delete mZipInflater; + mZipInflater = NULL; if (mFd > 0) { ::close(mFd); @@ -817,12 +833,6 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) if (mBuf != NULL) return mBuf; - if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { - LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", - (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); - goto bail; - } - /* * Allocate a buffer and read the file into it. */ @@ -853,7 +863,13 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) goto bail; } - /* success! */ + /* + * Success - now that we have the full asset in RAM we + * no longer need the streaming inflater + */ + delete mZipInflater; + mZipInflater = NULL; + mBuf = buf; buf = NULL; diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 5a05e6a61f..e09e755861 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -232,6 +232,12 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l } } +void AssetManager::getConfiguration(ResTable_config* outConfig) const +{ + AutoMutex _l(mLock); + *outConfig = *mConfig; +} + /* * Open an asset. * @@ -824,7 +830,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, // TODO: look for previously-created shared memory slice? int method; - long uncompressedLen; + size_t uncompressedLen; //printf("USING Zip '%s'\n", pEntry->getFileName()); diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp new file mode 100644 index 0000000000..e170ab88c1 --- /dev/null +++ b/libs/utils/ObbFile.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define LOG_TAG "ObbFile" +#include <utils/Log.h> +#include <utils/ObbFile.h> + +//#define DEBUG 1 + +#define kFooterTagSize 8 /* last two 32-bit integers */ + +#define kFooterMinSize 25 /* 32-bit signature version (4 bytes) + * 32-bit package version (4 bytes) + * 32-bit flags (4 bytes) + * 32-bit package name size (4-bytes) + * >=1-character package name (1 byte) + * 32-bit footer size (4 bytes) + * 32-bit footer marker (4 bytes) + */ + +#define kMaxBufSize 32768 /* Maximum file read buffer */ + +#define kSignature 0x01059983U /* ObbFile signature */ + +#define kSigVersion 1 /* We only know about signature version 1 */ + +/* offsets in version 1 of the header */ +#define kPackageVersionOffset 4 +#define kFlagsOffset 8 +#define kPackageNameLenOffset 12 +#define kPackageNameOffset 16 + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +/* + * Work around situations where off_t is 64-bit and use off64_t in + * situations where it's 32-bit. + */ +#ifdef OFF_T_IS_64_BIT +#define my_lseek64 lseek +typedef off_t my_off64_t; +#else +#define my_lseek64 lseek64 +typedef off64_t my_off64_t; +#endif + +namespace android { + +ObbFile::ObbFile() : + mPackageName(""), + mVersion(-1), + mFlags(0) +{ +} + +ObbFile::~ObbFile() { +} + +bool ObbFile::readFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDONLY); + if (fd < 0) { + LOGW("couldn't open file %s: %s", filename, strerror(errno)); + goto out; + } + success = readFrom(fd); + close(fd); + + if (!success) { + LOGW("failed to read from %s (fd=%d)\n", filename, fd); + } + +out: + return success; +} + +bool ObbFile::readFrom(int fd) +{ + if (fd < 0) { + LOGW("attempt to read from invalid fd\n"); + return false; + } + + return parseObbFile(fd); +} + +bool ObbFile::parseObbFile(int fd) +{ + my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + + if (fileLength < kFooterMinSize) { + if (fileLength < 0) { + LOGW("error seeking in ObbFile: %s\n", strerror(errno)); + } else { + LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); + } + return false; + } + + ssize_t actual; + size_t footerSize; + + { + my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + + char *footer = new char[kFooterTagSize]; + actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); + if (actual != kFooterTagSize) { + LOGW("couldn't read footer signature: %s\n", strerror(errno)); + return false; + } + + unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); + if (fileSig != kSignature) { + LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", + kSignature, fileSig); + return false; + } + + footerSize = get4LE((unsigned char*)footer); + if (footerSize > (size_t)fileLength - kFooterTagSize + || footerSize > kMaxBufSize) { + LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", + footerSize, fileLength); + return false; + } + + if (footerSize < (kFooterMinSize - kFooterTagSize)) { + LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", + footerSize, kFooterMinSize - kFooterTagSize); + return false; + } + } + + my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); + return false; + } + + mFooterStart = fileOffset; + + char* scanBuf = (char*)malloc(footerSize); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); + return false; + } + + actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); + // readAmount is guaranteed to be less than kMaxBufSize + if (actual != (ssize_t)footerSize) { + LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); + free(scanBuf); + return false; + } + +#ifdef DEBUG + for (int i = 0; i < footerSize; ++i) { + LOGI("char: 0x%02x", scanBuf[i]); + } +#endif + + uint32_t sigVersion = get4LE((unsigned char*)scanBuf); + if (sigVersion != kSigVersion) { + LOGW("Unsupported ObbFile version %d\n", sigVersion); + free(scanBuf); + return false; + } + + mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); + mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); + + uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen <= 0 + || packageNameLen > (footerSize - kPackageNameOffset)) { + LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + packageNameLen, footerSize - kPackageNameOffset); + free(scanBuf); + return false; + } + + char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); + mPackageName = String8(const_cast<char*>(packageName), packageNameLen); + + free(scanBuf); + +#ifdef DEBUG + LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); +#endif + + return true; +} + +bool ObbFile::writeTo(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_WRONLY); + if (fd < 0) { + goto out; + } + success = writeTo(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to write to %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::writeTo(int fd) +{ + if (fd < 0) { + return false; + } + + my_lseek64(fd, 0, SEEK_END); + + if (mPackageName.size() == 0 || mVersion == -1) { + LOGW("tried to write uninitialized ObbFile data"); + return false; + } + + unsigned char intBuf[sizeof(uint32_t)+1]; + memset(&intBuf, 0, sizeof(intBuf)); + + put4LE(intBuf, kSigVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write signature version: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, mVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package version"); + return false; + } + + put4LE(intBuf, mFlags); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package version"); + return false; + } + + size_t packageNameLen = mPackageName.size(); + put4LE(intBuf, packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package name length: %s", strerror(errno)); + return false; + } + + if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { + LOGW("couldn't write package name: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, kPackageNameOffset + packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer size: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, kSignature); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer magic signature: %s", strerror(errno)); + return false; + } + + return true; +} + +bool ObbFile::removeFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDWR); + if (fd < 0) { + goto out; + } + success = removeFrom(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::removeFrom(int fd) +{ + if (fd < 0) { + return false; + } + + if (!readFrom(fd)) { + return false; + } + + ftruncate(fd, mFooterStart); + + return true; +} + +} diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp new file mode 100644 index 0000000000..f740fa0d52 --- /dev/null +++ b/libs/utils/PollLoop.cpp @@ -0,0 +1,364 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A select loop implementation. +// +#define LOG_TAG "PollLoop" + +//#define LOG_NDEBUG 0 + +// Debugs poll and wake interactions. +#define DEBUG_POLL_AND_WAKE 0 + +// Debugs callback registration and invocation. +#define DEBUG_CALLBACKS 0 + +#include <cutils/log.h> +#include <utils/PollLoop.h> + +#include <unistd.h> +#include <fcntl.h> + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +PollLoop::PollLoop(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), mPolling(false), + mWaiters(0), mPendingFdsPos(0) { + openWakePipe(); +} + +PollLoop::~PollLoop() { + closeWakePipe(); +} + +void PollLoop::threadDestructor(void *st) { + PollLoop* const self = static_cast<PollLoop*>(st); + if (self != NULL) { + self->decStrong((void*)threadDestructor); + } +} + +void PollLoop::setForThread(const sp<PollLoop>& pollLoop) { + sp<PollLoop> old = getForThread(); + + if (pollLoop != NULL) { + pollLoop->incStrong((void*)threadDestructor); + } + + pthread_setspecific(gTLS, pollLoop.get()); + + if (old != NULL) { + old->decStrong((void*)threadDestructor); + } +} + +sp<PollLoop> PollLoop::getForThread() { + if (!gHaveTLS) { + pthread_mutex_lock(&gTLSMutex); + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + pthread_mutex_unlock(&gTLSMutex); + } + + return (PollLoop*)pthread_getspecific(gTLS); +} + +void PollLoop::openWakePipe() { + int wakeFds[2]; + int result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + // Add the wake pipe to the head of the request list with a null callback. + struct pollfd requestedFd; + requestedFd.fd = mWakeReadPipeFd; + requestedFd.events = POLLIN; + mRequestedFds.insertAt(requestedFd, 0); + + RequestedCallback requestedCallback; + requestedCallback.callback = NULL; + requestedCallback.looperCallback = NULL; + requestedCallback.data = NULL; + mRequestedCallbacks.insertAt(requestedCallback, 0); +} + +void PollLoop::closeWakePipe() { + close(mWakeReadPipeFd); + close(mWakeWritePipeFd); + + // Note: We don't need to remove the poll structure or callback entry because this + // method is currently only called by the destructor. +} + +int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { + // If there are still pending fds from the last call, dispatch those + // first, to avoid an earlier fd from starving later ones. + const size_t pendingFdsCount = mPendingFds.size(); + if (mPendingFdsPos < pendingFdsCount) { + const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); + mPendingFdsPos++; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + return pending.fd; + } + + mLock.lock(); + while (mWaiters != 0) { + mResume.wait(mLock); + } + mPolling = true; + mLock.unlock(); + + int32_t result; + size_t requestedCount = mRequestedFds.size(); + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount); + for (size_t i = 0; i < requestedCount; i++) { + LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events); + } +#endif + + int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); + + if (respondedCount == 0) { + // Timeout +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - timeout", this); +#endif + result = POLL_TIMEOUT; + goto Done; + } + + if (respondedCount < 0) { + // Error +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - error, errno=%d", this, errno); +#endif + if (errno != EINTR) { + LOGW("Poll failed with an unexpected error, errno=%d", errno); + } + result = POLL_ERROR; + goto Done; + } + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount); + for (size_t i = 0; i < requestedCount; i++) { + LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events, + mRequestedFds[i].revents); + } +#endif + + mPendingCallbacks.clear(); + mPendingFds.clear(); + mPendingFdsPos = 0; + if (outEvents != NULL) *outEvents = 0; + if (outData != NULL) *outData = NULL; + + result = POLL_CALLBACK; + for (size_t i = 0; i < requestedCount; i++) { + const struct pollfd& requestedFd = mRequestedFds.itemAt(i); + + short revents = requestedFd.revents; + if (revents) { + const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); + PendingCallback pending; + pending.fd = requestedFd.fd; + pending.events = revents; + pending.callback = requestedCallback.callback; + pending.looperCallback = requestedCallback.looperCallback; + pending.data = requestedCallback.data; + + if (pending.callback || pending.looperCallback) { + mPendingCallbacks.push(pending); + } else if (pending.fd != mWakeReadPipeFd) { + if (result == POLL_CALLBACK) { + result = pending.fd; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + } else { + mPendingFds.push(pending); + } + } else { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - awoken", this); +#endif + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); + } + + respondedCount -= 1; + if (respondedCount == 0) { + break; + } + } + } + +Done: + mLock.lock(); + mPolling = false; + if (mWaiters != 0) { + mAwake.broadcast(); + } + mLock.unlock(); + + if (result == POLL_CALLBACK || result >= 0) { + size_t pendingCount = mPendingCallbacks.size(); + for (size_t i = 0; i < pendingCount; i++) { + const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); +#endif + + bool keep = true; + if (pendingCallback.callback != NULL) { + keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, + pendingCallback.data); + } else { + keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events, + pendingCallback.data) != 0; + } + if (! keep) { + removeCallback(pendingCallback.fd); + } + } + } + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - done", this); +#endif + return result; +} + +void PollLoop::wake() { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ wake", this); +#endif + + ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); + if (nWrite != 1) { + if (errno != EAGAIN) { + LOGW("Could not write wake signal, errno=%d", errno); + } + } +} + +bool PollLoop::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + +void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { + setCallbackCommon(fd, events, callback, NULL, data); +} + +void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data) { + setCallbackCommon(fd, events, NULL, callback, data); +} + +void PollLoop::setCallbackCommon(int fd, int events, Callback callback, + ALooper_callbackFunc* looperCallback, void* data) { + +#if DEBUG_CALLBACKS + LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); +#endif + + if (! events) { + LOGE("Invalid attempt to set a callback with no selected poll events."); + removeCallback(fd); + return; + } + + if (! callback && ! looperCallback && ! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed."); + removeCallback(fd); + return; + } + + wakeAndLock(); + + struct pollfd requestedFd; + requestedFd.fd = fd; + requestedFd.events = events; + + RequestedCallback requestedCallback; + requestedCallback.callback = callback; + requestedCallback.looperCallback = looperCallback; + requestedCallback.data = data; + + ssize_t index = getRequestIndexLocked(fd); + if (index < 0) { + mRequestedFds.push(requestedFd); + mRequestedCallbacks.push(requestedCallback); + } else { + mRequestedFds.replaceAt(requestedFd, size_t(index)); + mRequestedCallbacks.replaceAt(requestedCallback, size_t(index)); + } + + mLock.unlock(); +} + +bool PollLoop::removeCallback(int fd) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeCallback - fd=%d", this, fd); +#endif + + wakeAndLock(); + + ssize_t index = getRequestIndexLocked(fd); + if (index >= 0) { + mRequestedFds.removeAt(size_t(index)); + mRequestedCallbacks.removeAt(size_t(index)); + } + + mLock.unlock(); + return index >= 0; +} + +ssize_t PollLoop::getRequestIndexLocked(int fd) { + size_t requestCount = mRequestedFds.size(); + + for (size_t i = 0; i < requestCount; i++) { + if (mRequestedFds.itemAt(i).fd == fd) { + return i; + } + } + + return -1; +} + +void PollLoop::wakeAndLock() { + mLock.lock(); + mWaiters += 1; + while (mPolling) { + wake(); + mAwake.wait(mLock); + } + mWaiters -= 1; + if (mWaiters == 0) { + mResume.signal(); + } +} + +} // namespace android diff --git a/libs/utils/Pool.cpp b/libs/utils/Pool.cpp new file mode 100644 index 0000000000..8f18cb913e --- /dev/null +++ b/libs/utils/Pool.cpp @@ -0,0 +1,37 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A simple memory pool. +// +#define LOG_TAG "Pool" + +//#define LOG_NDEBUG 0 + +#include <cutils/log.h> +#include <utils/Pool.h> + +#include <stdlib.h> + +namespace android { + +// TODO Provide a real implementation of a pool. This is just a stub for initial development. + +PoolImpl::PoolImpl(size_t objSize) : + mObjSize(objSize) { +} + +PoolImpl::~PoolImpl() { +} + +void* PoolImpl::allocImpl() { + void* ptr = malloc(mObjSize); + LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object."); + return ptr; +} + +void PoolImpl::freeImpl(void* obj) { + LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object."); + return free(obj); +} + +} // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7e0f881af6..a1401addf3 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4178,6 +4178,9 @@ void ResTable::print(bool inclValues) const case ResTable_config::SCREENSIZE_LARGE: printf(" (large)"); break; + case ResTable_config::SCREENSIZE_XLARGE: + printf(" (xlarge)"); + break; } printf(" lng=%d", type->config.screenLayout&ResTable_config::MASK_SCREENLONG); diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp index 68a1c52177..b5dda2facc 100644 --- a/libs/utils/StopWatch.cpp +++ b/libs/utils/StopWatch.cpp @@ -30,10 +30,9 @@ namespace android { StopWatch::StopWatch(const char *name, int clock, uint32_t flags) - : mName(name), mClock(clock), mFlags(flags), - mStartTime(0), mNumLaps(0) + : mName(name), mClock(clock), mFlags(flags) { - mStartTime = systemTime(mClock); + reset(); } StopWatch::~StopWatch() @@ -72,6 +71,12 @@ nsecs_t StopWatch::elapsedTime() const return systemTime(mClock) - mStartTime; } +void StopWatch::reset() +{ + mNumLaps = 0; + mStartTime = systemTime(mClock); +} + /*****************************************************************************/ diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp new file mode 100644 index 0000000000..7ebde78cd8 --- /dev/null +++ b/libs/utils/StreamingZipInflater.cpp @@ -0,0 +1,225 @@ +/* + * 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 "szipinf" +#include <utils/Log.h> + +#include <utils/FileMap.h> +#include <utils/StreamingZipInflater.h> +#include <string.h> +#include <stddef.h> +#include <assert.h> + +static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; } + +using namespace android; + +/* + * Streaming access to compressed asset data in an open fd + */ +StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, + size_t uncompSize, size_t compSize) { + mFd = fd; + mDataMap = NULL; + mInFileStart = compDataStart; + mOutTotalSize = uncompSize; + mInTotalSize = compSize; + + mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE; + mInBuf = new uint8_t[mInBufSize]; + + mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; + mOutBuf = new uint8_t[mOutBufSize]; + + initInflateState(); +} + +/* + * Streaming access to compressed data held in an mmapped region of memory + */ +StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { + mFd = -1; + mDataMap = dataMap; + mOutTotalSize = uncompSize; + mInTotalSize = dataMap->getDataLength(); + + mInBuf = (uint8_t*) dataMap->getDataPtr(); + mInBufSize = mInTotalSize; + + mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; + mOutBuf = new uint8_t[mOutBufSize]; + + initInflateState(); +} + +StreamingZipInflater::~StreamingZipInflater() { + // tear down the in-flight zip state just in case + ::inflateEnd(&mInflateState); + + if (mDataMap == NULL) { + delete [] mInBuf; + } + delete [] mOutBuf; +} + +void StreamingZipInflater::initInflateState() { + LOGD("Initializing inflate state"); + + memset(&mInflateState, 0, sizeof(mInflateState)); + mInflateState.zalloc = Z_NULL; + mInflateState.zfree = Z_NULL; + mInflateState.opaque = Z_NULL; + mInflateState.next_in = (Bytef*)mInBuf; + mInflateState.next_out = (Bytef*) mOutBuf; + mInflateState.avail_out = mOutBufSize; + mInflateState.data_type = Z_UNKNOWN; + + mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0; + mInNextChunkOffset = 0; + mStreamNeedsInit = true; + + if (mDataMap == NULL) { + ::lseek(mFd, mInFileStart, SEEK_SET); + mInflateState.avail_in = 0; // set when a chunk is read in + } else { + mInflateState.avail_in = mInBufSize; + } +} + +/* + * Basic approach: + * + * 1. If we have undelivered uncompressed data, send it. At this point + * either we've satisfied the request, or we've exhausted the available + * output data in mOutBuf. + * + * 2. While we haven't sent enough data to satisfy the request: + * 0. if the request is for more data than exists, bail. + * a. if there is no input data to decode, read some into the input buffer + * and readjust the z_stream input pointers + * b. point the output to the start of the output buffer and decode what we can + * c. deliver whatever output data we can + */ +ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { + uint8_t* dest = (uint8_t*) outBuf; + size_t bytesRead = 0; + size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition)); + while (toRead > 0) { + // First, write from whatever we already have decoded and ready to go + size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable); + if (deliverable > 0) { + if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable); + mOutDeliverable += deliverable; + mOutCurPosition += deliverable; + dest += deliverable; + bytesRead += deliverable; + toRead -= deliverable; + } + + // need more data? time to decode some. + if (toRead > 0) { + // if we don't have any data to decode, read some in. If we're working + // from mmapped data this won't happen, because the clipping to total size + // will prevent reading off the end of the mapped input chunk. + if (mInflateState.avail_in == 0) { + int err = readNextChunk(); + if (err < 0) { + LOGE("Unable to access asset data: %d", err); + if (!mStreamNeedsInit) { + ::inflateEnd(&mInflateState); + initInflateState(); + } + return -1; + } + } + // we know we've drained whatever is in the out buffer now, so just + // start from scratch there, reading all the input we have at present. + mInflateState.next_out = (Bytef*) mOutBuf; + mInflateState.avail_out = mOutBufSize; + + /* + LOGD("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", + mInflateState.avail_in, mInflateState.avail_out, + mInflateState.next_in, mInflateState.next_out); + */ + int result = Z_OK; + if (mStreamNeedsInit) { + LOGI("Initializing zlib to inflate"); + result = inflateInit2(&mInflateState, -MAX_WBITS); + mStreamNeedsInit = false; + } + if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH); + if (result < 0) { + // Whoops, inflation failed + LOGE("Error inflating asset: %d", result); + ::inflateEnd(&mInflateState); + initInflateState(); + return -1; + } else { + if (result == Z_STREAM_END) { + // we know we have to have reached the target size here and will + // not try to read any further, so just wind things up. + ::inflateEnd(&mInflateState); + } + + // Note how much data we got, and off we go + mOutDeliverable = 0; + mOutLastDecoded = mOutBufSize - mInflateState.avail_out; + } + } + } + return bytesRead; +} + +int StreamingZipInflater::readNextChunk() { + assert(mDataMap == NULL); + + if (mInNextChunkOffset < mInTotalSize) { + size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); + if (toRead > 0) { + ssize_t didRead = ::read(mFd, mInBuf, toRead); + //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead); + if (didRead < 0) { + // TODO: error + LOGE("Error reading asset data"); + return didRead; + } else { + mInNextChunkOffset += didRead; + mInflateState.next_in = (Bytef*) mInBuf; + mInflateState.avail_in = didRead; + } + } + } + return 0; +} + +// seeking backwards requires uncompressing fom the beginning, so is very +// expensive. seeking forwards only requires uncompressing from the current +// position to the destination. +off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { + if (absoluteInputPosition < mOutCurPosition) { + // rewind and reprocess the data from the beginning + if (!mStreamNeedsInit) { + ::inflateEnd(&mInflateState); + } + initInflateState(); + read(NULL, absoluteInputPosition); + } else if (absoluteInputPosition > mOutCurPosition) { + read(NULL, absoluteInputPosition - mOutCurPosition); + } + // else if the target position *is* our current position, do nothing + return absoluteInputPosition; +} diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 636cd83428..1c4f80c1f8 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -301,8 +301,9 @@ void String8::setTo(const String8& other) status_t String8::setTo(const char* other) { + const char *newString = allocFromUTF8(other, strlen(other)); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, strlen(other)); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -311,8 +312,9 @@ status_t String8::setTo(const char* other) status_t String8::setTo(const char* other, size_t len) { + const char *newString = allocFromUTF8(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -321,8 +323,9 @@ status_t String8::setTo(const char* other, size_t len) status_t String8::setTo(const char16_t* other, size_t len) { + const char *newString = allocFromUTF16(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF16(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -331,8 +334,9 @@ status_t String8::setTo(const char16_t* other, size_t len) status_t String8::setTo(const char32_t* other, size_t len) { + const char *newString = allocFromUTF32(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF32(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -368,6 +372,27 @@ status_t String8::append(const char* other, size_t otherLen) return real_append(other, otherLen); } +status_t String8::appendFormat(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int result = NO_ERROR; + int n = vsnprintf(NULL, 0, fmt, ap); + if (n != 0) { + size_t oldLength = length(); + char* buf = lockBuffer(oldLength + n); + if (buf) { + vsnprintf(buf + oldLength, n + 1, fmt, ap); + } else { + result = NO_MEMORY; + } + } + + va_end(ap); + return result; +} + status_t String8::real_append(const char* other, size_t otherLen) { const size_t myLen = bytes(); @@ -407,15 +432,16 @@ status_t String8::unlockBuffer(size_t size) if (size != this->size()) { SharedBuffer* buf = SharedBuffer::bufferFromData(mString) ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - str[size] = 0; - mString = str; - return NO_ERROR; + if (! buf) { + return NO_MEMORY; } + + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; } - - return NO_MEMORY; + + return NO_ERROR; } ssize_t String8::find(const char* other, size_t start) const diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 0322af7da7..289c826d3e 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -108,18 +108,28 @@ size_t VectorImpl::capacity() const ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) { + return insertArrayAt(vector.arrayImpl(), index, vector.size()); +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length) +{ if (index > size()) return BAD_INDEX; - void* where = _grow(index, vector.size()); + void* where = _grow(index, length); if (where) { - _do_copy(where, vector.arrayImpl(), vector.size()); + _do_copy(where, array, length); } return where ? index : (ssize_t)NO_MEMORY; } -ssize_t VectorImpl::appendVector(const VectorImpl& vector) +ssize_t VectorImpl::appendArray(const void* array, size_t length) { - return insertVectorAt(vector, size()); + return insertArrayAt(array, size(), length); } ssize_t VectorImpl::insertAt(size_t index, size_t numItems) diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 45f6c8baa7..16b219cad4 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -39,8 +39,8 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, } bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 6c701dd0b6..604f55876e 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -29,6 +29,22 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -38,6 +54,7 @@ using namespace android; #define kEOCDSignature 0x06054b50 #define kEOCDLen 22 #define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDSize 12 // size of the central directory #define kEOCDFileOffset 16 // offset to central directory #define kMaxCommentLen 65535 // longest possible in ushort @@ -90,9 +107,8 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const status_t ZipFileRO::open(const char* zipFileName) { int fd = -1; - off_t length; - assert(mFileMap == NULL); + assert(mDirectoryMap == NULL); /* * Open and map the specified file. @@ -103,172 +119,238 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - length = lseek(fd, 0, SEEK_END); - if (length < 0) { + mFileLength = lseek(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { close(fd); return UNKNOWN_ERROR; } - mFileMap = new FileMap(); - if (mFileMap == NULL) { - close(fd); - return NO_MEMORY; - } - if (!mFileMap->create(zipFileName, fd, 0, length, true)) { - LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); - close(fd); - return UNKNOWN_ERROR; + if (mFileName != NULL) { + free(mFileName); } + mFileName = strdup(zipFileName); mFd = fd; /* - * Got it mapped, verify it and create data structures for fast access. + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. */ if (!parseZipArchive()) { - mFileMap->release(); - mFileMap = NULL; - return UNKNOWN_ERROR; + goto bail; } return OK; + +bail: + free(mFileName); + mFileName = NULL; + close(fd); + return UNKNOWN_ERROR; } /* * Parse the Zip archive, verifying its contents and initializing internal * data structures. */ -bool ZipFileRO::parseZipArchive(void) +bool ZipFileRO::mapCentralDirectory(void) { -#define CHECK_OFFSET(_off) { \ - if ((unsigned int) (_off) >= maxOffset) { \ - LOGE("ERROR: bad offset %u (max %d): %s\n", \ - (unsigned int) (_off), maxOffset, #_off); \ - goto bail; \ - } \ - } - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr; - size_t length = mFileMap->getDataLength(); - bool result = false; - unsigned int i, numEntries, cdOffset; - unsigned int val; + size_t readAmount = kMaxEOCDSearch; + if (readAmount > (size_t) mFileLength) + readAmount = mFileLength; + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (kLFHSignature) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature - * (kEOCDSignature). + * Make sure this is a Zip archive. */ - val = get4LE(basePtr); - if (val == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); - goto bail; + if (lseek(mFd, 0, SEEK_SET) != 0) { + LOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + LOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + { + unsigned int header = get4LE(scanBuf); + if (header == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + free(scanBuf); + return false; + } else if (header != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + free(scanBuf); + return false; + } } /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. */ - ptr = basePtr + length - kEOCDLen; + off_t searchStart = mFileLength - readAmount; + + if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); + free(scanBuf); + return false; + } - while (ptr >= basePtr) { - if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + LOGV("+++ Found EOCD at buf+%d\n", i); break; - ptr--; + } } - if (ptr < basePtr) { - LOGI("Could not find end-of-central-directory in Zip\n"); - goto bail; + if (i < 0) { + LOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; } + off_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - * - * (There's actually a count of the #of entries in this file, and for - * all files which comprise a spanned archive, but for our purposes - * we're only interested in the current file. Besides, we expect the - * two to be equivalent for our stuff.) + * Grab the CD offset and size, and the number of entries in the + * archive. After that, we can release our EOCD hunt buffer. */ - numEntries = get2LE(ptr + kEOCDNumEntries); - cdOffset = get4LE(ptr + kEOCDFileOffset); + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + free(scanBuf); + + // Verify that they look reasonable. + if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { + LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) dirOffset, dirSize, (long) eocdOffset); + return false; + } + if (numEntries == 0) { + LOGW("empty archive?\n"); + return false; + } - /* valid offsets are [0,EOCD] */ - unsigned int maxOffset; - maxOffset = (ptr - basePtr) +1; + LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, dirSize, dirOffset); - LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, length); - goto bail; + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + LOGW("Unable to create directory map: %s", strerror(errno)); + return false; } + if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { + LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, + dirOffset, dirOffset + dirSize, strerror(errno)); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = dirOffset; + + return true; +} + +bool ZipFileRO::parseZipArchive(void) +{ + bool result = false; + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; + /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. */ - mNumEntries = numEntries; - mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); - mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); /* * Walk through the central directory, adding entries to the hash * table. */ - ptr = basePtr + cdOffset; - for (i = 0; i < numEntries; i++) { - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - unsigned int hash; - + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Missed a central dir sig (at %d)\n", i); goto bail; } - if (ptr + kCDELen > basePtr + length) { + if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Ran off the end (at %d)\n", i); goto bail; } - localHdrOffset = get4LE(ptr + kCDELocalOffset); - CHECK_OFFSET(localHdrOffset); + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int fileNameLen, extraLen, commentLen, hash; + fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); - //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); - /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - localHdr = basePtr + localHdrOffset; - if (get4LE(localHdr) != kLFHSignature) { - LOGW("Bad offset to local header: %d (at %d)\n", - localHdrOffset, i); + ptr += kCDELen + fileNameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + LOGW("bad CD advance (%d vs %zd) at entry %d\n", + (int) (ptr - cdPtr), cdLength, i); goto bail; } - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - CHECK_OFFSET(ptr - basePtr); } - + LOGV("+++ zip good scan %d entries\n", numEntries); result = true; bail: return result; -#undef CHECK_OFFSET } - /* * Simple string hash function for non-null-terminated strings. */ @@ -315,7 +397,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const memcmp(mHashTable[ent].name, fileName, nameLen) == 0) { /* match */ - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO)(long)(ent + kZipEntryAdj); } ent = (ent + 1) & (mHashTableSize-1); @@ -354,20 +436,24 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * Returns "false" if the offsets to the fields or the contents of the fields * appear to be bogus. */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const { - int ent = entryToIndex(entry); + bool ret = false; + + const int ent = entryToIndex(entry); if (ent < 0) return false; + HashEntry hashEntry = mHashTable[ent]; + /* * Recover the start of the central directory entry from the filename - * pointer. + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. */ - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; - size_t zipLength = mFileMap->getDataLength(); + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -380,48 +466,78 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + /* - * We need to make sure that the lengths are not so large that somebody - * trying to map the compressed or uncompressed data runs off the end - * of the mapped region. + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. */ - unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= zipLength) { - LOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - const unsigned char* localHdr = basePtr + localHdrOffset; - off_t dataOffset = localHdrOffset + kLFHLen - + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); - if ((unsigned long) dataOffset >= zipLength) { - LOGE("ERROR: bad data offset in zip\n"); - return false; - } + if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } - if (pCompLen != NULL) { - *pCompLen = get4LE(ptr + kCDECompLen); - if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { - LOGE("ERROR: bad compressed length in zip\n"); + unsigned char lfhBuf[kLFHLen]; + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } - } - if (pUncompLen != NULL) { - *pUncompLen = get4LE(ptr + kCDEUncompLen); - if (*pUncompLen < 0) { - LOGE("ERROR: negative uncompressed length in zip\n"); + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh, offset=%ld\n", + localHdrOffset); + return false; + } + + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + LOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((off_t)(dataOffset + compLen) > cdOffset) { + LOGW("bad compressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, compLen, (long) cdOffset); + return false; + } + if (method == kCompressStored && - (size_t)(dataOffset + *pUncompLen) >= zipLength) + (off_t)(dataOffset + uncompLen) > cdOffset) { - LOGE("ERROR: bad uncompressed length in zip\n"); + LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, uncompLen, (long) cdOffset); return false; } - } - if (pOffset != NULL) { *pOffset = dataOffset; } + return true; } @@ -457,14 +573,14 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ FileMap* newMap; - long compLen; + size_t compLen; off_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; newMap = new FileMap(); - if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + if (!newMap->create(mFileName, mFd, offset, compLen, true)) { newMap->release(); return NULL; } @@ -480,19 +596,26 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const { - const int kSequentialMin = 32768; + const size_t kSequentialMin = 32768; bool result = false; int ent = entryToIndex(entry); if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + /* * Experiment with madvise hint. When we want to uncompress a file, * we pull some stuff out of the central dir entry and then hit a @@ -507,20 +630,22 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const * pair of system calls are negated by a reduction in page faults. */ if (compLen > kSequentialMin) - mFileMap->advise(FileMap::SEQUENTIAL); + file->advise(FileMap::SEQUENTIAL); if (method == kCompressStored) { - memcpy(buffer, basePtr + offset, uncompLen); + memcpy(buffer, ptr, uncompLen); } else { - if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) - goto bail; + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) + goto unmap; } if (compLen > kSequentialMin) - mFileMap->advise(FileMap::NORMAL); + file->advise(FileMap::NORMAL); result = true; +unmap: + file->release(); bail: return result; } @@ -537,34 +662,41 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - if (method == kCompressStored) { - ssize_t actual; + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } - actual = write(fd, basePtr + offset, uncompLen); + ptr = (const unsigned char*) file->getDataPtr(); + + if (method == kCompressStored) { + ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); - goto bail; - } else if (actual != uncompLen) { - LOGE("Partial write during uncompress (%d of %ld)\n", - (int)actual, uncompLen); - goto bail; + goto unmap; + } else if ((size_t) actual != uncompLen) { + LOGE("Partial write during uncompress (%zd of %zd)\n", + (size_t)actual, (size_t)uncompLen); + goto unmap; } else { LOGI("+++ successful write\n"); } } else { - if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) - goto bail; + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) + goto unmap; } result = true; +unmap: + file->release(); bail: return result; } @@ -573,7 +705,7 @@ bail: * Uncompress "deflate" data from one buffer to another. */ /*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; z_stream zstream; @@ -582,7 +714,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -592,10 +724,10 @@ bail: zstream.avail_out = uncompLen; zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -619,8 +751,8 @@ bail: } /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } @@ -638,10 +770,10 @@ bail: * Uncompress "deflate" data from one buffer to an open file descriptor. */ /*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; - const int kWriteBufSize = 32768; + const size_t kWriteBufSize = 32768; unsigned char writeBuf[kWriteBufSize]; z_stream zstream; int zerr; @@ -649,7 +781,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -659,10 +791,10 @@ bail: zstream.avail_out = sizeof(writeBuf); zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -708,8 +840,8 @@ bail: assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk new file mode 100644 index 0000000000..725de9c5bb --- /dev/null +++ b/libs/utils/tests/Android.mk @@ -0,0 +1,45 @@ +# Build the unit tests. +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. +test_src_files := \ + ObbFile_test.cpp \ + PollLoop_test.cpp \ + String8_test.cpp + +shared_libraries := \ + libz \ + liblog \ + libcutils \ + libutils \ + libstlport + +static_libraries := \ + libgtest \ + libgtest_main + +c_includes := \ + external/zlib \ + external/icu4c/common \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport + +module_tags := eng tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +endif diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp new file mode 100644 index 0000000000..29bb70a66c --- /dev/null +++ b/libs/utils/tests/ObbFile_test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ObbFile_test" +#include <utils/Log.h> +#include <utils/ObbFile.h> +#include <utils/RefBase.h> +#include <utils/String8.h> + +#include <gtest/gtest.h> + +#include <fcntl.h> + +namespace android { + +#define TEST_FILENAME "/test.obb" + +class ObbFileTest : public testing::Test { +protected: + sp<ObbFile> mObbFile; + char* mExternalStorage; + char* mFileName; + + virtual void SetUp() { + mObbFile = new ObbFile(); + mExternalStorage = getenv("EXTERNAL_STORAGE"); + + const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; + mFileName = new char[totalLen]; + snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); + + int fd = ::open(mFileName, O_CREAT | O_TRUNC); + if (fd < 0) { + FAIL() << "Couldn't create " << mFileName << " for tests"; + } + } + + virtual void TearDown() { + } +}; + +TEST_F(ObbFileTest, ReadFailure) { + EXPECT_FALSE(mObbFile->readFrom(-1)) + << "No failure on invalid file descriptor"; +} + +TEST_F(ObbFileTest, WriteThenRead) { + const char* packageName = "com.example.obbfile"; + const int32_t versionNum = 1; + + mObbFile->setPackageName(String8(packageName)); + mObbFile->setVersion(versionNum); + + EXPECT_TRUE(mObbFile->writeTo(mFileName)) + << "couldn't write to fake .obb file"; + + mObbFile = new ObbFile(); + + EXPECT_TRUE(mObbFile->readFrom(mFileName)) + << "couldn't read from fake .obb file"; + + EXPECT_EQ(versionNum, mObbFile->getVersion()) + << "version didn't come out the same as it went in"; + const char* currentPackageName = mObbFile->getPackageName().string(); + EXPECT_STREQ(packageName, currentPackageName) + << "package name didn't come out the same as it went in"; +} + +} diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp new file mode 100644 index 0000000000..02f1808936 --- /dev/null +++ b/libs/utils/tests/PollLoop_test.cpp @@ -0,0 +1,370 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <utils/PollLoop.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <gtest/gtest.h> +#include <unistd.h> +#include <time.h> + +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 + +namespace android { + +class DelayedWake : public DelayedTask { + sp<PollLoop> mPollLoop; + +public: + DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) : + DelayedTask(delayMillis), mPollLoop(pollLoop) { + } + +protected: + virtual void doTask() { + mPollLoop->wake(); + } +}; + +class DelayedWriteSignal : public DelayedTask { + Pipe* mPipe; + +public: + DelayedWriteSignal(int delayMillis, Pipe* pipe) : + DelayedTask(delayMillis), mPipe(pipe) { + } + +protected: + virtual void doTask() { + mPipe->writeSignal(); + } +}; + +class CallbackHandler { +public: + void setCallback(const sp<PollLoop>& pollLoop, int fd, int events) { + pollLoop->setCallback(fd, events, staticHandler, this); + } + +protected: + virtual ~CallbackHandler() { } + + virtual bool handler(int fd, int events) = 0; + +private: + static bool staticHandler(int fd, int events, void* data) { + return static_cast<CallbackHandler*>(data)->handler(fd, events); + } +}; + +class StubCallbackHandler : public CallbackHandler { +public: + bool nextResult; + int callbackCount; + + int fd; + int events; + + StubCallbackHandler(bool nextResult) : nextResult(nextResult), + callbackCount(0), fd(-1), events(-1) { + } + +protected: + virtual bool handler(int fd, int events) { + callbackCount += 1; + this->fd = fd; + this->events = events; + return nextResult; + } +}; + +class PollLoopTest : public testing::Test { +protected: + sp<PollLoop> mPollLoop; + + virtual void SetUp() { + mPollLoop = new PollLoop(false); + } + + virtual void TearDown() { + mPollLoop.clear(); + } +}; + + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { + mPollLoop->wake(); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because wake() was called before waiting"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { + sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop); + delayedWake->run(); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal wake delay"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + + ASSERT_EQ(OK, pipe.writeSignal()); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + + pipe.writeSignal(); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + delayedWriteSignal->run(); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal signal delay"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + pipe.writeSignal(); // would cause FD to be considered signalled + mPollLoop->removeCallback(pipe.receiveFd); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout because FD was no longer registered"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not be invoked"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { + Pipe pipe; + StubCallbackHandler handler(false); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + // First loop: Callback is registered and FD is signalled. + pipe.writeSignal(); + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because FD was already signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked"; + + // Second loop: Callback is no longer registered and FD is signalled. + pipe.writeSignal(); + + stopWatch.reset(); + result = mPollLoop->pollOnce(0); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because timeout was zero"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should not be invoked this time"; +} + +TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) { + bool result = mPollLoop->removeCallback(1); + + EXPECT_FALSE(result) + << "removeCallback should return false because FD not registered"; +} + +TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) { + Pipe pipe; + StubCallbackHandler handler(false); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + // First time. + bool result = mPollLoop->removeCallback(pipe.receiveFd); + + EXPECT_TRUE(result) + << "removeCallback should return true first time because FD was registered"; + + // Second time. + result = mPollLoop->removeCallback(pipe.receiveFd); + + EXPECT_FALSE(result) + << "removeCallback should return false second time because FD was no longer registered"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { + Pipe pipe; + StubCallbackHandler handler1(true); + StubCallbackHandler handler2(true); + + handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it + pipe.writeSignal(); // would cause FD to be considered signalled + + StopWatch stopWatch("pollOnce"); + int32_t result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because FD was already signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(0, handler1.callbackCount) + << "original handler callback should not be invoked because it was replaced"; + EXPECT_EQ(1, handler2.callbackCount) + << "replacement handler callback should be invoked"; +} + + +} // namespace android diff --git a/libs/utils/tests/String8_test.cpp b/libs/utils/tests/String8_test.cpp new file mode 100644 index 0000000000..c42c68dcef --- /dev/null +++ b/libs/utils/tests/String8_test.cpp @@ -0,0 +1,75 @@ +/* + * 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 "String8_test" +#include <utils/Log.h> +#include <utils/String8.h> + +#include <gtest/gtest.h> + +namespace android { + +class String8Test : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(String8Test, Cstr) { + String8 tmp("Hello, world!"); + + EXPECT_STREQ(tmp.string(), "Hello, world!"); +} + +TEST_F(String8Test, OperatorPlus) { + String8 src1("Hello, "); + + // Test adding String8 + const char* + const char* ccsrc2 = "world!"; + String8 dst1 = src1 + ccsrc2; + EXPECT_STREQ(dst1.string(), "Hello, world!"); + EXPECT_STREQ(src1.string(), "Hello, "); + EXPECT_STREQ(ccsrc2, "world!"); + + // Test adding String8 + String8 + String8 ssrc2("world!"); + String8 dst2 = src1 + ssrc2; + EXPECT_STREQ(dst2.string(), "Hello, world!"); + EXPECT_STREQ(src1.string(), "Hello, "); + EXPECT_STREQ(ssrc2.string(), "world!"); +} + +TEST_F(String8Test, OperatorPlusEquals) { + String8 src1("My voice"); + + // Testing String8 += String8 + String8 src2(" is my passport."); + src1 += src2; + EXPECT_STREQ(src1.string(), "My voice is my passport."); + EXPECT_STREQ(src2.string(), " is my passport."); + + // Adding const char* to the previous string. + const char* src3 = " Verify me."; + src1 += src3; + EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me."); + EXPECT_STREQ(src2.string(), " is my passport."); + EXPECT_STREQ(src3, " Verify me."); +} + +} diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h new file mode 100644 index 0000000000..d8e985e9ce --- /dev/null +++ b/libs/utils/tests/TestHelpers.h @@ -0,0 +1,79 @@ +/* + * 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 TESTHELPERS_H +#define TESTHELPERS_H + +#include <utils/threads.h> + +namespace android { + +class Pipe { +public: + int sendFd; + int receiveFd; + + Pipe() { + int fds[2]; + ::pipe(fds); + + receiveFd = fds[0]; + sendFd = fds[1]; + } + + ~Pipe() { + if (sendFd != -1) { + ::close(sendFd); + } + + if (receiveFd != -1) { + ::close(receiveFd); + } + } + + status_t writeSignal() { + ssize_t nWritten = ::write(sendFd, "*", 1); + return nWritten == 1 ? 0 : -errno; + } + + status_t readSignal() { + char buf[1]; + ssize_t nRead = ::read(receiveFd, buf, 1); + return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; + } +}; + +class DelayedTask : public Thread { + int mDelayMillis; + +public: + DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } + +protected: + virtual ~DelayedTask() { } + + virtual void doTask() = 0; + + virtual bool threadLoop() { + usleep(mDelayMillis * 1000); + doTask(); + return false; + } +}; + +} // namespace android + +#endif // TESTHELPERS_H diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h index c269976f4a..99ea342a47 100644 --- a/opengl/include/EGL/egl.h +++ b/opengl/include/EGL/egl.h @@ -1,7 +1,7 @@ /* -*- mode: c; tab-width: 8; -*- */ /* vi: set sw=4 ts=8: */ /* Reference version of egl.h for EGL 1.4. - * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * $Revision: 9356 $ on $Date: 2009-10-21 02:52:25 -0700 (Wed, 21 Oct 2009) $ */ /* @@ -109,7 +109,6 @@ typedef void *EGLClientBuffer; #define EGL_NATIVE_RENDERABLE 0x302D #define EGL_NATIVE_VISUAL_ID 0x302E #define EGL_NATIVE_VISUAL_TYPE 0x302F -#define EGL_PRESERVED_RESOURCES 0x3030 #define EGL_SAMPLES 0x3031 #define EGL_SAMPLE_BUFFERS 0x3032 #define EGL_SURFACE_TYPE 0x3033 diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 545fd0e980..b12115820f 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -6,7 +6,7 @@ extern "C" { #endif /* -** Copyright (c) 2007-2009 The Khronos Group Inc. +** Copyright (c) 2007-2010 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the @@ -34,8 +34,8 @@ extern "C" { /* Header file version number */ /* Current version at http://www.khronos.org/registry/egl/ */ -/* $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ */ -#define EGL_EGLEXT_VERSION 3 +/* $Revision: 11249 $ on $Date: 2010-05-05 09:54:28 -0700 (Wed, 05 May 2010) $ */ +#define EGL_EGLEXT_VERSION 5 #ifndef EGL_KHR_config_attribs #define EGL_KHR_config_attribs 1 @@ -120,6 +120,36 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL #define EGL_GL_RENDERBUFFER_KHR 0x30B9 /* eglCreateImageKHR target */ #endif +#ifndef EGL_KHR_reusable_sync +#define EGL_KHR_reusable_sync 1 + +typedef void* EGLSyncKHR; +typedef khronos_utime_nanoseconds_t EGLTimeKHR; + +#define EGL_SYNC_STATUS_KHR 0x30F1 +#define EGL_SIGNALED_KHR 0x30F2 +#define EGL_UNSIGNALED_KHR 0x30F3 +#define EGL_TIMEOUT_EXPIRED_KHR 0x30F5 +#define EGL_CONDITION_SATISFIED_KHR 0x30F6 +#define EGL_SYNC_TYPE_KHR 0x30F7 +#define EGL_SYNC_REUSABLE_KHR 0x30FA +#define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR 0x0001 /* eglClientWaitSyncKHR <flags> bitfield */ +#define EGL_FOREVER_KHR 0xFFFFFFFFFFFFFFFFull +#define EGL_NO_SYNC_KHR ((EGLSyncKHR)0) +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync); +EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); +EGLAPI EGLBoolean EGLAPIENTRY eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode); +EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync); +typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value); +#endif + #ifndef EGL_KHR_image_base #define EGL_KHR_image_base 1 /* Most interfaces defined by EGL_KHR_image_pixmap above */ @@ -131,6 +161,67 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL /* Interfaces defined by EGL_KHR_image above */ #endif +#ifndef EGL_IMG_context_priority +#define EGL_IMG_context_priority 1 +#define EGL_CONTEXT_PRIORITY_LEVEL_IMG 0x3100 +#define EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101 +#define EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102 +#define EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103 +#endif + +#ifndef EGL_NV_coverage_sample +#define EGL_NV_coverage_sample 1 +#define EGL_COVERAGE_BUFFERS_NV 0x30E0 +#define EGL_COVERAGE_SAMPLES_NV 0x30E1 +#endif + +#ifndef EGL_NV_depth_nonlinear +#define EGL_NV_depth_nonlinear 1 +#define EGL_DEPTH_ENCODING_NV 0x30E2 +#define EGL_DEPTH_ENCODING_NONE_NV 0 +#define EGL_DEPTH_ENCODING_NONLINEAR_NV 0x30E3 +#endif + +#ifndef EGL_NV_sync +#define EGL_NV_sync 1 +#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV 0x30E6 +#define EGL_SYNC_STATUS_NV 0x30E7 +#define EGL_SIGNALED_NV 0x30E8 +#define EGL_UNSIGNALED_NV 0x30E9 +#define EGL_SYNC_FLUSH_COMMANDS_BIT_NV 0x0001 +#define EGL_FOREVER_NV 0xFFFFFFFFFFFFFFFFull +#define EGL_ALREADY_SIGNALED_NV 0x30EA +#define EGL_TIMEOUT_EXPIRED_NV 0x30EB +#define EGL_CONDITION_SATISFIED_NV 0x30EC +#define EGL_SYNC_TYPE_NV 0x30ED +#define EGL_SYNC_CONDITION_NV 0x30EE +#define EGL_SYNC_FENCE_NV 0x30EF +#define EGL_NO_SYNC_NV ((EGLSyncNV)0) +typedef void* EGLSyncNV; +typedef unsigned long long EGLTimeNV; +#ifdef EGL_EGLEXT_PROTOTYPES +EGLSyncNV eglCreateFenceSyncNV (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list); +EGLBoolean eglDestroySyncNV (EGLSyncNV sync); +EGLBoolean eglFenceNV (EGLSyncNV sync); +EGLint eglClientWaitSyncNV (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout); +EGLBoolean eglSignalSyncNV (EGLSyncNV sync, EGLenum mode); +EGLBoolean eglGetSyncAttribNV (EGLSyncNV sync, EGLint attribute, EGLint *value); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLSyncNV (EGLAPIENTRYP PFNEGLCREATEFENCESYNCNVPROC) (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCNVPROC) (EGLSyncNV sync); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLFENCENVPROC) (EGLSyncNV sync); +typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCNVPROC) (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCNVPROC) (EGLSyncNV sync, EGLenum mode); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBNVPROC) (EGLSyncNV sync, EGLint attribute, EGLint *value); +#endif + +#ifndef EGL_KHR_fence_sync +#define EGL_KHR_fence_sync 1 +/* Reuses most tokens and entry points from EGL_KHR_reusable_sync */ +#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR 0x30F0 +#define EGL_SYNC_CONDITION_KHR 0x30F8 +#define EGL_SYNC_FENCE_KHR 0x30F9 +#endif #ifndef EGL_ANDROID_image_native_buffer #define EGL_ANDROID_image_native_buffer 1 @@ -154,7 +245,6 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSur typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height); #endif - #ifdef __cplusplus } #endif diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h index 53e9e61169..25d7697ee3 100644 --- a/opengl/include/EGL/eglplatform.h +++ b/opengl/include/EGL/eglplatform.h @@ -25,7 +25,7 @@ */ /* Platform-specific types and definitions for egl.h - * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * $Revision: 9724 $ on $Date: 2009-12-02 02:05:33 -0800 (Wed, 02 Dec 2009) $ * * Adopters may modify khrplatform.h and this file to suit their platform. * You are encouraged to submit all modifications to the Khronos group so that @@ -50,8 +50,10 @@ #define EGLAPI KHRONOS_APICALL #endif +#ifndef EGLAPIENTRY #define EGLAPIENTRY KHRONOS_APIENTRY -#define EGLAPIENTRYP KHRONOS_APIENTRY* +#endif +#define EGLAPIENTRYP EGLAPIENTRY* /* The types NativeDisplayType, NativeWindowType, and NativePixmapType * are aliases of window-system-dependent types, such as X Display * or @@ -89,10 +91,11 @@ typedef Window EGLNativeWindowType; #elif defined(ANDROID) -struct android_native_window_t; +#include <android/native_window.h> + struct egl_native_pixmap_t; -typedef struct android_native_window_t* EGLNativeWindowType; +typedef struct ANativeWindow* EGLNativeWindowType; typedef struct egl_native_pixmap_t* EGLNativePixmapType; typedef void* EGLNativeDisplayType; diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h index 2e8b97107a..5b8d85a920 100644 --- a/opengl/include/GLES/gl.h +++ b/opengl/include/GLES/gl.h @@ -1,7 +1,7 @@ #ifndef __gl_h_ #define __gl_h_ -/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ +/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */ #include <GLES/glplatform.h> @@ -15,6 +15,7 @@ extern "C" { */ typedef void GLvoid; +typedef char GLchar; typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; @@ -678,7 +679,7 @@ GL_API void GL_APIENTRY glGetFixedv (GLenum pname, GLfixed *params); GL_API void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *params); GL_API void GL_APIENTRY glGetLightxv (GLenum light, GLenum pname, GLfixed *params); GL_API void GL_APIENTRY glGetMaterialxv (GLenum face, GLenum pname, GLfixed *params); -GL_API void GL_APIENTRY glGetPointerv (GLenum pname, void **params); +GL_API void GL_APIENTRY glGetPointerv (GLenum pname, GLvoid **params); GL_API const GLubyte * GL_APIENTRY glGetString (GLenum name); GL_API void GL_APIENTRY glGetTexEnviv (GLenum env, GLenum pname, GLint *params); GL_API void GL_APIENTRY glGetTexEnvxv (GLenum env, GLenum pname, GLfixed *params); diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h index a8fe2e9d56..a5b3eada3f 100644 --- a/opengl/include/GLES/glext.h +++ b/opengl/include/GLES/glext.h @@ -1,7 +1,7 @@ #ifndef __glext_h_ #define __glext_h_ -/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ +/* $Revision: 10965 $ on $Date:: 2010-04-09 02:11:29 -0700 #$ */ #ifdef __cplusplus extern "C" { @@ -68,6 +68,11 @@ extern "C" { typedef void* GLeglImageOES; #endif +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_UNSIGNED_INT 0x1405 +#endif + /* GL_OES_fixed_point */ #ifndef GL_OES_fixed_point #define GL_FIXED_OES 0x140C @@ -201,6 +206,16 @@ typedef void* GLeglImageOES; #define GL_MIRRORED_REPEAT_OES 0x8370 #endif +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#endif + +/* GL_OES_texture_external */ +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + /*------------------------------------------------------------------------* * AMD extension tokens *------------------------------------------------------------------------*/ @@ -219,15 +234,191 @@ typedef void* GLeglImageOES; #endif /*------------------------------------------------------------------------* + * APPLE extension tokens + *------------------------------------------------------------------------*/ + +/* GL_APPLE_texture_2D_limited_npot */ +/* No new tokens introduced by this extension. */ + +/*------------------------------------------------------------------------* * EXT extension tokens *------------------------------------------------------------------------*/ +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_COLOR_EXT 0x1800 +#define GL_DEPTH_EXT 0x1801 +#define GL_STENCIL_EXT 0x1802 +#endif + +/* GL_EXT_multi_draw_arrays */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_BGRA_EXT 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#endif + /* GL_EXT_texture_filter_anisotropic */ #ifndef GL_EXT_texture_filter_anisotropic #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #endif +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + +/* GL_EXT_texture_lod_bias */ +#ifndef GL_EXT_texture_lod_bias +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif + +/*------------------------------------------------------------------------* + * IMG extension tokens + *------------------------------------------------------------------------*/ + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_BGRA_IMG 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365 +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif + +/* GL_IMG_texture_env_enhanced_fixed_function */ +#ifndef GL_IMG_texture_env_enhanced_fixed_function +#define GL_MODULATE_COLOR_IMG 0x8C04 +#define GL_RECIP_ADD_SIGNED_ALPHA_IMG 0x8C05 +#define GL_TEXTURE_ALPHA_MODULATE_IMG 0x8C06 +#define GL_FACTOR_ALPHA_MODULATE_IMG 0x8C07 +#define GL_FRAGMENT_ALPHA_MODULATE_IMG 0x8C08 +#define GL_ADD_BLEND_IMG 0x8C09 +#define GL_DOT3_RGBA_IMG 0x86AF +#endif + +/* GL_IMG_user_clip_plane */ +#ifndef GL_IMG_user_clip_plane +#define GL_CLIP_PLANE0_IMG 0x3000 +#define GL_CLIP_PLANE1_IMG 0x3001 +#define GL_CLIP_PLANE2_IMG 0x3002 +#define GL_CLIP_PLANE3_IMG 0x3003 +#define GL_CLIP_PLANE4_IMG 0x3004 +#define GL_CLIP_PLANE5_IMG 0x3005 +#define GL_MAX_CLIP_PLANES_IMG 0x0D32 +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134 +#define GL_MAX_SAMPLES_IMG 0x9135 +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif + +/*------------------------------------------------------------------------* + * NV extension tokens + *------------------------------------------------------------------------*/ + +/* GL_NV_fence */ +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#endif + +/*------------------------------------------------------------------------* + * QCOM extension tokens + *------------------------------------------------------------------------*/ + +/* GL_QCOM_driver_control */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_TEXTURE_WIDTH_QCOM 0x8BD2 +#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3 +#define GL_TEXTURE_DEPTH_QCOM 0x8BD4 +#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5 +#define GL_TEXTURE_FORMAT_QCOM 0x8BD6 +#define GL_TEXTURE_TYPE_QCOM 0x8BD7 +#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8 +#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9 +#define GL_TEXTURE_TARGET_QCOM 0x8BDA +#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB +#define GL_STATE_RESTORE 0x8BDC +#endif + +/* GL_QCOM_extended_get2 */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_WRITEONLY_RENDERING_QCOM 0x8823 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001 +#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002 +#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004 +#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008 +#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010 +#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020 +#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040 +#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080 +#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100 +#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200 +#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400 +#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800 +#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000 +#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000 +#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000 +#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000 +#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000 +#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000 +#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000 +#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000 +#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000 +#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000 +#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000 +#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000 +#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000 +#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000 +#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000 +#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000 +#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000 +#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000 +#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000 +#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000 +#endif + +/*------------------------------------------------------------------------* + * End of extension tokens, start of corresponding extension functions + *------------------------------------------------------------------------*/ + /*------------------------------------------------------------------------* * OES extension functions *------------------------------------------------------------------------*/ @@ -456,11 +647,11 @@ typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPOESPROC) (GLenum target); #ifdef GL_GLEXT_PROTOTYPES GL_API void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); GL_API GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); -GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params); +GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid ** params); #endif typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); -typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid ** params); #endif /* GL_OES_matrix_get */ @@ -576,6 +767,26 @@ typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, #define GL_OES_texture_mirrored_repeat 1 #endif +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_OES_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBindVertexArrayOES (GLuint array); +GL_API void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays); +GL_API void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays); +GL_API GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array); +#endif +typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array); +typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays); +typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array); +#endif + +/* GL_OES_texture_external */ +#ifndef GL_OES_texture_external +#define GL_OES_texture_external 1 +#endif + /*------------------------------------------------------------------------* * AMD extension functions *------------------------------------------------------------------------*/ @@ -591,14 +802,207 @@ typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, #endif /*------------------------------------------------------------------------* + * APPLE extension functions + *------------------------------------------------------------------------*/ + +/* GL_APPLE_texture_2D_limited_npot */ +#ifndef GL_APPLE_texture_2D_limited_npot +#define GL_APPLE_texture_2D_limited_npot 1 +#endif + +/*------------------------------------------------------------------------* * EXT extension functions *------------------------------------------------------------------------*/ +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_EXT_discard_framebuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif +typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif + +/* GL_EXT_multi_draw_arrays */ +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glMultiDrawArraysEXT (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +GL_API void GL_APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_EXT_read_format_bgra 1 +#endif + /* GL_EXT_texture_filter_anisotropic */ #ifndef GL_EXT_texture_filter_anisotropic #define GL_EXT_texture_filter_anisotropic 1 #endif +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_EXT_texture_format_BGRA8888 1 +#endif + +/* GL_EXT_texture_lod_bias */ +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#endif + +/*------------------------------------------------------------------------* + * IMG extension functions + *------------------------------------------------------------------------*/ + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_IMG_read_format 1 +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_IMG_texture_compression_pvrtc 1 +#endif + +/* GL_IMG_texture_env_enhanced_fixed_function */ +#ifndef GL_IMG_texture_env_enhanced_fixed_function +#define GL_IMG_texture_env_enhanced_fixed_function 1 +#endif + +/* GL_IMG_user_clip_plane */ +#ifndef GL_IMG_user_clip_plane +#define GL_IMG_user_clip_plane 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glClipPlanefIMG (GLenum p, const GLfloat *eqn); +GL_API void GL_APIENTRY glClipPlanexIMG (GLenum p, const GLfixed *eqn); +#endif +typedef void (GL_APIENTRYP PFNGLCLIPPLANEFIMGPROC) (GLenum p, const GLfloat *eqn); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEXIMGPROC) (GLenum p, const GLfixed *eqn); +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_IMG_multisampled_render_to_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif + +/*------------------------------------------------------------------------* + * NV extension functions + *------------------------------------------------------------------------*/ + +/* NV_fence */ +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GL_API void GL_APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GL_API GLboolean GL_APIENTRY glIsFenceNV (GLuint fence); +GL_API GLboolean GL_APIENTRY glTestFenceNV (GLuint fence); +GL_API void GL_APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glFinishFenceNV (GLuint fence); +GL_API void GL_APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +typedef void (GL_APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (GL_APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (GL_APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (GL_APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#endif + +/*------------------------------------------------------------------------* + * QCOM extension functions + *------------------------------------------------------------------------*/ + +/* GL_QCOM_driver_control */ +#ifndef GL_QCOM_driver_control +#define GL_QCOM_driver_control 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls); +GL_API void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +GL_API void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl); +GL_API void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl); +#endif +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls); +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +#endif + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_QCOM_extended_get 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures); +GL_API void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +GL_API void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +GL_API void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +GL_API void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +GL_API void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params); +#endif + +/* GL_QCOM_extended_get2 */ +#ifndef GL_QCOM_extended_get2 +#define GL_QCOM_extended_get2 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders); +GL_API void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +GL_API GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program); +GL_API void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_QCOM_perfmon_global_mode 1 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_QCOM_writeonly_rendering 1 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_QCOM_tiled_rendering 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +GL_API void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); +#endif +typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask); +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/include/GLES/glplatform.h b/opengl/include/GLES/glplatform.h index 198e679db2..2db6ee2ca6 100644 --- a/opengl/include/GLES/glplatform.h +++ b/opengl/include/GLES/glplatform.h @@ -1,7 +1,7 @@ #ifndef __glplatform_h_ #define __glplatform_h_ -/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ +/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */ /* * This document is licensed under the SGI Free Software B License Version @@ -9,7 +9,6 @@ */ /* Platform-specific types and definitions for OpenGL ES 1.X gl.h - * Last modified on 2008/12/19 * * Adopters may modify khrplatform.h and this file to suit their platform. * You are encouraged to submit all modifications to the Khronos group so that @@ -24,10 +23,8 @@ #define GL_API KHRONOS_APICALL #endif -#if defined(ANDROID) - +#ifndef GL_APIENTRY #define GL_APIENTRY KHRONOS_APIENTRY - #endif #endif /* __glplatform_h_ */ diff --git a/opengl/include/GLES2/gl2.h b/opengl/include/GLES2/gl2.h index 0182a676d1..e1d3b87cc5 100644 --- a/opengl/include/GLES2/gl2.h +++ b/opengl/include/GLES2/gl2.h @@ -1,7 +1,7 @@ #ifndef __gl2_h_ #define __gl2_h_ -/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */ +/* $Revision: 10602 $ on $Date:: 2010-03-04 22:35:34 -0800 #$ */ #include <GLES2/gl2platform.h> @@ -19,6 +19,7 @@ extern "C" { *-----------------------------------------------------------------------*/ typedef void GLvoid; +typedef char GLchar; typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; @@ -472,7 +473,7 @@ typedef khronos_ssize_t GLsizeiptr; GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture); GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader); -GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const char* name); +GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar* name); GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); @@ -482,8 +483,8 @@ GL_APICALL void GL_APIENTRY glBlendEquation ( GLenum mode ); GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage); -GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data); +GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); +GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data); GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target); GL_APICALL void GL_APIENTRY glClear (GLbitfield mask); GL_APICALL void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); @@ -491,8 +492,8 @@ GL_APICALL void GL_APIENTRY glClearDepthf (GLclampf depth); GL_APICALL void GL_APIENTRY glClearStencil (GLint s); GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader); -GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); -GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); +GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data); GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GL_APICALL GLuint GL_APIENTRY glCreateProgram (void); @@ -511,7 +512,7 @@ GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shade GL_APICALL void GL_APIENTRY glDisable (GLenum cap); GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index); GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); -GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void* indices); +GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices); GL_APICALL void GL_APIENTRY glEnable (GLenum cap); GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index); GL_APICALL void GL_APIENTRY glFinish (void); @@ -524,10 +525,10 @@ GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target); GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers); GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers); GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint* textures); -GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); -GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); +GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); -GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const char* name); +GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const GLchar* name); GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params); GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params); GL_APICALL GLenum GL_APIENTRY glGetError (void); @@ -535,21 +536,21 @@ GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat* params); GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params); GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint* params); GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params); -GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); +GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog); GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params); GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params); -GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); +GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog); GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); -GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, char* source); +GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source); GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenum name); GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat* params); GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint* params); GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat* params); GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint* params); -GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const char* name); +GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const GLchar* name); GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params); GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params); -GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void** pointer); +GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid** pointer); GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode); GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap); @@ -562,25 +563,25 @@ GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width); GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program); GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); -GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); +GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels); GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void); GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GL_APICALL void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); -GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length); -GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const char** string, const GLint* length); +GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length); +GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar** string, const GLint* length); GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass); -GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat* params); GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint* params); -GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); +GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels); GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat x); GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat* v); GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint x); @@ -610,7 +611,7 @@ GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint indx, GLfloat x, GL GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint indx, const GLfloat* values); GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint indx, const GLfloat* values); -GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); +GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr); GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); #ifdef __cplusplus diff --git a/opengl/include/GLES2/gl2ext.h b/opengl/include/GLES2/gl2ext.h index 72f1ae79ad..de5d65a231 100644 --- a/opengl/include/GLES2/gl2ext.h +++ b/opengl/include/GLES2/gl2ext.h @@ -1,7 +1,7 @@ #ifndef __gl2ext_h_ #define __gl2ext_h_ -/* $Revision: 8271 $ on $Date:: 2009-05-21 09:33:40 -0700 #$ */ +/* $Revision: 10969 $ on $Date:: 2010-04-09 02:27:15 -0700 #$ */ #ifdef __cplusplus extern "C" { @@ -57,6 +57,11 @@ extern "C" { typedef void* GLeglImageOES; #endif +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_UNSIGNED_INT 0x1405 +#endif + /* GL_OES_get_program_binary */ #ifndef GL_OES_get_program_binary #define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 @@ -100,8 +105,8 @@ typedef void* GLeglImageOES; #define GL_STENCIL_INDEX4_OES 0x8D47 #endif -/* GL_OES_texture3D */ -#ifndef GL_OES_texture3D +/* GL_OES_texture_3D */ +#ifndef GL_OES_texture_3D #define GL_TEXTURE_WRAP_R_OES 0x8072 #define GL_TEXTURE_3D_OES 0x806F #define GL_TEXTURE_BINDING_3D_OES 0x806A @@ -110,11 +115,28 @@ typedef void* GLeglImageOES; #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4 #endif +/* GL_OES_texture_float */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_texture_float_linear */ +/* No new tokens introduced by this extension. */ + /* GL_OES_texture_half_float */ #ifndef GL_OES_texture_half_float #define GL_HALF_FLOAT_OES 0x8D61 #endif +/* GL_OES_texture_half_float_linear */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_texture_npot */ +/* No new tokens introduced by this extension. */ + +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#endif + /* GL_OES_vertex_half_float */ /* GL_HALF_FLOAT_OES defined in GL_OES_texture_half_float already. */ @@ -124,6 +146,11 @@ typedef void* GLeglImageOES; #define GL_INT_10_10_10_2_OES 0x8DF7 #endif +/* GL_OES_texture_external */ +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + /*------------------------------------------------------------------------* * AMD extension tokens *------------------------------------------------------------------------*/ @@ -141,11 +168,6 @@ typedef void* GLeglImageOES; #define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE #endif -/* GL_AMD_program_binary_Z400 */ -#ifndef GL_AMD_program_binary_Z400 -#define GL_Z400_BINARY_AMD 0x8740 -#endif - /* GL_AMD_performance_monitor */ #ifndef GL_AMD_performance_monitor #define GL_COUNTER_TYPE_AMD 0x8BC0 @@ -157,35 +179,78 @@ typedef void* GLeglImageOES; #define GL_PERFMON_RESULT_AMD 0x8BC6 #endif +/* GL_AMD_program_binary_Z400 */ +#ifndef GL_AMD_program_binary_Z400 +#define GL_Z400_BINARY_AMD 0x8740 +#endif + /*------------------------------------------------------------------------* * EXT extension tokens *------------------------------------------------------------------------*/ +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_COLOR_EXT 0x1800 +#define GL_DEPTH_EXT 0x1801 +#define GL_STENCIL_EXT 0x1802 +#endif + +/* GL_EXT_multi_draw_arrays */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_BGRA_EXT 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#endif + /* GL_EXT_texture_filter_anisotropic */ #ifndef GL_EXT_texture_filter_anisotropic #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #endif +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + /* GL_EXT_texture_type_2_10_10_10_REV */ #ifndef GL_EXT_texture_type_2_10_10_10_REV #define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368 #endif -/* GL_EXT_texture_format_BGRA8888 */ -#ifndef GL_EXT_texture_format_BGRA8888 -#define GL_BGRA 0x80E1 +/* GL_EXT_texture_compression_dxt1 */ +#ifndef GL_EXT_texture_compression_dxt1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #endif /*------------------------------------------------------------------------* * IMG extension tokens *------------------------------------------------------------------------*/ +/* GL_IMG_program_binary */ +#ifndef GL_IMG_program_binary +#define GL_SGX_PROGRAM_BINARY_IMG 0x9130 +#endif + /* GL_IMG_read_format */ #ifndef GL_IMG_read_format -#define GL_BGRA 0x80E1 -#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 -#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_BGRA_IMG 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365 +#endif + +/* GL_IMG_shader_binary */ +#ifndef GL_IMG_shader_binary +#define GL_SGX_BINARY_IMG 0x8C0A #endif /* GL_IMG_texture_compression_pvrtc */ @@ -196,6 +261,14 @@ typedef void* GLeglImageOES; #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 #endif +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134 +#define GL_MAX_SAMPLES_IMG 0x9135 +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif + /*------------------------------------------------------------------------* * NV extension tokens *------------------------------------------------------------------------*/ @@ -207,6 +280,24 @@ typedef void* GLeglImageOES; #define GL_FENCE_CONDITION_NV 0x84F4 #endif +/* GL_NV_coverage_sample */ +#ifndef GL_NV_coverage_sample +#define GL_COVERAGE_COMPONENT_NV 0x8ED0 +#define GL_COVERAGE_COMPONENT4_NV 0x8ED1 +#define GL_COVERAGE_ATTACHMENT_NV 0x8ED2 +#define GL_COVERAGE_BUFFERS_NV 0x8ED3 +#define GL_COVERAGE_SAMPLES_NV 0x8ED4 +#define GL_COVERAGE_ALL_FRAGMENTS_NV 0x8ED5 +#define GL_COVERAGE_EDGE_FRAGMENTS_NV 0x8ED6 +#define GL_COVERAGE_AUTOMATIC_NV 0x8ED7 +#define GL_COVERAGE_BUFFER_BIT_NV 0x8000 +#endif + +/* GL_NV_depth_nonlinear */ +#ifndef GL_NV_depth_nonlinear +#define GL_DEPTH_COMPONENT16_NONLINEAR_NV 0x8E2C +#endif + /*------------------------------------------------------------------------* * QCOM extension tokens *------------------------------------------------------------------------*/ @@ -214,11 +305,70 @@ typedef void* GLeglImageOES; /* GL_QCOM_driver_control */ /* No new tokens introduced by this extension. */ +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_TEXTURE_WIDTH_QCOM 0x8BD2 +#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3 +#define GL_TEXTURE_DEPTH_QCOM 0x8BD4 +#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5 +#define GL_TEXTURE_FORMAT_QCOM 0x8BD6 +#define GL_TEXTURE_TYPE_QCOM 0x8BD7 +#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8 +#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9 +#define GL_TEXTURE_TARGET_QCOM 0x8BDA +#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB +#define GL_STATE_RESTORE 0x8BDC +#endif + +/* GL_QCOM_extended_get2 */ +/* No new tokens introduced by this extension. */ + /* GL_QCOM_perfmon_global_mode */ #ifndef GL_QCOM_perfmon_global_mode #define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0 #endif +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_WRITEONLY_RENDERING_QCOM 0x8823 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001 +#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002 +#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004 +#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008 +#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010 +#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020 +#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040 +#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080 +#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100 +#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200 +#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400 +#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800 +#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000 +#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000 +#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000 +#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000 +#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000 +#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000 +#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000 +#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000 +#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000 +#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000 +#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000 +#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000 +#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000 +#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000 +#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000 +#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000 +#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000 +#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000 +#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000 +#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000 +#endif + /*------------------------------------------------------------------------* * End of extension tokens, start of corresponding extension functions *------------------------------------------------------------------------*/ @@ -237,17 +387,6 @@ typedef void* GLeglImageOES; #define GL_OES_compressed_paletted_texture 1 #endif -/* GL_OES_EGL_image */ -#ifndef GL_OES_EGL_image -#define GL_OES_EGL_image 1 -#ifdef GL_GLEXT_PROTOTYPES -GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); -GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); -#endif -typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); -typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); -#endif - /* GL_OES_depth24 */ #ifndef GL_OES_depth24 #define GL_OES_depth24 1 @@ -263,6 +402,17 @@ typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenu #define GL_OES_depth_texture 1 #endif +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +#define GL_OES_EGL_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); +#endif +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); +#endif + /* GL_OES_element_index_uint */ #ifndef GL_OES_element_index_uint #define GL_OES_element_index_uint 1 @@ -282,11 +432,11 @@ typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenu #ifndef GL_OES_get_program_binary #define GL_OES_get_program_binary 1 #ifdef GL_GLEXT_PROTOTYPES -GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); -GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const void *binary, GLint length); +GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length); #endif -typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); -typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLint length); +typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length); #endif /* GL_OES_mapbuffer */ @@ -295,11 +445,11 @@ typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum bi #ifdef GL_GLEXT_PROTOTYPES GL_APICALL void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); GL_APICALL GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); -GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params); +GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid** params); #endif typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); -typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid** params); #endif /* GL_OES_packed_depth_stencil */ @@ -331,46 +481,61 @@ typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum #ifndef GL_OES_texture_3D #define GL_OES_texture_3D 1 #ifdef GL_GLEXT_PROTOTYPES -GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels); -GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); +GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels); +GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels); GL_APICALL void GL_APIENTRY glCopyTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data); -GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data); +GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data); +GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); GL_APICALL void GL_APIENTRY glFramebufferTexture3DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); #endif typedef void (GL_APIENTRYP PFNGLTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels); -typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); +typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels); typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data); -typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data); +typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data); +typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); #endif -/* GL_OES_texture_float_linear */ -#ifndef GL_OES_texture_float_linear -#define GL_OES_texture_float_linear 1 -#endif - -/* GL_OES_texture_half_float_linear */ -#ifndef GL_OES_texture_half_float_linear -#define GL_OES_texture_half_float_linear 1 -#endif - /* GL_OES_texture_float */ #ifndef GL_OES_texture_float #define GL_OES_texture_float 1 #endif +/* GL_OES_texture_float_linear */ +#ifndef GL_OES_texture_float_linear +#define GL_OES_texture_float_linear 1 +#endif + /* GL_OES_texture_half_float */ #ifndef GL_OES_texture_half_float #define GL_OES_texture_half_float 1 #endif +/* GL_OES_texture_half_float_linear */ +#ifndef GL_OES_texture_half_float_linear +#define GL_OES_texture_half_float_linear 1 +#endif + /* GL_OES_texture_npot */ #ifndef GL_OES_texture_npot #define GL_OES_texture_npot 1 #endif +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_OES_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glBindVertexArrayOES (GLuint array); +GL_APICALL void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays); +GL_APICALL void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays); +GL_APICALL GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array); +#endif +typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array); +typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays); +typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array); +#endif + /* GL_OES_vertex_half_float */ #ifndef GL_OES_vertex_half_float #define GL_OES_vertex_half_float 1 @@ -381,6 +546,11 @@ typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum #define GL_OES_vertex_type_10_10_10_2 1 #endif +/* GL_OES_texture_external */ +#ifndef GL_OES_texture_external +#define GL_OES_texture_external 1 +#endif + /*------------------------------------------------------------------------* * AMD extension functions *------------------------------------------------------------------------*/ @@ -395,20 +565,15 @@ typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum #define GL_AMD_compressed_ATC_texture 1 #endif -/* GL_AMD_program_binary_Z400 */ -#ifndef GL_AMD_program_binary_Z400 -#define GL_AMD_program_binary_Z400 1 -#endif - /* AMD_performance_monitor */ #ifndef GL_AMD_performance_monitor #define GL_AMD_performance_monitor 1 #ifdef GL_GLEXT_PROTOTYPES GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); GL_APICALL void GL_APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); -GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString); -GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString); -GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, GLvoid *data); GL_APICALL void GL_APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); GL_APICALL void GL_APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); GL_APICALL void GL_APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList); @@ -418,9 +583,9 @@ GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLen #endif typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); -typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString); -typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString); -typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, GLvoid *data); typedef void (GL_APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); typedef void (GL_APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); typedef void (GL_APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList); @@ -429,39 +594,99 @@ typedef void (GL_APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); #endif +/* GL_AMD_program_binary_Z400 */ +#ifndef GL_AMD_program_binary_Z400 +#define GL_AMD_program_binary_Z400 1 +#endif + /*------------------------------------------------------------------------* * EXT extension functions *------------------------------------------------------------------------*/ +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_EXT_discard_framebuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif +typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glMultiDrawArraysEXT (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +GL_APICALL void GL_APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_EXT_read_format_bgra 1 +#endif + /* GL_EXT_texture_filter_anisotropic */ #ifndef GL_EXT_texture_filter_anisotropic #define GL_EXT_texture_filter_anisotropic 1 #endif +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_EXT_texture_format_BGRA8888 1 +#endif + /* GL_EXT_texture_type_2_10_10_10_REV */ #ifndef GL_EXT_texture_type_2_10_10_10_REV #define GL_EXT_texture_type_2_10_10_10_REV 1 #endif -/* GL_EXT_texture_format_BGRA8888 */ -#ifndef GL_EXT_texture_format_BGRA8888 -#define GL_EXT_texture_format_BGRA8888 1 +/* GL_EXT_texture_compression_dxt1 */ +#ifndef GL_EXT_texture_compression_dxt1 +#define GL_EXT_texture_compression_dxt1 1 #endif /*------------------------------------------------------------------------* * IMG extension functions *------------------------------------------------------------------------*/ +/* GL_IMG_program_binary */ +#ifndef GL_IMG_program_binary +#define GL_IMG_program_binary 1 +#endif + /* GL_IMG_read_format */ #ifndef GL_IMG_read_format #define GL_IMG_read_format 1 #endif +/* GL_IMG_shader_binary */ +#ifndef GL_IMG_shader_binary +#define GL_IMG_shader_binary 1 +#endif + /* GL_IMG_texture_compression_pvrtc */ #ifndef GL_IMG_texture_compression_pvrtc #define GL_IMG_texture_compression_pvrtc 1 #endif +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_IMG_multisampled_render_to_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif + /*------------------------------------------------------------------------* * NV extension functions *------------------------------------------------------------------------*/ @@ -487,6 +712,22 @@ typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); #endif +/* GL_NV_coverage_sample */ +#ifndef GL_NV_coverage_sample +#define GL_NV_coverage_sample 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glCoverageMaskNV (GLboolean mask); +GL_APICALL void GL_APIENTRY glCoverageOperationNV (GLenum operation); +#endif +typedef void (GL_APIENTRYP PFNGLCOVERAGEMASKNVPROC) (GLboolean mask); +typedef void (GL_APIENTRYP PFNGLCOVERAGEOPERATIONNVPROC) (GLenum operation); +#endif + +/* GL_NV_depth_nonlinear */ +#ifndef GL_NV_depth_nonlinear +#define GL_NV_depth_nonlinear 1 +#endif + /*------------------------------------------------------------------------* * QCOM extension functions *------------------------------------------------------------------------*/ @@ -496,21 +737,75 @@ typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition) #define GL_QCOM_driver_control 1 #ifdef GL_GLEXT_PROTOTYPES GL_APICALL void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls); -GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString); +GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); GL_APICALL void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl); GL_APICALL void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl); #endif typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls); -typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString); +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); #endif +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_QCOM_extended_get 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures); +GL_APICALL void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +GL_APICALL void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +GL_APICALL void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +GL_APICALL void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +GL_APICALL void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +GL_APICALL void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params); +#endif + +/* GL_QCOM_extended_get2 */ +#ifndef GL_QCOM_extended_get2 +#define GL_QCOM_extended_get2 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders); +GL_APICALL void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +GL_APICALL GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program); +GL_APICALL void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif + /* GL_QCOM_perfmon_global_mode */ #ifndef GL_QCOM_perfmon_global_mode #define GL_QCOM_perfmon_global_mode 1 #endif +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_QCOM_writeonly_rendering 1 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_QCOM_tiled_rendering 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); +#endif +typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask); +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/include/GLES2/gl2platform.h b/opengl/include/GLES2/gl2platform.h index 3e9036c162..c9fa3c4d64 100644 --- a/opengl/include/GLES2/gl2platform.h +++ b/opengl/include/GLES2/gl2platform.h @@ -1,7 +1,7 @@ #ifndef __gl2platform_h_ #define __gl2platform_h_ -/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */ +/* $Revision: 10602 $ on $Date:: 2010-03-04 22:35:34 -0800 #$ */ /* * This document is licensed under the SGI Free Software B License Version @@ -9,7 +9,6 @@ */ /* Platform-specific types and definitions for OpenGL ES 2.X gl2.h - * Last modified on 2008/12/19 * * Adopters may modify khrplatform.h and this file to suit their platform. * You are encouraged to submit all modifications to the Khronos group so that @@ -24,6 +23,8 @@ #define GL_APICALL KHRONOS_APICALL #endif +#ifndef GL_APIENTRY #define GL_APIENTRY KHRONOS_APIENTRY +#endif #endif /* __gl2platform_h_ */ diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk index 8abd649968..b5c018f462 100644 --- a/opengl/libagl/Android.mk +++ b/opengl/libagl/Android.mk @@ -6,9 +6,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -# Set to 1 to use gralloc and copybits -LIBAGL_USE_GRALLOC_COPYBITS := 1 - LOCAL_SRC_FILES:= \ egl.cpp \ state.cpp \ @@ -51,13 +48,6 @@ ifneq ($(TARGET_SIMULATOR),true) LOCAL_C_INCLUDES += bionic/libc/private endif -ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1) - LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS - LOCAL_SRC_FILES += copybit.cpp - LOCAL_SHARED_LIBRARIES += libui -endif - - LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl LOCAL_MODULE:= libGLES_android diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp index 255ccac356..bbb82fc48a 100644 --- a/opengl/libagl/TextureObjectManager.cpp +++ b/opengl/libagl/TextureObjectManager.cpp @@ -55,9 +55,6 @@ void EGLTextureObject::init() memset(crop_rect, 0, sizeof(crop_rect)); generate_mipmap = GL_FALSE; direct = GL_FALSE; -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - try_copybit = false; -#endif // LIBAGL_USE_GRALLOC_COPYBITS buffer = 0; } diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h index 279e0406c2..70e3bef6e7 100644 --- a/opengl/libagl/TextureObjectManager.h +++ b/opengl/libagl/TextureObjectManager.h @@ -80,9 +80,6 @@ public: GLint crop_rect[4]; GLint generate_mipmap; GLint direct; -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - bool try_copybit; -#endif // LIBAGL_USE_GRALLOC_COPYBITS android_native_buffer_t* buffer; }; diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp index 71825c502a..7fbe9b5f96 100644 --- a/opengl/libagl/array.cpp +++ b/opengl/libagl/array.cpp @@ -26,9 +26,6 @@ #include "primitives.h" #include "texture.h" #include "BufferObjectManager.h" -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include "copybit.h" -#endif // LIBAGL_USE_GRALLOC_COPYBITS // ---------------------------------------------------------------------------- @@ -688,8 +685,8 @@ static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c, } while (num); } if (count) { - v0 = c->vc.vBuffer + 2 + num - 2; - v1 = c->vc.vBuffer + 2 + num - 1; + v0 = c->vc.vBuffer + 2 + vcs - 2; + v1 = c->vc.vBuffer + 2 + vcs - 1; if ((winding&2) == 0) { // for strips copy back the two last compiled vertices c->vc.vBuffer[0] = *v0; @@ -707,12 +704,6 @@ void drawPrimitivesTriangleStrip(ogles_context_t* c, void drawPrimitivesTriangleFan(ogles_context_t* c, GLint first, GLsizei count) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTriangleFanWithCopybit(c, first, count)) { - return; - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - drawPrimitivesTriangleFanOrStrip(c, first, count, 2); } diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp deleted file mode 100644 index 67d1ce70be..0000000000 --- a/opengl/libagl/copybit.cpp +++ /dev/null @@ -1,618 +0,0 @@ -/* -** -** Copyright 2009, 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 "context.h" -#include "fp.h" -#include "state.h" -#include "matrix.h" -#include "vertex.h" -#include "light.h" -#include "primitives.h" -#include "texture.h" -#include "BufferObjectManager.h" -#include "TextureObjectManager.h" - -#include <hardware/gralloc.h> -#include <hardware/copybit.h> -#include <private/ui/android_natives_priv.h> - -#include <ui/GraphicBuffer.h> -#include <ui/Region.h> -#include <ui/Rect.h> - - -#define DEBUG_COPYBIT false - -// ---------------------------------------------------------------------------- - -namespace android { - -static void textureToCopyBitImage( - const GGLSurface* surface, int32_t opFormat, - android_native_buffer_t* buffer, copybit_image_t* img) -{ - img->w = surface->stride; - img->h = surface->height; - img->format = opFormat; - img->base = surface->data; - img->handle = (native_handle_t *)buffer->handle; -} - -struct clipRectRegion : public copybit_region_t { - clipRectRegion(ogles_context_t* c) - { - scissor_t const* scissor = &c->rasterizer.state.scissor; - r.l = scissor->left; - r.t = scissor->top; - r.r = scissor->right; - r.b = scissor->bottom; - next = iterate; - } -private: - static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { - *rect = static_cast<clipRectRegion const*>(self)->r; - const_cast<copybit_region_t *>(self)->next = iterate_done; - return 1; - } - static int iterate_done(copybit_region_t const *, copybit_rect_t*) { - return 0; - } -public: - copybit_rect_t r; -}; - -static bool supportedCopybitsFormat(int format) { - switch (format) { - case COPYBIT_FORMAT_RGBA_8888: - case COPYBIT_FORMAT_RGBX_8888: - case COPYBIT_FORMAT_RGB_888: - case COPYBIT_FORMAT_RGB_565: - case COPYBIT_FORMAT_BGRA_8888: - case COPYBIT_FORMAT_RGBA_5551: - case COPYBIT_FORMAT_RGBA_4444: - return true; - default: - return false; - } -} - -static bool hasAlpha(int format) { - switch (format) { - case COPYBIT_FORMAT_RGBA_8888: - case COPYBIT_FORMAT_BGRA_8888: - case COPYBIT_FORMAT_RGBA_5551: - case COPYBIT_FORMAT_RGBA_4444: - return true; - default: - return false; - } -} - -static inline int fixedToByte(GGLfixed val) { - return (val - (val >> 8)) >> 8; -} - -/** - * Performs a quick check of the rendering state. If this function returns - * false we cannot use the copybit driver. - */ - -static bool checkContext(ogles_context_t* c) { - - // By convention copybitQuickCheckContext() has already returned true. - // avoid checking the same information again. - - if (c->copybits.blitEngine == NULL) { - LOGD_IF(DEBUG_COPYBIT, "no copybit hal"); - return false; - } - - if (c->rasterizer.state.enables - & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { - LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog"); - return false; - } - - // Note: The drawSurfaceBuffer is only set for destination - // surfaces types that are supported by the hardware and - // do not have an alpha channel. So we don't have to re-check that here. - - static const int tmu = 0; - texture_unit_t& u(c->textures.tmu[tmu]); - EGLTextureObject* textureObject = u.texture; - - if (!supportedCopybitsFormat(textureObject->surface.format)) { - LOGD_IF(DEBUG_COPYBIT, "texture format not supported"); - return false; - } - return true; -} - - -static bool copybit(GLint x, GLint y, - GLint w, GLint h, - EGLTextureObject* textureObject, - const GLint* crop_rect, - int transform, - ogles_context_t* c) -{ - status_t err = NO_ERROR; - - // We assume checkContext has already been called and has already - // returned true. - - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - - y = cbSurface.height - (y + h); - - const GLint Ucr = crop_rect[0]; - const GLint Vcr = crop_rect[1]; - const GLint Wcr = crop_rect[2]; - const GLint Hcr = crop_rect[3]; - - GLint screen_w = w; - GLint screen_h = h; - int32_t dsdx = Wcr << 16; // dsdx = ((Wcr/screen_w)/Wt)*Wt - int32_t dtdy = Hcr << 16; // dtdy = -((Hcr/screen_h)/Ht)*Ht - if (transform & COPYBIT_TRANSFORM_ROT_90) { - swap(screen_w, screen_h); - } - if (dsdx!=screen_w || dtdy!=screen_h) { - // in most cases the divide is not needed - dsdx /= screen_w; - dtdy /= screen_h; - } - dtdy = -dtdy; // see equation of dtdy above - - // copybit doesn't say anything about filtering, so we can't - // discriminate. On msm7k, copybit will always filter. - // the code below handles min/mag filters, we keep it as a reference. - -#ifdef MIN_MAG_FILTER - int32_t texelArea = gglMulx(dtdy, dsdx); - if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) { - // Non-linear filtering on a texture enlargement. - LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR"); - return false; - } - if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) { - // Non-linear filtering on an texture shrink. - LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR"); - return false; - } -#endif - - const uint32_t enables = c->rasterizer.state.enables; - int planeAlpha = 255; - bool alphaPlaneWorkaround = false; - static const int tmu = 0; - texture_t& tev(c->rasterizer.state.texture[tmu]); - int32_t opFormat = textureObject->surface.format; - const bool srcTextureHasAlpha = hasAlpha(opFormat); - if (!srcTextureHasAlpha) { - planeAlpha = fixedToByte(c->currentColorClamped.a); - } - - const bool cbHasAlpha = hasAlpha(cbSurface.format); - bool blending = false; - if ((enables & GGL_ENABLE_BLENDING) - && !(c->rasterizer.state.blend.src == GL_ONE - && c->rasterizer.state.blend.dst == GL_ZERO)) { - // Blending is OK if it is - // the exact kind of blending that the copybits hardware supports. - // Note: The hardware only supports - // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA, - // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA. - // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case, - // because the performance is worth it, even if the results are - // not correct. - if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA - || c->rasterizer.state.blend.src == GL_ONE) - && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA - && c->rasterizer.state.blend.alpha_separate == 0)) { - // Incompatible blend mode. - LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode"); - return false; - } - blending = true; - } else { - if (cbHasAlpha) { - // NOTE: the result will be slightly wrong in this case because - // the destination alpha channel will be set to 1.0 instead of - // the iterated alpha value. *shrug*. - } - // disable plane blending and src blending for supported formats - planeAlpha = 255; - if (opFormat == COPYBIT_FORMAT_RGBA_8888) { - opFormat = COPYBIT_FORMAT_RGBX_8888; - } else { - if (srcTextureHasAlpha) { - LOGD_IF(DEBUG_COPYBIT, "texture format requires blending"); - return false; - } - } - } - - switch (tev.env) { - case GGL_REPLACE: - break; - case GGL_MODULATE: - // only cases allowed is: - // RGB source, color={1,1,1,a} -> can be done with GL_REPLACE - // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE - if (blending) { - if (c->currentColorClamped.r == c->currentColorClamped.a && - c->currentColorClamped.g == c->currentColorClamped.a && - c->currentColorClamped.b == c->currentColorClamped.a) { - // TODO: RGBA source, color={1,1,1,a} / regular-blending - // is equivalent - alphaPlaneWorkaround = true; - break; - } - } - LOGD_IF(DEBUG_COPYBIT, "GGL_MODULATE"); - return false; - default: - // Incompatible texture environment. - LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment"); - return false; - } - - copybit_device_t* copybit = c->copybits.blitEngine; - copybit_image_t src; - textureToCopyBitImage(&textureObject->surface, opFormat, - textureObject->buffer, &src); - copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr }; - - /* - * Below we perform extra passes needed to emulate things the h/w - * cannot do. - */ - - const GLfixed minScaleInv = gglDivQ(0x10000, c->copybits.minScale, 16); - const GLfixed maxScaleInv = gglDivQ(0x10000, c->copybits.maxScale, 16); - - sp<GraphicBuffer> tempBitmap; - - if (dsdx < maxScaleInv || dsdx > minScaleInv || - dtdy < maxScaleInv || dtdy > minScaleInv) - { - // The requested scale is out of the range the hardware - // can support. - LOGD_IF(DEBUG_COPYBIT, - "scale out of range dsdx=%08x (Wcr=%d / w=%d), " - "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d", - dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr); - - int32_t xscale=0x10000, yscale=0x10000; - if (dsdx > minScaleInv) xscale = c->copybits.minScale; - else if (dsdx < maxScaleInv) xscale = c->copybits.maxScale; - if (dtdy > minScaleInv) yscale = c->copybits.minScale; - else if (dtdy < maxScaleInv) yscale = c->copybits.maxScale; - dsdx = gglMulx(dsdx, xscale); - dtdy = gglMulx(dtdy, yscale); - - /* we handle only one step of resizing below. Handling an arbitrary - * number is relatively easy (replace "if" above by "while"), but requires - * two intermediate buffers and so far we never had the need. - */ - - if (dsdx < maxScaleInv || dsdx > minScaleInv || - dtdy < maxScaleInv || dtdy > minScaleInv) { - LOGD_IF(DEBUG_COPYBIT, - "scale out of range dsdx=%08x (Wcr=%d / w=%d), " - "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d", - dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr); - return false; - } - - const int tmp_w = gglMulx(srect.r - srect.l, xscale, 16); - const int tmp_h = gglMulx(srect.b - srect.t, yscale, 16); - - LOGD_IF(DEBUG_COPYBIT, - "xscale=%08x, yscale=%08x, dsdx=%08x, dtdy=%08x, tmp_w=%d, tmp_h=%d", - xscale, yscale, dsdx, dtdy, tmp_w, tmp_h); - - tempBitmap = new GraphicBuffer( - tmp_w, tmp_h, src.format, - GraphicBuffer::USAGE_HW_2D); - - err = tempBitmap->initCheck(); - if (err == NO_ERROR) { - copybit_image_t tmp_dst; - copybit_rect_t tmp_rect; - tmp_dst.w = tmp_w; - tmp_dst.h = tmp_h; - tmp_dst.format = tempBitmap->format; - tmp_dst.handle = (native_handle_t*)tempBitmap->getNativeBuffer()->handle; - tmp_rect.l = 0; - tmp_rect.t = 0; - tmp_rect.r = tmp_dst.w; - tmp_rect.b = tmp_dst.h; - region_iterator tmp_it(Region(Rect(tmp_rect.r, tmp_rect.b))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmp_dst, &src, &tmp_rect, &srect, &tmp_it); - src = tmp_dst; - srect = tmp_rect; - } - } - - copybit_image_t dst; - textureToCopyBitImage(&cbSurface, cbSurface.format, - c->copybits.drawSurfaceBuffer, &dst); - copybit_rect_t drect = {x, y, x+w, y+h}; - - - /* and now the alpha-plane hack. This handles the "Fade" case of a - * texture with an alpha channel. - */ - if (alphaPlaneWorkaround) { - sp<GraphicBuffer> tempCb = new GraphicBuffer( - w, h, COPYBIT_FORMAT_RGB_565, - GraphicBuffer::USAGE_HW_2D); - - err = tempCb->initCheck(); - - copybit_image_t tmpCbImg; - copybit_rect_t tmpCbRect; - copybit_rect_t tmpdrect = drect; - tmpCbImg.w = w; - tmpCbImg.h = h; - tmpCbImg.format = tempCb->format; - tmpCbImg.handle = (native_handle_t*)tempCb->getNativeBuffer()->handle; - tmpCbRect.l = 0; - tmpCbRect.t = 0; - - if (drect.l < 0) { - tmpCbRect.l = -tmpdrect.l; - tmpdrect.l = 0; - } - if (drect.t < 0) { - tmpCbRect.t = -tmpdrect.t; - tmpdrect.t = 0; - } - if (drect.l + tmpCbImg.w > dst.w) { - tmpCbImg.w = dst.w - drect.l; - tmpdrect.r = dst.w; - } - if (drect.t + tmpCbImg.h > dst.h) { - tmpCbImg.h = dst.h - drect.t; - tmpdrect.b = dst.h; - } - - tmpCbRect.r = tmpCbImg.w; - tmpCbRect.b = tmpCbImg.h; - - if (!err) { - // first make a copy of the destination buffer - region_iterator tmp_it(Region(Rect(w, h))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it); - } - if (!err) { - // then proceed as usual, but without the alpha plane - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, - (enables & GGL_ENABLE_DITHER) ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - clipRectRegion it(c); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - } - if (!err) { - // finally copy back the destination on top with 1-alphaplane - int invPlaneAlpha = 0xFF - fixedToByte(c->currentColorClamped.a); - clipRectRegion it(c); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, invPlaneAlpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - err = copybit->stretch(copybit, - &dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it); - } - } else { - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - (enables & GGL_ENABLE_DITHER) ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - clipRectRegion it(c); - - LOGD_IF(0, - "dst={%d, %d, %d, %p, %p}, " - "src={%d, %d, %d, %p, %p}, " - "drect={%d,%d,%d,%d}, " - "srect={%d,%d,%d,%d}, " - "it={%d,%d,%d,%d}, " , - dst.w, dst.h, dst.format, dst.base, dst.handle, - src.w, src.h, src.format, src.base, src.handle, - drect.l, drect.t, drect.r, drect.b, - srect.l, srect.t, srect.r, srect.b, - it.r.l, it.r.t, it.r.r, it.r.b - ); - - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - } - if (err != NO_ERROR) { - c->textures.tmu[0].texture->try_copybit = false; - } - return err == NO_ERROR ? true : false; -} - -/* - * Try to draw a triangle fan with copybit, return false if we fail. - */ -bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count) -{ - if (!checkContext(c)) { - return false; - } - - // FIXME: we should handle culling here - c->arrays.compileElements(c, c->vc.vBuffer, 0, 4); - - // we detect if we're dealing with a rectangle, by comparing the - // rectangles {v0,v2} and {v1,v3} which should be identical. - - // NOTE: we should check that the rectangle is window aligned, however - // if we do that, the optimization won't be taken in a lot of cases. - // Since this code is intended to be used with SurfaceFlinger only, - // so it's okay... - - const vec4_t& v0 = c->vc.vBuffer[0].window; - const vec4_t& v1 = c->vc.vBuffer[1].window; - const vec4_t& v2 = c->vc.vBuffer[2].window; - const vec4_t& v3 = c->vc.vBuffer[3].window; - int l = min(v0.x, v2.x); - int b = min(v0.y, v2.y); - int r = max(v0.x, v2.x); - int t = max(v0.y, v2.y); - if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) || - (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) { - LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle"); - return false; - } - - // fetch and transform texture coordinates - // NOTE: maybe it would be better to have a "compileElementsAll" method - // that would ensure all vertex data are fetched and transformed - const transform_t& tr = c->transforms.texture[0].transform; - for (size_t i=0 ; i<4 ; i++) { - const GLubyte* tp = c->arrays.texture[0].element(i); - vertex_t* const v = &c->vc.vBuffer[i]; - c->arrays.texture[0].fetch(c, v->texture[0].v, tp); - // FIXME: we should bail if q!=1 - c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]); - } - - const vec4_t& t0 = c->vc.vBuffer[0].texture[0]; - const vec4_t& t1 = c->vc.vBuffer[1].texture[0]; - const vec4_t& t2 = c->vc.vBuffer[2].texture[0]; - const vec4_t& t3 = c->vc.vBuffer[3].texture[0]; - int txl = min(t0.x, t2.x); - int txb = min(t0.y, t2.y); - int txr = max(t0.x, t2.x); - int txt = max(t0.y, t2.y); - if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) || - (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) { - LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle"); - return false; - } - if ((txl != 0) || (txb != 0) || - (txr != FIXED_ONE) || (txt != FIXED_ONE)) { - // we could probably handle this case, if we wanted to - LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x", - txl, txb, txr, txt); - return false; - } - - // at this point, we know we are dealing with a rectangle, so we - // only need to consider 3 vertices for computing the jacobians - - const int dx01 = v1.x - v0.x; - const int dx02 = v2.x - v0.x; - const int dy01 = v1.y - v0.y; - const int dy02 = v2.y - v0.y; - const int ds01 = t1.S - t0.S; - const int ds02 = t2.S - t0.S; - const int dt01 = t1.T - t0.T; - const int dt02 = t2.T - t0.T; - const int area = dx01*dy02 - dy01*dx02; - int dsdx, dsdy, dtdx, dtdy; - if (area >= 0) { - dsdx = ds01*dy02 - ds02*dy01; - dtdx = dt01*dy02 - dt02*dy01; - dsdy = ds02*dx01 - ds01*dx02; - dtdy = dt02*dx01 - dt01*dx02; - } else { - dsdx = ds02*dy01 - ds01*dy02; - dtdx = dt02*dy01 - dt01*dy02; - dsdy = ds01*dx02 - ds02*dx01; - dtdy = dt01*dx02 - dt02*dx01; - } - - // here we rely on the fact that we know the transform is - // a rigid-body transform AND that it can only rotate in 90 degrees - // increments - - int transform = 0; - if (dsdx == 0) { - // 90 deg rotation case - // [ 0 dtdx ] - // [ dsdx 0 ] - transform |= COPYBIT_TRANSFORM_ROT_90; - // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted - if (dtdx > 0) - transform |= COPYBIT_TRANSFORM_FLIP_H; - if (dsdy < 0) - transform |= COPYBIT_TRANSFORM_FLIP_V; - } else { - // [ dsdx 0 ] - // [ 0 dtdy ] - if (dsdx < 0) - transform |= COPYBIT_TRANSFORM_FLIP_H; - if (dtdy < 0) - transform |= COPYBIT_TRANSFORM_FLIP_V; - } - - //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform); - //LOGD("A=%f\tB=%f\nC=%f\tD=%f", - // dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0); - - int x = l >> 4; - int y = b >> 4; - int w = (r-l) >> 4; - int h = (t-b) >> 4; - texture_unit_t& u(c->textures.tmu[0]); - EGLTextureObject* textureObject = u.texture; - GLint tWidth = textureObject->surface.width; - GLint tHeight = textureObject->surface.height; - GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight}; - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - y = cbSurface.height - (y + h); - return copybit(x, y, w, h, textureObject, crop_rect, transform, c); -} - -/* - * Try to drawTexiOESWithCopybit, return false if we fail. - */ - -bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c) -{ - // quickly process empty rects - if ((w|h) <= 0) { - return true; - } - if (!checkContext(c)) { - return false; - } - texture_unit_t& u(c->textures.tmu[0]); - EGLTextureObject* textureObject = u.texture; - return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c); -} - -} // namespace android - diff --git a/opengl/libagl/copybit.h b/opengl/libagl/copybit.h deleted file mode 100644 index b8b5afda26..0000000000 --- a/opengl/libagl/copybit.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -** -** Copyright 2009, 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_OPENGLES_COPYBIT_H -#define ANDROID_OPENGLES_COPYBIT_H - -#include <stdlib.h> - -#include <GLES/gl.h> - -#include "TextureObjectManager.h" -namespace android { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - -bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c); - -bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, - GLsizei count); - -inline bool copybitQuickCheckContext(ogles_context_t* c) { - return c->copybits.drawSurfaceBuffer != 0 - && c->rasterizer.state.enabled_tmu == 1 - && c->textures.tmu[0].texture->try_copybit; -} - -/* - * Tries to draw a drawTexiOES using copybit hardware. - * Returns true if successful. - */ -inline bool drawTexiOESWithCopybit(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c) { - if (!copybitQuickCheckContext(c)) { - return false; - } - - return drawTexiOESWithCopybit_impl(x, y, z, w, h, c); -} - -/* - * Tries to draw a triangle fan using copybit hardware. - * Returns true if successful. - */ -inline bool drawTriangleFanWithCopybit(ogles_context_t* c, GLint first, - GLsizei count) { - /* - * We are looking for the glDrawArrays call made by SurfaceFlinger. - */ - - if ((count!=4) || first || !copybitQuickCheckContext(c)) - return false; - - return drawTriangleFanWithCopybit_impl(c, first, count); -} - - -#endif // LIBAGL_USE_GRALLOC_COPYBITS - -} // namespace android - -#endif // ANDROID_OPENGLES_COPYBIT_H diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 79c5ecb939..53cc3984dd 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -213,7 +213,7 @@ struct egl_window_surface_v2_t : public egl_surface_t egl_window_surface_v2_t( EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - android_native_window_t* window); + ANativeWindow* window); ~egl_window_surface_v2_t(); @@ -235,7 +235,7 @@ struct egl_window_surface_v2_t : public egl_surface_t private: status_t lock(android_native_buffer_t* buf, int usage, void** vaddr); status_t unlock(android_native_buffer_t* buf); - android_native_window_t* nativeWindow; + ANativeWindow* nativeWindow; android_native_buffer_t* buffer; android_native_buffer_t* previousBuffer; gralloc_module_t const* module; @@ -355,7 +355,7 @@ private: egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - android_native_window_t* window) + ANativeWindow* window) : egl_surface_t(dpy, config, depthFormat), nativeWindow(window), buffer(0), previousBuffer(0), module(0), blitengine(0), bits(NULL) @@ -576,41 +576,44 @@ EGLBoolean egl_window_surface_v2_t::swapBuffers() buffer = 0; // dequeue a new buffer - nativeWindow->dequeueBuffer(nativeWindow, &buffer); - - // TODO: lockBuffer should rather be executed when the very first - // direct rendering occurs. - nativeWindow->lockBuffer(nativeWindow, buffer); - - // reallocate the depth-buffer if needed - if ((width != buffer->width) || (height != buffer->height)) { - // TODO: we probably should reset the swap rect here - // if the window size has changed - width = buffer->width; - height = buffer->height; - if (depth.data) { - free(depth.data); - depth.width = width; - depth.height = height; - depth.stride = buffer->stride; - depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_FALSE); - return EGL_FALSE; + if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) { + + // TODO: lockBuffer should rather be executed when the very first + // direct rendering occurs. + nativeWindow->lockBuffer(nativeWindow, buffer); + + // reallocate the depth-buffer if needed + if ((width != buffer->width) || (height != buffer->height)) { + // TODO: we probably should reset the swap rect here + // if the window size has changed + width = buffer->width; + height = buffer->height; + if (depth.data) { + free(depth.data); + depth.width = width; + depth.height = height; + depth.stride = buffer->stride; + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_FALSE); + return EGL_FALSE; + } } } - } - - // keep a reference on the buffer - buffer->common.incRef(&buffer->common); - // finally pin the buffer down - if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { - LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)", - buffer, buffer->width, buffer->height); - return setError(EGL_BAD_ACCESS, EGL_FALSE); - // FIXME: we should make sure we're not accessing the buffer anymore + // keep a reference on the buffer + buffer->common.incRef(&buffer->common); + + // finally pin the buffer down + if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { + LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)", + buffer, buffer->width, buffer->height); + return setError(EGL_BAD_ACCESS, EGL_FALSE); + // FIXME: we should make sure we're not accessing the buffer anymore + } + } else { + return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE); } return EGL_TRUE; @@ -628,23 +631,6 @@ EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const return buffer; } -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - -static bool supportedCopybitsDestinationFormat(int format) { - // Hardware supported - switch (format) { - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGBA_4444: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_BGRA_8888: - return true; - } - return false; -} -#endif - EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl) { GGLSurface buffer; @@ -658,18 +644,6 @@ EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl) if (depth.data != gl->rasterizer.state.buffers.depth.data) gl->rasterizer.procs.depthBuffer(gl, &depth); -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - gl->copybits.drawSurfaceBuffer = 0; - if (gl->copybits.blitEngine != NULL) { - if (supportedCopybitsDestinationFormat(buffer.format)) { - buffer_handle_t handle = this->buffer->handle; - if (handle != NULL) { - gl->copybits.drawSurfaceBuffer = this->buffer; - } - } - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - return EGL_TRUE; } EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl) @@ -1300,7 +1274,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, if (!(surfaceType & EGL_WINDOW_BIT)) return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - if (static_cast<android_native_window_t*>(window)->common.magic != + if (static_cast<ANativeWindow*>(window)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) { return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); } @@ -1323,7 +1297,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, egl_surface_t* surface; surface = new egl_window_surface_v2_t(dpy, config, depthFormat, - static_cast<android_native_window_t*>(window)); + static_cast<ANativeWindow*>(window)); if (!surface->initCheck()) { // there was a problem in the ctor, the error @@ -1525,8 +1499,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, } if (ggl_unlikely(attrib_list==0)) { - *num_config = 0; - return EGL_TRUE; + /* + * A NULL attrib_list should be treated as though it was an empty + * one (terminated with EGL_NONE) as defined in + * section 3.4.1 "Querying Configurations" in the EGL specification. + */ + static const EGLint dummy = EGL_NONE; + attrib_list = &dummy; } int numAttributes = 0; diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp index 27bb5454fd..a0f720a0d1 100644 --- a/opengl/libagl/state.cpp +++ b/opengl/libagl/state.cpp @@ -28,10 +28,6 @@ #include "BufferObjectManager.h" #include "TextureObjectManager.h" -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include <hardware/copybit.h> -#endif // LIBAGL_USE_GRALLOC_COPYBITS - namespace android { // ---------------------------------------------------------------------------- @@ -101,35 +97,6 @@ ogles_context_t *ogles_init(size_t extra) // OpenGL enables dithering by default c->rasterizer.procs.enable(c, GL_DITHER); - c->copybits.blitEngine = NULL; - c->copybits.minScale = 0; - c->copybits.maxScale = 0; - c->copybits.drawSurfaceBuffer = 0; - -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - hw_module_t const* module; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - struct copybit_device_t* copyBits; - if (copybit_open(module, ©Bits) == 0) { - c->copybits.blitEngine = copyBits; - { - int minLim = copyBits->get(copyBits, - COPYBIT_MINIFICATION_LIMIT); - if (minLim != -EINVAL && minLim > 0) { - c->copybits.minScale = (1 << 16) / minLim; - } - } - { - int magLim = copyBits->get(copyBits, - COPYBIT_MAGNIFICATION_LIMIT); - if (magLim != -EINVAL && magLim > 0) { - c->copybits.maxScale = min(32*1024-1, magLim) << 16; - } - } - } - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - return c; } @@ -144,11 +111,6 @@ void ogles_uninit(ogles_context_t* c) c->bufferObjectManager->decStrong(c); ggl_uninit_context(&(c->rasterizer)); free(c->rasterizer.base); -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (c->copybits.blitEngine != NULL) { - copybit_close((struct copybit_device_t*) c->copybits.blitEngine); - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS } void _ogles_error(ogles_context_t* c, GLenum error) diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index d67612e681..eb9689551f 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -26,10 +26,6 @@ #include <private/ui/android_natives_priv.h> #include <ETC1/etc1.h> -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include "copybit.h" -#endif // LIBAGL_USE_GRALLOC_COPYBITS - namespace android { // ---------------------------------------------------------------------------- @@ -763,17 +759,10 @@ static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, ogles_context_t* c) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTexiOESWithCopybit(gglFixedToIntRound(x), - gglFixedToIntRound(y), gglFixedToIntRound(z), - gglFixedToIntRound(w), gglFixedToIntRound(h), c)) { - return; - } -#else // quickly reject empty rects if ((w|h) <= 0) return; -#endif + drawTexxOESImp(x, y, z, w, h, c); } @@ -785,11 +774,6 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte // which is a lot faster. if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTexiOESWithCopybit(x, y, z, w, h, c)) { - return; - } -#endif const int tmu = 0; texture_unit_t& u(c->textures.tmu[tmu]); EGLTextureObject* textureObject = u.texture; @@ -797,9 +781,7 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte const GLint Hcr = textureObject->crop_rect[3]; if ((w == Wcr) && (h == -Hcr)) { -#ifndef LIBAGL_USE_GRALLOC_COPYBITS if ((w|h) <= 0) return; // quickly reject empty rects -#endif if (u.dirty) { c->rasterizer.procs.activeTexture(c, tmu); @@ -1646,13 +1628,6 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) // bind it to the texture unit sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); tex->setImage(native_buffer); - -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - tex->try_copybit = false; - if (c->copybits.blitEngine != NULL) { - tex->try_copybit = true; - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS } void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 6b7020ff0a..ae924cd6e5 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -6,10 +6,11 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ - EGL/egl.cpp \ - EGL/hooks.cpp \ - EGL/Loader.cpp \ +LOCAL_SRC_FILES:= \ + EGL/egl.cpp \ + EGL/getProcAddress.cpp.arm \ + EGL/hooks.cpp \ + EGL/Loader.cpp \ # LOCAL_SHARED_LIBRARIES += libcutils libutils diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index de740a36b0..94b60a1064 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -37,12 +37,13 @@ #include <cutils/memory.h> #include <utils/SortedVector.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> #include "hooks.h" #include "egl_impl.h" #include "Loader.h" -#define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index))) #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r) // ---------------------------------------------------------------------------- @@ -143,6 +144,22 @@ public: SortedVector<egl_object_t*> egl_object_t::sObjects; Mutex egl_object_t::sLock; + +struct egl_config_t { + egl_config_t() {} + egl_config_t(int impl, EGLConfig config) + : impl(impl), config(config), configId(0), implConfigId(0) { } + int impl; // the implementation this config is for + EGLConfig config; // the implementation's EGLConfig + EGLint configId; // our CONFIG_ID + EGLint implConfigId; // the implementation's CONFIG_ID + inline bool operator < (const egl_config_t& rhs) const { + if (impl < rhs.impl) return true; + if (impl > rhs.impl) return false; + return config < rhs.config; + } +}; + struct egl_display_t { enum { NOT_INITIALIZED, INITIALIZED, TERMINATED }; @@ -163,13 +180,14 @@ struct egl_display_t { strings_t queryString; }; - uint32_t magic; - DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS]; - EGLint numTotalConfigs; - uint32_t refs; - Mutex lock; + uint32_t magic; + DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS]; + EGLint numTotalConfigs; + egl_config_t* configs; + uint32_t refs; + Mutex lock; - egl_display_t() : magic('_dpy'), numTotalConfigs(0) { } + egl_display_t() : magic('_dpy'), numTotalConfigs(0), configs(0) { } ~egl_display_t() { magic = 0; } inline bool isValid() const { return magic == '_dpy'; } inline bool isAlive() const { return isValid(); } @@ -179,14 +197,15 @@ struct egl_surface_t : public egl_object_t { typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref; - egl_surface_t(EGLDisplay dpy, EGLSurface surface, + egl_surface_t(EGLDisplay dpy, EGLSurface surface, EGLConfig config, int impl, egl_connection_t const* cnx) - : dpy(dpy), surface(surface), impl(impl), cnx(cnx) { + : dpy(dpy), surface(surface), config(config), impl(impl), cnx(cnx) { } ~egl_surface_t() { } EGLDisplay dpy; EGLSurface surface; + EGLConfig config; int impl; egl_connection_t const* cnx; }; @@ -195,7 +214,7 @@ struct egl_context_t : public egl_object_t { typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; - egl_context_t(EGLDisplay dpy, EGLContext context, + egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, int impl, egl_connection_t const* cnx, int version) : dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx), version(version) @@ -203,6 +222,7 @@ struct egl_context_t : public egl_object_t } EGLDisplay dpy; EGLContext context; + EGLConfig config; EGLSurface read; EGLSurface draw; int impl; @@ -239,7 +259,7 @@ struct tls_t // ---------------------------------------------------------------------------- -egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; +static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; static egl_display_t gDisplay[NUM_DISPLAYS]; static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t gEGLThreadLocalStorageKey = -1; @@ -354,7 +374,7 @@ int binarySearch( { while (first <= last) { int mid = (first + last) / 2; - if (key > sortedArray[mid]) { + if (sortedArray[mid] < key) { first = mid + 1; } else if (key < sortedArray[mid]) { last = mid - 1; @@ -365,26 +385,11 @@ int binarySearch( return -1; } -static EGLint configToUniqueId(egl_display_t const* dp, int i, int index) -{ - // NOTE: this mapping works only if we have no more than two EGLimpl - return (i>0 ? dp->disp[0].numConfigs : 0) + index; -} - -static void uniqueIdToConfig(egl_display_t const* dp, EGLint configId, - int& i, int& index) -{ - // NOTE: this mapping works only if we have no more than two EGLimpl - size_t numConfigs = dp->disp[0].numConfigs; - i = configId / numConfigs; - index = configId % numConfigs; -} - static int cmp_configs(const void* a, const void *b) { - EGLConfig c0 = *(EGLConfig const *)a; - EGLConfig c1 = *(EGLConfig const *)b; - return c0<c1 ? -1 : (c0>c1 ? 1 : 0); + const egl_config_t& c0 = *(egl_config_t const *)a; + const egl_config_t& c1 = *(egl_config_t const *)b; + return c0<c1 ? -1 : (c1<c0 ? 1 : 0); } struct extention_map_t { @@ -407,7 +412,11 @@ static const extention_map_t gExtentionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID }, }; -static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS]; +extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; + +// accesses protected by gInitDriverMutex +static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> gGLExtentionMap; +static int gGLExtentionSlot = 0; static void(*findProcAddress(const char* name, const extention_map_t* map, size_t n))() @@ -477,20 +486,15 @@ egl_image_t* get_image(EGLImageKHR image) { static egl_connection_t* validate_display_config( EGLDisplay dpy, EGLConfig config, - egl_display_t const*& dp, int& impl, int& index) + egl_display_t const*& dp) { dp = get_display(dpy); if (!dp) return setError(EGL_BAD_DISPLAY, (egl_connection_t*)NULL); - impl = uintptr_t(config)>>24; - if (uint32_t(impl) >= IMPL_NUM_IMPLEMENTATIONS) { - return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); - } - index = uintptr_t(config) & 0xFFFFFF; - if (index >= dp->disp[impl].numConfigs) { + if (intptr_t(config) >= dp->numTotalConfigs) { return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); } - egl_connection_t* const cnx = &gEGLImpl[impl]; + egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(config)].impl]; if (cnx->dso == 0) { return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); } @@ -718,11 +722,6 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) dp->disp[i].dpy, dp->disp[i].config, n, &dp->disp[i].numConfigs)) { - // sort the configurations so we can do binary searches - qsort( dp->disp[i].config, - dp->disp[i].numConfigs, - sizeof(EGLConfig), cmp_configs); - dp->numTotalConfigs += n; res = EGL_TRUE; } @@ -732,6 +731,30 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) } if (res == EGL_TRUE) { + dp->configs = new egl_config_t[ dp->numTotalConfigs ]; + for (int i=0, k=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso && cnx->major>=0 && cnx->minor>=0) { + for (int j=0 ; j<dp->disp[i].numConfigs ; j++) { + dp->configs[k].impl = i; + dp->configs[k].config = dp->disp[i].config[j]; + dp->configs[k].configId = k + 1; // CONFIG_ID start at 1 + // store the implementation's CONFIG_ID + cnx->egl.eglGetConfigAttrib( + dp->disp[i].dpy, + dp->disp[i].config[j], + EGL_CONFIG_ID, + &dp->configs[k].implConfigId); + k++; + } + } + } + + // sort our configurations so we can do binary-searches + qsort( dp->configs, + dp->numTotalConfigs, + sizeof(egl_config_t), cmp_configs); + dp->refs++; if (major != NULL) *major = VERSION_MAJOR; if (minor != NULL) *minor = VERSION_MINOR; @@ -784,6 +807,7 @@ EGLBoolean eglTerminate(EGLDisplay dpy) dp->refs--; dp->numTotalConfigs = 0; + delete [] dp->configs; clearTLS(); return res; } @@ -804,14 +828,13 @@ EGLBoolean eglGetConfigs( EGLDisplay dpy, *num_config = numConfigs; return EGL_TRUE; } + GLint n = 0; - for (int j=0 ; j<IMPL_NUM_IMPLEMENTATIONS ; j++) { - for (int i=0 ; i<dp->disp[j].numConfigs && config_size ; i++) { - *configs++ = MAKE_CONFIG(j, i); - config_size--; - n++; - } - } + for (intptr_t i=0 ; i<dp->numTotalConfigs && config_size ; i++) { + *configs++ = EGLConfig(i); + config_size--; + n++; + } *num_config = n; return EGL_TRUE; @@ -834,7 +857,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, // It is unfortunate, but we need to remap the EGL_CONFIG_IDs, - // to do this, we have to go through the attrib_list array once + // to do this, we have to go through the attrib_list array once // to figure out both its size and if it contains an EGL_CONFIG_ID // key. If so, the full array is copied and patched. // NOTE: we assume that there can be only one occurrence @@ -843,8 +866,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, EGLint patch_index = -1; GLint attr; size_t size = 0; - - if (attrib_list != NULL) { + if (attrib_list) { while ((attr=attrib_list[size]) != EGL_NONE) { if (attr == EGL_CONFIG_ID) patch_index = size; @@ -859,16 +881,20 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, memcpy(new_list, attrib_list, size*sizeof(EGLint)); // patch the requested EGL_CONFIG_ID - int i, index; + bool found = false; + EGLConfig ourConfig(0); EGLint& configId(new_list[patch_index+1]); - uniqueIdToConfig(dp, configId, i, index); - - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - cnx->egl.eglGetConfigAttrib( - dp->disp[i].dpy, dp->disp[i].config[index], - EGL_CONFIG_ID, &configId); + for (intptr_t i=0 ; i<dp->numTotalConfigs ; i++) { + if (dp->configs[i].configId == configId) { + ourConfig = EGLConfig(i); + configId = dp->configs[i].implConfigId; + found = true; + break; + } + } + egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(ourConfig)].impl]; + if (found && cnx->dso) { // and switch to the new list attrib_list = const_cast<const EGLint *>(new_list); @@ -881,12 +907,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, // which one. res = cnx->egl.eglChooseConfig( - dp->disp[i].dpy, attrib_list, configs, config_size, &n); + dp->disp[ dp->configs[intptr_t(ourConfig)].impl ].dpy, + attrib_list, configs, config_size, &n); if (res && n>0) { // n has to be 0 or 1, by construction, and we already know // which config it will return (since there can be only one). if (configs) { - configs[0] = MAKE_CONFIG(i, index); + configs[0] = ourConfig; } *num_config = 1; } @@ -896,6 +923,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, return res; } + for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; if (cnx->dso) { @@ -903,15 +931,14 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, dp->disp[i].dpy, attrib_list, configs, config_size, &n)) { if (configs) { // now we need to convert these client EGLConfig to our - // internal EGLConfig format. This is done in O(n log n). + // internal EGLConfig format. + // This is done in O(n Log(n)) time. for (int j=0 ; j<n ; j++) { - int index = binarySearch<EGLConfig>( - dp->disp[i].config, 0, - dp->disp[i].numConfigs-1, configs[j]); + egl_config_t key(i, configs[j]); + intptr_t index = binarySearch<egl_config_t>( + dp->configs, 0, dp->numTotalConfigs, key); if (index >= 0) { - if (configs) { - configs[j] = MAKE_CONFIG(i, index); - } + configs[j] = EGLConfig(index); } else { return setError(EGL_BAD_CONFIG, EGL_FALSE); } @@ -931,18 +958,16 @@ EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (!cnx) return EGL_FALSE; if (attribute == EGL_CONFIG_ID) { - // EGL_CONFIG_IDs must be unique, just use the order of the selected - // EGLConfig. - *value = configToUniqueId(dp, i, index); + *value = dp->configs[intptr_t(config)].configId; return EGL_TRUE; } return cnx->egl.eglGetConfigAttrib( - dp->disp[i].dpy, dp->disp[i].config[index], attribute, value); + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + dp->configs[intptr_t(config)].config, attribute, value); } // ---------------------------------------------------------------------------- @@ -954,13 +979,14 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { EGLSurface surface = cnx->egl.eglCreateWindowSurface( - dp->disp[i].dpy, dp->disp[i].config[index], window, attrib_list); + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + dp->configs[intptr_t(config)].config, window, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx); + egl_surface_t* s = new egl_surface_t(dpy, surface, config, + dp->configs[intptr_t(config)].impl, cnx); return s; } } @@ -972,13 +998,14 @@ EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { EGLSurface surface = cnx->egl.eglCreatePixmapSurface( - dp->disp[i].dpy, dp->disp[i].config[index], pixmap, attrib_list); + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + dp->configs[intptr_t(config)].config, pixmap, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx); + egl_surface_t* s = new egl_surface_t(dpy, surface, config, + dp->configs[intptr_t(config)].impl, cnx); return s; } } @@ -989,13 +1016,14 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { EGLSurface surface = cnx->egl.eglCreatePbufferSurface( - dp->disp[i].dpy, dp->disp[i].config[index], attrib_list); + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + dp->configs[intptr_t(config)].config, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx); + egl_surface_t* s = new egl_surface_t(dpy, surface, config, + dp->configs[intptr_t(config)].impl, cnx); return s; } } @@ -1031,27 +1059,35 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, egl_display_t const * const dp = get_display(dpy); egl_surface_t const * const s = get_surface(surface); - return s->cnx->egl.eglQuerySurface( - dp->disp[s->impl].dpy, s->surface, attribute, value); + EGLBoolean result(EGL_TRUE); + if (attribute == EGL_CONFIG_ID) { + // We need to remap EGL_CONFIG_IDs + *value = dp->configs[intptr_t(s->config)].configId; + } else { + result = s->cnx->egl.eglQuerySurface( + dp->disp[s->impl].dpy, s->surface, attribute, value); + } + + return result; } // ---------------------------------------------------------------------------- -// contextes +// Contexts // ---------------------------------------------------------------------------- EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { if (share_list != EGL_NO_CONTEXT) { egl_context_t* const c = get_context(share_list); share_list = c->context; } EGLContext context = cnx->egl.eglCreateContext( - dp->disp[i].dpy, dp->disp[i].config[index], + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + dp->configs[intptr_t(config)].config, share_list, attrib_list); if (context != EGL_NO_CONTEXT) { // figure out if it's a GLESv1 or GLESv2 @@ -1069,7 +1105,8 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, } }; } - egl_context_t* c = new egl_context_t(dpy, context, i, cnx, version); + egl_context_t* c = new egl_context_t(dpy, context, config, + dp->configs[intptr_t(config)].impl, cnx, version); return c; } } @@ -1214,8 +1251,16 @@ EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, egl_display_t const * const dp = get_display(dpy); egl_context_t * const c = get_context(ctx); - return c->cnx->egl.eglQueryContext( - dp->disp[c->impl].dpy, c->context, attribute, value); + EGLBoolean result(EGL_TRUE); + if (attribute == EGL_CONFIG_ID) { + *value = dp->configs[intptr_t(c->config)].configId; + } else { + // We need to remap EGL_CONFIG_IDs + result = c->cnx->egl.eglQueryContext( + dp->disp[c->impl].dpy, c->context, attribute, value); + } + + return result; } EGLContext eglGetCurrentContext(void) @@ -1330,55 +1375,55 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap)); if (addr) return addr; - return NULL; // TODO: finish implementation below + // this protects accesses to gGLExtentionMap and gGLExtentionSlot + pthread_mutex_lock(&gInitDriverMutex); - addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap)); - if (addr) return addr; - - addr = 0; - int slot = -1; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglGetProcAddress) { - addr = cnx->egl.eglGetProcAddress(procname); - if (addr) { - if (slot == -1) { - slot = 0; // XXX: find free slot - if (slot == -1) { - addr = 0; - break; - } - } - //cnx->hooks->ext.extensions[slot] = addr; + /* + * Since eglGetProcAddress() is not associated to anything, it needs + * to return a function pointer that "works" regardless of what + * the current context is. + * + * For this reason, we return a "forwarder", a small stub that takes + * care of calling the function associated with the context + * currently bound. + * + * We first look for extensions we've already resolved, if we're seeing + * this extension for the first time, we go through all our + * implementations and call eglGetProcAddress() and record the + * result in the appropriate implementation hooks and return the + * address of the forwarder corresponding to that hook set. + * + */ + + const String8 name(procname); + addr = gGLExtentionMap.valueFor(name); + const int slot = gGLExtentionSlot; + + LOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, + "no more slots for eglGetProcAddress(\"%s\")", + procname); + + if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + bool found = false; + for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso && cnx->egl.eglGetProcAddress) { + found = true; + // Extensions are independent of the bound context + cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] = + cnx->egl.eglGetProcAddress(procname); } } + if (found) { + addr = gExtensionForwarders[slot]; + gGLExtentionMap.add(name, addr); + gGLExtentionSlot++; + } } - } - - if (slot >= 0) { - addr = 0; // XXX: address of stub 'slot' - gGLExtentionMap[slot].name = strdup(procname); - gGLExtentionMap[slot].address = addr; - } - - return addr; - - /* - * TODO: For OpenGL ES extensions, we must generate a stub - * that looks like - * mov r12, #0xFFFF0FFF - * ldr r12, [r12, #-15] - * ldr r12, [r12, #TLS_SLOT_OPENGL_API*4] - * mov r12, [r12, #api_offset] - * ldrne pc, r12 - * mov pc, #unsupported_extension - * - * and write the address of the extension in *all* - * gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t - * - */ + pthread_mutex_unlock(&gInitDriverMutex); + return addr; } EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) @@ -1587,13 +1632,13 @@ EGLSurface eglCreatePbufferFromClientBuffer( EGLConfig config, const EGLint *attrib_list) { egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (!cnx) return EGL_FALSE; if (cnx->egl.eglCreatePbufferFromClientBuffer) { return cnx->egl.eglCreatePbufferFromClientBuffer( - dp->disp[i].dpy, buftype, buffer, - dp->disp[i].config[index], attrib_list); + dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, + buftype, buffer, + dp->configs[intptr_t(config)].config, attrib_list); } return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); } diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp new file mode 100644 index 0000000000..dcf8735c63 --- /dev/null +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -0,0 +1,122 @@ +/* + ** Copyright 2009, 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 <ctype.h> +#include <stdlib.h> +#include <errno.h> + +#include <cutils/log.h> + +#include "hooks.h" + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +#undef API_ENTRY +#undef CALL_GL_EXTENSION_API +#undef GL_EXTENSION +#undef GL_EXTENSION_NAME +#undef GL_EXTENSION_ARRAY +#undef GL_EXTENSION_LIST +#undef GET_TLS + +#if defined(__arm__) + + #ifdef HAVE_ARM_TLS_REGISTER + #define GET_TLS(reg) \ + "mrc p15, 0, " #reg ", c13, c0, 3 \n" + #else + #define GET_TLS(reg) \ + "mov " #reg ", #0xFFFF0FFF \n" \ + "ldr " #reg ", [" #reg ", #-15] \n" + #endif + + #define API_ENTRY(_api) __attribute__((naked)) _api + + #define CALL_GL_EXTENSION_API(_api) \ + asm volatile( \ + GET_TLS(r12) \ + "ldr r12, [r12, %[tls]] \n" \ + "cmp r12, #0 \n" \ + "ldrne r12, [r12, %[api]] \n" \ + "cmpne r12, #0 \n" \ + "bxne r12 \n" \ + "bx lr \n" \ + : \ + : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ + [api] "J"(__builtin_offsetof(gl_hooks_t, \ + ext.extensions[_api])) \ + : \ + ); + + #define GL_EXTENSION_NAME(_n) __glExtFwd##_n + + #define GL_EXTENSION(_n) \ + void API_ENTRY(GL_EXTENSION_NAME(_n))() { \ + CALL_GL_EXTENSION_API(_n); \ + } + + +#else + + #define GL_EXTENSION_NAME(_n) NULL + + #define GL_EXTENSION(_n) + + #warning "eglGetProcAddress() partially supported on this architecture" + +#endif + +#define GL_EXTENSION_LIST(name) \ + name(0) name(1) name(2) name(3) \ + name(4) name(5) name(6) name(7) \ + name(8) name(9) name(10) name(11) \ + name(12) name(13) name(14) name(15) \ + name(16) name(17) name(18) name(19) \ + name(20) name(21) name(22) name(23) \ + name(24) name(25) name(26) name(27) \ + name(28) name(29) name(30) name(31) \ + name(32) name(33) name(34) name(35) \ + name(36) name(37) name(38) name(39) \ + name(40) name(41) name(42) name(43) \ + name(44) name(45) name(46) name(47) \ + name(48) name(49) name(50) name(51) \ + name(52) name(53) name(54) name(55) \ + name(56) name(57) name(58) name(59) \ + name(60) name(61) name(62) name(63) + + +GL_EXTENSION_LIST( GL_EXTENSION ) + +#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n), + +extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = { + GL_EXTENSION_LIST( GL_EXTENSION_ARRAY ) + }; + +#undef GET_TLS +#undef GL_EXTENSION_LIST +#undef GL_EXTENSION_ARRAY +#undef GL_EXTENSION_NAME +#undef GL_EXTENSION +#undef API_ENTRY +#undef CALL_GL_EXTENSION_API + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in index 9c2e69aea0..5164450ba9 100644 --- a/opengl/libs/GLES2/gl2_api.in +++ b/opengl/libs/GLES2/gl2_api.in @@ -4,7 +4,7 @@ void API_ENTRY(glActiveTexture)(GLenum texture) { void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) { CALL_GL_API(glAttachShader, program, shader); } -void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const char* name) { +void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar* name) { CALL_GL_API(glBindAttribLocation, program, index, name); } void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { @@ -34,10 +34,10 @@ void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { void API_ENTRY(glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { CALL_GL_API(glBlendFuncSeparate, srcRGB, dstRGB, srcAlpha, dstAlpha); } -void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void* data, GLenum usage) { +void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) { CALL_GL_API(glBufferData, target, size, data, usage); } -void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) { +void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) { CALL_GL_API(glBufferSubData, target, offset, size, data); } GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) { @@ -61,10 +61,10 @@ void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLbo void API_ENTRY(glCompileShader)(GLuint shader) { CALL_GL_API(glCompileShader, shader); } -void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) { +void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) { CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data); } -void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) { +void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) { CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data); } void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { @@ -121,7 +121,7 @@ void API_ENTRY(glDisableVertexAttribArray)(GLuint index) { void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { CALL_GL_API(glDrawArrays, mode, first, count); } -void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices) { +void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { CALL_GL_API(glDrawElements, mode, count, type, indices); } void API_ENTRY(glEnable)(GLenum cap) { @@ -160,16 +160,16 @@ void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers) { void API_ENTRY(glGenTextures)(GLsizei n, GLuint* textures) { CALL_GL_API(glGenTextures, n, textures); } -void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) { +void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) { CALL_GL_API(glGetActiveAttrib, program, index, bufsize, length, size, type, name); } -void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) { +void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) { CALL_GL_API(glGetActiveUniform, program, index, bufsize, length, size, type, name); } void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) { CALL_GL_API(glGetAttachedShaders, program, maxcount, count, shaders); } -int API_ENTRY(glGetAttribLocation)(GLuint program, const char* name) { +int API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar* name) { CALL_GL_API_RETURN(glGetAttribLocation, program, name); } void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean* params) { @@ -193,7 +193,7 @@ void API_ENTRY(glGetIntegerv)(GLenum pname, GLint* params) { void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint* params) { CALL_GL_API(glGetProgramiv, program, pname, params); } -void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) { +void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) { CALL_GL_API(glGetProgramInfoLog, program, bufsize, length, infolog); } void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params) { @@ -202,13 +202,13 @@ void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint* params) { CALL_GL_API(glGetShaderiv, shader, pname, params); } -void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) { +void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) { CALL_GL_API(glGetShaderInfoLog, shader, bufsize, length, infolog); } void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) { CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision); } -void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) { +void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) { CALL_GL_API(glGetShaderSource, shader, bufsize, length, source); } const GLubyte* API_ENTRY(glGetString)(GLenum name) { @@ -226,7 +226,7 @@ void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat* params) void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint* params) { CALL_GL_API(glGetUniformiv, program, location, params); } -int API_ENTRY(glGetUniformLocation)(GLuint program, const char* name) { +int API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar* name) { CALL_GL_API_RETURN(glGetUniformLocation, program, name); } void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params) { @@ -235,7 +235,7 @@ void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params) void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params) { CALL_GL_API(glGetVertexAttribiv, index, pname, params); } -void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer) { +void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, GLvoid** pointer) { CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer); } void API_ENTRY(glHint)(GLenum target, GLenum mode) { @@ -274,7 +274,7 @@ void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { CALL_GL_API(glPolygonOffset, factor, units); } -void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { +void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) { CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); } void API_ENTRY(glReleaseShaderCompiler)(void) { @@ -289,10 +289,10 @@ void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) { void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { CALL_GL_API(glScissor, x, y, width, height); } -void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) { +void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) { CALL_GL_API(glShaderBinary, n, shaders, binaryformat, binary, length); } -void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length) { +void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar** string, const GLint* length) { CALL_GL_API(glShaderSource, shader, count, string, length); } void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { @@ -313,7 +313,7 @@ void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) { CALL_GL_API(glStencilOpSeparate, face, fail, zfail, zpass); } -void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { +void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels); } void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { @@ -328,7 +328,7 @@ void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint* params) { CALL_GL_API(glTexParameteriv, target, pname, params); } -void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) { +void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) { CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels); } void API_ENTRY(glUniform1f)(GLint location, GLfloat x) { @@ -418,7 +418,7 @@ void API_ENTRY(glVertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, G void API_ENTRY(glVertexAttrib4fv)(GLuint indx, const GLfloat* values) { CALL_GL_API(glVertexAttrib4fv, indx, values); } -void API_ENTRY(glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) { +void API_ENTRY(glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) { CALL_GL_API(glVertexAttribPointer, indx, size, type, normalized, stride, ptr); } void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in index 6eeecb33dc..e965625178 100644 --- a/opengl/libs/GLES2/gl2ext_api.in +++ b/opengl/libs/GLES2/gl2ext_api.in @@ -4,10 +4,10 @@ void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES imag void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) { CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image); } -void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { +void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) { CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary); } -void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) { +void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length) { CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length); } void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { @@ -16,40 +16,52 @@ void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) { CALL_GL_API_RETURN(glUnmapBufferOES, target); } -void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void** params) { +void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, GLvoid** params) { CALL_GL_API(glGetBufferPointervOES, target, pname, params); } -void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) { +void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels); } -void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) { +void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) { CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); } void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height); } -void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data) { +void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data) { CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data); } -void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) { +void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data) { CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); } void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset); } +void API_ENTRY(glBindVertexArrayOES)(GLuint array) { + CALL_GL_API(glBindVertexArrayOES, array); +} +void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) { + CALL_GL_API(glDeleteVertexArraysOES, n, arrays); +} +void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) { + CALL_GL_API(glGenVertexArraysOES, n, arrays); +} +GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) { + CALL_GL_API_RETURN(glIsVertexArrayOES, array); +} void API_ENTRY(glGetPerfMonitorGroupsAMD)(GLint *numGroups, GLsizei groupsSize, GLuint *groups) { CALL_GL_API(glGetPerfMonitorGroupsAMD, numGroups, groupsSize, groups); } void API_ENTRY(glGetPerfMonitorCountersAMD)(GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) { CALL_GL_API(glGetPerfMonitorCountersAMD, group, numCounters, maxActiveCounters, counterSize, counters); } -void API_ENTRY(glGetPerfMonitorGroupStringAMD)(GLuint group, GLsizei bufSize, GLsizei *length, char *groupString) { +void API_ENTRY(glGetPerfMonitorGroupStringAMD)(GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString) { CALL_GL_API(glGetPerfMonitorGroupStringAMD, group, bufSize, length, groupString); } -void API_ENTRY(glGetPerfMonitorCounterStringAMD)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString) { +void API_ENTRY(glGetPerfMonitorCounterStringAMD)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString) { CALL_GL_API(glGetPerfMonitorCounterStringAMD, group, counter, bufSize, length, counterString); } -void API_ENTRY(glGetPerfMonitorCounterInfoAMD)(GLuint group, GLuint counter, GLenum pname, void *data) { +void API_ENTRY(glGetPerfMonitorCounterInfoAMD)(GLuint group, GLuint counter, GLenum pname, GLvoid *data) { CALL_GL_API(glGetPerfMonitorCounterInfoAMD, group, counter, pname, data); } void API_ENTRY(glGenPerfMonitorsAMD)(GLsizei n, GLuint *monitors) { @@ -70,6 +82,21 @@ void API_ENTRY(glEndPerfMonitorAMD)(GLuint monitor) { void API_ENTRY(glGetPerfMonitorCounterDataAMD)(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) { CALL_GL_API(glGetPerfMonitorCounterDataAMD, monitor, pname, dataSize, data, bytesWritten); } +void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { + CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments); +} +void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) { + CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount); +} +void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) { + CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount); +} +void API_ENTRY(glRenderbufferStorageMultisampleIMG)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glRenderbufferStorageMultisampleIMG, target, samples, internalformat, width, height); +} +void API_ENTRY(glFramebufferTexture2DMultisampleIMG)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) { + CALL_GL_API(glFramebufferTexture2DMultisampleIMG, target, attachment, textarget, texture, level, samples); +} void API_ENTRY(glDeleteFencesNV)(GLsizei n, const GLuint *fences) { CALL_GL_API(glDeleteFencesNV, n, fences); } @@ -91,10 +118,16 @@ void API_ENTRY(glFinishFenceNV)(GLuint fence) { void API_ENTRY(glSetFenceNV)(GLuint fence, GLenum condition) { CALL_GL_API(glSetFenceNV, fence, condition); } +void API_ENTRY(glCoverageMaskNV)(GLboolean mask) { + CALL_GL_API(glCoverageMaskNV, mask); +} +void API_ENTRY(glCoverageOperationNV)(GLenum operation) { + CALL_GL_API(glCoverageOperationNV, operation); +} void API_ENTRY(glGetDriverControlsQCOM)(GLint *num, GLsizei size, GLuint *driverControls) { CALL_GL_API(glGetDriverControlsQCOM, num, size, driverControls); } -void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString) { +void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) { CALL_GL_API(glGetDriverControlStringQCOM, driverControl, bufSize, length, driverControlString); } void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) { @@ -103,3 +136,45 @@ void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) { void API_ENTRY(glDisableDriverControlQCOM)(GLuint driverControl) { CALL_GL_API(glDisableDriverControlQCOM, driverControl); } +void API_ENTRY(glExtGetTexturesQCOM)(GLuint *textures, GLint maxTextures, GLint *numTextures) { + CALL_GL_API(glExtGetTexturesQCOM, textures, maxTextures, numTextures); +} +void API_ENTRY(glExtGetBuffersQCOM)(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) { + CALL_GL_API(glExtGetBuffersQCOM, buffers, maxBuffers, numBuffers); +} +void API_ENTRY(glExtGetRenderbuffersQCOM)(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) { + CALL_GL_API(glExtGetRenderbuffersQCOM, renderbuffers, maxRenderbuffers, numRenderbuffers); +} +void API_ENTRY(glExtGetFramebuffersQCOM)(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) { + CALL_GL_API(glExtGetFramebuffersQCOM, framebuffers, maxFramebuffers, numFramebuffers); +} +void API_ENTRY(glExtGetTexLevelParameterivQCOM)(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) { + CALL_GL_API(glExtGetTexLevelParameterivQCOM, texture, face, level, pname, params); +} +void API_ENTRY(glExtTexObjectStateOverrideiQCOM)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glExtTexObjectStateOverrideiQCOM, target, pname, param); +} +void API_ENTRY(glExtGetTexSubImageQCOM)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) { + CALL_GL_API(glExtGetTexSubImageQCOM, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels); +} +void API_ENTRY(glExtGetBufferPointervQCOM)(GLenum target, GLvoid **params) { + CALL_GL_API(glExtGetBufferPointervQCOM, target, params); +} +void API_ENTRY(glExtGetShadersQCOM)(GLuint *shaders, GLint maxShaders, GLint *numShaders) { + CALL_GL_API(glExtGetShadersQCOM, shaders, maxShaders, numShaders); +} +void API_ENTRY(glExtGetProgramsQCOM)(GLuint *programs, GLint maxPrograms, GLint *numPrograms) { + CALL_GL_API(glExtGetProgramsQCOM, programs, maxPrograms, numPrograms); +} +GLboolean API_ENTRY(glExtIsProgramBinaryQCOM)(GLuint program) { + CALL_GL_API_RETURN(glExtIsProgramBinaryQCOM, program); +} +void API_ENTRY(glExtGetProgramBinarySourceQCOM)(GLuint program, GLenum shadertype, GLchar *source, GLint *length) { + CALL_GL_API(glExtGetProgramBinarySourceQCOM, program, shadertype, source, length); +} +void API_ENTRY(glStartTilingQCOM)(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) { + CALL_GL_API(glStartTilingQCOM, x, y, width, height, preserveMask); +} +void API_ENTRY(glEndTilingQCOM)(GLbitfield preserveMask) { + CALL_GL_API(glEndTilingQCOM, preserveMask); +} diff --git a/opengl/libs/GLES_CM/gl_api.in b/opengl/libs/GLES_CM/gl_api.in index 5437d47f20..7f20c4fda4 100644 --- a/opengl/libs/GLES_CM/gl_api.in +++ b/opengl/libs/GLES_CM/gl_api.in @@ -259,7 +259,7 @@ void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) { void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) { CALL_GL_API(glGetMaterialxv, face, pname, params); } -void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { +void API_ENTRY(glGetPointerv)(GLenum pname, GLvoid **params) { CALL_GL_API(glGetPointerv, pname, params); } const GLubyte * API_ENTRY(glGetString)(GLenum name) { diff --git a/opengl/libs/GLES_CM/glext_api.in b/opengl/libs/GLES_CM/glext_api.in index 2c8648e259..5393fa61a0 100644 --- a/opengl/libs/GLES_CM/glext_api.in +++ b/opengl/libs/GLES_CM/glext_api.in @@ -205,7 +205,7 @@ void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) { CALL_GL_API_RETURN(glUnmapBufferOES, target); } -void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void** params) { +void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, GLvoid ** params) { CALL_GL_API(glGetBufferPointervOES, target, pname, params); } void API_ENTRY(glCurrentPaletteMatrixOES)(GLuint matrixpaletteindex) { @@ -268,3 +268,111 @@ void API_ENTRY(glGetTexGenivOES)(GLenum coord, GLenum pname, GLint *params) { void API_ENTRY(glGetTexGenxvOES)(GLenum coord, GLenum pname, GLfixed *params) { CALL_GL_API(glGetTexGenxvOES, coord, pname, params); } +void API_ENTRY(glBindVertexArrayOES)(GLuint array) { + CALL_GL_API(glBindVertexArrayOES, array); +} +void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) { + CALL_GL_API(glDeleteVertexArraysOES, n, arrays); +} +void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) { + CALL_GL_API(glGenVertexArraysOES, n, arrays); +} +GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) { + CALL_GL_API_RETURN(glIsVertexArrayOES, array); +} +void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { + CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments); +} +void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) { + CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount); +} +void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) { + CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount); +} +void API_ENTRY(glClipPlanefIMG)(GLenum p, const GLfloat *eqn) { + CALL_GL_API(glClipPlanefIMG, p, eqn); +} +void API_ENTRY(glClipPlanexIMG)(GLenum p, const GLfixed *eqn) { + CALL_GL_API(glClipPlanexIMG, p, eqn); +} +void API_ENTRY(glRenderbufferStorageMultisampleIMG)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glRenderbufferStorageMultisampleIMG, target, samples, internalformat, width, height); +} +void API_ENTRY(glFramebufferTexture2DMultisampleIMG)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) { + CALL_GL_API(glFramebufferTexture2DMultisampleIMG, target, attachment, textarget, texture, level, samples); +} +void API_ENTRY(glDeleteFencesNV)(GLsizei n, const GLuint *fences) { + CALL_GL_API(glDeleteFencesNV, n, fences); +} +void API_ENTRY(glGenFencesNV)(GLsizei n, GLuint *fences) { + CALL_GL_API(glGenFencesNV, n, fences); +} +GLboolean API_ENTRY(glIsFenceNV)(GLuint fence) { + CALL_GL_API_RETURN(glIsFenceNV, fence); +} +GLboolean API_ENTRY(glTestFenceNV)(GLuint fence) { + CALL_GL_API_RETURN(glTestFenceNV, fence); +} +void API_ENTRY(glGetFenceivNV)(GLuint fence, GLenum pname, GLint *params) { + CALL_GL_API(glGetFenceivNV, fence, pname, params); +} +void API_ENTRY(glFinishFenceNV)(GLuint fence) { + CALL_GL_API(glFinishFenceNV, fence); +} +void API_ENTRY(glSetFenceNV)(GLuint fence, GLenum condition) { + CALL_GL_API(glSetFenceNV, fence, condition); +} +void API_ENTRY(glGetDriverControlsQCOM)(GLint *num, GLsizei size, GLuint *driverControls) { + CALL_GL_API(glGetDriverControlsQCOM, num, size, driverControls); +} +void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) { + CALL_GL_API(glGetDriverControlStringQCOM, driverControl, bufSize, length, driverControlString); +} +void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) { + CALL_GL_API(glEnableDriverControlQCOM, driverControl); +} +void API_ENTRY(glDisableDriverControlQCOM)(GLuint driverControl) { + CALL_GL_API(glDisableDriverControlQCOM, driverControl); +} +void API_ENTRY(glExtGetTexturesQCOM)(GLuint *textures, GLint maxTextures, GLint *numTextures) { + CALL_GL_API(glExtGetTexturesQCOM, textures, maxTextures, numTextures); +} +void API_ENTRY(glExtGetBuffersQCOM)(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) { + CALL_GL_API(glExtGetBuffersQCOM, buffers, maxBuffers, numBuffers); +} +void API_ENTRY(glExtGetRenderbuffersQCOM)(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) { + CALL_GL_API(glExtGetRenderbuffersQCOM, renderbuffers, maxRenderbuffers, numRenderbuffers); +} +void API_ENTRY(glExtGetFramebuffersQCOM)(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) { + CALL_GL_API(glExtGetFramebuffersQCOM, framebuffers, maxFramebuffers, numFramebuffers); +} +void API_ENTRY(glExtGetTexLevelParameterivQCOM)(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) { + CALL_GL_API(glExtGetTexLevelParameterivQCOM, texture, face, level, pname, params); +} +void API_ENTRY(glExtTexObjectStateOverrideiQCOM)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glExtTexObjectStateOverrideiQCOM, target, pname, param); +} +void API_ENTRY(glExtGetTexSubImageQCOM)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) { + CALL_GL_API(glExtGetTexSubImageQCOM, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels); +} +void API_ENTRY(glExtGetBufferPointervQCOM)(GLenum target, GLvoid **params) { + CALL_GL_API(glExtGetBufferPointervQCOM, target, params); +} +void API_ENTRY(glExtGetShadersQCOM)(GLuint *shaders, GLint maxShaders, GLint *numShaders) { + CALL_GL_API(glExtGetShadersQCOM, shaders, maxShaders, numShaders); +} +void API_ENTRY(glExtGetProgramsQCOM)(GLuint *programs, GLint maxPrograms, GLint *numPrograms) { + CALL_GL_API(glExtGetProgramsQCOM, programs, maxPrograms, numPrograms); +} +GLboolean API_ENTRY(glExtIsProgramBinaryQCOM)(GLuint program) { + CALL_GL_API_RETURN(glExtIsProgramBinaryQCOM, program); +} +void API_ENTRY(glExtGetProgramBinarySourceQCOM)(GLuint program, GLenum shadertype, GLchar *source, GLint *length) { + CALL_GL_API(glExtGetProgramBinarySourceQCOM, program, shadertype, source, length); +} +void API_ENTRY(glStartTilingQCOM)(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) { + CALL_GL_API(glStartTilingQCOM, x, y, width, height, preserveMask); +} +void API_ENTRY(glEndTilingQCOM)(GLbitfield preserveMask) { + CALL_GL_API(glEndTilingQCOM, preserveMask); +} diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index 1fba209f77..c8f529ac05 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -31,6 +31,7 @@ namespace android { struct egl_connection_t { + inline egl_connection_t() : dso(0) { } void * dso; gl_hooks_t * hooks[2]; EGLint major; diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in index bbe3e236b2..61acb5f09e 100644 --- a/opengl/libs/entries.in +++ b/opengl/libs/entries.in @@ -4,13 +4,14 @@ GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref) GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLclampx ref) GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader) GL_ENTRY(void, glBeginPerfMonitorAMD, GLuint monitor) -GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const char* name) +GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar* name) GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer) GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer) GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer) GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer) GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) GL_ENTRY(void, glBlendColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) GL_ENTRY(void, glBlendEquation, GLenum mode ) GL_ENTRY(void, glBlendEquationOES, GLenum mode) @@ -34,8 +35,10 @@ GL_ENTRY(void, glClearDepthxOES, GLclampx depth) GL_ENTRY(void, glClearStencil, GLint s) GL_ENTRY(void, glClientActiveTexture, GLenum texture) GL_ENTRY(void, glClipPlanef, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn) GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation) GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation) +GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn) GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation) GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) @@ -45,12 +48,14 @@ GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLbo GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) GL_ENTRY(void, glCompileShader, GLuint shader) GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) -GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data) +GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data) GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data) -GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) +GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data) GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCoverageMaskNV, GLboolean mask) +GL_ENTRY(void, glCoverageOperationNV, GLenum operation) GL_ENTRY(GLuint, glCreateProgram, void) GL_ENTRY(GLuint, glCreateShader, GLenum type) GL_ENTRY(void, glCullFace, GLenum mode) @@ -65,6 +70,7 @@ GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint* renderbuffers) GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint* renderbuffers) GL_ENTRY(void, glDeleteShader, GLuint shader) GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) GL_ENTRY(void, glDepthFunc, GLenum func) GL_ENTRY(void, glDepthMask, GLboolean flag) GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar) @@ -76,6 +82,7 @@ GL_ENTRY(void, glDisable, GLenum cap) GL_ENTRY(void, glDisableClientState, GLenum array) GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl) GL_ENTRY(void, glDisableVertexAttribArray, GLuint index) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height) @@ -93,6 +100,19 @@ GL_ENTRY(void, glEnableClientState, GLenum array) GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl) GL_ENTRY(void, glEnableVertexAttribArray, GLuint index) GL_ENTRY(void, glEndPerfMonitorAMD, GLuint monitor) +GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask) +GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, GLvoid **params) +GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length) +GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms) +GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders) +GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) +GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures) +GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program) +GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param) GL_ENTRY(void, glFinish, void) GL_ENTRY(void, glFinishFenceNV, GLuint fence) GL_ENTRY(void, glFlush, void) @@ -105,6 +125,7 @@ GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *params) GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) GL_ENTRY(void, glFrontFace, GLenum mode) @@ -120,20 +141,21 @@ GL_ENTRY(void, glGenPerfMonitorsAMD, GLsizei n, GLuint *monitors) GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint* renderbuffers) GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint* renderbuffers) GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) GL_ENTRY(void, glGenerateMipmap, GLenum target) GL_ENTRY(void, glGenerateMipmapOES, GLenum target) -GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) -GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) -GL_ENTRY(int, glGetAttribLocation, GLuint program, const char* name) +GL_ENTRY(int, glGetAttribLocation, GLuint program, const GLchar* name) GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *params) GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void** params) +GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, GLvoid ** params) GL_ENTRY(void, glGetClipPlanef, GLenum pname, GLfloat eqn[4]) GL_ENTRY(void, glGetClipPlanefOES, GLenum pname, GLfloat eqn[4]) GL_ENTRY(void, glGetClipPlanex, GLenum pname, GLfixed eqn[4]) GL_ENTRY(void, glGetClipPlanexOES, GLenum pname, GLfixed eqn[4]) -GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString) +GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls) GL_ENTRY(GLenum, glGetError, void) GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params) @@ -150,20 +172,20 @@ GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params) GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params) GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params) GL_ENTRY(void, glGetPerfMonitorCounterDataAMD, GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) -GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, void *data) -GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString) +GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, GLvoid *data) +GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString) GL_ENTRY(void, glGetPerfMonitorCountersAMD, GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) -GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, char *groupString) +GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString) GL_ENTRY(void, glGetPerfMonitorGroupsAMD, GLint *numGroups, GLsizei groupsSize, GLuint *groups) -GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) -GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) -GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) +GL_ENTRY(void, glGetPointerv, GLenum pname, GLvoid **params) +GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) +GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint* params) GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint* params) GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint* params) -GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) +GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) -GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, char* source) +GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint* params) GL_ENTRY(const GLubyte *, glGetString, GLenum name) GL_ENTRY(void, glGetTexEnvfv, GLenum env, GLenum pname, GLfloat *params) @@ -177,10 +199,10 @@ GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params) GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params) -GL_ENTRY(int, glGetUniformLocation, GLuint program, const char* name) +GL_ENTRY(int, glGetUniformLocation, GLuint program, const GLchar* name) GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat* params) GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint* params) -GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void** pointer) +GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, GLvoid** pointer) GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params) GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint* params) GL_ENTRY(void, glHint, GLenum target, GLenum mode) @@ -194,6 +216,7 @@ GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer) GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer) GL_ENTRY(GLboolean, glIsShader, GLuint shader) GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param) GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params) GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param) @@ -228,6 +251,8 @@ GL_ENTRY(void, glMatrixMode, GLenum mode) GL_ENTRY(void, glMultMatrixf, const GLfloat *m) GL_ENTRY(void, glMultMatrixx, const GLfixed *m) GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) GL_ENTRY(void, glMultiTexCoord4x, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) GL_ENTRY(void, glMultiTexCoord4xOES, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) @@ -254,12 +279,13 @@ GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units) GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units) GL_ENTRY(void, glPopMatrix, void) -GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length) +GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length) GL_ENTRY(void, glPushMatrix, void) GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed mantissa[16], GLint exponent[16]) GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) GL_ENTRY(void, glReleaseShaderCompiler, void) GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) @@ -274,8 +300,9 @@ GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) GL_ENTRY(void, glSelectPerfMonitorCountersAMD, GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList) GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition) GL_ENTRY(void, glShadeModel, GLenum mode) -GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) -GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const char** string, const GLint* length) +GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) +GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar** string, const GLint* length) +GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask) GL_ENTRY(void, glStencilMask, GLuint mask) @@ -298,8 +325,8 @@ GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param) GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params) GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param) GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params) -GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) -GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels) GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) @@ -309,7 +336,7 @@ GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param) GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params) GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params) GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) -GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) +GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z) GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z) GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z) @@ -343,7 +370,7 @@ GL_ENTRY(void, glVertexAttrib3f, GLuint indx, GLfloat x, GLfloat y, GLfloat z) GL_ENTRY(void, glVertexAttrib3fv, GLuint indx, const GLfloat* values) GL_ENTRY(void, glVertexAttrib4f, GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) GL_ENTRY(void, glVertexAttrib4fv, GLuint indx, const GLfloat* values) -GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) +GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index f47f0930ba..1ab58cc833 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -37,7 +37,7 @@ #endif #undef NELEM #define NELEM(x) (sizeof(x)/sizeof(*(x))) -#define MAX_NUMBER_OF_GL_EXTENSIONS 32 +#define MAX_NUMBER_OF_GL_EXTENSIONS 64 #if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && __OPTIMIZE__ @@ -86,7 +86,7 @@ struct gl_hooks_t { #include "entries.in" } gl; struct gl_ext_t { - void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void); + __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS]; } ext; }; #undef GL_ENTRY diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp index 2361db5f32..f274c7c4ef 100644 --- a/opengl/tests/gl2_basic/gl2_basic.cpp +++ b/opengl/tests/gl2_basic/gl2_basic.cpp @@ -195,7 +195,6 @@ void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { X(EGL_NATIVE_RENDERABLE), X(EGL_NATIVE_VISUAL_ID), X(EGL_NATIVE_VISUAL_TYPE), - X(EGL_PRESERVED_RESOURCES), X(EGL_SAMPLES), X(EGL_SAMPLE_BUFFERS), X(EGL_SURFACE_TYPE), diff --git a/opengl/tests/gl_basic/gl_basic.cpp b/opengl/tests/gl_basic/gl_basic.cpp index feb964a151..0cc8398a4a 100644 --- a/opengl/tests/gl_basic/gl_basic.cpp +++ b/opengl/tests/gl_basic/gl_basic.cpp @@ -114,7 +114,6 @@ void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { X(EGL_NATIVE_RENDERABLE), X(EGL_NATIVE_VISUAL_ID), X(EGL_NATIVE_VISUAL_TYPE), - X(EGL_PRESERVED_RESOURCES), X(EGL_SAMPLES), X(EGL_SAMPLE_BUFFERS), X(EGL_SURFACE_TYPE), diff --git a/opengl/tools/glgen/specs/gles11/GLES20.spec b/opengl/tools/glgen/specs/gles11/GLES20.spec index 61094d17e4..ee88f59211 100644 --- a/opengl/tools/glgen/specs/gles11/GLES20.spec +++ b/opengl/tools/glgen/specs/gles11/GLES20.spec @@ -39,6 +39,7 @@ void glDetachShader ( GLuint program, GLuint shader ) void glDisable ( GLenum cap )
void glDisableVertexAttribArray ( GLuint index )
void glDrawArrays ( GLenum mode, GLint first, GLsizei count )
+void glDrawElements ( GLenum mode, GLsizei count, GLenum type, GLint offset )
void glDrawElements ( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices )
void glEnable ( GLenum cap )
void glEnableVertexAttribArray ( GLuint index )
@@ -138,5 +139,6 @@ void glVertexAttrib3f ( GLuint indx, GLfloat x, GLfloat y, GLfloat z ) void glVertexAttrib3fv ( GLuint indx, const GLfloat *values )
void glVertexAttrib4f ( GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w )
void glVertexAttrib4fv ( GLuint indx, const GLfloat *values )
+void glVertexAttribPointer ( GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLint offset )
void glVertexAttribPointer ( GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr )
-void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height )
\ No newline at end of file +void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height )
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java index ebaca908d1..9d8c5a0616 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -695,7 +695,7 @@ public class JniCodeEmitter { boolean isPointerFunc = isPointerFunc(jfunc); boolean isVBOPointerFunc = (outName.endsWith("Pointer") || outName.endsWith("PointerOES") || - outName.endsWith("DrawElements")) && + outName.endsWith("DrawElements") || outName.endsWith("VertexAttribPointer")) && !jfunc.getCFunc().hasPointerArg(); if (isPointerFunc) { outName += "Bounds"; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 86eb78d8dd..a14bfb569b 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ BlurFilter.cpp.arm \ + GLExtensions.cpp \ Layer.cpp \ LayerBase.cpp \ LayerBuffer.cpp \ @@ -13,14 +14,14 @@ LOCAL_SRC_FILES:= \ LayerDim.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ - Tokenizer.cpp \ + TextureManager.cpp \ Transform.cpp LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -ifeq ($(TARGET_BOARD_PLATFORM), msm7k) - LOCAL_CFLAGS += -DDIM_WITH_TEXTURE +ifeq ($(TARGET_BOARD_PLATFORM), omap3) + LOCAL_CFLAGS += -DNO_RGBX_8888 endif # need "-lrt" on Linux simulator to pick up clock_gettime diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h index e2bcf6a049..6f8507e241 100644 --- a/services/surfaceflinger/Barrier.h +++ b/services/surfaceflinger/Barrier.h @@ -29,10 +29,6 @@ public: inline Barrier() : state(CLOSED) { } inline ~Barrier() { } void open() { - // gcc memory barrier, this makes sure all memory writes - // have been issued by gcc. On an SMP system we'd need a real - // h/w barrier. - asm volatile ("":::"memory"); Mutex::Autolock _l(lock); state = OPENED; cv.broadcast(); diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index ea68352828..2eac0a80a0 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -40,6 +40,8 @@ #include <hardware/overlay.h> #include <hardware/gralloc.h> +#include "GLExtensions.h" + using namespace android; @@ -73,7 +75,8 @@ void checkEGLErrors(const char* token) DisplayHardware::DisplayHardware( const sp<SurfaceFlinger>& flinger, uint32_t dpy) - : DisplayHardwareBase(flinger, dpy) + : DisplayHardwareBase(flinger, dpy), + mFlags(0) { init(dpy); } @@ -97,6 +100,9 @@ void DisplayHardware::init(uint32_t dpy) { mNativeWindow = new FramebufferNativeWindow(); framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; mOverlayEngine = NULL; hw_module_t const* module; @@ -104,6 +110,11 @@ void DisplayHardware::init(uint32_t dpy) overlay_control_open(module, &mOverlayEngine); } + EGLint w, h, dummy; + EGLint numConfigs=0; + EGLSurface surface; + EGLContext context; + // initialize EGL EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -121,12 +132,6 @@ void DisplayHardware::init(uint32_t dpy) } } - EGLint w, h, dummy; - EGLint numConfigs=0; - EGLSurface surface; - EGLContext context; - mFlags = CACHED_BUFFERS; - // TODO: all the extensions below should be queried through // eglGetProcAddress(). @@ -145,22 +150,6 @@ void DisplayHardware::init(uint32_t dpy) eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); - /* - * Gather EGL extensions - */ - - const char* const egl_extensions = eglQueryString( - display, EGL_EXTENSIONS); - - LOGI("EGL informations:"); - LOGI("# of configs : %d", numConfigs); - LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); - LOGI("version : %s", eglQueryString(display, EGL_VERSION)); - LOGI("extensions: %s", egl_extensions); - LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); - - if (mNativeWindow->isUpdateOnDemand()) { mFlags |= PARTIAL_UPDATES; } @@ -175,6 +164,8 @@ void DisplayHardware::init(uint32_t dpy) */ surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); if (mFlags & PARTIAL_UPDATES) { // if we have partial updates, we definitely don't need to @@ -188,31 +179,6 @@ void DisplayHardware::init(uint32_t dpy) mFlags |= BUFFER_PRESERVED; } } - - eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); - eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); - -#ifdef EGL_ANDROID_swap_rectangle - if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) { - if (eglSetSwapRectangleANDROID(display, surface, - 0, 0, mWidth, mHeight) == EGL_TRUE) { - // This could fail if this extension is not supported by this - // specific surface (of config) - mFlags |= SWAP_RECTANGLE; - } - } - // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE - // choose PARTIAL_UPDATES, which should be more efficient - if (mFlags & PARTIAL_UPDATES) - mFlags &= ~SWAP_RECTANGLE; -#endif - - - LOGI("flags : %08x", mFlags); - - mDpiX = mNativeWindow->xdpi; - mDpiY = mNativeWindow->ydpi; - mRefreshRate = fbDev->fps; /* Read density from build-specific ro.sf.lcd_density property * except if it is overridden by qemu.sf.lcd_density. @@ -235,61 +201,67 @@ void DisplayHardware::init(uint32_t dpy) context = eglCreateContext(display, config, NULL, NULL); + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = fbDev->format; + mPageFlipCount = 0; + /* * Gather OpenGL ES extensions */ eglMakeCurrent(display, surface, surface, context); - const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); - const char* const gl_renderer = (const char*)glGetString(GL_RENDERER); - LOGI("OpenGL informations:"); - LOGI("vendor : %s", glGetString(GL_VENDOR)); - LOGI("renderer : %s", gl_renderer); - LOGI("version : %s", glGetString(GL_VERSION)); - LOGI("extensions: %s", gl_extensions); + + GLExtensions& extensions(GLExtensions::getInstance()); + extensions.initWithGLStrings( + glGetString(GL_VENDOR), + glGetString(GL_RENDERER), + glGetString(GL_VERSION), + glGetString(GL_EXTENSIONS), + eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS)); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); - LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); - LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); -#if 0 - // for drivers that don't have proper support for flushing cached buffers - // on gralloc unlock, uncomment this block and test for the specific - // renderer substring - if (strstr(gl_renderer, "<some vendor string>")) { - LOGD("Assuming uncached graphics buffers."); - mFlags &= ~CACHED_BUFFERS; - } -#endif - if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { - mFlags |= NPOT_EXTENSION; - } - if (strstr(gl_extensions, "GL_OES_draw_texture")) { - mFlags |= DRAW_TEXTURE_EXTENSION; - } -#ifdef EGL_ANDROID_image_native_buffer - if (strstr( gl_extensions, "GL_OES_EGL_image") && - (strstr(egl_extensions, "EGL_KHR_image_base") || - strstr(egl_extensions, "EGL_KHR_image")) && - strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) { - mFlags |= DIRECT_TEXTURE; +#ifdef EGL_ANDROID_swap_rectangle + if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { + if (eglSetSwapRectangleANDROID(display, surface, + 0, 0, mWidth, mHeight) == EGL_TRUE) { + // This could fail if this extension is not supported by this + // specific surface (of config) + mFlags |= SWAP_RECTANGLE; + } } -#else -#warning "EGL_ANDROID_image_native_buffer not supported" + // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE + // choose PARTIAL_UPDATES, which should be more efficient + if (mFlags & PARTIAL_UPDATES) + mFlags &= ~SWAP_RECTANGLE; #endif + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", extensions.getEglVendor()); + LOGI("version : %s", extensions.getEglVersion()); + LOGI("extensions: %s", extensions.getEglExtension()); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + + LOGI("OpenGL informations:"); + LOGI("vendor : %s", extensions.getVendor()); + LOGI("renderer : %s", extensions.getRenderer()); + LOGI("version : %s", extensions.getVersion()); + LOGI("extensions: %s", extensions.getExtension()); + LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); + LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + LOGI("flags = %08x", mFlags); // Unbind the context from this thread eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - mDisplay = display; - mConfig = config; - mSurface = surface; - mContext = context; - mFormat = fbDev->format; - mPageFlipCount = 0; } /* diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h index df046af95a..66bf5215d5 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -29,6 +29,8 @@ #include <pixelflinger/pixelflinger.h> +#include "GLExtensions.h" + #include "DisplayHardware/DisplayHardwareBase.h" struct overlay_control_device_t; @@ -43,15 +45,11 @@ class DisplayHardware : public DisplayHardwareBase { public: enum { - DIRECT_TEXTURE = 0x00000002, - COPY_BITS_EXTENSION = 0x00000008, - NPOT_EXTENSION = 0x00000100, - DRAW_TEXTURE_EXTENSION = 0x00000200, - BUFFER_PRESERVED = 0x00010000, - PARTIAL_UPDATES = 0x00020000, // video driver feature - SLOW_CONFIG = 0x00040000, // software - SWAP_RECTANGLE = 0x00080000, - CACHED_BUFFERS = 0x00100000 + COPY_BITS_EXTENSION = 0x00000008, + BUFFER_PRESERVED = 0x00010000, + PARTIAL_UPDATES = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, }; DisplayHardware( diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp new file mode 100644 index 0000000000..7f4f9fc074 --- /dev/null +++ b/services/surfaceflinger/GLExtensions.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#include "GLExtensions.h" + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions ) + +GLExtensions::GLExtensions() + : mHaveTextureExternal(false), + mHaveNpot(false), + mHaveDirectTexture(false) +{ +} + +void GLExtensions::initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions) +{ + mVendor = (char const*)vendor; + mRenderer = (char const*)renderer; + mVersion = (char const*)version; + mExtensions = (char const*)extensions; + mEglVendor = egl_vendor; + mEglVersion = egl_version; + mEglExtensions = egl_extensions; + + char const* curr = (char const*)extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + + curr = egl_extensions; + head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + +#ifdef EGL_ANDROID_image_native_buffer + if (hasExtension("GL_OES_EGL_image") && + (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) && + hasExtension("EGL_ANDROID_image_native_buffer")) + { + mHaveDirectTexture = true; + } +#else +#warning "EGL_ANDROID_image_native_buffer not supported" +#endif + + if (hasExtension("GL_ARB_texture_non_power_of_two")) { + mHaveNpot = true; + } + + if (hasExtension("GL_OES_texture_external")) { + mHaveTextureExternal = true; + } else if (strstr(mRenderer.string(), "Adreno")) { + // hack for Adreno 200 + mHaveTextureExternal = true; + } +} + +bool GLExtensions::hasExtension(char const* extension) const +{ + const String8 s(extension); + return mExtensionList.indexOf(s) >= 0; +} + +char const* GLExtensions::getVendor() const { + return mVendor.string(); +} + +char const* GLExtensions::getRenderer() const { + return mRenderer.string(); +} + +char const* GLExtensions::getVersion() const { + return mVersion.string(); +} + +char const* GLExtensions::getExtension() const { + return mExtensions.string(); +} + +char const* GLExtensions::getEglVendor() const { + return mEglVendor.string(); +} + +char const* GLExtensions::getEglVersion() const { + return mEglVersion.string(); +} + +char const* GLExtensions::getEglExtension() const { + return mEglExtensions.string(); +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h new file mode 100644 index 0000000000..bbb284e768 --- /dev/null +++ b/services/surfaceflinger/GLExtensions.h @@ -0,0 +1,94 @@ +/* + * 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_SF_GLEXTENSION_H +#define ANDROID_SF_GLEXTENSION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/String8.h> +#include <utils/SortedVector.h> +#include <utils/Singleton.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +namespace android { +// --------------------------------------------------------------------------- + +class GLExtensions : public Singleton<GLExtensions> +{ + friend class Singleton<GLExtensions>; + + bool mHaveTextureExternal : 1; + bool mHaveNpot : 1; + bool mHaveDirectTexture : 1; + + String8 mVendor; + String8 mRenderer; + String8 mVersion; + String8 mExtensions; + String8 mEglVendor; + String8 mEglVersion; + String8 mEglExtensions; + SortedVector<String8> mExtensionList; + + GLExtensions(const GLExtensions&); + GLExtensions& operator = (const GLExtensions&); + +protected: + GLExtensions(); + +public: + inline bool haveTextureExternal() const { + return mHaveTextureExternal; + } + inline bool haveNpot() const { + return mHaveNpot; + } + inline bool haveDirectTexture() const { + return mHaveDirectTexture; + } + + void initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions); + + char const* getVendor() const; + char const* getRenderer() const; + char const* getVersion() const; + char const* getExtension() const; + + char const* getEglVendor() const; + char const* getEglVersion() const; + char const* getEglExtension() const; + + bool hasExtension(char const* extension) const; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SF_GLEXTENSION_H diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index ce7e9aa3f9..695cbfa0a8 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -31,6 +31,7 @@ #include <surfaceflinger/Surface.h> #include "clz.h" +#include "GLExtensions.h" #include "Layer.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" @@ -47,47 +48,73 @@ template <typename T> inline T min(T a, T b) { // --------------------------------------------------------------------------- -const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; -const char* const Layer::typeID = "Layer"; - -// --------------------------------------------------------------------------- - -Layer::Layer(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& c, int32_t i) - : LayerBaseClient(flinger, display, c, i), - mSecure(false), - mNoEGLImageForSwBuffers(false), +Layer::Layer(SurfaceFlinger* flinger, + DisplayID display, const sp<Client>& client) + : LayerBaseClient(flinger, display, client), + mGLExtensions(GLExtensions::getInstance()), mNeedsBlending(true), - mNeedsDithering(false) + mNeedsDithering(false), + mSecure(false), + mTextureManager(), + mBufferManager(mTextureManager), + mWidth(0), mHeight(0), mFixedSize(false) { - // no OpenGL operation is possible here, since we might not be - // in the OpenGL thread. - mFrontBufferIndex = lcblk->getFrontBuffer(); } Layer::~Layer() { - destroy(); - // the actual buffers will be destroyed here + // FIXME: must be called from the main UI thread + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + + // we can use getUserClientUnsafe here because we know we're + // single-threaded at that point. + sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe()); + if (ourClient != 0) { + ourClient->detachLayer(this); + } } -void Layer::destroy() +status_t Layer::setToken(const sp<UserClient>& userClient, + SharedClient* sharedClient, int32_t token) { - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - if (mTextures[i].name != -1U) { - glDeleteTextures(1, &mTextures[i].name); - mTextures[i].name = -1U; - } - if (mTextures[i].image != EGL_NO_IMAGE_KHR) { - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - eglDestroyImageKHR(dpy, mTextures[i].image); - mTextures[i].image = EGL_NO_IMAGE_KHR; - } - Mutex::Autolock _l(mLock); - mBuffers[i].clear(); - mWidth = mHeight = 0; + sp<SharedBufferServer> lcblk = new SharedBufferServer( + sharedClient, token, mBufferManager.getDefaultBufferCount(), + getIdentity()); + + status_t err = mUserClientRef.setToken(userClient, lcblk, token); + + LOGE_IF(err != NO_ERROR, + "ClientRef::setToken(%p, %p, %u) failed", + userClient.get(), lcblk.get(), token); + + if (err == NO_ERROR) { + // we need to free the buffers associated with this surface + } + + return err; +} + +int32_t Layer::getToken() const +{ + return mUserClientRef.getToken(); +} + +sp<UserClient> Layer::getClient() const +{ + return mUserClientRef.getClient(); +} + +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void Layer::onRemoved() +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + // wake up the condition + lcblk->setStatus(NO_INIT); } - mSurface.clear(); } sp<LayerBaseClient::Surface> Layer::createSurface() const @@ -97,9 +124,17 @@ sp<LayerBaseClient::Surface> Layer::createSurface() const status_t Layer::ditch() { + // NOTE: Called from the main UI thread + // the layer is not on screen anymore. free as much resources as possible mFreezeLock.clear(); - destroy(); + + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + mSurface.clear(); + + Mutex::Autolock _l(mLock); + mWidth = mHeight = 0; return NO_ERROR; } @@ -129,26 +164,26 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mFormat = format; mWidth = w; mHeight = h; + + mReqFormat = format; + mReqWidth = w; + mReqHeight = h; + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; - mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); // we use the red index int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); mNeedsDithering = layerRedsize > displayRedSize; - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mBuffers[i] = new GraphicBuffer(); - } - mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); + mSurface = new SurfaceLayer(mFlinger, this); return NO_ERROR; } void Layer::reloadTexture(const Region& dirty) { - Mutex::Autolock _l(mLock); - sp<GraphicBuffer> buffer(getFrontBufferLocked()); + sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); if (buffer == NULL) { // this situation can happen if we ran out of memory for instance. // not much we can do. continue to use whatever texture was bound @@ -156,130 +191,33 @@ void Layer::reloadTexture(const Region& dirty) return; } - const int index = mFrontBufferIndex; - - // create the new texture name if needed - if (UNLIKELY(mTextures[index].name == -1U)) { - mTextures[index].name = createTexture(); - mTextures[index].width = 0; - mTextures[index].height = 0; - } - -#ifdef EGL_ANDROID_image_native_buffer - if (mFlags & DisplayHardware::DIRECT_TEXTURE) { - if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { - if (mTextures[index].dirty) { - if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - goto slowpath; - } - } - } else { - if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || - mHybridBuffer->height != buffer->height)) { - mHybridBuffer.clear(); - mHybridBuffer = new GraphicBuffer( - buffer->width, buffer->height, buffer->format, - GraphicBuffer::USAGE_SW_WRITE_OFTEN | - GraphicBuffer::USAGE_HW_TEXTURE); - if (initializeEglImage( - mHybridBuffer, &mTextures[0]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - mHybridBuffer.clear(); - goto slowpath; - } - } - - GGLSurface t; + if (mGLExtensions.haveDirectTexture()) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { + // not sure what we can do here... + goto slowpath; + } + } else { +slowpath: + GGLSurface t; + if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) { status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); LOGE_IF(res, "error %d (%s) locking buffer %p", res, strerror(res), buffer.get()); if (res == NO_ERROR) { - Texture* const texture(&mTextures[0]); - - glBindTexture(GL_TEXTURE_2D, texture->name); - - sp<GraphicBuffer> buf(mHybridBuffer); - void* vaddr; - res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); - if (res == NO_ERROR) { - int bpp = 0; - switch (t.format) { - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_4444: - bpp = 2; - break; - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - bpp = 4; - break; - default: - if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - bpp = 1; - break; - } - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - if (bpp) { - const Rect bounds(dirty.getBounds()); - size_t src_stride = t.stride; - size_t dst_stride = buf->stride; - if (src_stride == dst_stride && - bounds.width() == t.width && - bounds.height() == t.height) - { - memcpy(vaddr, t.data, t.height * t.stride * bpp); - } else { - GLubyte const * src = t.data + - (bounds.left + bounds.top * src_stride) * bpp; - GLubyte * dst = (GLubyte *)vaddr + - (bounds.left + bounds.top * dst_stride) * bpp; - const size_t length = bounds.width() * bpp; - size_t h = bounds.height(); - src_stride *= bpp; - dst_stride *= bpp; - while (h--) { - memcpy(dst, src, length); - dst += dst_stride; - src += src_stride; - } - } - } - buf->unlock(); - } + mBufferManager.loadTexture(dirty, t); buffer->unlock(); } - } - } else -#endif - { -slowpath: - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mTextures[i].image = EGL_NO_IMAGE_KHR; - } - GGLSurface t; - status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); - LOGE_IF(res, "error %d (%s) locking buffer %p", - res, strerror(res), buffer.get()); - if (res == NO_ERROR) { - loadTexture(&mTextures[0], dirty, t); - buffer->unlock(); + } else { + // we can't do anything } } } void Layer::onDraw(const Region& clip) const { - int index = mFrontBufferIndex; - if (mTextures[index].image == EGL_NO_IMAGE_KHR) - index = 0; - GLuint textureName = mTextures[index].name; - if (UNLIKELY(textureName == -1LU)) { + Texture tex(mBufferManager.getActiveTexture()); + if (tex.name == -1LU) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client @@ -301,21 +239,61 @@ void Layer::onDraw(const Region& clip) const // if not everything below us is covered, we plug the holes! Region holes(clip.subtract(under)); if (!holes.isEmpty()) { - clearWithOpenGL(holes); + clearWithOpenGL(holes, 0, 0, 0, 1); } return; } - drawWithOpenGL(clip, mTextures[index]); + drawWithOpenGL(clip, tex); } -sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) +bool Layer::needsFiltering() const +{ + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // NOTE: there is a race here, because mFixedSize is updated in a + // binder transaction. however, it doesn't really matter since it is + // evaluated each time we draw. To be perfectly correct, this flag + // would have to be associated with a buffer. + if (mFixedSize) + return true; + } + return LayerBase::needsFiltering(); +} + + +status_t Layer::setBufferCount(int bufferCount) +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { + // oops, the client is already gone + return DEAD_OBJECT; + } + + // NOTE: lcblk->resize() is protected by an internal lock + status_t err = lcblk->resize(bufferCount); + if (err == NO_ERROR) + mBufferManager.resize(bufferCount); + + return err; +} + +sp<GraphicBuffer> Layer::requestBuffer(int index, + uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat, + uint32_t usage) { sp<GraphicBuffer> buffer; + if (int32_t(reqWidth | reqHeight | reqFormat) < 0) + return buffer; + + if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight)) + return buffer; + // this ensures our client doesn't go away while we're accessing // the shared area. - sp<Client> ourClient(client.promote()); - if (ourClient == 0) { + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { // oops, the client is already gone return buffer; } @@ -324,46 +302,38 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) * This is called from the client's Surface::dequeue(). This can happen * at any time, especially while we're in the middle of using the * buffer 'index' as our front buffer. - * - * Make sure the buffer we're resizing is not the front buffer and has been - * dequeued. Once this condition is asserted, we are guaranteed that this - * buffer cannot become the front buffer under our feet, since we're called - * from Surface::dequeue() */ - status_t err = lcblk->assertReallocate(index); - LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err)); - if (err != NO_ERROR) { - // the surface may have died - return buffer; - } - uint32_t w, h; + status_t err = NO_ERROR; + uint32_t w, h, f; { // scope for the lock Mutex::Autolock _l(mLock); - w = mWidth; - h = mHeight; - buffer = mBuffers[index]; - - // destroy() could have been called before we get here, we log it - // because it's uncommon, and the code below should handle it - LOGW_IF(buffer==0, - "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", - index, w, h); - - mBuffers[index].clear(); + + // zero means default + if (!reqFormat) reqFormat = mFormat; + if (!reqWidth) reqWidth = mWidth; + if (!reqHeight) reqHeight = mHeight; + + w = reqWidth; + h = reqHeight; + f = reqFormat; + + if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) || + (reqFormat != mReqFormat)) { + mReqWidth = reqWidth; + mReqHeight = reqHeight; + mReqFormat = reqFormat; + + lcblk->reallocateAllExcept(index); + } } + // here we have to reallocate a new buffer because the buffer could be + // used as the front buffer, or by a client in our process + // (eg: status bar), and we can't release the handle under its feet. const uint32_t effectiveUsage = getEffectiveUsage(usage); - if (buffer!=0 && buffer->getStrongCount() == 1) { - err = buffer->reallocate(w, h, mFormat, effectiveUsage); - } else { - // here we have to reallocate a new buffer because we could have a - // client in our process with a reference to it (eg: status bar), - // and we can't release the handle under its feet. - buffer.clear(); - buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage); - err = buffer->initCheck(); - } + buffer = new GraphicBuffer(w, h, f, effectiveUsage); + err = buffer->initCheck(); if (err || buffer->handle == 0) { LOGE_IF(err || buffer->handle == 0, @@ -377,15 +347,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) if (err == NO_ERROR && buffer->handle != 0) { Mutex::Autolock _l(mLock); - if (mWidth && mHeight) { - // and we have new buffer - mBuffers[index] = buffer; - // texture is now dirty... - mTextures[index].dirty = true; - } else { - // oops we got killed while we were allocating the buffer - buffer.clear(); - } + mBufferManager.attachBuffer(index, buffer); } return buffer; } @@ -411,15 +373,8 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const } else { // it's allowed to modify the usage flags here, but generally // the requested flags should be honored. - if (mNoEGLImageForSwBuffers) { - if (usage & GraphicBuffer::USAGE_HW_MASK) { - // request EGLImage for h/w buffers only - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } - } else { - // request EGLImage for all buffers - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; } return usage; } @@ -429,42 +384,50 @@ uint32_t Layer::doTransaction(uint32_t flags) const Layer::State& front(drawingState()); const Layer::State& temp(currentState()); - if ((front.requested_w != temp.requested_w) || - (front.requested_h != temp.requested_h)) { + const bool sizeChanged = (front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h); + + if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, - "resize (layer=%p), requested (%dx%d), " - "drawing (%d,%d), (%dx%d), (%dx%d)", - this, - int(temp.requested_w), int(temp.requested_h), - int(front.requested_w), int(front.requested_h), - int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), - int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); - - // we're being resized and there is a freeze display request, - // acquire a freeze lock, so that the screen stays put - // until we've redrawn at the new size; this is to avoid - // glitches upon orientation changes. - if (mFlinger->hasFreezeRequest()) { - // if the surface is hidden, don't try to acquire the - // freeze lock, since hidden surfaces may never redraw - if (!(front.flags & ISurfaceComposer::eLayerHidden)) { - mFreezeLock = mFlinger->getFreezeLock(); + "resize (layer=%p), requested (%dx%d), drawing (%d,%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h)); + + if (!isFixedSize()) { + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } } - } - - // this will make sure LayerBase::doTransaction doesn't update - // the drawing state's size - Layer::State& editDraw(mDrawingState); - editDraw.requested_w = temp.requested_w; - editDraw.requested_h = temp.requested_h; - - // record the new size, form this point on, when the client request a - // buffer, it'll get the new size. - setDrawingSize(temp.requested_w, temp.requested_h); - // all buffers need reallocation - lcblk->reallocate(); + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; + + // record the new size, form this point on, when the client request + // a buffer, it'll get the new size. + setBufferSize(temp.requested_w, temp.requested_h); + + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + // all buffers need reallocation + lcblk->reallocateAll(); + } + } else { + // record the new size + setBufferSize(temp.requested_w, temp.requested_h); + } } if (temp.sequence != front.sequence) { @@ -478,39 +441,55 @@ uint32_t Layer::doTransaction(uint32_t flags) return LayerBase::doTransaction(flags); } -void Layer::setDrawingSize(uint32_t w, uint32_t h) { +void Layer::setBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock _l(mLock); mWidth = w; mHeight = h; } +bool Layer::isFixedSize() const { + Mutex::Autolock _l(mLock); + return mFixedSize; +} + // ---------------------------------------------------------------------------- // pageflip handling... // ---------------------------------------------------------------------------- void Layer::lockPageFlip(bool& recomputeVisibleRegions) { + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { + // client died + recomputeVisibleRegions = true; + return; + } + ssize_t buf = lcblk->retireAndLock(); - if (buf < NO_ERROR) { - //LOGW("nothing to retire (%s)", strerror(-buf)); - // NOTE: here the buffer is locked because we will used + if (buf == NOT_ENOUGH_DATA) { + // NOTE: This is not an error, it simply means there is nothing to + // retire. The buffer is locked because we will use it // for composition later in the loop return; } - // ouch, this really should never happen - if (uint32_t(buf)>=NUM_BUFFERS) { - LOGE("retireAndLock() buffer index (%d) out of range", buf); + if (buf < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); mPostedDirtyRegion.clear(); return; } // we retired a buffer, which becomes the new front buffer - mFrontBufferIndex = buf; + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); + mPostedDirtyRegion.clear(); + return; + } - // get the dirty region sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); if (newFrontBuffer != NULL) { + // get the dirty region // compute the posted region const Region dirty(lcblk->getDirtyRegion(buf)); mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); @@ -546,6 +525,13 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we now have the correct size, unfreeze the screen mFreezeLock.clear(); } + + // get the crop region + setBufferCrop( lcblk->getCrop(buf) ); + + // get the transformation + setBufferTransform( lcblk->getTransform(buf) ); + } else { // this should not happen unless we ran out of memory while // allocating the buffer. we're hoping that things will get back @@ -559,9 +545,15 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) mFlinger->signalEvent(); } - if (!mPostedDirtyRegion.isEmpty()) { - reloadTexture( mPostedDirtyRegion ); - } + /* a buffer was posted, so we need to call reloadTexture(), which + * will update our internal data structures (eg: EGLImageKHR or + * texture names). we need to do this even if mPostedDirtyRegion is + * empty -- it's orthogonal to the fact that a new buffer was posted, + * for instance, a degenerate case could be that the user did an empty + * update but repainted the buffer with appropriate content (after a + * resize for instance). + */ + reloadTexture( mPostedDirtyRegion ); } void Layer::unlockPageFlip( @@ -585,24 +577,265 @@ void Layer::unlockPageFlip( } if (visibleRegionScreen.isEmpty()) { // an invisible layer should not hold a freeze-lock - // (because it may never be updated and thereore never release it) + // (because it may never be updated and therefore never release it) mFreezeLock.clear(); } } void Layer::finishPageFlip() { - status_t err = lcblk->unlock( mFrontBufferIndex ); - LOGE_IF(err!=NO_ERROR, - "layer %p, buffer=%d wasn't locked!", - this, mFrontBufferIndex); + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + int buf = mBufferManager.getActiveBufferIndex(); + if (buf >= 0) { + status_t err = lcblk->unlock( buf ); + LOGE_IF(err!=NO_ERROR, + "layer %p, buffer=%d wasn't locked!", + this, buf); + } + } +} + + +void Layer::dump(String8& result, char* buffer, size_t SIZE) const +{ + LayerBaseClient::dump(result, buffer, SIZE); + + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + uint32_t totalTime = 0; + if (lcblk) { + SharedBufferStack::Statistics stats = lcblk->getStats(); + totalTime= stats.totalTime; + result.append( lcblk->dump(" ") ); + } + + sp<const GraphicBuffer> buf0(getBuffer(0)); + sp<const GraphicBuffer> buf1(getBuffer(1)); + uint32_t w0=0, h0=0, s0=0; + uint32_t w1=0, h1=0, s1=0; + if (buf0 != 0) { + w0 = buf0->getWidth(); + h0 = buf0->getHeight(); + s0 = buf0->getStride(); + } + if (buf1 != 0) { + w1 = buf1->getWidth(); + h1 = buf1->getHeight(); + s1 = buf1->getStride(); + } + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," + " freezeLock=%p, dq-q-time=%u us\n", + mFormat, w0, h0, s0, w1, h1, s1, + getFreezeLock().get(), totalTime); + + result.append(buffer); +} + +// --------------------------------------------------------------------------- + +Layer::ClientRef::ClientRef() + : mControlBlock(0), mToken(-1) { +} + +Layer::ClientRef::~ClientRef() { +} + +int32_t Layer::ClientRef::getToken() const { + Mutex::Autolock _l(mLock); + return mToken; +} + +sp<UserClient> Layer::ClientRef::getClient() const { + Mutex::Autolock _l(mLock); + return mUserClient.promote(); +} + +status_t Layer::ClientRef::setToken(const sp<UserClient>& uc, + const sp<SharedBufferServer>& sharedClient, int32_t token) { + Mutex::Autolock _l(mLock); + + { // scope for strong mUserClient reference + sp<UserClient> userClient(mUserClient.promote()); + if (mUserClient != 0 && mControlBlock != 0) { + mControlBlock->setStatus(NO_INIT); + } + } + + mUserClient = uc; + mToken = token; + mControlBlock = sharedClient; + return NO_ERROR; +} + +sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const { + return mUserClient.promote(); +} + +// this class gives us access to SharedBufferServer safely +// it makes sure the UserClient (and its associated shared memory) +// won't go away while we're accessing it. +Layer::ClientRef::Access::Access(const ClientRef& ref) + : mControlBlock(0) +{ + Mutex::Autolock _l(ref.mLock); + mUserClientStrongRef = ref.mUserClient.promote(); + if (mUserClientStrongRef != 0) + mControlBlock = ref.mControlBlock; +} + +Layer::ClientRef::Access::~Access() +{ +} + +// --------------------------------------------------------------------------- + +Layer::BufferManager::BufferManager(TextureManager& tm) + : mNumBuffers(NUM_BUFFERS), mTextureManager(tm), + mActiveBuffer(-1), mFailover(false) +{ +} + +Layer::BufferManager::~BufferManager() +{ +} + +status_t Layer::BufferManager::resize(size_t size) +{ + Mutex::Autolock _l(mLock); + mNumBuffers = size; + return NO_ERROR; +} + +// only for debugging +sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const { + return mBufferData[index].buffer; +} + +status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { + mActiveBuffer = index; + return NO_ERROR; +} + +size_t Layer::BufferManager::getActiveBufferIndex() const { + return mActiveBuffer; +} + +Texture Layer::BufferManager::getActiveTexture() const { + Texture res; + if (mFailover || mActiveBuffer<0) { + res = mFailoverTexture; + } else { + static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture; + } + return res; +} + +sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const { + sp<GraphicBuffer> result; + const ssize_t activeBuffer = mActiveBuffer; + if (activeBuffer >= 0) { + BufferData const * const buffers = mBufferData; + Mutex::Autolock _l(mLock); + result = buffers[activeBuffer].buffer; + } + return result; +} + +sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index) +{ + BufferData* const buffers = mBufferData; + sp<GraphicBuffer> buffer; + Mutex::Autolock _l(mLock); + buffer = buffers[index].buffer; + buffers[index].buffer = 0; + return buffer; +} + +status_t Layer::BufferManager::attachBuffer(size_t index, + const sp<GraphicBuffer>& buffer) +{ + BufferData* const buffers = mBufferData; + Mutex::Autolock _l(mLock); + buffers[index].buffer = buffer; + buffers[index].texture.dirty = true; + return NO_ERROR; +} + +status_t Layer::BufferManager::destroy(EGLDisplay dpy) +{ + BufferData* const buffers = mBufferData; + size_t num; + { // scope for the lock + Mutex::Autolock _l(mLock); + num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + buffers[i].buffer = 0; + } + } + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&buffers[i].texture, dpy); + } + destroyTexture(&mFailoverTexture, dpy); + return NO_ERROR; +} + +status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_INIT; + ssize_t index = mActiveBuffer; + if (index >= 0) { + if (!mFailover) { + Image& texture(mBufferData[index].texture); + err = mTextureManager.initEglImage(&texture, dpy, buffer); + // if EGLImage fails, we switch to regular texture mode, and we + // free all resources associated with using EGLImages. + if (err == NO_ERROR) { + mFailover = false; + destroyTexture(&mFailoverTexture, dpy); + } else { + mFailover = true; + const size_t num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + } + } + } else { + // we failed once, don't try again + err = BAD_VALUE; + } + } + return err; +} + +status_t Layer::BufferManager::loadTexture( + const Region& dirty, const GGLSurface& t) +{ + return mTextureManager.loadTexture(&mFailoverTexture, dirty, t); +} + +status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy) +{ + if (tex->name != -1U) { + glDeleteTextures(1, &tex->name); + tex->name = -1U; + } + if (tex->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, tex->image); + tex->image = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; } // --------------------------------------------------------------------------- Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, - SurfaceID id, const sp<Layer>& owner) - : Surface(flinger, id, owner->getIdentity(), owner) + const sp<Layer>& owner) + : Surface(flinger, owner->getIdentity(), owner) { } @@ -610,20 +843,37 @@ Layer::SurfaceLayer::~SurfaceLayer() { } -sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { sp<GraphicBuffer> buffer; sp<Layer> owner(getOwner()); if (owner != 0) { - LOGE_IF(uint32_t(index)>=NUM_BUFFERS, - "getBuffer() index (%d) out of range", index); - if (uint32_t(index) < NUM_BUFFERS) { - buffer = owner->requestBuffer(index, usage); - } + /* + * requestBuffer() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + buffer = owner->requestBuffer(index, w, h, format, usage); } return buffer; } +status_t Layer::SurfaceLayer::setBufferCount(int bufferCount) +{ + status_t err = DEAD_OBJECT; + sp<Layer> owner(getOwner()); + if (owner != 0) { + /* + * setBufferCount() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + err = owner->setBufferCount(bufferCount); + } + return err; +} + // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 743afb4c47..e1d283bedd 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -31,36 +31,43 @@ #include "LayerBase.h" #include "Transform.h" +#include "TextureManager.h" namespace android { // --------------------------------------------------------------------------- -class Client; class FreezeLock; +class Client; +class GLExtensions; +class UserClient; // --------------------------------------------------------------------------- -const size_t NUM_BUFFERS = 2; - class Layer : public LayerBaseClient { -public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - - Layer(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i); +public: + Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); - virtual ~Layer(); + virtual ~Layer(); + virtual const char* getTypeId() const { return "Layer"; } + + // the this layer's size and format status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags=0); - void setDrawingSize(uint32_t w, uint32_t h); + // associate a UserClient to this Layer + status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx); + int32_t getToken() const; + sp<UserClient> getClient() const; + + // Set this Layer's buffers size + void setBufferSize(uint32_t w, uint32_t h); + bool isFixedSize() const; + // LayerBase interface virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t transactionFlags); virtual void lockPageFlip(bool& recomputeVisibleRegions); @@ -68,63 +75,161 @@ public: virtual void finishPageFlip(); virtual bool needsBlending() const { return mNeedsBlending; } virtual bool needsDithering() const { return mNeedsDithering; } + virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } virtual sp<Surface> createSurface() const; virtual status_t ditch(); - - // only for debugging - inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } - // only for debugging - inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + virtual void onRemoved(); + // only for debugging - inline PixelFormat pixelFormat() const { return mFormat; } + inline sp<GraphicBuffer> getBuffer(int i) const { + return mBufferManager.getBuffer(i); } // only for debugging - inline int getFrontBufferIndex() const { return mFrontBufferIndex; } + inline const sp<FreezeLock>& getFreezeLock() const { + return mFreezeLock; } + +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; private: - inline sp<GraphicBuffer> getFrontBufferLocked() { - return mBuffers[mFrontBufferIndex]; - } - void reloadTexture(const Region& dirty); - uint32_t getEffectiveUsage(uint32_t usage) const; + sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + status_t setBufferCount(int bufferCount); - sp<GraphicBuffer> requestBuffer(int index, int usage); - void destroy(); + // ----------------------------------------------------------------------- class SurfaceLayer : public LayerBaseClient::Surface { public: - SurfaceLayer(const sp<SurfaceFlinger>& flinger, - SurfaceID id, const sp<Layer>& owner); + SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner); ~SurfaceLayer(); private: - virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); sp<Layer> getOwner() const { return static_cast<Layer*>(Surface::getOwner().get()); } }; friend class SurfaceLayer; - - sp<Surface> mSurface; - - bool mSecure; - bool mNoEGLImageForSwBuffers; - int32_t mFrontBufferIndex; - bool mNeedsBlending; - bool mNeedsDithering; - Region mPostedDirtyRegion; - sp<FreezeLock> mFreezeLock; - PixelFormat mFormat; - - // protected by mLock - sp<GraphicBuffer> mBuffers[NUM_BUFFERS]; - Texture mTextures[NUM_BUFFERS]; - sp<GraphicBuffer> mHybridBuffer; - uint32_t mWidth; - uint32_t mHeight; - - mutable Mutex mLock; + + // ----------------------------------------------------------------------- + + class ClientRef { + ClientRef(const ClientRef& rhs); + ClientRef& operator = (const ClientRef& rhs); + mutable Mutex mLock; + // binder thread, page-flip thread + sp<SharedBufferServer> mControlBlock; + wp<UserClient> mUserClient; + int32_t mToken; + public: + ClientRef(); + ~ClientRef(); + int32_t getToken() const; + sp<UserClient> getClient() const; + status_t setToken(const sp<UserClient>& uc, + const sp<SharedBufferServer>& sharedClient, int32_t token); + sp<UserClient> getUserClientUnsafe() const; + class Access { + Access(const Access& rhs); + Access& operator = (const Access& rhs); + sp<UserClient> mUserClientStrongRef; + sp<SharedBufferServer> mControlBlock; + public: + Access(const ClientRef& ref); + ~Access(); + inline SharedBufferServer* get() const { return mControlBlock.get(); } + }; + friend class Access; + }; + + // ----------------------------------------------------------------------- + + class BufferManager { + static const size_t NUM_BUFFERS = 2; + struct BufferData { + sp<GraphicBuffer> buffer; + Image texture; + }; + // this lock protect mBufferData[].buffer but since there + // is very little contention, we have only one like for + // the whole array, we also use it to protect mNumBuffers. + mutable Mutex mLock; + BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX]; + size_t mNumBuffers; + Texture mFailoverTexture; + TextureManager& mTextureManager; + ssize_t mActiveBuffer; + bool mFailover; + static status_t destroyTexture(Image* tex, EGLDisplay dpy); + + public: + static size_t getDefaultBufferCount() { return NUM_BUFFERS; } + BufferManager(TextureManager& tm); + ~BufferManager(); + + // detach/attach buffer from/to given index + sp<GraphicBuffer> detachBuffer(size_t index); + status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer); + // resize the number of active buffers + status_t resize(size_t size); + + // ---------------------------------------------- + // must be called from GL thread + + // set/get active buffer index + status_t setActiveBufferIndex(size_t index); + size_t getActiveBufferIndex() const; + // return the active buffer + sp<GraphicBuffer> getActiveBuffer() const; + // return the active texture (or fail-over) + Texture getActiveTexture() const; + // frees resources associated with all buffers + status_t destroy(EGLDisplay dpy); + // load bitmap data into the active buffer + status_t loadTexture(const Region& dirty, const GGLSurface& t); + // make active buffer an EGLImage if needed + status_t initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer); + + // ---------------------------------------------- + // only for debugging + sp<GraphicBuffer> getBuffer(size_t index) const; + }; + + // ----------------------------------------------------------------------- + + // thread-safe + ClientRef mUserClientRef; + + // constants + sp<Surface> mSurface; + PixelFormat mFormat; + const GLExtensions& mGLExtensions; + bool mNeedsBlending; + bool mNeedsDithering; + + // page-flip thread (currently main thread) + bool mSecure; + Region mPostedDirtyRegion; + + // page-flip thread and transaction thread (currently main thread) + sp<FreezeLock> mFreezeLock; + + // see threading usage in declaration + TextureManager mTextureManager; + BufferManager mBufferManager; + + // binder thread, transaction thread + mutable Mutex mLock; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mReqWidth; + uint32_t mReqHeight; + uint32_t mReqFormat; + bool mFixedSize; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index a8b735ef6a..6fc50109eb 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -32,33 +32,30 @@ #include "LayerBase.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBase::typeInfo = 1; -const char* const LayerBase::typeID = "LayerBase"; - -const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; -const char* const LayerBaseClient::typeID = "LayerBaseClient"; - -// --------------------------------------------------------------------------- +int32_t LayerBase::sSequence = 1; LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), + sequence(uint32_t(android_atomic_inc(&sSequence))), mFlinger(flinger), - mTransformed(false), - mUseLinearFiltering(false), + mNeedsFiltering(false), mOrientation(0), mLeft(0), mTop(0), mTransactionFlags(0), - mPremultipliedAlpha(true), mDebug(false), + mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), mInvalidate(0) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); + mBufferCrop.makeInvalid(); + mBufferTransform = 0; } LayerBase::~LayerBase() @@ -159,7 +156,6 @@ bool LayerBase::setAlpha(uint8_t alpha) { return true; } bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { - // TODO: check the matrix has changed mCurrentState.sequence++; mCurrentState.transform.set( matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); @@ -167,7 +163,6 @@ bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { return true; } bool LayerBase::setTransparentRegionHint(const Region& transparent) { - // TODO: check the region has changed mCurrentState.sequence++; mCurrentState.transparentRegion = transparent; requestTransaction(); @@ -221,13 +216,12 @@ uint32_t LayerBase::doTransaction(uint32_t flags) flags |= eVisibleRegion; this->contentDirty = true; - const bool linearFiltering = mUseLinearFiltering; - mUseLinearFiltering = false; + mNeedsFiltering = false; if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { // we may use linear filtering, if the matrix scales us const uint8_t type = temp.transform.getType(); if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) { - mUseLinearFiltering = true; + mNeedsFiltering = true; } } } @@ -267,7 +261,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform) // cache a few things... mOrientation = tr.getOrientation(); mTransformedBounds = tr.makeBounds(w, h); - mTransformed = transformed; mLeft = tr.tx(); mTop = tr.ty(); } @@ -316,65 +309,31 @@ void LayerBase::drawRegion(const Region& reg) const } } -void LayerBase::draw(const Region& inClip) const +void LayerBase::draw(const Region& clip) const { - // invalidate the region we'll update - Region clip(inClip); // copy-on-write, so no-op most of the time - - // Remove the transparent area from the clipping region - const State& s = drawingState(); - if (LIKELY(!s.transparentRegion.isEmpty())) { - clip.subtract(transparentRegionScreen); - if (clip.isEmpty()) { - // usually this won't happen because this should be taken care of - // by SurfaceFlinger::computeVisibleRegions() - return; - } - } - // reset GL state glEnable(GL_SCISSOR_TEST); onDraw(clip); - - /* - glDisable(GL_TEXTURE_2D); - glDisable(GL_DITHER); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0, 0x8000, 0, 0x10000); - drawRegion(transparentRegionScreen); - glDisable(GL_BLEND); - */ } -GLuint LayerBase::createTexture() const -{ - GLuint textureName = -1; - glGenTextures(1, &textureName); - glBindTexture(GL_TEXTURE_2D, textureName); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - return textureName; -} - -void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, - GLclampx green, GLclampx blue, - GLclampx alpha) const +void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, + GLclampf green, GLclampf blue, + GLclampf alpha) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); - glColor4x(red,green,blue,alpha); - glDisable(GL_TEXTURE_2D); + glColor4f(red,green,blue,alpha); + + TextureManager::deactivateTextures(); + glDisable(GL_BLEND); glDisable(GL_DITHER); Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); glEnable(GL_SCISSOR_TEST); - glVertexPointer(2, GL_FIXED, 0, mVertices); + glVertexPointer(2, GL_FLOAT, 0, mVertices); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); @@ -388,6 +347,14 @@ void LayerBase::clearWithOpenGL(const Region& clip) const clearWithOpenGL(clip,0,0,0,0); } +template <typename T> +static inline +void swap(T& a, T& b) { + T t(a); + a = b; + b = t; +} + void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); @@ -395,39 +362,25 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const const State& s(drawingState()); // bind our texture - validateTexture(texture.name); + TextureManager::activateTexture(texture, needsFiltering()); uint32_t width = texture.width; uint32_t height = texture.height; - - glEnable(GL_TEXTURE_2D); + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; if (UNLIKELY(s.alpha < 0xFF)) { - // We have an alpha-modulation. We need to modulate all - // texture components by alpha because we're always using - // premultiplied alpha. - - // If the texture doesn't have an alpha channel we can - // use REPLACE and switch to non premultiplied alpha - // blending (SRCA/ONE_MINUS_SRCA). - - GLenum env, src; - if (needsBlending()) { - env = GL_MODULATE; - src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + const GLfloat alpha = s.alpha * (1.0f/255.0f); + if (mPremultipliedAlpha) { + glColor4f(alpha, alpha, alpha, alpha); } else { - env = GL_REPLACE; - src = GL_SRC_ALPHA; + glColor4f(1, 1, 1, alpha); } - const GGLfixed alpha = (s.alpha << 16)/255; - glColor4x(alpha, alpha, alpha, alpha); glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } else { + glColor4f(1, 1, 1, 1); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glColor4x(0x10000, 0x10000, 0x10000, 0x10000); if (needsBlending()) { - GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); } else { @@ -435,44 +388,85 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } } - Region::const_iterator it = clip.begin(); - Region::const_iterator const end = clip.end(); + /* + * compute texture coordinates + * here, we handle NPOT, cropping and buffer transformations + */ + + GLfloat cl, ct, cr, cb; + if (!mBufferCrop.isEmpty()) { + // source is cropped + const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width; + const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height; + cl = mBufferCrop.left * us; + ct = mBufferCrop.top * vs; + cr = mBufferCrop.right * us; + cb = mBufferCrop.bottom * vs; + } else { + cl = 0; + ct = 0; + cr = (texture.NPOTAdjust ? texture.wScale : 1.0f); + cb = (texture.NPOTAdjust ? texture.hScale : 1.0f); + } - //StopWatch watch("GL transformed"); - const GLfixed texCoords[4][2] = { - { 0, 0 }, - { 0, 0x10000 }, - { 0x10000, 0x10000 }, - { 0x10000, 0 } + struct TexCoords { + GLfloat u; + GLfloat v; }; - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); + enum { + // name of the corners in the texture map + LB = 0, // left-bottom + LT = 1, // left-top + RT = 2, // right-top + RB = 3 // right-bottom + }; + + // vertices in screen space + int vLT = LB; + int vLB = LT; + int vRB = RT; + int vRT = RB; // the texture's source is rotated - switch (texture.transform) { - case HAL_TRANSFORM_ROT_90: - glTranslatef(0, 1, 0); - glRotatef(-90, 0, 0, 1); - break; - case HAL_TRANSFORM_ROT_180: - glTranslatef(1, 1, 0); - glRotatef(-180, 0, 0, 1); - break; - case HAL_TRANSFORM_ROT_270: - glTranslatef(1, 0, 0); - glRotatef(-270, 0, 0, 1); - break; + uint32_t transform = mBufferTransform; + if (transform & HAL_TRANSFORM_ROT_90) { + vLT = RB; + vLB = LB; + vRB = LT; + vRT = RT; + } + if (transform & HAL_TRANSFORM_FLIP_V) { + swap(vLT, vLB); + swap(vRB, vRT); } + if (transform & HAL_TRANSFORM_FLIP_H) { + swap(vLT, vRB); + swap(vLB, vRT); + } + + TexCoords texCoords[4]; + texCoords[vLT].u = cl; + texCoords[vLT].v = ct; + texCoords[vLB].u = cl; + texCoords[vLB].v = cb; + texCoords[vRB].u = cr; + texCoords[vRB].v = cb; + texCoords[vRT].u = cr; + texCoords[vRT].v = ct; - if (texture.NPOTAdjust) { - glScalef(texture.wScale, texture.hScale, 1.0f); + if (needsDithering()) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); } glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FIXED, 0, mVertices); - glTexCoordPointer(2, GL_FIXED, 0, texCoords); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); @@ -482,246 +476,50 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glDisableClientState(GL_TEXTURE_COORD_ARRAY); } -void LayerBase::validateTexture(GLint textureName) const -{ - glBindTexture(GL_TEXTURE_2D, textureName); - // TODO: reload the texture if needed - // this is currently done in loadTexture() below - if (mUseLinearFiltering) { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - if (needsDithering()) { - glEnable(GL_DITHER); - } else { - glDisable(GL_DITHER); - } -} - -bool LayerBase::isSupportedYuvFormat(int format) const -{ - switch (format) { - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: - case HAL_PIXEL_FORMAT_YCbCr_420_P: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_YCbCr_420_I: - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - return true; +void LayerBase::setBufferCrop(const Rect& crop) { + if (!crop.isEmpty()) { + mBufferCrop = crop; } - return false; } -void LayerBase::loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const -{ - if (texture->name == -1U) { - // uh? - return; - } - - glBindTexture(GL_TEXTURE_2D, texture->name); - - /* - * In OpenGL ES we can't specify a stride with glTexImage2D (however, - * GL_UNPACK_ALIGNMENT is a limited form of stride). - * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we - * need to do something reasonable (here creating a bigger texture). - * - * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); - * - * This situation doesn't happen often, but some h/w have a limitation - * for their framebuffer (eg: must be multiple of 8 pixels), and - * we need to take that into account when using these buffers as - * textures. - * - * This should never be a problem with POT textures - */ - - int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); - unpack = 1 << ((unpack > 3) ? 3 : unpack); - glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); - - /* - * round to POT if needed - */ - if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { - texture->NPOTAdjust = true; - } - - if (texture->NPOTAdjust) { - // find the smallest power-of-two that will accommodate our surface - texture->potWidth = 1 << (31 - clz(t.width)); - texture->potHeight = 1 << (31 - clz(t.height)); - if (texture->potWidth < t.width) texture->potWidth <<= 1; - if (texture->potHeight < t.height) texture->potHeight <<= 1; - texture->wScale = float(t.width) / texture->potWidth; - texture->hScale = float(t.height) / texture->potHeight; - } else { - texture->potWidth = t.width; - texture->potHeight = t.height; - } - - Rect bounds(dirty.bounds()); - GLvoid* data = 0; - if (texture->width != t.width || texture->height != t.height) { - texture->width = t.width; - texture->height = t.height; - - // texture size changed, we need to create a new one - bounds.set(Rect(t.width, t.height)); - if (t.width == texture->potWidth && - t.height == texture->potHeight) { - // we can do it one pass - data = t.data; - } - - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, texture->potWidth, texture->potHeight, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, data); - } else { - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - } - if (!data) { - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride*4); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_LUMINANCE, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride); - } - } +void LayerBase::setBufferTransform(uint32_t transform) { + mBufferTransform = transform; } -status_t LayerBase::initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture) +void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { - status_t err = NO_ERROR; - - // we need to recreate the texture - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - - // free the previous image - if (texture->image != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(dpy, texture->image); - texture->image = EGL_NO_IMAGE_KHR; - } - - // construct an EGL_NATIVE_BUFFER_ANDROID - android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); - - // create the new EGLImageKHR - const EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_NONE, EGL_NONE - }; - texture->image = eglCreateImageKHR( - dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - (EGLClientBuffer)clientBuf, attrs); - - if (texture->image != EGL_NO_IMAGE_KHR) { - glBindTexture(GL_TEXTURE_2D, texture->name); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, - (GLeglImageOES)texture->image); - GLint error = glGetError(); - if (UNLIKELY(error != GL_NO_ERROR)) { - LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " - "failed err=0x%04x", - this, texture->image, error); - err = INVALID_OPERATION; - } else { - // Everything went okay! - texture->NPOTAdjust = false; - texture->dirty = false; - texture->width = clientBuf->width; - texture->height = clientBuf->height; - } - } else { - LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x", - this, eglGetError()); - err = INVALID_OPERATION; - } - return err; + const Layer::State& s(drawingState()); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + getTypeId(), this, s.z, tx(), ty(), s.w, s.h, + needsBlending(), needsDithering(), contentDirty, + s.alpha, s.flags, + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); + result.append(buffer); } - // --------------------------------------------------------------------------- -int32_t LayerBaseClient::sIdentity = 0; +int32_t LayerBaseClient::sIdentity = 1; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i) - : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i), + const sp<Client>& client) + : LayerBase(flinger, display), mClientRef(client), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { - lcblk = new SharedBufferServer( - client->ctrlblk, i, NUM_BUFFERS, - mIdentity); -} - -void LayerBaseClient::onFirstRef() -{ - sp<Client> client(this->client.promote()); - if (client != 0) { - client->bindLayer(this, mIndex); - } } LayerBaseClient::~LayerBaseClient() { - sp<Client> client(this->client.promote()); - if (client != 0) { - client->free(mIndex); - } - delete lcblk; -} - -int32_t LayerBaseClient::serverIndex() const -{ - sp<Client> client(this->client.promote()); - if (client != 0) { - return (client->cid<<16)|mIndex; + sp<Client> c(mClientRef.promote()); + if (c != 0) { + c->detachLayer(this); } - return 0xFFFF0000 | mIndex; } sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() @@ -738,25 +536,31 @@ sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const { - return new Surface(mFlinger, clientIndex(), mIdentity, + return new Surface(mFlinger, mIdentity, const_cast<LayerBaseClient *>(this)); } -// called with SurfaceFlinger::mStateLock as soon as the layer is entered -// in the purgatory list -void LayerBaseClient::onRemoved() +void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const { - // wake up the condition - lcblk->setStatus(NO_INIT); + LayerBase::dump(result, buffer, SIZE); + + sp<Client> client(mClientRef.promote()); + snprintf(buffer, SIZE, + " name=%s\n" + " client=%p, identity=%u\n", + getName().string(), + client.get(), getIdentity()); + + result.append(buffer); } // --------------------------------------------------------------------------- LayerBaseClient::Surface::Surface( const sp<SurfaceFlinger>& flinger, - SurfaceID id, int identity, + int identity, const sp<LayerBaseClient>& owner) - : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner) + : mFlinger(flinger), mIdentity(identity), mOwner(owner) { } @@ -799,11 +603,17 @@ status_t LayerBaseClient::Surface::onTransact( return BnSurface::onTransact(code, data, reply, flags); } -sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage) +sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { return NULL; } +status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) +{ + return INVALID_OPERATION; +} + status_t LayerBaseClient::Surface::registerBuffers( const ISurface::BufferHeap& buffers) { diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 62ec8399ad..8cba287f8e 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -29,7 +29,7 @@ #include <ui/Region.h> #include <ui/Overlay.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <private/surfaceflinger/SharedBufferStack.h> #include <private/surfaceflinger/LayerState.h> @@ -45,46 +45,25 @@ class DisplayHardware; class Client; class GraphicBuffer; class GraphicPlane; +class LayerBaseClient; class SurfaceFlinger; +class Texture; // --------------------------------------------------------------------------- class LayerBase : public RefBase { - // poor man's dynamic_cast below - template<typename T> - struct getTypeInfoOfAnyType { - static uint32_t get() { return T::typeInfo; } - }; - - template<typename T> - struct getTypeInfoOfAnyType<T*> { - static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); } - }; + static int32_t sSequence; public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - - template<typename T> - static T dynamicCast(LayerBase* base) { - uint32_t mostDerivedInfo = base->getTypeInfo(); - uint32_t castToInfo = getTypeInfoOfAnyType<T>::get(); - if ((mostDerivedInfo & castToInfo) == castToInfo) - return static_cast<T>(base); - return 0; - } + LayerBase(SurfaceFlinger* flinger, DisplayID display); - - LayerBase(SurfaceFlinger* flinger, DisplayID display); - DisplayID dpy; mutable bool contentDirty; Region visibleRegionScreen; Region transparentRegionScreen; Region coveredRegionScreen; + int32_t sequence; struct State { uint32_t w; @@ -125,6 +104,10 @@ public: void invalidate(); + virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; } + + virtual const char* getTypeId() const { return "LayerBase"; } + /** * draw - performs some global clipping optimizations * and calls onDraw(). @@ -199,9 +182,9 @@ public: virtual bool needsDithering() const { return false; } /** - * transformed -- true is this surface needs a to be transformed + * needsLinearFiltering - true if this surface needs filtering */ - virtual bool transformed() const { return mTransformed; } + virtual bool needsFiltering() const { return mNeedsFiltering; } /** * isSecure - true if this surface is secure, that is if it prevents @@ -217,7 +200,10 @@ public: * current list */ virtual void onRemoved() { }; - + /** always call base class first */ + virtual void dump(String8& result, char* scratch, size_t size) const; + + enum { // flags for doTransaction() eVisibleRegion = 0x00000002, }; @@ -227,12 +213,6 @@ public: inline const State& currentState() const { return mCurrentState; } inline State& currentState() { return mCurrentState; } - static int compareCurrentStateZ( - sp<LayerBase> const * layerA, - sp<LayerBase> const * layerB) { - return layerA[0]->currentState().z - layerB[0]->currentState().z; - } - int32_t getOrientation() const { return mOrientation; } int tx() const { return mLeft; } int ty() const { return mTop; } @@ -241,44 +221,26 @@ protected: const GraphicPlane& graphicPlane(int dpy) const; GraphicPlane& graphicPlane(int dpy); - GLuint createTexture() const; - - struct Texture { - Texture() : name(-1U), width(0), height(0), - image(EGL_NO_IMAGE_KHR), transform(0), - NPOTAdjust(false), dirty(true) { } - GLuint name; - GLuint width; - GLuint height; - GLuint potWidth; - GLuint potHeight; - GLfloat wScale; - GLfloat hScale; - EGLImageKHR image; - uint32_t transform; - bool NPOTAdjust; - bool dirty; - }; - - void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g, - GLclampx b, GLclampx alpha) const; + void clearWithOpenGL(const Region& clip, GLclampf r, GLclampf g, + GLclampf b, GLclampf alpha) const; void clearWithOpenGL(const Region& clip) const; void drawWithOpenGL(const Region& clip, const Texture& texture) const; - void loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const; - status_t initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture); - - bool isSupportedYuvFormat(int format) const; + // these must be called from the post/drawing thread + void setBufferCrop(const Rect& crop); + void setBufferTransform(uint32_t transform); + sp<SurfaceFlinger> mFlinger; uint32_t mFlags; + // post/drawing thread + Rect mBufferCrop; + uint32_t mBufferTransform; + // cached during validateVisibility() - bool mTransformed; - bool mUseLinearFiltering; + bool mNeedsFiltering; int32_t mOrientation; - GLfixed mVertices[4][2]; + GLfloat mVertices[4][2]; Rect mTransformedBounds; int mLeft; int mTop; @@ -303,7 +265,6 @@ protected: private: LayerBase(const LayerBase& rhs); - void validateTexture(GLint textureName) const; }; @@ -313,42 +274,25 @@ class LayerBaseClient : public LayerBase { public: class Surface; - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - - // lcblk is (almost) only accessed from the main SF thread, in the places - // where it's not, a reference to Client must be held - SharedBufferServer* lcblk; - LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i); + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); virtual ~LayerBaseClient(); - virtual void onFirstRef(); - - const wp<Client> client; - inline uint32_t getIdentity() const { return mIdentity; } - inline int32_t clientIndex() const { return mIndex; } - int32_t serverIndex() const; - - sp<Surface> getSurface(); virtual sp<Surface> createSurface() const; - - virtual void onRemoved(); + virtual sp<LayerBaseClient> getLayerBaseClient() const { + return const_cast<LayerBaseClient*>(this); } + virtual const char* getTypeId() const { return "LayerBaseClient"; } + uint32_t getIdentity() const { return mIdentity; } - class Surface : public BnSurface - { + class Surface : public BnSurface { public: - int32_t getToken() const { return mToken; } int32_t getIdentity() const { return mIdentity; } protected: - Surface(const sp<SurfaceFlinger>& flinger, - SurfaceID id, int identity, + Surface(const sp<SurfaceFlinger>& flinger, int identity, const sp<LayerBaseClient>& owner); virtual ~Surface(); virtual status_t onTransact(uint32_t code, const Parcel& data, @@ -356,7 +300,10 @@ public: sp<LayerBaseClient> getOwner() const; private: - virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); @@ -366,20 +313,22 @@ public: protected: friend class LayerBaseClient; sp<SurfaceFlinger> mFlinger; - int32_t mToken; int32_t mIdentity; wp<LayerBaseClient> mOwner; }; friend class Surface; +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; + private: - int32_t mIndex; - mutable Mutex mLock; - mutable wp<Surface> mClientSurface; + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + const wp<Client> mClientRef; // only read - const uint32_t mIdentity; - static int32_t sIdentity; + const uint32_t mIdentity; + static int32_t sIdentity; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp index 5fd7904be6..2ee21b92b5 100644 --- a/services/surfaceflinger/LayerBlur.cpp +++ b/services/surfaceflinger/LayerBlur.cpp @@ -33,14 +33,9 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; -const char* const LayerBlur::typeID = "LayerBlur"; - -// --------------------------------------------------------------------------- - LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i) - : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), + const sp<Client>& client) + : LayerBaseClient(flinger, display, client), mCacheDirty(true), mRefreshCache(true), mCacheAge(0), mTextureName(-1U), mWidthScale(1.0f), mHeightScale(1.0f), mBlurFormat(GGL_PIXEL_FORMAT_RGB_565) @@ -100,7 +95,9 @@ void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirty mCacheDirty = false; } else { if (!mAutoRefreshPending) { - mFlinger->signalDelayedEvent(ms2ns(500)); + mFlinger->postMessageAsync( + new MessageBase(MessageQueue::INVALIDATE), + ms2ns(500)); mAutoRefreshPending = true; } } @@ -149,6 +146,11 @@ void LayerBlur::onDraw(const Region& clip) const Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); if (it != end) { +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mTextureName); @@ -181,7 +183,7 @@ void LayerBlur::onDraw(const Region& clip) const bl.data = (GGLubyte*)pixels; blurFilter(&bl, 8, 2); - if (mFlags & (DisplayHardware::NPOT_EXTENSION)) { + if (GLExtensions::getInstance().haveNpot()) { glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0, mReadFormat, mReadType, pixels); mWidthScale = 1.0f / w; @@ -206,8 +208,8 @@ void LayerBlur::onDraw(const Region& clip) const const State& s = drawingState(); if (UNLIKELY(s.alpha < 0xFF)) { - const GGLfixed alpha = (s.alpha << 16)/255; - glColor4x(0, 0, 0, alpha); + const GLfloat alpha = s.alpha * (1.0f/255.0f); + glColor4f(0, 0, 0, alpha); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); @@ -225,38 +227,22 @@ void LayerBlur::onDraw(const Region& clip) const glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - if (UNLIKELY(transformed() - || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { - // This is a very rare scenario. - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glScalef(mWidthScale, mHeightScale, 1); - glTranslatef(-x, mYOffset - y, 0); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FIXED, 0, mVertices); - glTexCoordPointer(2, GL_FIXED, 0, mVertices); - while (it != end) { - const Rect& r = *it++; - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } else { - // NOTE: this is marginally faster with the software gl, because - // glReadPixels() reads the fb bottom-to-top, however we'll - // skip all the jaccobian computations. - Rect r; - GLint crop[4] = { 0, 0, w, h }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - y = fbHeight - (y + h); - while (it != end) { - const Rect& r = *it++; - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, w, h); - } + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(mWidthScale, mHeightScale, 1); + glTranslatef(-x, mYOffset - y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); } } diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h index 5b63dec290..4c9ec647a0 100644 --- a/services/surfaceflinger/LayerBlur.h +++ b/services/surfaceflinger/LayerBlur.h @@ -31,18 +31,14 @@ namespace android { class LayerBlur : public LayerBaseClient { public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerBlur(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i); + const sp<Client>& client); virtual ~LayerBlur(); virtual void onDraw(const Region& clip) const; virtual bool needsBlending() const { return true; } virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerBlur"; } virtual uint32_t doTransaction(uint32_t flags); virtual void setVisibleRegion(const Region& visibleRegion); diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp index 0869283d27..fdf9abc81a 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -39,15 +39,13 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; -const char* const LayerBuffer::typeID = "LayerBuffer"; gralloc_module_t const* LayerBuffer::sGrallocModule = 0; // --------------------------------------------------------------------------- LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i) - : LayerBaseClient(flinger, display, client, i), + const sp<Client>& client) + : LayerBaseClient(flinger, display, client), mNeedsBlending(false), mBlitEngine(0) { } @@ -62,8 +60,7 @@ LayerBuffer::~LayerBuffer() void LayerBuffer::onFirstRef() { LayerBaseClient::onFirstRef(); - mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(), - const_cast<LayerBuffer *>(this)); + mSurface = new SurfaceLayerBuffer(mFlinger, this); hw_module_t const* module = (hw_module_t const*)sGrallocModule; if (!module) { @@ -120,7 +117,7 @@ uint32_t LayerBuffer::doTransaction(uint32_t flags) source->onTransaction(flags); uint32_t res = LayerBase::doTransaction(flags); // we always want filtering for these surfaces - mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); + mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); return res; } @@ -145,14 +142,6 @@ void LayerBuffer::onDraw(const Region& clip) const } } -bool LayerBuffer::transformed() const -{ - sp<Source> source(getSource()); - if (LIKELY(source != 0)) - return source->transformed(); - return false; -} - void LayerBuffer::serverDestroy() { sp<Source> source(clearSource()); @@ -214,9 +203,9 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() { // LayerBuffer::SurfaceLayerBuffer // ============================================================================ -LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, - SurfaceID id, const sp<LayerBuffer>& owner) - : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner) +LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer( + const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner) { } @@ -321,16 +310,12 @@ void LayerBuffer::Source::postBuffer(ssize_t offset) { } void LayerBuffer::Source::unregisterBuffers() { } -bool LayerBuffer::Source::transformed() const { - return mLayer.mTransformed; -} // --------------------------------------------------------------------------- LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) - : Source(layer), mStatus(NO_ERROR), mBufferSize(0), - mUseEGLImageDirectly(true) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -388,11 +373,11 @@ LayerBuffer::BufferSource::~BufferSource() if (mTexture.name != -1U) { // GL textures can only be destroyed from the GL thread - mLayer.mFlinger->mEventQueue.postMessage( - new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) ); + getFlinger()->mEventQueue.postMessage( + new MessageDestroyTexture(getFlinger(), mTexture.name) ); } if (mTexture.image != EGL_NO_IMAGE_KHR) { - EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); eglDestroyImageKHR(dpy, mTexture.image); } } @@ -444,11 +429,6 @@ void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) mBuffer = buffer; } -bool LayerBuffer::BufferSource::transformed() const -{ - return mBufferHeap.transform ? true : Source::transformed(); -} - void LayerBuffer::BufferSource::onDraw(const Region& clip) const { sp<Buffer> ourBuffer(getBuffer()); @@ -462,35 +442,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const NativeBuffer src(ourBuffer->getBuffer()); const Rect transformedBounds(mLayer.getTransformedBounds()); - if (UNLIKELY(mTexture.name == -1LU)) { - mTexture.name = mLayer.createTexture(); - } - #if defined(EGL_ANDROID_image_native_buffer) - if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (GLExtensions::getInstance().haveDirectTexture()) { err = INVALID_OPERATION; if (ourBuffer->supportsCopybit()) { - - // there are constraints on buffers used by the GPU and these may not - // be honored here. We need to change the API so the buffers - // are allocated with gralloc. For now disable this code-path -#if 0 - // First, try to use the buffer as an EGLImage directly - if (mUseEGLImageDirectly) { - // NOTE: Assume the buffer is allocated with the proper USAGE flags - - sp<GraphicBuffer> buffer = new GraphicBuffer( - src.img.w, src.img.h, src.img.format, - GraphicBuffer::USAGE_HW_TEXTURE, - src.img.w, src.img.handle, false); - - err = mLayer.initializeEglImage(buffer, &mTexture); - if (err != NO_ERROR) { - mUseEGLImageDirectly = false; - } - } -#endif - copybit_device_t* copybit = mLayer.mBlitEngine; if (copybit && err != NO_ERROR) { // create our EGLImageKHR the first time @@ -527,10 +482,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.format = src.img.format; t.data = (GGLubyte*)src.img.base; const Region dirty(Rect(t.width, t.height)); - mLayer.loadTexture(&mTexture, dirty, t); + mTextureManager.loadTexture(&mTexture, dirty, t); } - mTexture.transform = mBufferHeap.transform; + mLayer.setBufferTransform(mBufferHeap.transform); mLayer.drawWithOpenGL(clip, mTexture); } @@ -569,7 +524,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const // figure out if we need linear filtering if (buffers.w * h == buffers.h * w) { // same pixel area, don't use filtering - mLayer.mUseLinearFiltering = false; + mLayer.mNeedsFiltering = false; } // Allocate a temporary buffer and create the corresponding EGLImageKHR @@ -593,7 +548,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const dst.crop.r = w; dst.crop.b = h; - err = mLayer.initializeEglImage(buffer, &mTexture); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); + err = mTextureManager.initEglImage(&mTexture, dpy, buffer); } return err; @@ -602,14 +558,13 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const void LayerBuffer::BufferSource::clearTempBufferImage() const { // delete the image - EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); eglDestroyImageKHR(dpy, mTexture.image); // and the associated texture (recreate a name) glDeleteTextures(1, &mTexture.name); Texture defaultTexture; mTexture = defaultTexture; - mTexture.name = mLayer.createTexture(); } // --------------------------------------------------------------------------- @@ -620,7 +575,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, : Source(layer), mVisibilityChanged(false), mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation) { - overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine(); + overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine(); if (overlay_dev == NULL) { // overlays not supported return; @@ -651,7 +606,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, *overlayRef = new OverlayRef(mOverlayHandle, channel, mWidth, mHeight, mFormat, mWidthStride, mHeightStride); - mLayer.mFlinger->signalEvent(); + getFlinger()->signalEvent(); } LayerBuffer::OverlaySource::~OverlaySource() @@ -665,9 +620,9 @@ LayerBuffer::OverlaySource::~OverlaySource() void LayerBuffer::OverlaySource::onDraw(const Region& clip) const { // this would be where the color-key would be set, should we need it. - GLclampx red = 0; - GLclampx green = 0; - GLclampx blue = 0; + GLclampf red = 0; + GLclampf green = 0; + GLclampf blue = 0; mLayer.clearWithOpenGL(clip, red, green, blue, 0); } diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h index b1766238cd..1c0bf830e1 100644 --- a/services/surfaceflinger/LayerBuffer.h +++ b/services/surfaceflinger/LayerBuffer.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include "LayerBase.h" +#include "TextureManager.h" struct copybit_device_t; @@ -45,31 +46,26 @@ class LayerBuffer : public LayerBaseClient virtual void onVisibilityResolved(const Transform& planeTransform); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual bool transformed() const; virtual void destroy() { } + SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); } protected: LayerBuffer& mLayer; }; public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerBuffer(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i); + const sp<Client>& client); virtual ~LayerBuffer(); virtual void onFirstRef(); virtual bool needsBlending() const; + virtual const char* getTypeId() const { return "LayerBuffer"; } virtual sp<LayerBaseClient::Surface> createSurface() const; virtual status_t ditch(); virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual bool transformed() const; status_t registerBuffers(const ISurface::BufferHeap& buffers); void postBuffer(ssize_t offset); @@ -133,7 +129,6 @@ private: virtual void onDraw(const Region& clip) const; virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual bool transformed() const; virtual void destroy() { } private: status_t initTempBuffer() const; @@ -143,9 +138,9 @@ private: status_t mStatus; ISurface::BufferHeap mBufferHeap; size_t mBufferSize; - mutable LayerBase::Texture mTexture; + mutable Texture mTexture; mutable NativeBuffer mTempBuffer; - mutable bool mUseEGLImageDirectly; + mutable TextureManager mTextureManager; }; class OverlaySource : public Source { @@ -195,7 +190,7 @@ private: { public: SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, - SurfaceID id, const sp<LayerBuffer>& owner); + const sp<LayerBuffer>& owner); virtual ~SurfaceLayerBuffer(); virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp index fd61e30d8f..a1f339e488 100644 --- a/services/surfaceflinger/LayerDim.cpp +++ b/services/surfaceflinger/LayerDim.cpp @@ -30,9 +30,6 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; -const char* const LayerDim::typeID = "LayerDim"; - bool LayerDim::sUseTexture; GLuint LayerDim::sTexId; EGLImageKHR LayerDim::sImage; @@ -42,8 +39,8 @@ int32_t LayerDim::sHeight; // --------------------------------------------------------------------------- LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i) - : LayerBaseClient(flinger, display, client, i) + const sp<Client>& client) + : LayerBaseClient(flinger, display, client) { } @@ -54,54 +51,6 @@ void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) sWidth = w; sHeight = h; sUseTexture = false; - -#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) - -#warning "using a texture to implement LayerDim" - - /* On some h/w like msm7K, it is faster to use a texture because the - * software renderer will defer to copybit, for this to work we need to - * use an EGLImage texture so copybit can actually make use of it. - * This burns a full-screen worth of graphic memory. - */ - - const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); - uint32_t flags = hw.getFlags(); - - if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) { - sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565, - GraphicBuffer::USAGE_SW_WRITE_OFTEN | - GraphicBuffer::USAGE_HW_TEXTURE); - - android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); - - glGenTextures(1, &sTexId); - glBindTexture(GL_TEXTURE_2D, sTexId); - - EGLDisplay dpy = eglGetCurrentDisplay(); - sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0); - if (sImage == EGL_NO_IMAGE_KHR) { - LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); - return; - } - - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage); - GLint error = glGetError(); - if (error != GL_NO_ERROR) { - eglDestroyImageKHR(dpy, sImage); - LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error); - return; - } - - // initialize the texture with zeros - GGLSurface t; - buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN); - memset(t.data, 0, t.stride * t.height * 2); - buffer->unlock(); - sUseTexture = true; - } -#endif } LayerDim::~LayerDim() @@ -115,33 +64,19 @@ void LayerDim::onDraw(const Region& clip) const Region::const_iterator const end = clip.end(); if (s.alpha>0 && (it != end)) { const DisplayHardware& hw(graphicPlane(0).displayHardware()); - const GGLfixed alpha = (s.alpha << 16)/255; + const GLfloat alpha = s.alpha/255.0f; const uint32_t fbHeight = hw.getHeight(); glDisable(GL_DITHER); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0, 0, 0, alpha); - -#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) - if (sUseTexture) { - glBindTexture(GL_TEXTURE_2D, sTexId); - glEnable(GL_TEXTURE_2D); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - const GLshort texCoords[4][2] = { - { 0, 0 }, - { 0, 1 }, - { 1, 1 }, - { 1, 0 } - }; - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_SHORT, 0, texCoords); - } else -#endif - { - glDisable(GL_TEXTURE_2D); + glColor4f(0, 0, 0, alpha); + +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); } +#endif + glDisable(GL_TEXTURE_2D); GLshort w = sWidth; GLshort h = sHeight; diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index d4672a1c44..f0323149ac 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -37,18 +37,14 @@ class LayerDim : public LayerBaseClient static int32_t sWidth; static int32_t sHeight; public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerDim(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& client, int32_t i); + const sp<Client>& client); virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; virtual bool needsBlending() const { return true; } virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerDim"; } static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); }; diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index b43d80173a..d668e88d47 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -60,9 +60,9 @@ MessageQueue::~MessageQueue() { } -MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) +sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) { - MessageList::value_type result; + sp<MessageBase> result; bool again; do { @@ -132,6 +132,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) if (again) { // the message has been processed. release our reference to it // without holding the lock. + result->notify(); result = 0; } @@ -141,7 +142,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) } status_t MessageQueue::postMessage( - const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) { return queueMessage(message, relTime, flags); } @@ -154,7 +155,7 @@ status_t MessageQueue::invalidate() { } status_t MessageQueue::queueMessage( - const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) { Mutex::Autolock _l(mLock); message->when = systemTime() + relTime; @@ -167,13 +168,13 @@ status_t MessageQueue::queueMessage( return NO_ERROR; } -void MessageQueue::dump(const MessageList::value_type& message) +void MessageQueue::dump(const sp<MessageBase>& message) { Mutex::Autolock _l(mLock); dumpLocked(message); } -void MessageQueue::dumpLocked(const MessageList::value_type& message) +void MessageQueue::dumpLocked(const sp<MessageBase>& message) { LIST::const_iterator cur(mMessages.begin()); LIST::const_iterator end(mMessages.end()); diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h index dc8138d166..890f809a3b 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/MessageQueue.h @@ -25,6 +25,7 @@ #include <utils/Timers.h> #include <utils/List.h> +#include "Barrier.h" namespace android { @@ -37,7 +38,6 @@ class MessageList List< sp<MessageBase> > mList; typedef List< sp<MessageBase> > LIST; public: - typedef sp<MessageBase> value_type; inline LIST::iterator begin() { return mList.begin(); } inline LIST::const_iterator begin() const { return mList.begin(); } inline LIST::iterator end() { return mList.end(); } @@ -63,11 +63,19 @@ public: // return true if message has a handler virtual bool handler() { return false; } + + // waits for the handler to be processed + void wait() const { barrier.wait(); } + // releases all waiters. this is done automatically if + // handler returns true + void notify() const { barrier.open(); } + protected: virtual ~MessageBase() { } private: + mutable Barrier barrier; friend class LightRefBase<MessageBase>; }; @@ -82,42 +90,33 @@ class MessageQueue typedef List< sp<MessageBase> > LIST; public: - // this is a work-around the multichar constant warning. A macro would - // work too, but would pollute the namespace. - template <int a, int b, int c, int d> - struct WHAT { - static const uint32_t Value = - (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)| - (uint32_t(c&0xff)<<8)|uint32_t(d&0xff); - }; - MessageQueue(); ~MessageQueue(); // pre-defined messages enum { - INVALIDATE = WHAT<'_','p','d','t'>::Value + INVALIDATE = '_upd' }; - MessageList::value_type waitMessage(nsecs_t timeout = -1); + sp<MessageBase> waitMessage(nsecs_t timeout = -1); - status_t postMessage(const MessageList::value_type& message, + status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0, uint32_t flags = 0); - + status_t invalidate(); - void dump(const MessageList::value_type& message); + void dump(const sp<MessageBase>& message); private: - status_t queueMessage(const MessageList::value_type& message, + status_t queueMessage(const sp<MessageBase>& message, nsecs_t reltime, uint32_t flags); - void dumpLocked(const MessageList::value_type& message); + void dumpLocked(const sp<MessageBase>& message); Mutex mLock; Condition mCondition; MessageList mMessages; bool mInvalidate; - MessageList::value_type mInvalidateMessage; + sp<MessageBase> mInvalidateMessage; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 0722fda55e..637ae48277 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -44,6 +44,7 @@ #include <GLES/gl.h> #include "clz.h" +#include "GLExtensions.h" #include "Layer.h" #include "LayerBlur.h" #include "LayerBuffer.h" @@ -62,111 +63,8 @@ #define DISPLAY_COUNT 1 namespace android { - -// --------------------------------------------------------------------------- - -void SurfaceFlinger::instantiate() { - defaultServiceManager()->addService( - String16("SurfaceFlinger"), new SurfaceFlinger()); -} - -void SurfaceFlinger::shutdown() { - // we should unregister here, but not really because - // when (if) the service manager goes away, all the services - // it has a reference to will leave too. -} - // --------------------------------------------------------------------------- -SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) - : lookup(rhs.lookup), layers(rhs.layers) -{ -} - -ssize_t SurfaceFlinger::LayerVector::indexOf( - const sp<LayerBase>& key, size_t guess) const -{ - if (guess<size() && lookup.keyAt(guess) == key) - return guess; - const ssize_t i = lookup.indexOfKey(key); - if (i>=0) { - const size_t idx = lookup.valueAt(i); - LOGE_IF(layers[idx]!=key, - "LayerVector[%p]: layers[%d]=%p, key=%p", - this, int(idx), layers[idx].get(), key.get()); - return idx; - } - return i; -} - -ssize_t SurfaceFlinger::LayerVector::add( - const sp<LayerBase>& layer, - Vector< sp<LayerBase> >::compar_t cmp) -{ - size_t count = layers.size(); - ssize_t l = 0; - ssize_t h = count-1; - ssize_t mid; - sp<LayerBase> const* a = layers.array(); - while (l <= h) { - mid = l + (h - l)/2; - const int c = cmp(a+mid, &layer); - if (c == 0) { l = mid; break; } - else if (c<0) { l = mid+1; } - else { h = mid-1; } - } - size_t order = l; - while (order<count && !cmp(&layer, a+order)) { - order++; - } - count = lookup.size(); - for (size_t i=0 ; i<count ; i++) { - if (lookup.valueAt(i) >= order) { - lookup.editValueAt(i)++; - } - } - layers.insertAt(layer, order); - lookup.add(layer, order); - return order; -} - -ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer) -{ - const ssize_t keyIndex = lookup.indexOfKey(layer); - if (keyIndex >= 0) { - const size_t index = lookup.valueAt(keyIndex); - LOGE_IF(layers[index]!=layer, - "LayerVector[%p]: layers[%u]=%p, layer=%p", - this, int(index), layers[index].get(), layer.get()); - layers.removeItemsAt(index); - lookup.removeItemsAt(keyIndex); - const size_t count = lookup.size(); - for (size_t i=0 ; i<count ; i++) { - if (lookup.valueAt(i) >= size_t(index)) { - lookup.editValueAt(i)--; - } - } - return index; - } - return NAME_NOT_FOUND; -} - -ssize_t SurfaceFlinger::LayerVector::reorder( - const sp<LayerBase>& layer, - Vector< sp<LayerBase> >::compar_t cmp) -{ - // XXX: it's a little lame. but oh well... - ssize_t err = remove(layer); - if (err >=0) - err = add(layer, cmp); - return err; -} - -// --------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), @@ -206,8 +104,8 @@ void SurfaceFlinger::init() property_get("debug.sf.showbackground", value, "0"); mDebugBackground = atoi(value); - LOGI_IF(mDebugRegion, "showupdates enabled"); - LOGI_IF(mDebugBackground, "showbackground enabled"); + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); } SurfaceFlinger::~SurfaceFlinger() @@ -225,56 +123,29 @@ sp<IMemoryHeap> SurfaceFlinger::getCblk() const return mServerHeap; } -sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() +sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { - Mutex::Autolock _l(mStateLock); - uint32_t token = mTokens.acquire(); - - sp<Client> client = new Client(token, this); - if (client->ctrlblk == 0) { - mTokens.release(token); - return 0; + sp<ISurfaceComposerClient> bclient; + sp<Client> client(new Client(this)); + status_t err = client->initCheck(); + if (err == NO_ERROR) { + bclient = client; } - status_t err = mClientsMap.add(token, client); - if (err < 0) { - mTokens.release(token); - return 0; - } - sp<BClient> bclient = - new BClient(this, token, client->getControlBlockMemory()); return bclient; } -void SurfaceFlinger::destroyConnection(ClientID cid) +sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection() { - Mutex::Autolock _l(mStateLock); - sp<Client> client = mClientsMap.valueFor(cid); - if (client != 0) { - // free all the layers this client owns - Vector< wp<LayerBaseClient> > layers(client->getLayers()); - const size_t count = layers.size(); - for (size_t i=0 ; i<count ; i++) { - sp<LayerBaseClient> layer(layers[i].promote()); - if (layer != 0) { - purgatorizeLayer_l(layer); - } - } - - // the resources associated with this client will be freed - // during the next transaction, after these surfaces have been - // properly removed from the screen - - // remove this client from our ClientID->Client mapping. - mClientsMap.removeItem(cid); - - // and add it to the list of disconnected clients - mDisconnectedClients.add(client); - - // request a transaction - setTransactionFlags(eTransactionNeeded); + sp<ISurfaceComposerClient> bclient; + sp<UserClient> client(new UserClient(this)); + status_t err = client->initCheck(); + if (err == NO_ERROR) { + bclient = client; } + return bclient; } + const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const { LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy); @@ -357,16 +228,8 @@ status_t SurfaceFlinger::readyToRun() dcblk->ydpi = hw.getDpiY(); dcblk->fps = hw.getRefreshRate(); dcblk->density = hw.getDensity(); - asm volatile ("":::"memory"); // Initialize OpenGL|ES - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_PACK_ALIGNMENT, 4); glEnableClientState(GL_VERTEX_ARRAY); @@ -427,7 +290,7 @@ void SurfaceFlinger::waitForEvent() timeout = waitTime>0 ? waitTime : 0; } - MessageList::value_type msg = mEventQueue.waitMessage(timeout); + sp<MessageBase> msg = mEventQueue.waitMessage(timeout); // see if we timed out if (isFrozen()) { @@ -462,9 +325,20 @@ void SurfaceFlinger::signal() const { const_cast<SurfaceFlinger*>(this)->signalEvent(); } -void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) { - mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); + return mEventQueue.postMessage(msg, reltime, flags); +} + +status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) +{ + status_t res = mEventQueue.postMessage(msg, reltime, flags); + if (res == NO_ERROR) { + msg->wait(); + } + return res; } // ---------------------------------------------------------------------------- @@ -558,6 +432,10 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { Vector< sp<LayerBase> > ditchedLayers; + /* + * Perform and commit the transaction + */ + { // scope for the lock Mutex::Autolock _l(mStateLock); const nsecs_t now = systemTime(); @@ -565,9 +443,13 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) handleTransactionLocked(transactionFlags, ditchedLayers); mLastTransactionTime = systemTime() - now; mDebugInTransaction = 0; + // here the transaction has been committed } - // do this without lock held + /* + * Clean-up all layers that went away + * (do this without the lock held) + */ const size_t count = ditchedLayers.size(); for (size_t i=0 ; i<count ; i++) { if (ditchedLayers[i] != 0) { @@ -655,10 +537,6 @@ void SurfaceFlinger::handleTransactionLocked( } } } - - // get rid of all resources we don't need anymore - // (layers and clients) - free_resources_l(); } commitTransaction(); @@ -805,7 +683,8 @@ void SurfaceFlinger::commitTransaction() void SurfaceFlinger::handlePageFlip() { bool visibleRegions = mVisibleRegionsDirty; - LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ); + LayerVector& currentLayers = const_cast<LayerVector&>( + mDrawingState.layersSortedByZ); visibleRegions |= lockPageFlip(currentLayers); const DisplayHardware& hw = graphicPlane(0).displayHardware(); @@ -813,6 +692,19 @@ void SurfaceFlinger::handlePageFlip() if (visibleRegions) { Region opaqueRegion; computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); + + /* + * rebuild the visible layer list + */ + mVisibleLayersSortedByZ.clear(); + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); + size_t count = currentLayers.size(); + mVisibleLayersSortedByZ.setCapacity(count); + for (size_t i=0 ; i<count ; i++) { + if (!currentLayers[i]->visibleRegionScreen.isEmpty()) + mVisibleLayersSortedByZ.add(currentLayers[i]); + } + mWormholeRegion = screenRegion.subtract(opaqueRegion); mVisibleRegionsDirty = false; } @@ -827,7 +719,7 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) size_t count = currentLayers.size(); sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - const sp<LayerBase>& layer = layers[i]; + const sp<LayerBase>& layer(layers[i]); layer->lockPageFlip(recomputeVisibleRegions); } return recomputeVisibleRegions; @@ -840,7 +732,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) size_t count = currentLayers.size(); sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - const sp<LayerBase>& layer = layers[i]; + const sp<LayerBase>& layer(layers[i]); layer->unlockPageFlip(planeTransform, mDirtyRegion); } } @@ -872,7 +764,7 @@ void SurfaceFlinger::handleRepaint() // takes a rectangle, we must make sure to update that whole // rectangle in that case if (flags & DisplayHardware::SWAP_RECTANGLE) { - // FIXME: we really should be able to pass a region to + // TODO: we really should be able to pass a region to // SWAP_RECTANGLE so that we don't have to redraw all this. mDirtyRegion.set(mInvalidRegion.bounds()); } else { @@ -909,18 +801,13 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty) // draw something... drawWormhole(); } - const SurfaceFlinger& flinger(*this); - const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); - const size_t count = drawingLayers.size(); - sp<LayerBase> const* const layers = drawingLayers.array(); + const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; ++i) { - const sp<LayerBase>& layer = layers[i]; - const Region& visibleRegion(layer->visibleRegionScreen); - if (!visibleRegion.isEmpty()) { - const Region clip(dirty.intersect(visibleRegion)); - if (!clip.isEmpty()) { - layer->draw(clip); - } + const sp<LayerBase>& layer(layers[i]); + const Region clip(dirty.intersect(layer->visibleRegionScreen)); + if (!clip.isEmpty()) { + layer->draw(clip); } } } @@ -938,17 +825,18 @@ void SurfaceFlinger::unlockClients() void SurfaceFlinger::debugFlashRegions() { - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - const uint32_t flags = hw.getFlags(); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t flags = hw.getFlags(); + + if (!((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED))) { + const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ? + mDirtyRegion.bounds() : hw.bounds()); + composeSurfaces(repaint); + } + + TextureManager::deactivateTextures(); - if (!((flags & DisplayHardware::SWAP_RECTANGLE) || - (flags & DisplayHardware::BUFFER_PRESERVED))) { - const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ? - mDirtyRegion.bounds() : hw.bounds()); - composeSurfaces(repaint); - } - - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); @@ -956,9 +844,9 @@ void SurfaceFlinger::debugFlashRegions() static int toggle = 0; toggle = 1 - toggle; if (toggle) { - glColor4x(0x10000, 0, 0x10000, 0x10000); + glColor4f(1, 0, 1, 1); } else { - glColor4x(0x10000, 0x10000, 0, 0x10000); + glColor4f(1, 1, 0, 1); } Region::const_iterator it = mDirtyRegion.begin(); @@ -974,7 +862,7 @@ void SurfaceFlinger::debugFlashRegions() glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } - + if (mInvalidRegion.isEmpty()) { mDirtyRegion.dump("mDirtyRegion"); mInvalidRegion.dump("mInvalidRegion"); @@ -982,7 +870,7 @@ void SurfaceFlinger::debugFlashRegions() hw.flip(mInvalidRegion); if (mDebugRegion > 1) - usleep(mDebugRegion * 1000); + usleep(mDebugRegion * 1000); glEnable(GL_SCISSOR_TEST); //mDirtyRegion.dump("mDirtyRegion"); @@ -1002,7 +890,7 @@ void SurfaceFlinger::drawWormhole() const glDisable(GL_DITHER); if (LIKELY(!mDebugBackground)) { - glClearColorx(0,0,0,0); + glClearColor(0,0,0,0); Region::const_iterator it = region.begin(); Region::const_iterator const end = region.end(); while (it != end) { @@ -1018,6 +906,11 @@ void SurfaceFlinger::drawWormhole() const glVertexPointer(2, GL_SHORT, 0, vertices); glTexCoordPointer(2, GL_SHORT, 0, tcoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mWormholeTexName); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); @@ -1061,6 +954,26 @@ status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer) return NO_ERROR; } +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) +{ + ssize_t i = mCurrentState.layersSortedByZ.add(layer); + return (i < 0) ? status_t(i) : status_t(NO_ERROR); +} + +ssize_t SurfaceFlinger::addClientLayer(const sp<Client>& client, + const sp<LayerBaseClient>& lbc) +{ + Mutex::Autolock _l(mStateLock); + + // attach this layer to the client + ssize_t name = client->attachLayer(lbc); + + // add this layer to the current state list + addLayer_l(lbc); + + return name; +} + status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); @@ -1070,36 +983,15 @@ status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) return err; } -status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) -{ - layer->forceVisibilityTransaction(); - setTransactionFlags(eTraversalNeeded); - return NO_ERROR; -} - -status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) { - if (layer == 0) - return BAD_VALUE; - ssize_t i = mCurrentState.layersSortedByZ.add( - layer, &LayerBase::compareCurrentStateZ); - sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + sp<LayerBaseClient> lbc(layerBase->getLayerBaseClient()); if (lbc != 0) { - mLayerMap.add(lbc->serverIndex(), lbc); + mLayerMap.removeItem( lbc->getSurface()->asBinder() ); } - return NO_ERROR; -} - -status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) -{ ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); if (index >= 0) { mLayersRemoved = true; - sp<LayerBaseClient> layer = - LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get()); - if (layer != 0) { - mLayerMap.removeItem(layer->serverIndex()); - } return NO_ERROR; } return status_t(index); @@ -1114,22 +1006,16 @@ status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) // it's possible that we don't find a layer, because it might // have been destroyed already -- this is not technically an error - // from the user because there is a race between BClient::destroySurface(), - // ~BClient() and ~ISurface(). + // from the user because there is a race between Client::destroySurface(), + // ~Client() and ~ISurface(). return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err; } - -void SurfaceFlinger::free_resources_l() +status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) { - // free resources associated with disconnected clients - Vector< sp<Client> >& disconnectedClients(mDisconnectedClients); - const size_t count = disconnectedClients.size(); - for (size_t i=0 ; i<count ; i++) { - sp<Client> client = disconnectedClients[i]; - mTokens.release(client->cid); - } - disconnectedClients.clear(); + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; } uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) @@ -1137,15 +1023,11 @@ uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) return android_atomic_and(~flags, &mTransactionFlags) & flags; } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t old = android_atomic_or(flags, &mTransactionFlags); if ((old & flags)==0) { // wake the server up - if (delay > 0) { - signalDelayedEvent(delay); - } else { - signalEvent(); - } + signalEvent(); } return old; } @@ -1224,8 +1106,8 @@ int SurfaceFlinger::setOrientation(DisplayID dpy, return orientation; } -sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, - const String8& name, ISurfaceFlingerClient::surface_data_t* params, +sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid, + const String8& name, ISurfaceComposerClient::surface_data_t* params, DisplayID d, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { @@ -1238,57 +1120,52 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, return surfaceHandle; } - Mutex::Autolock _l(mStateLock); - sp<Client> client = mClientsMap.valueFor(clientId); - if (UNLIKELY(client == 0)) { - LOGE("createSurface() failed, client not found (id=%d)", clientId); - return surfaceHandle; - } - //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); - int32_t id = client->generateId(pid); - if (uint32_t(id) >= NUM_LAYERS_MAX) { - LOGE("createSurface() failed, generateId = %d", id); - return surfaceHandle; - } - + sp<Layer> normalLayer; switch (flags & eFXSurfaceMask) { case eFXSurfaceNormal: if (UNLIKELY(flags & ePushBuffers)) { - layer = createPushBuffersSurfaceLocked(client, d, id, - w, h, flags); + layer = createPushBuffersSurface(client, d, w, h, flags); } else { - layer = createNormalSurfaceLocked(client, d, id, - w, h, flags, format); + normalLayer = createNormalSurface(client, d, w, h, flags, format); + layer = normalLayer; } break; case eFXSurfaceBlur: - layer = createBlurSurfaceLocked(client, d, id, w, h, flags); + layer = createBlurSurface(client, d, w, h, flags); break; case eFXSurfaceDim: - layer = createDimSurfaceLocked(client, d, id, w, h, flags); + layer = createDimSurface(client, d, w, h, flags); break; } if (layer != 0) { + layer->initStates(w, h, flags); layer->setName(name); - setTransactionFlags(eTransactionNeeded); + ssize_t token = addClientLayer(client, layer); + surfaceHandle = layer->getSurface(); if (surfaceHandle != 0) { - params->token = surfaceHandle->getToken(); + params->token = token; params->identity = surfaceHandle->getIdentity(); params->width = w; params->height = h; params->format = format; + if (normalLayer != 0) { + Mutex::Autolock _l(mStateLock); + mLayerMap.add(surfaceHandle->asBinder(), normalLayer); + } } + + setTransactionFlags(eTransactionNeeded); } return surfaceHandle; } -sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( +sp<Layer> SurfaceFlinger::createNormalSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags, + uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format) { // initialize the surfaces @@ -1298,53 +1175,56 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( format = PIXEL_FORMAT_RGBA_8888; break; case PIXEL_FORMAT_OPAQUE: +#ifdef NO_RGBX_8888 format = PIXEL_FORMAT_RGB_565; +#else + format = PIXEL_FORMAT_RGBX_8888; +#endif break; } - sp<Layer> layer = new Layer(this, display, client, id); +#ifdef NO_RGBX_8888 + if (format == PIXEL_FORMAT_RGBX_8888) + format = PIXEL_FORMAT_RGBA_8888; +#endif + + sp<Layer> layer = new Layer(this, display, client); status_t err = layer->setBuffers(w, h, format, flags); - if (LIKELY(err == NO_ERROR)) { - layer->initStates(w, h, flags); - addLayer_l(layer); - } else { + if (LIKELY(err != NO_ERROR)) { LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); layer.clear(); } return layer; } -sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( +sp<LayerBlur> SurfaceFlinger::createBlurSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags) + uint32_t w, uint32_t h, uint32_t flags) { - sp<LayerBlur> layer = new LayerBlur(this, display, client, id); + sp<LayerBlur> layer = new LayerBlur(this, display, client); layer->initStates(w, h, flags); - addLayer_l(layer); return layer; } -sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( +sp<LayerDim> SurfaceFlinger::createDimSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags) + uint32_t w, uint32_t h, uint32_t flags) { - sp<LayerDim> layer = new LayerDim(this, display, client, id); + sp<LayerDim> layer = new LayerDim(this, display, client); layer->initStates(w, h, flags); - addLayer_l(layer); return layer; } -sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( +sp<LayerBuffer> SurfaceFlinger::createPushBuffersSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags) + uint32_t w, uint32_t h, uint32_t flags) { - sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); + sp<LayerBuffer> layer = new LayerBuffer(this, display, client); layer->initStates(w, h, flags); - addLayer_l(layer); return layer; } -status_t SurfaceFlinger::removeSurface(SurfaceID index) +status_t SurfaceFlinger::removeSurface(const sp<Client>& client, SurfaceID sid) { /* * called by the window manager, when a surface should be marked for @@ -1357,7 +1237,7 @@ status_t SurfaceFlinger::removeSurface(SurfaceID index) status_t err = NAME_NOT_FOUND; Mutex::Autolock _l(mStateLock); - sp<LayerBaseClient> layer = getLayerUser_l(index); + sp<LayerBaseClient> layer = client->getLayerUser(sid); if (layer != 0) { err = purgatorizeLayer_l(layer); if (err == NO_ERROR) { @@ -1397,21 +1277,20 @@ status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) } }; - mEventQueue.postMessage( new MessageDestroySurface(this, layer) ); + postMessageAsync( new MessageDestroySurface(this, layer) ); return NO_ERROR; } status_t SurfaceFlinger::setClientState( - ClientID cid, + const sp<Client>& client, int32_t count, const layer_state_t* states) { Mutex::Autolock _l(mStateLock); uint32_t flags = 0; - cid <<= 16; for (int i=0 ; i<count ; i++) { - const layer_state_t& s = states[i]; - sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid)); + const layer_state_t& s(states[i]); + sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); if (layer != 0) { const uint32_t what = s.what; if (what & ePositionChanged) { @@ -1419,9 +1298,10 @@ status_t SurfaceFlinger::setClientState( flags |= eTraversalNeeded; } if (what & eLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); if (layer->setLayer(s.z)) { - mCurrentState.layersSortedByZ.reorder( - layer, &Layer::compareCurrentStateZ); + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); // we need traversal (state changed) // AND transaction (list changed) flags |= eTransactionNeeded|eTraversalNeeded; @@ -1457,12 +1337,6 @@ status_t SurfaceFlinger::setClientState( return NO_ERROR; } -sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const -{ - sp<LayerBaseClient> layer = mLayerMap.valueFor(s); - return layer; -} - void SurfaceFlinger::screenReleased(int dpy) { // this may be called by a signal handler, we can't do too much in here @@ -1512,83 +1386,17 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) result.append(buffer); } - size_t s = mClientsMap.size(); - char name[64]; - for (size_t i=0 ; i<s ; i++) { - sp<Client> client = mClientsMap.valueAt(i); - sprintf(name, " Client (id=0x%08x)", client->cid); - client->dump(name); - } const LayerVector& currentLayers = mCurrentState.layersSortedByZ; const size_t count = currentLayers.size(); for (size_t i=0 ; i<count ; i++) { - /*** LayerBase ***/ - const sp<LayerBase>& layer = currentLayers[i]; - const Layer::State& s = layer->drawingState(); - snprintf(buffer, SIZE, - "+ %s %p\n" - " " - "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " - "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " - "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - layer->getTypeID(), layer.get(), - s.z, layer->tx(), layer->ty(), s.w, s.h, - layer->needsBlending(), layer->needsDithering(), - layer->contentDirty, - s.alpha, s.flags, - s.transform[0][0], s.transform[0][1], - s.transform[1][0], s.transform[1][1]); - result.append(buffer); - buffer[0] = 0; - /*** LayerBaseClient ***/ - sp<LayerBaseClient> lbc = - LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); - if (lbc != 0) { - sp<Client> client(lbc->client.promote()); - snprintf(buffer, SIZE, - " name=%s\n", lbc->getName().string()); - result.append(buffer); - snprintf(buffer, SIZE, - " id=0x%08x, client=0x%08x, identity=%u\n", - lbc->clientIndex(), client.get() ? client->cid : 0, - lbc->getIdentity()); - - result.append(buffer); - buffer[0] = 0; - } - /*** Layer ***/ - sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); - if (l != 0) { - SharedBufferStack::Statistics stats = l->lcblk->getStats(); - result.append( l->lcblk->dump(" ") ); - sp<const GraphicBuffer> buf0(l->getBuffer(0)); - sp<const GraphicBuffer> buf1(l->getBuffer(1)); - uint32_t w0=0, h0=0, s0=0; - uint32_t w1=0, h1=0, s1=0; - if (buf0 != 0) { - w0 = buf0->getWidth(); - h0 = buf0->getHeight(); - s0 = buf0->getStride(); - } - if (buf1 != 0) { - w1 = buf1->getWidth(); - h1 = buf1->getHeight(); - s1 = buf1->getStride(); - } - snprintf(buffer, SIZE, - " " - "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," - " freezeLock=%p, dq-q-time=%u us\n", - l->pixelFormat(), - w0, h0, s0, w1, h1, s1, - l->getFreezeLock().get(), stats.totalTime); - result.append(buffer); - buffer[0] = 0; - } + const sp<LayerBase>& layer(currentLayers[i]); + layer->dump(result, buffer, SIZE); + const Layer::State& s(layer->drawingState()); s.transparentRegion.dump(result, "transparentRegion"); layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); } + mWormholeRegion.dump(result, "WormholeRegion"); const DisplayHardware& hw(graphicPlane(0).displayHardware()); snprintf(buffer, SIZE, @@ -1601,18 +1409,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) " last transaction time : %f us\n", mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0); result.append(buffer); + if (inSwapBuffersDuration || !locked) { snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", inSwapBuffersDuration/1000.0); result.append(buffer); } + if (inTransactionDuration || !locked) { snprintf(buffer, SIZE, " transaction time: %f us\n", inTransactionDuration/1000.0); result.append(buffer); } - snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size()); - result.append(buffer); + const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); alloc.dump(result); @@ -1705,116 +1514,189 @@ status_t SurfaceFlinger::onTransact( } // --------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif -Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) - : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) +sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const { - const int pgsize = getpagesize(); - const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1)); + sp<Layer> result; + Mutex::Autolock _l(mStateLock); + result = mLayerMap.valueFor( sur->asBinder() ).promote(); + return result; +} - mCblkHeap = new MemoryHeapBase(cblksize, 0, - "SurfaceFlinger Client control-block"); +// --------------------------------------------------------------------------- - ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase()); - if (ctrlblk) { // construct the shared structure in-place. - new(ctrlblk) SharedClient; - } +Client::Client(const sp<SurfaceFlinger>& flinger) + : mFlinger(flinger), mNameGenerator(1) +{ } -Client::~Client() { - if (ctrlblk) { - ctrlblk->~SharedClient(); // destroy our shared-structure. +Client::~Client() +{ + const size_t count = mLayers.size(); + for (size_t i=0 ; i<count ; i++) { + sp<LayerBaseClient> layer(mLayers.valueAt(i).promote()); + if (layer != 0) { + mFlinger->removeLayer(layer); + } } } -int32_t Client::generateId(int pid) -{ - const uint32_t i = clz( ~mBitmap ); - if (i >= NUM_LAYERS_MAX) { - return NO_MEMORY; - } - mPid = pid; - mInUse.add(uint8_t(i)); - mBitmap |= 1<<(31-i); - return i; +status_t Client::initCheck() const { + return NO_ERROR; } -status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id) +ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer) { - ssize_t idx = mInUse.indexOf(id); - if (idx < 0) - return NAME_NOT_FOUND; - return mLayers.insertAt(layer, idx); + int32_t name = android_atomic_inc(&mNameGenerator); + mLayers.add(name, layer); + return name; } -void Client::free(int32_t id) +void Client::detachLayer(const LayerBaseClient* layer) { - ssize_t idx = mInUse.remove(uint8_t(id)); - if (idx >= 0) { - mBitmap &= ~(1<<(31-id)); - mLayers.removeItemsAt(idx); + // we do a linear search here, because this doesn't happen often + const size_t count = mLayers.size(); + for (size_t i=0 ; i<count ; i++) { + if (mLayers.valueAt(i) == layer) { + mLayers.removeItemsAt(i, 1); + break; + } } } - -bool Client::isValid(int32_t i) const { - return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); -} - sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { sp<LayerBaseClient> lbc; - ssize_t idx = mInUse.indexOf(uint8_t(i)); - if (idx >= 0) { - lbc = mLayers[idx].promote(); - LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx)); + const wp<LayerBaseClient>& layer(mLayers.valueFor(i)); + if (layer != 0) { + lbc = layer.promote(); + LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i)); } return lbc; } -void Client::dump(const char* what) +sp<IMemoryHeap> Client::getControlBlock() const { + return 0; +} +ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const { + return -1; +} +sp<ISurface> Client::createSurface( + ISurfaceComposerClient::surface_data_t* params, int pid, + const String8& name, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) { + return mFlinger->createSurface(this, pid, name, params, + display, w, h, format, flags); +} +status_t Client::destroySurface(SurfaceID sid) { + return mFlinger->removeSurface(this, sid); +} +status_t Client::setState(int32_t count, const layer_state_t* states) { + return mFlinger->setClientState(this, count, states); } // --------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif -BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk) - : mId(cid), mFlinger(flinger), mCblk(cblk) +UserClient::UserClient(const sp<SurfaceFlinger>& flinger) + : ctrlblk(0), mBitmap(0), mFlinger(flinger) { + const int pgsize = getpagesize(); + const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1)); + + mCblkHeap = new MemoryHeapBase(cblksize, 0, + "SurfaceFlinger Client control-block"); + + ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) SharedClient; + } } -BClient::~BClient() { - // destroy all resources attached to this client - mFlinger->destroyConnection(mId); +UserClient::~UserClient() +{ + if (ctrlblk) { + ctrlblk->~SharedClient(); // destroy our shared-structure. + } + + /* + * When a UserClient dies, it's unclear what to do exactly. + * We could go ahead and destroy all surfaces linked to that client + * however, it wouldn't be fair to the main Client + * (usually the the window-manager), which might want to re-target + * the layer to another UserClient. + * I think the best is to do nothing, or not much; in most cases the + * WM itself will go ahead and clean things up when it detects a client of + * his has died. + * The remaining question is what to display? currently we keep + * just keep the current buffer. + */ } -sp<IMemoryHeap> BClient::getControlBlock() const { - return mCblk; +status_t UserClient::initCheck() const { + return ctrlblk == 0 ? NO_INIT : NO_ERROR; } -sp<ISurface> BClient::createSurface( - ISurfaceFlingerClient::surface_data_t* params, int pid, - const String8& name, - DisplayID display, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags) +void UserClient::detachLayer(const Layer* layer) { - return mFlinger->createSurface(mId, pid, name, params, display, w, h, - format, flags); + int32_t name = layer->getToken(); + if (name >= 0) { + int32_t mask = 1LU<<name; + if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) { + LOGW("token %d wasn't marked as used %08x", name, int(mBitmap)); + } + } } -status_t BClient::destroySurface(SurfaceID sid) -{ - sid |= (mId << 16); // add the client-part to id - return mFlinger->removeSurface(sid); +sp<IMemoryHeap> UserClient::getControlBlock() const { + return mCblkHeap; } -status_t BClient::setState(int32_t count, const layer_state_t* states) +ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const { - return mFlinger->setClientState(mId, count, states); + int32_t name = NAME_NOT_FOUND; + sp<Layer> layer(mFlinger->getLayer(sur)); + if (layer == 0) return name; + + // if this layer already has a token, just return it + name = layer->getToken(); + if ((name >= 0) && (layer->getClient() == this)) + return name; + + name = 0; + do { + int32_t mask = 1LU<<name; + if ((android_atomic_or(mask, &mBitmap) & mask) == 0) { + // we found and locked that name + status_t err = layer->setToken( + const_cast<UserClient*>(this), ctrlblk, name); + if (err != NO_ERROR) { + // free the name + android_atomic_and(~mask, &mBitmap); + name = err; + } + break; + } + if (++name > 31) + name = NO_MEMORY; + } while(name >= 0); + + //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)", + // sur->asBinder().get(), name, this, mBitmap); + return name; +} + +sp<ISurface> UserClient::createSurface( + ISurfaceComposerClient::surface_data_t* params, int pid, + const String8& name, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) { + return 0; +} +status_t UserClient::destroySurface(SurfaceID sid) { + return INVALID_OPERATION; +} +status_t UserClient::setState(int32_t count, const layer_state_t* states) { + return INVALID_OPERATION; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index d75dc15f18..8ecfc018e4 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -29,74 +29,96 @@ #include <binder/IMemory.h> #include <binder/Permission.h> +#include <binder/BinderService.h> #include <ui/PixelFormat.h> #include <surfaceflinger/ISurfaceComposer.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include "Barrier.h" #include "Layer.h" -#include "Tokenizer.h" #include "MessageQueue.h" -struct copybit_device_t; -struct overlay_device_t; - namespace android { // --------------------------------------------------------------------------- class Client; -class BClient; class DisplayHardware; class FreezeLock; class Layer; +class LayerBlur; +class LayerDim; class LayerBuffer; -typedef int32_t ClientID; - #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) // --------------------------------------------------------------------------- -class Client : public RefBase +class Client : public BnSurfaceComposerClient +{ +public: + Client(const sp<SurfaceFlinger>& flinger); + ~Client(); + + status_t initCheck() const; + + // protected by SurfaceFlinger::mStateLock + ssize_t attachLayer(const sp<LayerBaseClient>& layer); + void detachLayer(const LayerBaseClient* layer); + sp<LayerBaseClient> getLayerUser(int32_t i) const; + +private: + + // ISurfaceComposerClient interface + virtual sp<IMemoryHeap> getControlBlock() const; + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, const String8& name, + DisplayID display, uint32_t w, uint32_t h,PixelFormat format, + uint32_t flags); + virtual status_t destroySurface(SurfaceID surfaceId); + virtual status_t setState(int32_t count, const layer_state_t* states); + + DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers; + sp<SurfaceFlinger> mFlinger; + int32_t mNameGenerator; +}; + +class UserClient : public BnSurfaceComposerClient { public: - Client(ClientID cid, const sp<SurfaceFlinger>& flinger); - ~Client(); - - int32_t generateId(int pid); - void free(int32_t id); - status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id); - - inline bool isValid(int32_t i) const; - sp<LayerBaseClient> getLayerUser(int32_t i) const; - void dump(const char* what); - - const Vector< wp<LayerBaseClient> >& getLayers() const { - return mLayers; - } - - const sp<IMemoryHeap>& getControlBlockMemory() const { - return mCblkHeap; - } - // pointer to this client's control block - SharedClient* ctrlblk; - ClientID cid; + SharedClient* ctrlblk; + +public: + UserClient(const sp<SurfaceFlinger>& flinger); + ~UserClient(); + + status_t initCheck() const; + + // protected by SurfaceFlinger::mStateLock + void detachLayer(const Layer* layer); - private: - int getClientPid() const { return mPid; } - - int mPid; - uint32_t mBitmap; - SortedVector<uint8_t> mInUse; - Vector< wp<LayerBaseClient> > mLayers; - sp<IMemoryHeap> mCblkHeap; - sp<SurfaceFlinger> mFlinger; + + // ISurfaceComposerClient interface + virtual sp<IMemoryHeap> getControlBlock() const; + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, const String8& name, + DisplayID display, uint32_t w, uint32_t h,PixelFormat format, + uint32_t flags); + virtual status_t destroySurface(SurfaceID surfaceId); + virtual status_t setState(int32_t count, const layer_state_t* states); + + // atomic-ops + mutable volatile int32_t mBitmap; + + sp<IMemoryHeap> mCblkHeap; + sp<SurfaceFlinger> mFlinger; }; // --------------------------------------------------------------------------- @@ -143,11 +165,13 @@ enum { eTraversalNeeded = 0x02 }; -class SurfaceFlinger : public BnSurfaceComposer, protected Thread +class SurfaceFlinger : + public BinderService<SurfaceFlinger>, + public BnSurfaceComposer, + protected Thread { public: - static void instantiate(); - static void shutdown(); + static char const* getServiceName() { return "SurfaceFlinger"; } SurfaceFlinger(); virtual ~SurfaceFlinger(); @@ -159,7 +183,8 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); // ISurfaceComposer interface - virtual sp<ISurfaceFlingerClient> createConnection(); + virtual sp<ISurfaceComposerClient> createConnection(); + virtual sp<ISurfaceComposerClient> createClientConnection(); virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); virtual void openGlobalTransaction(); @@ -174,13 +199,14 @@ public: overlay_control_device_t* getOverlayEngine() const; - status_t removeLayer(const sp<LayerBase>& layer); status_t addLayer(const sp<LayerBase>& layer); status_t invalidateLayerVisibility(const sp<LayerBase>& layer); - + + sp<Layer> getLayer(const sp<ISurface>& sur) const; + private: - friend class BClient; + friend class Client; friend class LayerBase; friend class LayerBuffer; friend class LayerBaseClient; @@ -189,47 +215,47 @@ private: friend class LayerBlur; friend class LayerDim; - sp<ISurface> createSurface(ClientID client, int pid, const String8& name, - ISurfaceFlingerClient::surface_data_t* params, + sp<ISurface> createSurface(const sp<Client>& client, + int pid, const String8& name, + ISurfaceComposerClient::surface_data_t* params, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); - sp<LayerBaseClient> createNormalSurfaceLocked( + sp<Layer> createNormalSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags, + uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format); - sp<LayerBaseClient> createBlurSurfaceLocked( + sp<LayerBlur> createBlurSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags); + uint32_t w, uint32_t h, uint32_t flags); - sp<LayerBaseClient> createDimSurfaceLocked( + sp<LayerDim> createDimSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags); + uint32_t w, uint32_t h, uint32_t flags); - sp<LayerBaseClient> createPushBuffersSurfaceLocked( + sp<LayerBuffer> createPushBuffersSurface( const sp<Client>& client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, uint32_t flags); + uint32_t w, uint32_t h, uint32_t flags); - status_t removeSurface(SurfaceID surface_id); + status_t removeSurface(const sp<Client>& client, SurfaceID sid); status_t destroySurface(const sp<LayerBaseClient>& layer); - status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + status_t setClientState(const sp<Client>& client, + int32_t count, const layer_state_t* states); - - class LayerVector { + class LayerVector : public SortedVector< sp<LayerBase> > { public: - inline LayerVector() { } - LayerVector(const LayerVector&); - inline size_t size() const { return layers.size(); } - inline sp<LayerBase> const* array() const { return layers.array(); } - ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); - ssize_t remove(const sp<LayerBase>&); - ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); - ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const; - inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; } - private: - KeyedVector< sp<LayerBase> , size_t> lookup; - Vector< sp<LayerBase> > layers; + LayerVector() { } + LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { } + virtual int do_compare(const void* lhs, const void* rhs) const { + const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs)); + const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs)); + // sort layers by Z order + uint32_t lz = l->currentState().z; + uint32_t rz = r->currentState().z; + // then by sequence, so we get a stable ordering + return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence); + } }; struct State { @@ -256,8 +282,6 @@ private: public: // hack to work around gcc 4.0.3 bug void signalEvent(); private: - void signalDelayedEvent(nsecs_t delay); - void handleConsoleEvents(); void handleTransaction(uint32_t transactionFlags); void handleTransactionLocked( @@ -278,15 +302,14 @@ private: void unlockClients(); - void destroyConnection(ClientID cid); - sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const; + ssize_t addClientLayer(const sp<Client>& client, + const sp<LayerBaseClient>& lbc); status_t addLayer_l(const sp<LayerBase>& layer); status_t removeLayer_l(const sp<LayerBase>& layer); status_t purgatorizeLayer_l(const sp<LayerBase>& layer); - void free_resources_l(); uint32_t getTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + uint32_t setTransactionFlags(uint32_t flags); void commitTransaction(); @@ -310,9 +333,13 @@ private: mutable MessageQueue mEventQueue; - - - + + status_t postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); + // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; @@ -321,14 +348,11 @@ private: volatile int32_t mTransactionCount; Condition mTransactionCV; bool mResizeTransationPending; - + // protected by mStateLock (but we could use another lock) - Tokenizer mTokens; - DefaultKeyedVector<ClientID, sp<Client> > mClientsMap; - DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap; - GraphicPlane mGraphicPlanes[1]; - bool mLayersRemoved; - Vector< sp<Client> > mDisconnectedClients; + GraphicPlane mGraphicPlanes[1]; + bool mLayersRemoved; + DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayerMap; // constant members (no synchronization needed for access) sp<IMemoryHeap> mServerHeap; @@ -350,6 +374,8 @@ private: bool mFreezeDisplay; int32_t mFreezeCount; nsecs_t mFreezeDisplayTime; + Vector< sp<LayerBase> > mVisibleLayersSortedByZ; + // don't use a lock for these, we don't care int mDebugRegion; @@ -389,32 +415,6 @@ public: }; // --------------------------------------------------------------------------- - -class BClient : public BnSurfaceFlingerClient -{ -public: - BClient(SurfaceFlinger *flinger, ClientID cid, - const sp<IMemoryHeap>& cblk); - ~BClient(); - - // ISurfaceFlingerClient interface - virtual sp<IMemoryHeap> getControlBlock() const; - - virtual sp<ISurface> createSurface( - surface_data_t* params, int pid, const String8& name, - DisplayID display, uint32_t w, uint32_t h,PixelFormat format, - uint32_t flags); - - virtual status_t destroySurface(SurfaceID surfaceId); - virtual status_t setState(int32_t count, const layer_state_t* states); - -private: - ClientID mId; - SurfaceFlinger* mFlinger; - sp<IMemoryHeap> mCblk; -}; - -// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp new file mode 100644 index 0000000000..76f61598b1 --- /dev/null +++ b/services/surfaceflinger/TextureManager.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "GLExtensions.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager() + : mGLExtensions(GLExtensions::getInstance()) +{ +} + +GLenum TextureManager::getTextureTarget(const Image* image) { +#if defined(GL_OES_texture_external) + switch (image->target) { + case Texture::TEXTURE_EXTERNAL: + return GL_TEXTURE_EXTERNAL_OES; + } +#endif + return GL_TEXTURE_2D; +} + +status_t TextureManager::initTexture(Texture* texture) +{ + if (texture->name != -1UL) + return INVALID_OPERATION; + + GLuint textureName = -1; + glGenTextures(1, &textureName); + texture->name = textureName; + texture->width = 0; + texture->height = 0; + + const GLenum target = GL_TEXTURE_2D; + glBindTexture(target, textureName); + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return NO_ERROR; +} + +status_t TextureManager::initTexture(Image* pImage, int32_t format) +{ + if (pImage->name != -1UL) + return INVALID_OPERATION; + + GLuint textureName = -1; + glGenTextures(1, &textureName); + pImage->name = textureName; + pImage->width = 0; + pImage->height = 0; + + GLenum target = GL_TEXTURE_2D; +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + if (format && isYuvFormat(format)) { + target = GL_TEXTURE_EXTERNAL_OES; + pImage->target = Texture::TEXTURE_EXTERNAL; + } + } +#endif + + glBindTexture(target, textureName); + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return NO_ERROR; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + return true; + } + return false; +} + +bool TextureManager::isYuvFormat(int format) +{ + switch (format) { + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return true; + } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + + return false; +} + +status_t TextureManager::initEglImage(Image* pImage, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_ERROR; + if (!pImage->dirty) return err; + + // free the previous image + if (pImage->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, pImage->image); + pImage->image = EGL_NO_IMAGE_KHR; + } + + // construct an EGL_NATIVE_BUFFER_ANDROID + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + // create the new EGLImageKHR + const EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, EGL_NONE + }; + pImage->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (pImage->image != EGL_NO_IMAGE_KHR) { + if (pImage->name == -1UL) { + initTexture(pImage, buffer->format); + } + const GLenum target = getTextureTarget(pImage); + glBindTexture(target, pImage->name); + glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", + pImage->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + pImage->dirty = false; + pImage->width = clientBuf->width; + pImage->height = clientBuf->height; + } + } else { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + +status_t TextureManager::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) +{ + if (texture->name == -1UL) { + status_t err = initTexture(texture); + LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err)); + return err; + } + + if (texture->target != Texture::TEXTURE_2D) + return INVALID_OPERATION; + + glBindTexture(GL_TEXTURE_2D, texture->name); + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is a limited form of stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); + unpack = 1 << ((unpack > 3) ? 3 : unpack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + + /* + * round to POT if needed + */ + if (!mGLExtensions.haveNpot()) { + texture->NPOTAdjust = true; + } + + if (texture->NPOTAdjust) { + // find the smallest power-of-two that will accommodate our surface + texture->potWidth = 1 << (31 - clz(t.width)); + texture->potHeight = 1 << (31 - clz(t.height)); + if (texture->potWidth < t.width) texture->potWidth <<= 1; + if (texture->potHeight < t.height) texture->potHeight <<= 1; + texture->wScale = float(t.width) / texture->potWidth; + texture->hScale = float(t.height) / texture->potHeight; + } else { + texture->potWidth = t.width; + texture->potHeight = t.height; + } + + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture->width != t.width || texture->height != t.height) { + texture->width = t.width; + texture->height = t.height; + + // texture size changed, we need to create a new one + bounds.set(Rect(t.width, t.height)); + if (t.width == texture->potWidth && + t.height == texture->potHeight) { + // we can do it one pass + data = t.data; + } + + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture->potWidth, texture->potHeight, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("texture=%d, using format %d, which is not " + "supported by the GL", texture->name, t.format); + } + } + if (!data) { + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride*4); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_LUMINANCE, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride); + } + } + return NO_ERROR; +} + +void TextureManager::activateTexture(const Texture& texture, bool filter) +{ + const GLenum target = getTextureTarget(&texture); + if (target == GL_TEXTURE_2D) { + glBindTexture(GL_TEXTURE_2D, texture.name); + glEnable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } + } else { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name); + glEnable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); +#endif + } + + if (filter) { + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } +} + +void TextureManager::deactivateTextures() +{ + glDisable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h new file mode 100644 index 0000000000..18c4348693 --- /dev/null +++ b/services/surfaceflinger/TextureManager.h @@ -0,0 +1,93 @@ +/* + * 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_TEXTURE_MANAGER_H +#define ANDROID_TEXTURE_MANAGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <ui/Region.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class GLExtensions; +class GraphicBuffer; + +// --------------------------------------------------------------------------- + +struct Image { + enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 }; + Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0), + dirty(1), target(TEXTURE_2D) { } + GLuint name; + EGLImageKHR image; + GLuint width; + GLuint height; + unsigned dirty : 1; + unsigned target : 1; +}; + +struct Texture : public Image { + Texture() : Image(), NPOTAdjust(0) { } + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + unsigned NPOTAdjust : 1; +}; + +// --------------------------------------------------------------------------- + +class TextureManager { + const GLExtensions& mGLExtensions; + static status_t initTexture(Image* texture, int32_t format); + static status_t initTexture(Texture* texture); + static bool isSupportedYuvFormat(int format); + static bool isYuvFormat(int format); + static GLenum getTextureTarget(const Image* pImage); +public: + + TextureManager(); + + // load bitmap data into the active buffer + status_t loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(Image* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer); + + // activate a texture + static void activateTexture(const Texture& texture, bool filter); + + // deactivate a texture + static void deactivateTextures(); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_TEXTURE_MANAGER_H diff --git a/services/surfaceflinger/Tokenizer.cpp b/services/surfaceflinger/Tokenizer.cpp deleted file mode 100644 index be3a239836..0000000000 --- a/services/surfaceflinger/Tokenizer.cpp +++ /dev/null @@ -1,173 +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. - */ - -#include <stdio.h> - -#include "Tokenizer.h" - -// ---------------------------------------------------------------------------- - -namespace android { - -ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) - -Tokenizer::Tokenizer() -{ -} - -Tokenizer::Tokenizer(const Tokenizer& other) - : mRanges(other.mRanges) -{ -} - -Tokenizer::~Tokenizer() -{ -} - -uint32_t Tokenizer::acquire() -{ - if (!mRanges.size() || mRanges[0].first) { - _insertTokenAt(0,0); - return 0; - } - - // just extend the first run - const run_t& run = mRanges[0]; - uint32_t token = run.first + run.length; - _insertTokenAt(token, 1); - return token; -} - -bool Tokenizer::isAcquired(uint32_t token) const -{ - return (_indexOrderOf(token) >= 0); -} - -status_t Tokenizer::reserve(uint32_t token) -{ - size_t o; - const ssize_t i = _indexOrderOf(token, &o); - if (i >= 0) { - return BAD_VALUE; // this token is already taken - } - ssize_t err = _insertTokenAt(token, o); - return (err<0) ? err : status_t(NO_ERROR); -} - -status_t Tokenizer::release(uint32_t token) -{ - const ssize_t i = _indexOrderOf(token); - if (i >= 0) { - const run_t& run = mRanges[i]; - if ((token >= run.first) && (token < run.first+run.length)) { - // token in this range, we need to split - run_t& run = mRanges.editItemAt(i); - if ((token == run.first) || (token == run.first+run.length-1)) { - if (token == run.first) { - run.first += 1; - } - run.length -= 1; - if (run.length == 0) { - // XXX: should we systematically remove a run that's empty? - mRanges.removeItemsAt(i); - } - } else { - // split the run - run_t new_run; - new_run.first = token+1; - new_run.length = run.first+run.length - new_run.first; - run.length = token - run.first; - mRanges.insertAt(new_run, i+1); - } - return NO_ERROR; - } - } - return NAME_NOT_FOUND; -} - -ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const -{ - // binary search - ssize_t err = NAME_NOT_FOUND; - ssize_t l = 0; - ssize_t h = mRanges.size()-1; - ssize_t mid; - const run_t* a = mRanges.array(); - while (l <= h) { - mid = l + (h - l)/2; - const run_t* const curr = a + mid; - int c = 0; - if (token < curr->first) c = 1; - else if (token >= curr->first+curr->length) c = -1; - if (c == 0) { - err = l = mid; - break; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - if (order) *order = l; - return err; -} - -ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) -{ - const size_t c = mRanges.size(); - - if (index >= 1) { - // do we need to merge with the previous run? - run_t& p = mRanges.editItemAt(index-1); - if (p.first+p.length == token) { - p.length += 1; - if (index < c) { - const run_t& n = mRanges[index]; - if (token+1 == n.first) { - p.length += n.length; - mRanges.removeItemsAt(index); - } - } - return index; - } - } - - if (index < c) { - // do we need to merge with the next run? - run_t& n = mRanges.editItemAt(index); - if (token+1 == n.first) { - n.first -= 1; - n.length += 1; - return index; - } - } - - return mRanges.insertAt(run_t(token,1), index); -} - -void Tokenizer::dump() const -{ - const run_t* ranges = mRanges.array(); - const size_t c = mRanges.size(); - printf("Tokenizer (%p, size = %d)\n", this, int(c)); - for (size_t i=0 ; i<c ; i++) { - printf("%u: (%u, %u)\n", i, - uint32_t(ranges[i].first), uint32_t(ranges[i].length)); - } -} - -}; // namespace android - diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp index 175f989ef0..5e27cc9bd9 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -229,14 +229,13 @@ Transform::vec3 Transform::transform(const vec3& v) const { return r; } -void Transform::transform(fixed1616* point, int x, int y) const +void Transform::transform(float* point, int x, int y) const { - const float toFixed = 65536.0f; const mat33& M(mMatrix); vec2 v(x, y); v = transform(v); - point[0] = v[0] * toFixed; - point[1] = v[1] * toFixed; + point[0] = v[0]; + point[1] = v[1]; } Rect Transform::makeBounds(int w, int h) const diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h index 2e5b893ece..20fa11a46a 100644 --- a/services/surfaceflinger/Transform.h +++ b/services/surfaceflinger/Transform.h @@ -37,8 +37,6 @@ public: explicit Transform(uint32_t orientation); ~Transform(); - typedef int32_t fixed1616; - // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h enum orientation_flags { ROT_0 = 0x00000000, @@ -76,7 +74,7 @@ public: // transform data Rect makeBounds(int w, int h) const; - void transform(fixed1616* point, int x, int y) const; + void transform(float* point, int x, int y) const; Region transform(const Region& reg) const; Transform operator * (const Transform& rhs) const; diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk new file mode 100644 index 0000000000..ce0e8072f7 --- /dev/null +++ b/services/surfaceflinger/tests/surface/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + surface.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-surface + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp new file mode 100644 index 0000000000..b4de4b459c --- /dev/null +++ b/services/surfaceflinger/tests/surface/surface.cpp @@ -0,0 +1,54 @@ +#include <cutils/memory.h> + +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <ui/Overlay.h> + +using namespace android; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp<SurfaceControl> surfaceControl = client->createSurface( + getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565); + client->openTransaction(); + surfaceControl->setLayer(100000); + client->closeTransaction(); + + // pretend it went cross-process + Parcel parcel; + SurfaceControl::writeSurfaceToParcel(surfaceControl, &parcel); + parcel.setDataPosition(0); + sp<Surface> surface = Surface::readFromParcel(parcel); + ANativeWindow* window = surface.get(); + + printf("window=%p\n", window); + + int err = native_window_set_buffer_count(window, 8); + android_native_buffer_t* buffer; + + for (int i=0 ; i<8 ; i++) { + window->dequeueBuffer(window, &buffer); + printf("buffer %d: %p\n", i, buffer); + } + + printf("test complete. CTRL+C to finish.\n"); + + IPCThreadState::self()->joinThreadPool(); + return 0; +} diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java index ce522c848f..ce40b5d0c1 100644 --- a/vpn/java/android/net/vpn/VpnManager.java +++ b/vpn/java/android/net/vpn/VpnManager.java @@ -85,7 +85,8 @@ public class VpnManager { // TODO(oam): Test VPN when EFS is enabled (will do later)... public static String getProfilePath() { - return Environment.getDataDirectory().getPath() + PROFILES_PATH; + // This call will return the correct path if Encrypted FS is enabled or not. + return Environment.getSecureDataDirectory().getPath() + PROFILES_PATH; } /** |