From 1a84fd1fb7a51f3fe4f8865e1cdd09f3490f696c Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 2 Jun 2011 01:26:32 -0700 Subject: Add a preference panel for mouse speed. Bug: 4124987 Change-Id: I3ce175d268a1d043cf5878481261b1049a15a149 --- api/current.xml | 11 +++++ core/java/android/provider/Settings.java | 11 +++++ core/java/android/view/IWindowManager.aidl | 5 ++ core/res/AndroidManifest.xml | 7 +++ core/res/res/values/strings.xml | 7 +++ packages/SettingsProvider/res/values/defaults.xml | 3 ++ .../android/providers/settings/DatabaseHelper.java | 3 ++ services/input/EventHub.cpp | 53 ++++++++++++++++------ services/input/EventHub.h | 17 +++++-- services/input/InputReader.cpp | 26 ++++++++--- services/input/InputReader.h | 17 +++++-- services/input/tests/InputReader_test.cpp | 7 ++- .../java/com/android/server/wm/InputManager.java | 48 +++++++++++++++++++- .../android/server/wm/WindowManagerService.java | 13 ++++++ services/jni/com_android_server_InputManager.cpp | 39 ++++++++++++++++ 15 files changed, 232 insertions(+), 35 deletions(-) diff --git a/api/current.xml b/api/current.xml index 83e571d14071..117ecf9aa009 100644 --- a/api/current.xml +++ b/api/current.xml @@ -1024,6 +1024,17 @@ visibility="public" > + + + + + + + change pointer speed + + Allows an application to change + the mouse or trackpad pointer speed at any time. Should never be needed for + normal applications. + send Linux signals to applications diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index bf06f947a514..2e2768f92bc0 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -125,4 +125,7 @@ 500 + + 0 + diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index d901c2c9c7e2..2ed968b4f5b1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1214,6 +1214,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.System.NOTIFICATIONS_USE_RING_VOLUME, R.bool.def_notifications_use_ring_volume); + loadIntegerSetting(stmt, Settings.System.POINTER_SPEED, + R.integer.def_pointer_speed); + } finally { if (stmt != null) stmt.close(); } diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index d80abe895b5f..f748e7cd9dd2 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -127,6 +128,7 @@ EventHub::EventHub(void) : mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), mOpened(false), mNeedToSendFinishedDeviceScan(false), + mNeedToReopenDevices(0), mNeedToScanDevices(false), mInputFdIndex(1) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); @@ -380,12 +382,10 @@ status_t EventHub::mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo return NAME_NOT_FOUND; } -void EventHub::addExcludedDevice(const char* deviceName) -{ +void EventHub::setExcludedDevices(const Vector& devices) { AutoMutex _l(mLock); - String8 name(deviceName); - mExcludedDevices.push_back(name); + mExcludedDevices = devices; } bool EventHub::hasLed(int32_t deviceId, int32_t led) const { @@ -453,9 +453,11 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz assert(bufferSize >= 1); if (!mOpened) { + android_atomic_acquire_store(0, &mNeedToReopenDevices); + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; mOpened = true; - mNeedToSendFinishedDeviceScan = true; + mNeedToScanDevices = true; } struct input_event readBuffer[bufferSize]; @@ -465,6 +467,20 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz for (;;) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + // Reopen input devices if needed. + if (android_atomic_acquire_load(&mNeedToReopenDevices)) { + android_atomic_acquire_store(0, &mNeedToReopenDevices); + + LOGI("Reopening all input devices due to a configuration change."); + + AutoMutex _l(mLock); + while (mDevices.size() > 1) { + closeDeviceAtIndexLocked(mDevices.size() - 1); + } + mNeedToScanDevices = true; + break; // return to the caller before we actually rescan + } + // Report any devices that had last been added/removed. while (mClosingDevices) { Device* device = mClosingDevices; @@ -482,6 +498,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } } + if (mNeedToScanDevices) { + mNeedToScanDevices = false; + scanDevices(); + mNeedToSendFinishedDeviceScan = true; + } + while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; LOGV("Reporting device opened: id=%d, name=%s\n", @@ -683,13 +705,14 @@ bool EventHub::openPlatformInput(void) { pollfd.revents = 0; mFds.push(pollfd); mDevices.push(NULL); + return true; +} - res = scanDir(DEVICE_PATH); +void EventHub::scanDevices() { + int res = scanDir(DEVICE_PATH); if(res < 0) { LOGE("scan dir failed for %s\n", DEVICE_PATH); } - - return true; } // ---------------------------------------------------------------------------- @@ -742,12 +765,10 @@ int EventHub::openDevice(const char *devicePath) { } // Check to see if the device is on our excluded list - List::iterator iter = mExcludedDevices.begin(); - List::iterator end = mExcludedDevices.end(); - for ( ; iter != end; iter++) { - const char* test = *iter; - if (identifier.name == test) { - LOGI("ignoring event id %s driver %s\n", devicePath, test); + for (size_t i = 0; i < mExcludedDevices.size(); i++) { + const String8& item = mExcludedDevices.itemAt(i); + if (identifier.name == item) { + LOGI("ignoring event id %s driver %s\n", devicePath, item.string()); close(fd); return -1; } @@ -1210,6 +1231,10 @@ int EventHub::scanDir(const char *dirname) return 0; } +void EventHub::reopenDevices() { + android_atomic_release_store(1, &mNeedToReopenDevices); +} + void EventHub::dump(String8& dump) { dump.append("Event Hub State:\n"); diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 4d26a9565efd..853a0bd867ef 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -178,9 +178,9 @@ public: virtual status_t mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) 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; + // Sets devices that are excluded from opening. + // This can be used to ignore input devices for sensors. + virtual void setExcludedDevices(const Vector& devices) = 0; /* * Wait for events to become available and returns them. @@ -215,6 +215,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector& outVirtualKeys) const = 0; + virtual void reopenDevices() = 0; + virtual void dump(String8& dump) = 0; }; @@ -242,7 +244,7 @@ public: virtual status_t mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) const; - virtual void addExcludedDevice(const char* deviceName); + virtual void setExcludedDevices(const Vector& devices); virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; @@ -259,6 +261,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector& outVirtualKeys) const; + virtual void reopenDevices(); + virtual void dump(String8& dump); protected: @@ -271,6 +275,7 @@ private: int closeDevice(const char *devicePath); int closeDeviceAtIndexLocked(int index); int scanDir(const char *dirname); + void scanDevices(); int readNotify(int nfd); status_t mError; @@ -333,7 +338,9 @@ private: bool mOpened; bool mNeedToSendFinishedDeviceScan; - List mExcludedDevices; + volatile int32_t mNeedToReopenDevices; // must be modified atomically + bool mNeedToScanDevices; + Vector mExcludedDevices; // device ids that report particular switches. int32_t mSwitches[SW_MAX + 1]; diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 724af39e998b..a22ec1c0f11b 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -38,6 +38,7 @@ #include "InputReader.h" +#include #include #include #include @@ -219,10 +220,9 @@ InputReader::InputReader(const sp& eventHub, const sp& policy, const sp& dispatcher) : mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), - mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX) { - mPolicy->getReaderConfiguration(&mConfig); - - configureExcludedDevices(); + mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), + mRefreshConfiguration(0) { + configure(true /*firstTime*/); updateGlobalMetaState(); updateInputConfiguration(); } @@ -234,6 +234,11 @@ InputReader::~InputReader() { } void InputReader::loopOnce() { + if (android_atomic_acquire_load(&mRefreshConfiguration)) { + android_atomic_release_store(0, &mRefreshConfiguration); + configure(false /*firstTime*/); + } + int32_t timeoutMillis = -1; if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -454,9 +459,12 @@ void InputReader::handleConfigurationChanged(nsecs_t when) { mDispatcher->notifyConfigurationChanged(when); } -void InputReader::configureExcludedDevices() { - for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) { - mEventHub->addExcludedDevice(mConfig.excludedDeviceNames[i]); +void InputReader::configure(bool firstTime) { + mPolicy->getReaderConfiguration(&mConfig); + mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); + + if (!firstTime) { + mEventHub->reopenDevices(); } } @@ -677,6 +685,10 @@ bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, s } // release device registy reader lock } +void InputReader::refreshConfiguration() { + android_atomic_release_store(1, &mRefreshConfiguration); +} + void InputReader::dump(String8& dump) { mEventHub->dump(dump); dump.append("\n"); diff --git a/services/input/InputReader.h b/services/input/InputReader.h index f1b89a25b710..5028b6017e73 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -145,7 +145,7 @@ struct InputReaderConfiguration { pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees pointerGestureSwipeMaxWidthRatio(0.333f), - pointerGestureMovementSpeedRatio(0.5f), + pointerGestureMovementSpeedRatio(0.3f), pointerGestureZoomSpeedRatio(0.3f) { } }; @@ -234,6 +234,9 @@ public: /* 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; + + /* Reopens and reconfigures all input devices. */ + virtual void refreshConfiguration() = 0; }; @@ -298,6 +301,8 @@ public: virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + virtual void refreshConfiguration(); + protected: // These methods are protected virtual so they can be overridden and instrumented // by test cases. @@ -339,18 +344,17 @@ private: void timeoutExpired(nsecs_t when); void handleConfigurationChanged(nsecs_t when); - void configureExcludedDevices(); // state management for all devices Mutex mStateLock; - int32_t mGlobalMetaState; + int32_t mGlobalMetaState; // guarded by mStateLock virtual void updateGlobalMetaState(); virtual int32_t getGlobalMetaState(); virtual void fadePointer(); - InputConfiguration mInputConfiguration; + InputConfiguration mInputConfiguration; // guarded by mStateLock void updateInputConfiguration(); nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread @@ -358,9 +362,12 @@ private: virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, int32_t scanCode); - nsecs_t mNextTimeout; // only accessed by reader thread + nsecs_t mNextTimeout; // only accessed by reader thread, not guarded virtual void requestTimeoutAtTime(nsecs_t when); + volatile int32_t mRefreshConfiguration; // atomic + void configure(bool firstTime); + // 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, diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index b5f603ac8e87..e85ae1fae136 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -617,8 +617,8 @@ private: return NAME_NOT_FOUND; } - virtual void addExcludedDevice(const char* deviceName) { - mExcludedDevices.add(String8(deviceName)); + virtual void setExcludedDevices(const Vector& devices) { + mExcludedDevices = devices; } virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { @@ -716,6 +716,9 @@ private: virtual void dump(String8& dump) { } + + virtual void reopenDevices() { + } }; diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index ab781f492e6d..4eda68474547 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -23,10 +23,14 @@ import org.xmlpull.v1.XmlPullParser; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.database.ContentObserver; import android.os.Environment; +import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; import android.os.SystemProperties; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; @@ -56,7 +60,7 @@ public class InputManager { private final Callbacks mCallbacks; private final Context mContext; private final WindowManagerService mWindowManagerService; - + private static native void nativeInit(Context context, Callbacks callbacks, MessageQueue messageQueue); private static native void nativeStart(); @@ -85,6 +89,7 @@ public class InputManager { private static native int[] nativeGetInputDeviceIds(); private static native boolean nativeTransferTouchFocus(InputChannel fromChannel, InputChannel toChannel); + private static native void nativeSetPointerSpeed(int speed); private static native String nativeDump(); // Input event injection constants defined in InputDispatcher.h. @@ -123,10 +128,13 @@ public class InputManager { Slog.i(TAG, "Initializing input manager"); nativeInit(mContext, mCallbacks, looper.getQueue()); } - + public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(); + + registerPointerSpeedSettingObserver(); + updatePointerSpeedFromSettings(); } public void setDisplaySize(int displayId, int width, int height) { @@ -359,6 +367,42 @@ public class InputManager { return nativeTransferTouchFocus(fromChannel, toChannel); } + /** + * Set the pointer speed. + * @param speed The pointer speed as a value between -7 (slowest) and 7 (fastest) + * where 0 is the default speed. + */ + public void setPointerSpeed(int speed) { + speed = Math.min(Math.max(speed, -7), 7); + nativeSetPointerSpeed(speed); + } + + public void updatePointerSpeedFromSettings() { + int speed = getPointerSpeedSetting(0); + setPointerSpeed(speed); + } + + private void registerPointerSpeedSettingObserver() { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.POINTER_SPEED), true, + new ContentObserver(mWindowManagerService.mH) { + @Override + public void onChange(boolean selfChange) { + updatePointerSpeedFromSettings(); + } + }); + } + + private int getPointerSpeedSetting(int defaultValue) { + int speed = defaultValue; + try { + speed = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.POINTER_SPEED); + } catch (SettingNotFoundException snfe) { + } + return speed; + } + public void dump(PrintWriter pw) { String dumpStr = nativeDump(); if (dumpStr != null) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 77608979fca8..4ff6b0644407 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5947,6 +5947,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * Temporarily set the pointer speed. Does not save the new setting. + * Used by the settings application. + */ + public void setPointerSpeed(int speed) { + if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED, + "setPointerSpeed()")) { + throw new SecurityException("Requires SET_POINTER_SPEED permission"); + } + + mInputManager.setPointerSpeed(speed); + } + private WindowState getFocusedWindow() { synchronized (mWindowMap) { return getFocusedWindowLocked(); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index fef41c9b04d1..270464754c30 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -53,6 +53,11 @@ namespace android { +// The exponent used to calculate the pointer speed scaling factor. +// The scaling factor is calculated as 2 ^ (speed * exponent), +// where the speed ranges from -7 to + 7 and is supplied by the user. +static const float POINTER_SPEED_EXPONENT = 1.0f / 3; + static struct { jclass clazz; @@ -179,6 +184,7 @@ public: void setFocusedApplication(JNIEnv* env, jobject applicationObj); void setInputDispatchMode(bool enabled, bool frozen); void setSystemUiVisibility(int32_t visibility); + void setPointerSpeed(int32_t speed); /* --- InputReaderPolicyInterface implementation --- */ @@ -227,6 +233,9 @@ private: // System UI visibility. int32_t systemUiVisibility; + // Pointer speed. + int32_t pointerSpeed; + // Sprite controller singleton, created on first use. sp spriteController; @@ -266,6 +275,7 @@ NativeInputManager::NativeInputManager(jobject contextObj, mLocked.displayOrientation = ROTATION_0; mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; + mLocked.pointerSpeed = 0; } sp eventHub = new EventHub(); @@ -429,6 +439,13 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) { outConfig->pointerGestureTapSlop = touchSlop; } + + { // acquire lock + AutoMutex _l(mLock); + + outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed + * POINTER_SPEED_EXPONENT); + } // release lock } sp NativeInputManager::obtainPointerController(int32_t deviceId) { @@ -634,6 +651,17 @@ void NativeInputManager::updateInactivityTimeoutLocked(const spgetReader()->refreshConfiguration(); + } +} + bool NativeInputManager::isScreenOn() { return android_server_PowerManagerService_isScreenOn(); } @@ -1180,6 +1208,15 @@ static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env transferTouchFocus(fromChannel, toChannel); } +static void android_server_InputManager_nativeSetPointerSpeed(JNIEnv* env, + jclass clazz, jint speed) { + if (checkInputManagerUnitialized(env)) { + return; + } + + gNativeInputManager->setPointerSpeed(speed); +} + static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return NULL; @@ -1234,6 +1271,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeGetInputConfiguration }, { "nativeTransferTouchFocus", "(Landroid/view/InputChannel;Landroid/view/InputChannel;)Z", (void*) android_server_InputManager_nativeTransferTouchFocus }, + { "nativeSetPointerSpeed", "(I)V", + (void*) android_server_InputManager_nativeSetPointerSpeed }, { "nativeDump", "()Ljava/lang/String;", (void*) android_server_InputManager_nativeDump }, }; -- cgit v1.2.3-59-g8ed1b