michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=4 sw=4 sts=4 tw=80 et: */ michael@0: /* Copyright 2012 Mozilla Foundation and Mozilla contributors michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #ifndef _GNU_SOURCE michael@0: #define _GNU_SOURCE michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "GonkPermission.h" michael@0: #include "nscore.h" michael@0: #ifdef MOZ_OMX_DECODER michael@0: #include "MediaResourceManagerService.h" michael@0: #endif michael@0: #include "mozilla/TouchEvents.h" michael@0: #include "mozilla/FileUtils.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #if ANDROID_VERSION >= 18 michael@0: #include "nativewindow/FakeSurfaceComposer.h" michael@0: #endif michael@0: #include "nsAppShell.h" michael@0: #include "mozilla/dom/Touch.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIScreen.h" michael@0: #include "nsScreenManagerGonk.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsWindow.h" michael@0: #include "OrientationObserver.h" michael@0: #include "GonkMemoryPressureMonitoring.h" michael@0: michael@0: #include "android/log.h" michael@0: #include "libui/EventHub.h" michael@0: #include "libui/InputReader.h" michael@0: #include "libui/InputDispatcher.h" michael@0: #include "cutils/properties.h" michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: #include "ipc/Nuwa.h" michael@0: #endif michael@0: michael@0: #include "GeckoProfiler.h" michael@0: michael@0: // Defines kKeyMapping and GetKeyNameIndex() michael@0: #include "GonkKeyMapping.h" michael@0: michael@0: #define LOG(args...) \ michael@0: __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) michael@0: #ifdef VERBOSE_LOG_ENABLED michael@0: # define VERBOSE_LOG(args...) \ michael@0: __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) michael@0: #else michael@0: # define VERBOSE_LOG(args...) \ michael@0: (void)0 michael@0: #endif michael@0: michael@0: using namespace android; michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::services; michael@0: using namespace mozilla::widget; michael@0: michael@0: bool gDrawRequest = false; michael@0: static nsAppShell *gAppShell = nullptr; michael@0: static int epollfd = 0; michael@0: static int signalfds[2] = {0}; michael@0: static bool sDevInputAudioJack; michael@0: static int32_t sHeadphoneState; michael@0: static int32_t sMicrophoneState; michael@0: michael@0: // Amount of time in MS before an input is considered expired. michael@0: static const uint64_t kInputExpirationThresholdMs = 1000; michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver) michael@0: michael@0: static uint64_t michael@0: nanosecsToMillisecs(nsecs_t nsecs) michael@0: { michael@0: return nsecs / 1000000; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: bool ProcessNextEvent() michael@0: { michael@0: return gAppShell->ProcessNextNativeEvent(true); michael@0: } michael@0: michael@0: void NotifyEvent() michael@0: { michael@0: gAppShell->NotifyNativeEvent(); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: static void michael@0: pipeHandler(int fd, FdHandler *data) michael@0: { michael@0: ssize_t len; michael@0: do { michael@0: char tmp[32]; michael@0: len = read(fd, tmp, sizeof(tmp)); michael@0: } while (len > 0); michael@0: } michael@0: michael@0: struct Touch { michael@0: int32_t id; michael@0: PointerCoords coords; michael@0: }; michael@0: michael@0: struct UserInputData { michael@0: uint64_t timeMs; michael@0: enum { michael@0: MOTION_DATA, michael@0: KEY_DATA michael@0: } type; michael@0: int32_t action; michael@0: int32_t flags; michael@0: int32_t metaState; michael@0: int32_t deviceId; michael@0: union { michael@0: struct { michael@0: int32_t keyCode; michael@0: int32_t scanCode; michael@0: } key; michael@0: struct { michael@0: int32_t touchCount; michael@0: ::Touch touches[MAX_POINTERS]; michael@0: } motion; michael@0: }; michael@0: michael@0: Modifiers DOMModifiers() const; michael@0: }; michael@0: michael@0: Modifiers michael@0: UserInputData::DOMModifiers() const michael@0: { michael@0: Modifiers result = 0; michael@0: if (metaState & (AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { michael@0: result |= MODIFIER_ALT; michael@0: } michael@0: if (metaState & (AMETA_SHIFT_ON | michael@0: AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { michael@0: result |= MODIFIER_SHIFT; michael@0: } michael@0: if (metaState & AMETA_FUNCTION_ON) { michael@0: result |= MODIFIER_FN; michael@0: } michael@0: if (metaState & (AMETA_CTRL_ON | michael@0: AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { michael@0: result |= MODIFIER_CONTROL; michael@0: } michael@0: if (metaState & (AMETA_META_ON | michael@0: AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { michael@0: result |= MODIFIER_META; michael@0: } michael@0: if (metaState & AMETA_CAPS_LOCK_ON) { michael@0: result |= MODIFIER_CAPSLOCK; michael@0: } michael@0: if (metaState & AMETA_NUM_LOCK_ON) { michael@0: result |= MODIFIER_NUMLOCK; michael@0: } michael@0: if (metaState & AMETA_SCROLL_LOCK_ON) { michael@0: result |= MODIFIER_SCROLLLOCK; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static void michael@0: sendMouseEvent(uint32_t msg, UserInputData& data, bool forwardToChildren) michael@0: { michael@0: WidgetMouseEvent event(true, msg, nullptr, michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: michael@0: event.refPoint.x = data.motion.touches[0].coords.getX(); michael@0: event.refPoint.y = data.motion.touches[0].coords.getY(); michael@0: event.time = data.timeMs; michael@0: event.button = WidgetMouseEvent::eLeftButton; michael@0: event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: if (msg != NS_MOUSE_MOVE) michael@0: event.clickCount = 1; michael@0: event.modifiers = data.DOMModifiers(); michael@0: michael@0: event.mFlags.mNoCrossProcessBoundaryForwarding = !forwardToChildren; michael@0: michael@0: nsWindow::DispatchInputEvent(event); michael@0: } michael@0: michael@0: static void michael@0: addDOMTouch(UserInputData& data, WidgetTouchEvent& event, int i) michael@0: { michael@0: const ::Touch& touch = data.motion.touches[i]; michael@0: event.touches.AppendElement( michael@0: new dom::Touch(touch.id, michael@0: nsIntPoint(floor(touch.coords.getX() + 0.5), floor(touch.coords.getY() + 0.5)), michael@0: nsIntPoint(touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), michael@0: touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE)), michael@0: 0, michael@0: touch.coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)) michael@0: ); michael@0: } michael@0: michael@0: static nsEventStatus michael@0: sendTouchEvent(UserInputData& data, bool* captured) michael@0: { michael@0: uint32_t msg; michael@0: int32_t action = data.action & AMOTION_EVENT_ACTION_MASK; michael@0: switch (action) { michael@0: case AMOTION_EVENT_ACTION_DOWN: michael@0: case AMOTION_EVENT_ACTION_POINTER_DOWN: michael@0: msg = NS_TOUCH_START; michael@0: break; michael@0: case AMOTION_EVENT_ACTION_MOVE: michael@0: msg = NS_TOUCH_MOVE; michael@0: break; michael@0: case AMOTION_EVENT_ACTION_UP: michael@0: case AMOTION_EVENT_ACTION_POINTER_UP: michael@0: msg = NS_TOUCH_END; michael@0: break; michael@0: case AMOTION_EVENT_ACTION_OUTSIDE: michael@0: case AMOTION_EVENT_ACTION_CANCEL: michael@0: msg = NS_TOUCH_CANCEL; michael@0: break; michael@0: default: michael@0: msg = NS_EVENT_NULL; michael@0: break; michael@0: } michael@0: michael@0: WidgetTouchEvent event(true, msg, nullptr); michael@0: michael@0: event.time = data.timeMs; michael@0: event.modifiers = data.DOMModifiers(); michael@0: michael@0: int32_t i; michael@0: if (msg == NS_TOUCH_END) { michael@0: i = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK; michael@0: i >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; michael@0: addDOMTouch(data, event, i); michael@0: } else { michael@0: for (i = 0; i < data.motion.touchCount; ++i) michael@0: addDOMTouch(data, event, i); michael@0: } michael@0: michael@0: return nsWindow::DispatchInputEvent(event, captured); michael@0: } michael@0: michael@0: class MOZ_STACK_CLASS KeyEventDispatcher michael@0: { michael@0: public: michael@0: KeyEventDispatcher(const UserInputData& aData, michael@0: KeyCharacterMap* aKeyCharMap); michael@0: void Dispatch(); michael@0: michael@0: private: michael@0: const UserInputData& mData; michael@0: sp mKeyCharMap; michael@0: michael@0: char16_t mChar; michael@0: char16_t mUnmodifiedChar; michael@0: michael@0: uint32_t mDOMKeyCode; michael@0: KeyNameIndex mDOMKeyNameIndex; michael@0: char16_t mDOMPrintableKeyValue; michael@0: michael@0: bool IsKeyPress() const michael@0: { michael@0: return mData.action == AKEY_EVENT_ACTION_DOWN; michael@0: } michael@0: bool IsRepeat() const michael@0: { michael@0: return IsKeyPress() && (mData.flags & AKEY_EVENT_FLAG_LONG_PRESS); michael@0: } michael@0: michael@0: char16_t PrintableKeyValue() const; michael@0: michael@0: int32_t UnmodifiedMetaState() const michael@0: { michael@0: return mData.metaState & michael@0: ~(AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON | michael@0: AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON | michael@0: AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); michael@0: } michael@0: michael@0: static bool IsControlChar(char16_t aChar) michael@0: { michael@0: return (aChar < ' ' || aChar == 0x7F); michael@0: } michael@0: michael@0: void DispatchKeyDownEvent(); michael@0: void DispatchKeyUpEvent(); michael@0: nsEventStatus DispatchKeyEventInternal(uint32_t aEventMessage); michael@0: }; michael@0: michael@0: KeyEventDispatcher::KeyEventDispatcher(const UserInputData& aData, michael@0: KeyCharacterMap* aKeyCharMap) : michael@0: mData(aData), mKeyCharMap(aKeyCharMap), mChar(0), mUnmodifiedChar(0), michael@0: mDOMPrintableKeyValue(0) michael@0: { michael@0: // XXX Printable key's keyCode value should be computed with actual michael@0: // input character. michael@0: mDOMKeyCode = (mData.key.keyCode < (ssize_t)ArrayLength(kKeyMapping)) ? michael@0: kKeyMapping[mData.key.keyCode] : 0; michael@0: mDOMKeyNameIndex = GetKeyNameIndex(mData.key.keyCode); michael@0: michael@0: if (!mKeyCharMap.get()) { michael@0: return; michael@0: } michael@0: michael@0: mChar = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState); michael@0: if (IsControlChar(mChar)) { michael@0: mChar = 0; michael@0: } michael@0: int32_t unmodifiedMetaState = UnmodifiedMetaState(); michael@0: if (mData.metaState == unmodifiedMetaState) { michael@0: mUnmodifiedChar = mChar; michael@0: } else { michael@0: mUnmodifiedChar = mKeyCharMap->getCharacter(mData.key.keyCode, michael@0: unmodifiedMetaState); michael@0: if (IsControlChar(mUnmodifiedChar)) { michael@0: mUnmodifiedChar = 0; michael@0: } michael@0: } michael@0: michael@0: mDOMPrintableKeyValue = PrintableKeyValue(); michael@0: } michael@0: michael@0: char16_t michael@0: KeyEventDispatcher::PrintableKeyValue() const michael@0: { michael@0: if (mDOMKeyNameIndex != KEY_NAME_INDEX_USE_STRING) { michael@0: return 0; michael@0: } michael@0: return mChar ? mChar : mUnmodifiedChar; michael@0: } michael@0: michael@0: nsEventStatus michael@0: KeyEventDispatcher::DispatchKeyEventInternal(uint32_t aEventMessage) michael@0: { michael@0: WidgetKeyboardEvent event(true, aEventMessage, nullptr); michael@0: if (aEventMessage == NS_KEY_PRESS) { michael@0: // XXX If the charCode is not a printable character, the charCode michael@0: // should be computed without Ctrl/Alt/Meta modifiers. michael@0: event.charCode = static_cast(mChar); michael@0: } michael@0: if (!event.charCode) { michael@0: event.keyCode = mDOMKeyCode; michael@0: } michael@0: event.isChar = !!event.charCode; michael@0: event.mIsRepeat = IsRepeat(); michael@0: event.mKeyNameIndex = mDOMKeyNameIndex; michael@0: if (mDOMPrintableKeyValue) { michael@0: event.mKeyValue = mDOMPrintableKeyValue; michael@0: } michael@0: event.modifiers = mData.DOMModifiers(); michael@0: event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE; michael@0: event.time = mData.timeMs; michael@0: return nsWindow::DispatchInputEvent(event); michael@0: } michael@0: michael@0: void michael@0: KeyEventDispatcher::Dispatch() michael@0: { michael@0: // XXX Even if unknown key is pressed, DOM key event should be michael@0: // dispatched since Gecko for the other platforms are implemented michael@0: // as so. michael@0: if (!mDOMKeyCode && mDOMKeyNameIndex == KEY_NAME_INDEX_Unidentified) { michael@0: VERBOSE_LOG("Got unknown key event code. " michael@0: "type 0x%04x code 0x%04x value %d", michael@0: mData.action, mData.key.keyCode, IsKeyPress()); michael@0: return; michael@0: } michael@0: michael@0: if (IsKeyPress()) { michael@0: DispatchKeyDownEvent(); michael@0: } else { michael@0: DispatchKeyUpEvent(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: KeyEventDispatcher::DispatchKeyDownEvent() michael@0: { michael@0: nsEventStatus status = DispatchKeyEventInternal(NS_KEY_DOWN); michael@0: if (status != nsEventStatus_eConsumeNoDefault) { michael@0: DispatchKeyEventInternal(NS_KEY_PRESS); michael@0: } michael@0: } michael@0: michael@0: void michael@0: KeyEventDispatcher::DispatchKeyUpEvent() michael@0: { michael@0: DispatchKeyEventInternal(NS_KEY_UP); michael@0: } michael@0: michael@0: class SwitchEventRunnable : public nsRunnable { michael@0: public: michael@0: SwitchEventRunnable(hal::SwitchEvent& aEvent) : mEvent(aEvent) michael@0: {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: hal::NotifySwitchStateFromInputDevice(mEvent.device(), michael@0: mEvent.status()); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: hal::SwitchEvent mEvent; michael@0: }; michael@0: michael@0: static void michael@0: updateHeadphoneSwitch() michael@0: { michael@0: hal::SwitchEvent event; michael@0: michael@0: switch (sHeadphoneState) { michael@0: case AKEY_STATE_UP: michael@0: event.status() = hal::SWITCH_STATE_OFF; michael@0: break; michael@0: case AKEY_STATE_DOWN: michael@0: event.status() = sMicrophoneState == AKEY_STATE_DOWN ? michael@0: hal::SWITCH_STATE_HEADSET : hal::SWITCH_STATE_HEADPHONE; michael@0: break; michael@0: default: michael@0: return; michael@0: } michael@0: michael@0: event.device() = hal::SWITCH_HEADPHONES; michael@0: NS_DispatchToMainThread(new SwitchEventRunnable(event)); michael@0: } michael@0: michael@0: class GeckoPointerController : public PointerControllerInterface { michael@0: float mX; michael@0: float mY; michael@0: int32_t mButtonState; michael@0: InputReaderConfiguration* mConfig; michael@0: public: michael@0: GeckoPointerController(InputReaderConfiguration* config) michael@0: : mX(0) michael@0: , mY(0) michael@0: , mButtonState(0) michael@0: , mConfig(config) michael@0: {} michael@0: michael@0: virtual bool getBounds(float* outMinX, float* outMinY, michael@0: float* outMaxX, float* outMaxY) const; michael@0: virtual void move(float deltaX, float deltaY); michael@0: virtual void setButtonState(int32_t buttonState); michael@0: virtual int32_t getButtonState() const; michael@0: virtual void setPosition(float x, float y); michael@0: virtual void getPosition(float* outX, float* outY) const; michael@0: virtual void fade(Transition transition) {} michael@0: virtual void unfade(Transition transition) {} michael@0: virtual void setPresentation(Presentation presentation) {} michael@0: virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, michael@0: BitSet32 spotIdBits) {} michael@0: virtual void clearSpots() {} michael@0: }; michael@0: michael@0: bool michael@0: GeckoPointerController::getBounds(float* outMinX, michael@0: float* outMinY, michael@0: float* outMaxX, michael@0: float* outMaxY) const michael@0: { michael@0: DisplayViewport viewport; michael@0: michael@0: mConfig->getDisplayInfo(false, &viewport); michael@0: michael@0: *outMinX = *outMinY = 0; michael@0: *outMaxX = viewport.logicalRight; michael@0: *outMaxY = viewport.logicalBottom; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: GeckoPointerController::move(float deltaX, float deltaY) michael@0: { michael@0: float minX, minY, maxX, maxY; michael@0: getBounds(&minX, &minY, &maxX, &maxY); michael@0: michael@0: mX = clamped(mX + deltaX, minX, maxX); michael@0: mY = clamped(mY + deltaY, minY, maxY); michael@0: } michael@0: michael@0: void michael@0: GeckoPointerController::setButtonState(int32_t buttonState) michael@0: { michael@0: mButtonState = buttonState; michael@0: } michael@0: michael@0: int32_t michael@0: GeckoPointerController::getButtonState() const michael@0: { michael@0: return mButtonState; michael@0: } michael@0: michael@0: void michael@0: GeckoPointerController::setPosition(float x, float y) michael@0: { michael@0: mX = x; michael@0: mY = y; michael@0: } michael@0: michael@0: void michael@0: GeckoPointerController::getPosition(float* outX, float* outY) const michael@0: { michael@0: *outX = mX; michael@0: *outY = mY; michael@0: } michael@0: michael@0: class GeckoInputReaderPolicy : public InputReaderPolicyInterface { michael@0: InputReaderConfiguration mConfig; michael@0: public: michael@0: GeckoInputReaderPolicy() {} michael@0: michael@0: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); michael@0: virtual sp obtainPointerController(int32_t michael@0: deviceId) michael@0: { michael@0: return new GeckoPointerController(&mConfig); michael@0: }; michael@0: virtual void notifyInputDevicesChanged(const android::Vector& inputDevices) {}; michael@0: virtual sp getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) michael@0: { michael@0: return nullptr; michael@0: }; michael@0: virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) michael@0: { michael@0: return String8::empty(); michael@0: }; michael@0: michael@0: void setDisplayInfo(); michael@0: michael@0: protected: michael@0: virtual ~GeckoInputReaderPolicy() {} michael@0: }; michael@0: michael@0: class GeckoInputDispatcher : public InputDispatcherInterface { michael@0: public: michael@0: GeckoInputDispatcher(sp &aEventHub) michael@0: : mQueueLock("GeckoInputDispatcher::mQueueMutex") michael@0: , mEventHub(aEventHub) michael@0: , mTouchDownCount(0) michael@0: , mKeyDownCount(0) michael@0: , mTouchEventsFiltered(false) michael@0: , mKeyEventsFiltered(false) michael@0: {} michael@0: michael@0: virtual void dump(String8& dump); michael@0: michael@0: virtual void monitor() {} michael@0: michael@0: // Called on the main thread michael@0: virtual void dispatchOnce(); michael@0: michael@0: // notify* methods are called on the InputReaderThread michael@0: virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); michael@0: virtual void notifyKey(const NotifyKeyArgs* args); michael@0: virtual void notifyMotion(const NotifyMotionArgs* args); michael@0: virtual void notifySwitch(const NotifySwitchArgs* args); michael@0: virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); michael@0: michael@0: virtual int32_t injectInputEvent(const InputEvent* event, michael@0: int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, michael@0: uint32_t policyFlags); michael@0: michael@0: virtual void setInputWindows(const android::Vector >& inputWindowHandles); michael@0: virtual void setFocusedApplication(const sp& inputApplicationHandle); michael@0: michael@0: virtual void setInputDispatchMode(bool enabled, bool frozen); michael@0: virtual void setInputFilterEnabled(bool enabled) {} michael@0: virtual bool transferTouchFocus(const sp& fromChannel, michael@0: const sp& toChannel) { return true; } michael@0: michael@0: virtual status_t registerInputChannel(const sp& inputChannel, michael@0: const sp& inputWindowHandle, bool monitor); michael@0: virtual status_t unregisterInputChannel(const sp& inputChannel); michael@0: michael@0: michael@0: michael@0: protected: michael@0: virtual ~GeckoInputDispatcher() {} michael@0: michael@0: private: michael@0: // mQueueLock should generally be locked while using mEventQueue. michael@0: // UserInputData is pushed on on the InputReaderThread and michael@0: // popped and dispatched on the main thread. michael@0: mozilla::Mutex mQueueLock; michael@0: std::queue mEventQueue; michael@0: sp mEventHub; michael@0: michael@0: int mTouchDownCount; michael@0: int mKeyDownCount; michael@0: bool mTouchEventsFiltered; michael@0: bool mKeyEventsFiltered; michael@0: BitSet32 mTouchDown; michael@0: }; michael@0: michael@0: // GeckoInputReaderPolicy michael@0: void michael@0: GeckoInputReaderPolicy::setDisplayInfo() michael@0: { michael@0: static_assert(nsIScreen::ROTATION_0_DEG == michael@0: DISPLAY_ORIENTATION_0, michael@0: "Orientation enums not matched!"); michael@0: static_assert(nsIScreen::ROTATION_90_DEG == michael@0: DISPLAY_ORIENTATION_90, michael@0: "Orientation enums not matched!"); michael@0: static_assert(nsIScreen::ROTATION_180_DEG == michael@0: DISPLAY_ORIENTATION_180, michael@0: "Orientation enums not matched!"); michael@0: static_assert(nsIScreen::ROTATION_270_DEG == michael@0: DISPLAY_ORIENTATION_270, michael@0: "Orientation enums not matched!"); michael@0: michael@0: DisplayViewport viewport; michael@0: viewport.displayId = 0; michael@0: viewport.orientation = nsScreenGonk::GetRotation(); michael@0: viewport.physicalRight = viewport.deviceWidth = gScreenBounds.width; michael@0: viewport.physicalBottom = viewport.deviceHeight = gScreenBounds.height; michael@0: if (viewport.orientation == DISPLAY_ORIENTATION_90 || michael@0: viewport.orientation == DISPLAY_ORIENTATION_270) { michael@0: viewport.logicalRight = gScreenBounds.height; michael@0: viewport.logicalBottom = gScreenBounds.width; michael@0: } else { michael@0: viewport.logicalRight = gScreenBounds.width; michael@0: viewport.logicalBottom = gScreenBounds.height; michael@0: } michael@0: mConfig.setDisplayInfo(false, viewport); michael@0: } michael@0: michael@0: void GeckoInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) michael@0: { michael@0: *outConfig = mConfig; michael@0: } michael@0: michael@0: michael@0: // GeckoInputDispatcher michael@0: void michael@0: GeckoInputDispatcher::dump(String8& dump) michael@0: { michael@0: } michael@0: michael@0: static bool michael@0: isExpired(const UserInputData& data) michael@0: { michael@0: uint64_t timeNowMs = michael@0: nanosecsToMillisecs(systemTime(SYSTEM_TIME_MONOTONIC)); michael@0: return (timeNowMs - data.timeMs) > kInputExpirationThresholdMs; michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::dispatchOnce() michael@0: { michael@0: UserInputData data; michael@0: { michael@0: MutexAutoLock lock(mQueueLock); michael@0: if (mEventQueue.empty()) michael@0: return; michael@0: data = mEventQueue.front(); michael@0: mEventQueue.pop(); michael@0: if (!mEventQueue.empty()) michael@0: gAppShell->NotifyNativeEvent(); michael@0: } michael@0: michael@0: switch (data.type) { michael@0: case UserInputData::MOTION_DATA: { michael@0: if (!mTouchDownCount) { michael@0: // No pending events, the filter state can be updated. michael@0: mTouchEventsFiltered = isExpired(data); michael@0: } michael@0: michael@0: int32_t action = data.action & AMOTION_EVENT_ACTION_MASK; michael@0: int32_t index = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK; michael@0: index >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; michael@0: switch (action) { michael@0: case AMOTION_EVENT_ACTION_DOWN: michael@0: case AMOTION_EVENT_ACTION_POINTER_DOWN: michael@0: if (!mTouchDown.hasBit(index)) { michael@0: mTouchDown.markBit(index); michael@0: mTouchDownCount++; michael@0: } michael@0: break; michael@0: case AMOTION_EVENT_ACTION_MOVE: michael@0: case AMOTION_EVENT_ACTION_HOVER_MOVE: michael@0: // No need to update the count on move. michael@0: break; michael@0: case AMOTION_EVENT_ACTION_UP: michael@0: case AMOTION_EVENT_ACTION_POINTER_UP: michael@0: case AMOTION_EVENT_ACTION_OUTSIDE: michael@0: case AMOTION_EVENT_ACTION_CANCEL: michael@0: if (mTouchDown.hasBit(index)) { michael@0: mTouchDown.clearBit(index); michael@0: mTouchDownCount--; michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: if (mTouchEventsFiltered) { michael@0: return; michael@0: } michael@0: michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: if (action != AMOTION_EVENT_ACTION_HOVER_MOVE) { michael@0: bool captured; michael@0: status = sendTouchEvent(data, &captured); michael@0: if (captured) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: uint32_t msg; michael@0: switch (action) { michael@0: case AMOTION_EVENT_ACTION_DOWN: michael@0: msg = NS_MOUSE_BUTTON_DOWN; michael@0: break; michael@0: case AMOTION_EVENT_ACTION_POINTER_DOWN: michael@0: case AMOTION_EVENT_ACTION_POINTER_UP: michael@0: case AMOTION_EVENT_ACTION_MOVE: michael@0: case AMOTION_EVENT_ACTION_HOVER_MOVE: michael@0: msg = NS_MOUSE_MOVE; michael@0: break; michael@0: case AMOTION_EVENT_ACTION_OUTSIDE: michael@0: case AMOTION_EVENT_ACTION_CANCEL: michael@0: case AMOTION_EVENT_ACTION_UP: michael@0: msg = NS_MOUSE_BUTTON_UP; michael@0: break; michael@0: default: michael@0: msg = NS_EVENT_NULL; michael@0: break; michael@0: } michael@0: if (msg != NS_EVENT_NULL) { michael@0: sendMouseEvent(msg, data, michael@0: status != nsEventStatus_eConsumeNoDefault); michael@0: } michael@0: break; michael@0: } michael@0: case UserInputData::KEY_DATA: { michael@0: if (!mKeyDownCount) { michael@0: // No pending events, the filter state can be updated. michael@0: mKeyEventsFiltered = isExpired(data); michael@0: } michael@0: michael@0: mKeyDownCount += (data.action == AKEY_EVENT_ACTION_DOWN) ? 1 : -1; michael@0: if (mKeyEventsFiltered) { michael@0: return; michael@0: } michael@0: michael@0: sp kcm = mEventHub->getKeyCharacterMap(data.deviceId); michael@0: KeyEventDispatcher dispatcher(data, kcm.get()); michael@0: dispatcher.Dispatch(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs*) michael@0: { michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::notifyKey(const NotifyKeyArgs* args) michael@0: { michael@0: UserInputData data; michael@0: data.timeMs = nanosecsToMillisecs(args->eventTime); michael@0: data.type = UserInputData::KEY_DATA; michael@0: data.action = args->action; michael@0: data.flags = args->flags; michael@0: data.metaState = args->metaState; michael@0: data.deviceId = args->deviceId; michael@0: data.key.keyCode = args->keyCode; michael@0: data.key.scanCode = args->scanCode; michael@0: { michael@0: MutexAutoLock lock(mQueueLock); michael@0: mEventQueue.push(data); michael@0: } michael@0: gAppShell->NotifyNativeEvent(); michael@0: } michael@0: michael@0: michael@0: void michael@0: GeckoInputDispatcher::notifyMotion(const NotifyMotionArgs* args) michael@0: { michael@0: UserInputData data; michael@0: data.timeMs = nanosecsToMillisecs(args->eventTime); michael@0: data.type = UserInputData::MOTION_DATA; michael@0: data.action = args->action; michael@0: data.flags = args->flags; michael@0: data.metaState = args->metaState; michael@0: data.deviceId = args->deviceId; michael@0: MOZ_ASSERT(args->pointerCount <= MAX_POINTERS); michael@0: data.motion.touchCount = args->pointerCount; michael@0: for (uint32_t i = 0; i < args->pointerCount; ++i) { michael@0: ::Touch& touch = data.motion.touches[i]; michael@0: touch.id = args->pointerProperties[i].id; michael@0: memcpy(&touch.coords, &args->pointerCoords[i], sizeof(*args->pointerCoords)); michael@0: } michael@0: { michael@0: MutexAutoLock lock(mQueueLock); michael@0: if (!mEventQueue.empty() && michael@0: mEventQueue.back().type == UserInputData::MOTION_DATA && michael@0: ((mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) == michael@0: AMOTION_EVENT_ACTION_MOVE || michael@0: (mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) == michael@0: AMOTION_EVENT_ACTION_HOVER_MOVE)) michael@0: mEventQueue.back() = data; michael@0: else michael@0: mEventQueue.push(data); michael@0: } michael@0: gAppShell->NotifyNativeEvent(); michael@0: } michael@0: michael@0: michael@0: michael@0: void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args) michael@0: { michael@0: if (!sDevInputAudioJack) michael@0: return; michael@0: michael@0: bool needSwitchUpdate = false; michael@0: michael@0: if (args->switchMask & (1 << SW_HEADPHONE_INSERT)) { michael@0: sHeadphoneState = (args->switchValues & (1 << SW_HEADPHONE_INSERT)) ? michael@0: AKEY_STATE_DOWN : AKEY_STATE_UP; michael@0: needSwitchUpdate = true; michael@0: } michael@0: michael@0: if (args->switchMask & (1 << SW_MICROPHONE_INSERT)) { michael@0: sMicrophoneState = (args->switchValues & (1 << SW_MICROPHONE_INSERT)) ? michael@0: AKEY_STATE_DOWN : AKEY_STATE_UP; michael@0: needSwitchUpdate = true; michael@0: } michael@0: michael@0: if (needSwitchUpdate) michael@0: updateHeadphoneSwitch(); michael@0: } michael@0: michael@0: void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) michael@0: { michael@0: } michael@0: michael@0: int32_t GeckoInputDispatcher::injectInputEvent( michael@0: const InputEvent* event, michael@0: int32_t injectorPid, int32_t injectorUid, int32_t syncMode, michael@0: int32_t timeoutMillis, uint32_t policyFlags) michael@0: { michael@0: return INPUT_EVENT_INJECTION_SUCCEEDED; michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::setInputWindows(const android::Vector >& inputWindowHandles) michael@0: { michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::setFocusedApplication(const sp& inputApplicationHandle) michael@0: { michael@0: } michael@0: michael@0: void michael@0: GeckoInputDispatcher::setInputDispatchMode(bool enabled, bool frozen) michael@0: { michael@0: } michael@0: michael@0: status_t michael@0: GeckoInputDispatcher::registerInputChannel(const sp& inputChannel, michael@0: const sp& inputWindowHandle, bool monitor) michael@0: { michael@0: return OK; michael@0: } michael@0: michael@0: status_t michael@0: GeckoInputDispatcher::unregisterInputChannel(const sp& inputChannel) michael@0: { michael@0: return OK; michael@0: } michael@0: michael@0: nsAppShell::nsAppShell() michael@0: : mNativeCallbackRequest(false) michael@0: , mEnableDraw(false) michael@0: , mHandlers() michael@0: { michael@0: gAppShell = this; michael@0: } michael@0: michael@0: nsAppShell::~nsAppShell() michael@0: { michael@0: // mReaderThread and mEventHub will both be null if InitInputDevices michael@0: // is not called. michael@0: if (mReaderThread.get()) { michael@0: // We separate requestExit() and join() here so we can wake the EventHub's michael@0: // input loop, and stop it from polling for input events michael@0: mReaderThread->requestExit(); michael@0: mEventHub->wake(); michael@0: michael@0: status_t result = mReaderThread->requestExitAndWait(); michael@0: if (result) michael@0: LOG("Could not stop reader thread - %d", result); michael@0: } michael@0: gAppShell = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsAppShell::Init() michael@0: { michael@0: nsresult rv = nsBaseAppShell::Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: epollfd = epoll_create(16); michael@0: NS_ENSURE_TRUE(epollfd >= 0, NS_ERROR_UNEXPECTED); michael@0: michael@0: int ret = pipe2(signalfds, O_NONBLOCK); michael@0: NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); michael@0: michael@0: rv = AddFdHandler(signalfds[0], pipeHandler, ""); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: InitGonkMemoryPressureMonitoring(); michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: #ifdef MOZ_OMX_DECODER michael@0: android::MediaResourceManagerService::instantiate(); michael@0: #endif michael@0: #if ANDROID_VERSION >= 18 && (defined(MOZ_OMX_DECODER) || defined(MOZ_B2G_CAMERA)) michael@0: android::FakeSurfaceComposer::instantiate(); michael@0: #endif michael@0: GonkPermissionService::instantiate(); michael@0: michael@0: // Causes the kernel timezone to be set, which in turn causes the michael@0: // timestamps on SD cards to have the local time rather than UTC time. michael@0: hal::SetTimezone(hal::GetTimezone()); michael@0: } michael@0: michael@0: nsCOMPtr obsServ = GetObserverService(); michael@0: if (obsServ) { michael@0: obsServ->AddObserver(this, "browser-ui-startup-complete", false); michael@0: obsServ->AddObserver(this, "network-connection-state-changed", false); michael@0: } michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: // Make sure main thread was woken up after Nuwa fork. michael@0: NuwaAddConstructor((void (*)(void *))&NotifyEvent, nullptr); michael@0: #endif michael@0: michael@0: // Delay initializing input devices until the screen has been michael@0: // initialized (and we know the resolution). michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAppShell::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!strcmp(aTopic, "network-connection-state-changed")) { michael@0: NS_ConvertUTF16toUTF8 type(aData); michael@0: if (!type.IsEmpty()) { michael@0: hal::NotifyNetworkChange(hal::NetworkInformation(atoi(type.get()), 0, 0)); michael@0: } michael@0: return NS_OK; michael@0: } else if (!strcmp(aTopic, "browser-ui-startup-complete")) { michael@0: if (sDevInputAudioJack) { michael@0: sHeadphoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_HEADPHONE_INSERT); michael@0: sMicrophoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_MICROPHONE_INSERT); michael@0: updateHeadphoneSwitch(); michael@0: } michael@0: mEnableDraw = true; michael@0: NotifyEvent(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return nsBaseAppShell::Observe(aSubject, aTopic, aData); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAppShell::Exit() michael@0: { michael@0: OrientationObserver::ShutDown(); michael@0: nsCOMPtr obsServ = GetObserverService(); michael@0: if (obsServ) { michael@0: obsServ->RemoveObserver(this, "browser-ui-startup-complete"); michael@0: obsServ->RemoveObserver(this, "network-connection-state-changed"); michael@0: } michael@0: return nsBaseAppShell::Exit(); michael@0: } michael@0: michael@0: void michael@0: nsAppShell::InitInputDevices() michael@0: { michael@0: char value[PROPERTY_VALUE_MAX]; michael@0: property_get("ro.moz.devinputjack", value, "0"); michael@0: sDevInputAudioJack = !strcmp(value, "1"); michael@0: sHeadphoneState = AKEY_STATE_UNKNOWN; michael@0: sMicrophoneState = AKEY_STATE_UNKNOWN; michael@0: michael@0: mEventHub = new EventHub(); michael@0: mReaderPolicy = new GeckoInputReaderPolicy(); michael@0: mReaderPolicy->setDisplayInfo(); michael@0: mDispatcher = new GeckoInputDispatcher(mEventHub); michael@0: michael@0: mReader = new InputReader(mEventHub, mReaderPolicy, mDispatcher); michael@0: mReaderThread = new InputReaderThread(mReader); michael@0: michael@0: status_t result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); michael@0: if (result) { michael@0: LOG("Failed to initialize InputReader thread, bad things are going to happen..."); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc, michael@0: const char* deviceName) michael@0: { michael@0: epoll_event event = { michael@0: EPOLLIN, michael@0: { 0 } michael@0: }; michael@0: michael@0: FdHandler *handler = mHandlers.AppendElement(); michael@0: handler->fd = fd; michael@0: strncpy(handler->name, deviceName, sizeof(handler->name) - 1); michael@0: handler->func = handlerFunc; michael@0: event.data.u32 = mHandlers.Length() - 1; michael@0: return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ? michael@0: NS_ERROR_UNEXPECTED : NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsAppShell::ScheduleNativeEventCallback() michael@0: { michael@0: mNativeCallbackRequest = true; michael@0: NotifyEvent(); michael@0: } michael@0: michael@0: bool michael@0: nsAppShell::ProcessNextNativeEvent(bool mayWait) michael@0: { michael@0: PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent"); michael@0: epoll_event events[16] = {{ 0 }}; michael@0: michael@0: int event_count; michael@0: { michael@0: PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent::Wait"); michael@0: if ((event_count = epoll_wait(epollfd, events, 16, mayWait ? -1 : 0)) <= 0) michael@0: return true; michael@0: } michael@0: michael@0: for (int i = 0; i < event_count; i++) michael@0: mHandlers[events[i].data.u32].run(); michael@0: michael@0: if (mDispatcher.get()) michael@0: mDispatcher->dispatchOnce(); michael@0: michael@0: // NativeEventCallback always schedules more if it needs it michael@0: // so we can coalesce these. michael@0: // See the implementation in nsBaseAppShell.cpp for more info michael@0: if (mNativeCallbackRequest) { michael@0: mNativeCallbackRequest = false; michael@0: NativeEventCallback(); michael@0: } michael@0: michael@0: if (gDrawRequest && mEnableDraw) { michael@0: gDrawRequest = false; michael@0: nsWindow::DoDraw(); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsAppShell::NotifyNativeEvent() michael@0: { michael@0: write(signalfds[1], "w", 1); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsAppShell::NotifyScreenInitialized() michael@0: { michael@0: gAppShell->InitInputDevices(); michael@0: michael@0: // Getting the instance of OrientationObserver to initialize it. michael@0: OrientationObserver::GetInstance(); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsAppShell::NotifyScreenRotation() michael@0: { michael@0: gAppShell->mReaderPolicy->setDisplayInfo(); michael@0: gAppShell->mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); michael@0: michael@0: hal::NotifyScreenConfigurationChange(nsScreenGonk::GetConfiguration()); michael@0: }