widget/gonk/nsAppShell.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:82985b5ce11d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 sw=4 sts=4 tw=80 et: */
3 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
21
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <sys/epoll.h>
27 #include <sys/ioctl.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <utils/BitSet.h>
33
34 #include "base/basictypes.h"
35 #include "GonkPermission.h"
36 #include "nscore.h"
37 #ifdef MOZ_OMX_DECODER
38 #include "MediaResourceManagerService.h"
39 #endif
40 #include "mozilla/TouchEvents.h"
41 #include "mozilla/FileUtils.h"
42 #include "mozilla/Hal.h"
43 #include "mozilla/MouseEvents.h"
44 #include "mozilla/Mutex.h"
45 #include "mozilla/Services.h"
46 #include "mozilla/TextEvents.h"
47 #if ANDROID_VERSION >= 18
48 #include "nativewindow/FakeSurfaceComposer.h"
49 #endif
50 #include "nsAppShell.h"
51 #include "mozilla/dom/Touch.h"
52 #include "nsGkAtoms.h"
53 #include "nsIObserverService.h"
54 #include "nsIScreen.h"
55 #include "nsScreenManagerGonk.h"
56 #include "nsThreadUtils.h"
57 #include "nsWindow.h"
58 #include "OrientationObserver.h"
59 #include "GonkMemoryPressureMonitoring.h"
60
61 #include "android/log.h"
62 #include "libui/EventHub.h"
63 #include "libui/InputReader.h"
64 #include "libui/InputDispatcher.h"
65 #include "cutils/properties.h"
66
67 #ifdef MOZ_NUWA_PROCESS
68 #include "ipc/Nuwa.h"
69 #endif
70
71 #include "GeckoProfiler.h"
72
73 // Defines kKeyMapping and GetKeyNameIndex()
74 #include "GonkKeyMapping.h"
75
76 #define LOG(args...) \
77 __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
78 #ifdef VERBOSE_LOG_ENABLED
79 # define VERBOSE_LOG(args...) \
80 __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
81 #else
82 # define VERBOSE_LOG(args...) \
83 (void)0
84 #endif
85
86 using namespace android;
87 using namespace mozilla;
88 using namespace mozilla::dom;
89 using namespace mozilla::services;
90 using namespace mozilla::widget;
91
92 bool gDrawRequest = false;
93 static nsAppShell *gAppShell = nullptr;
94 static int epollfd = 0;
95 static int signalfds[2] = {0};
96 static bool sDevInputAudioJack;
97 static int32_t sHeadphoneState;
98 static int32_t sMicrophoneState;
99
100 // Amount of time in MS before an input is considered expired.
101 static const uint64_t kInputExpirationThresholdMs = 1000;
102
103 NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver)
104
105 static uint64_t
106 nanosecsToMillisecs(nsecs_t nsecs)
107 {
108 return nsecs / 1000000;
109 }
110
111 namespace mozilla {
112
113 bool ProcessNextEvent()
114 {
115 return gAppShell->ProcessNextNativeEvent(true);
116 }
117
118 void NotifyEvent()
119 {
120 gAppShell->NotifyNativeEvent();
121 }
122
123 } // namespace mozilla
124
125 static void
126 pipeHandler(int fd, FdHandler *data)
127 {
128 ssize_t len;
129 do {
130 char tmp[32];
131 len = read(fd, tmp, sizeof(tmp));
132 } while (len > 0);
133 }
134
135 struct Touch {
136 int32_t id;
137 PointerCoords coords;
138 };
139
140 struct UserInputData {
141 uint64_t timeMs;
142 enum {
143 MOTION_DATA,
144 KEY_DATA
145 } type;
146 int32_t action;
147 int32_t flags;
148 int32_t metaState;
149 int32_t deviceId;
150 union {
151 struct {
152 int32_t keyCode;
153 int32_t scanCode;
154 } key;
155 struct {
156 int32_t touchCount;
157 ::Touch touches[MAX_POINTERS];
158 } motion;
159 };
160
161 Modifiers DOMModifiers() const;
162 };
163
164 Modifiers
165 UserInputData::DOMModifiers() const
166 {
167 Modifiers result = 0;
168 if (metaState & (AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
169 result |= MODIFIER_ALT;
170 }
171 if (metaState & (AMETA_SHIFT_ON |
172 AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
173 result |= MODIFIER_SHIFT;
174 }
175 if (metaState & AMETA_FUNCTION_ON) {
176 result |= MODIFIER_FN;
177 }
178 if (metaState & (AMETA_CTRL_ON |
179 AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
180 result |= MODIFIER_CONTROL;
181 }
182 if (metaState & (AMETA_META_ON |
183 AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
184 result |= MODIFIER_META;
185 }
186 if (metaState & AMETA_CAPS_LOCK_ON) {
187 result |= MODIFIER_CAPSLOCK;
188 }
189 if (metaState & AMETA_NUM_LOCK_ON) {
190 result |= MODIFIER_NUMLOCK;
191 }
192 if (metaState & AMETA_SCROLL_LOCK_ON) {
193 result |= MODIFIER_SCROLLLOCK;
194 }
195 return result;
196 }
197
198 static void
199 sendMouseEvent(uint32_t msg, UserInputData& data, bool forwardToChildren)
200 {
201 WidgetMouseEvent event(true, msg, nullptr,
202 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
203
204 event.refPoint.x = data.motion.touches[0].coords.getX();
205 event.refPoint.y = data.motion.touches[0].coords.getY();
206 event.time = data.timeMs;
207 event.button = WidgetMouseEvent::eLeftButton;
208 event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
209 if (msg != NS_MOUSE_MOVE)
210 event.clickCount = 1;
211 event.modifiers = data.DOMModifiers();
212
213 event.mFlags.mNoCrossProcessBoundaryForwarding = !forwardToChildren;
214
215 nsWindow::DispatchInputEvent(event);
216 }
217
218 static void
219 addDOMTouch(UserInputData& data, WidgetTouchEvent& event, int i)
220 {
221 const ::Touch& touch = data.motion.touches[i];
222 event.touches.AppendElement(
223 new dom::Touch(touch.id,
224 nsIntPoint(floor(touch.coords.getX() + 0.5), floor(touch.coords.getY() + 0.5)),
225 nsIntPoint(touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE),
226 touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE)),
227 0,
228 touch.coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE))
229 );
230 }
231
232 static nsEventStatus
233 sendTouchEvent(UserInputData& data, bool* captured)
234 {
235 uint32_t msg;
236 int32_t action = data.action & AMOTION_EVENT_ACTION_MASK;
237 switch (action) {
238 case AMOTION_EVENT_ACTION_DOWN:
239 case AMOTION_EVENT_ACTION_POINTER_DOWN:
240 msg = NS_TOUCH_START;
241 break;
242 case AMOTION_EVENT_ACTION_MOVE:
243 msg = NS_TOUCH_MOVE;
244 break;
245 case AMOTION_EVENT_ACTION_UP:
246 case AMOTION_EVENT_ACTION_POINTER_UP:
247 msg = NS_TOUCH_END;
248 break;
249 case AMOTION_EVENT_ACTION_OUTSIDE:
250 case AMOTION_EVENT_ACTION_CANCEL:
251 msg = NS_TOUCH_CANCEL;
252 break;
253 default:
254 msg = NS_EVENT_NULL;
255 break;
256 }
257
258 WidgetTouchEvent event(true, msg, nullptr);
259
260 event.time = data.timeMs;
261 event.modifiers = data.DOMModifiers();
262
263 int32_t i;
264 if (msg == NS_TOUCH_END) {
265 i = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK;
266 i >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
267 addDOMTouch(data, event, i);
268 } else {
269 for (i = 0; i < data.motion.touchCount; ++i)
270 addDOMTouch(data, event, i);
271 }
272
273 return nsWindow::DispatchInputEvent(event, captured);
274 }
275
276 class MOZ_STACK_CLASS KeyEventDispatcher
277 {
278 public:
279 KeyEventDispatcher(const UserInputData& aData,
280 KeyCharacterMap* aKeyCharMap);
281 void Dispatch();
282
283 private:
284 const UserInputData& mData;
285 sp<KeyCharacterMap> mKeyCharMap;
286
287 char16_t mChar;
288 char16_t mUnmodifiedChar;
289
290 uint32_t mDOMKeyCode;
291 KeyNameIndex mDOMKeyNameIndex;
292 char16_t mDOMPrintableKeyValue;
293
294 bool IsKeyPress() const
295 {
296 return mData.action == AKEY_EVENT_ACTION_DOWN;
297 }
298 bool IsRepeat() const
299 {
300 return IsKeyPress() && (mData.flags & AKEY_EVENT_FLAG_LONG_PRESS);
301 }
302
303 char16_t PrintableKeyValue() const;
304
305 int32_t UnmodifiedMetaState() const
306 {
307 return mData.metaState &
308 ~(AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON |
309 AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON |
310 AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
311 }
312
313 static bool IsControlChar(char16_t aChar)
314 {
315 return (aChar < ' ' || aChar == 0x7F);
316 }
317
318 void DispatchKeyDownEvent();
319 void DispatchKeyUpEvent();
320 nsEventStatus DispatchKeyEventInternal(uint32_t aEventMessage);
321 };
322
323 KeyEventDispatcher::KeyEventDispatcher(const UserInputData& aData,
324 KeyCharacterMap* aKeyCharMap) :
325 mData(aData), mKeyCharMap(aKeyCharMap), mChar(0), mUnmodifiedChar(0),
326 mDOMPrintableKeyValue(0)
327 {
328 // XXX Printable key's keyCode value should be computed with actual
329 // input character.
330 mDOMKeyCode = (mData.key.keyCode < (ssize_t)ArrayLength(kKeyMapping)) ?
331 kKeyMapping[mData.key.keyCode] : 0;
332 mDOMKeyNameIndex = GetKeyNameIndex(mData.key.keyCode);
333
334 if (!mKeyCharMap.get()) {
335 return;
336 }
337
338 mChar = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState);
339 if (IsControlChar(mChar)) {
340 mChar = 0;
341 }
342 int32_t unmodifiedMetaState = UnmodifiedMetaState();
343 if (mData.metaState == unmodifiedMetaState) {
344 mUnmodifiedChar = mChar;
345 } else {
346 mUnmodifiedChar = mKeyCharMap->getCharacter(mData.key.keyCode,
347 unmodifiedMetaState);
348 if (IsControlChar(mUnmodifiedChar)) {
349 mUnmodifiedChar = 0;
350 }
351 }
352
353 mDOMPrintableKeyValue = PrintableKeyValue();
354 }
355
356 char16_t
357 KeyEventDispatcher::PrintableKeyValue() const
358 {
359 if (mDOMKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
360 return 0;
361 }
362 return mChar ? mChar : mUnmodifiedChar;
363 }
364
365 nsEventStatus
366 KeyEventDispatcher::DispatchKeyEventInternal(uint32_t aEventMessage)
367 {
368 WidgetKeyboardEvent event(true, aEventMessage, nullptr);
369 if (aEventMessage == NS_KEY_PRESS) {
370 // XXX If the charCode is not a printable character, the charCode
371 // should be computed without Ctrl/Alt/Meta modifiers.
372 event.charCode = static_cast<uint32_t>(mChar);
373 }
374 if (!event.charCode) {
375 event.keyCode = mDOMKeyCode;
376 }
377 event.isChar = !!event.charCode;
378 event.mIsRepeat = IsRepeat();
379 event.mKeyNameIndex = mDOMKeyNameIndex;
380 if (mDOMPrintableKeyValue) {
381 event.mKeyValue = mDOMPrintableKeyValue;
382 }
383 event.modifiers = mData.DOMModifiers();
384 event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE;
385 event.time = mData.timeMs;
386 return nsWindow::DispatchInputEvent(event);
387 }
388
389 void
390 KeyEventDispatcher::Dispatch()
391 {
392 // XXX Even if unknown key is pressed, DOM key event should be
393 // dispatched since Gecko for the other platforms are implemented
394 // as so.
395 if (!mDOMKeyCode && mDOMKeyNameIndex == KEY_NAME_INDEX_Unidentified) {
396 VERBOSE_LOG("Got unknown key event code. "
397 "type 0x%04x code 0x%04x value %d",
398 mData.action, mData.key.keyCode, IsKeyPress());
399 return;
400 }
401
402 if (IsKeyPress()) {
403 DispatchKeyDownEvent();
404 } else {
405 DispatchKeyUpEvent();
406 }
407 }
408
409 void
410 KeyEventDispatcher::DispatchKeyDownEvent()
411 {
412 nsEventStatus status = DispatchKeyEventInternal(NS_KEY_DOWN);
413 if (status != nsEventStatus_eConsumeNoDefault) {
414 DispatchKeyEventInternal(NS_KEY_PRESS);
415 }
416 }
417
418 void
419 KeyEventDispatcher::DispatchKeyUpEvent()
420 {
421 DispatchKeyEventInternal(NS_KEY_UP);
422 }
423
424 class SwitchEventRunnable : public nsRunnable {
425 public:
426 SwitchEventRunnable(hal::SwitchEvent& aEvent) : mEvent(aEvent)
427 {}
428
429 NS_IMETHOD Run()
430 {
431 hal::NotifySwitchStateFromInputDevice(mEvent.device(),
432 mEvent.status());
433 return NS_OK;
434 }
435 private:
436 hal::SwitchEvent mEvent;
437 };
438
439 static void
440 updateHeadphoneSwitch()
441 {
442 hal::SwitchEvent event;
443
444 switch (sHeadphoneState) {
445 case AKEY_STATE_UP:
446 event.status() = hal::SWITCH_STATE_OFF;
447 break;
448 case AKEY_STATE_DOWN:
449 event.status() = sMicrophoneState == AKEY_STATE_DOWN ?
450 hal::SWITCH_STATE_HEADSET : hal::SWITCH_STATE_HEADPHONE;
451 break;
452 default:
453 return;
454 }
455
456 event.device() = hal::SWITCH_HEADPHONES;
457 NS_DispatchToMainThread(new SwitchEventRunnable(event));
458 }
459
460 class GeckoPointerController : public PointerControllerInterface {
461 float mX;
462 float mY;
463 int32_t mButtonState;
464 InputReaderConfiguration* mConfig;
465 public:
466 GeckoPointerController(InputReaderConfiguration* config)
467 : mX(0)
468 , mY(0)
469 , mButtonState(0)
470 , mConfig(config)
471 {}
472
473 virtual bool getBounds(float* outMinX, float* outMinY,
474 float* outMaxX, float* outMaxY) const;
475 virtual void move(float deltaX, float deltaY);
476 virtual void setButtonState(int32_t buttonState);
477 virtual int32_t getButtonState() const;
478 virtual void setPosition(float x, float y);
479 virtual void getPosition(float* outX, float* outY) const;
480 virtual void fade(Transition transition) {}
481 virtual void unfade(Transition transition) {}
482 virtual void setPresentation(Presentation presentation) {}
483 virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
484 BitSet32 spotIdBits) {}
485 virtual void clearSpots() {}
486 };
487
488 bool
489 GeckoPointerController::getBounds(float* outMinX,
490 float* outMinY,
491 float* outMaxX,
492 float* outMaxY) const
493 {
494 DisplayViewport viewport;
495
496 mConfig->getDisplayInfo(false, &viewport);
497
498 *outMinX = *outMinY = 0;
499 *outMaxX = viewport.logicalRight;
500 *outMaxY = viewport.logicalBottom;
501 return true;
502 }
503
504 void
505 GeckoPointerController::move(float deltaX, float deltaY)
506 {
507 float minX, minY, maxX, maxY;
508 getBounds(&minX, &minY, &maxX, &maxY);
509
510 mX = clamped(mX + deltaX, minX, maxX);
511 mY = clamped(mY + deltaY, minY, maxY);
512 }
513
514 void
515 GeckoPointerController::setButtonState(int32_t buttonState)
516 {
517 mButtonState = buttonState;
518 }
519
520 int32_t
521 GeckoPointerController::getButtonState() const
522 {
523 return mButtonState;
524 }
525
526 void
527 GeckoPointerController::setPosition(float x, float y)
528 {
529 mX = x;
530 mY = y;
531 }
532
533 void
534 GeckoPointerController::getPosition(float* outX, float* outY) const
535 {
536 *outX = mX;
537 *outY = mY;
538 }
539
540 class GeckoInputReaderPolicy : public InputReaderPolicyInterface {
541 InputReaderConfiguration mConfig;
542 public:
543 GeckoInputReaderPolicy() {}
544
545 virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
546 virtual sp<PointerControllerInterface> obtainPointerController(int32_t
547 deviceId)
548 {
549 return new GeckoPointerController(&mConfig);
550 };
551 virtual void notifyInputDevicesChanged(const android::Vector<InputDeviceInfo>& inputDevices) {};
552 virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor)
553 {
554 return nullptr;
555 };
556 virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier)
557 {
558 return String8::empty();
559 };
560
561 void setDisplayInfo();
562
563 protected:
564 virtual ~GeckoInputReaderPolicy() {}
565 };
566
567 class GeckoInputDispatcher : public InputDispatcherInterface {
568 public:
569 GeckoInputDispatcher(sp<EventHub> &aEventHub)
570 : mQueueLock("GeckoInputDispatcher::mQueueMutex")
571 , mEventHub(aEventHub)
572 , mTouchDownCount(0)
573 , mKeyDownCount(0)
574 , mTouchEventsFiltered(false)
575 , mKeyEventsFiltered(false)
576 {}
577
578 virtual void dump(String8& dump);
579
580 virtual void monitor() {}
581
582 // Called on the main thread
583 virtual void dispatchOnce();
584
585 // notify* methods are called on the InputReaderThread
586 virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
587 virtual void notifyKey(const NotifyKeyArgs* args);
588 virtual void notifyMotion(const NotifyMotionArgs* args);
589 virtual void notifySwitch(const NotifySwitchArgs* args);
590 virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
591
592 virtual int32_t injectInputEvent(const InputEvent* event,
593 int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
594 uint32_t policyFlags);
595
596 virtual void setInputWindows(const android::Vector<sp<InputWindowHandle> >& inputWindowHandles);
597 virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
598
599 virtual void setInputDispatchMode(bool enabled, bool frozen);
600 virtual void setInputFilterEnabled(bool enabled) {}
601 virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
602 const sp<InputChannel>& toChannel) { return true; }
603
604 virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
605 const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
606 virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
607
608
609
610 protected:
611 virtual ~GeckoInputDispatcher() {}
612
613 private:
614 // mQueueLock should generally be locked while using mEventQueue.
615 // UserInputData is pushed on on the InputReaderThread and
616 // popped and dispatched on the main thread.
617 mozilla::Mutex mQueueLock;
618 std::queue<UserInputData> mEventQueue;
619 sp<EventHub> mEventHub;
620
621 int mTouchDownCount;
622 int mKeyDownCount;
623 bool mTouchEventsFiltered;
624 bool mKeyEventsFiltered;
625 BitSet32 mTouchDown;
626 };
627
628 // GeckoInputReaderPolicy
629 void
630 GeckoInputReaderPolicy::setDisplayInfo()
631 {
632 static_assert(nsIScreen::ROTATION_0_DEG ==
633 DISPLAY_ORIENTATION_0,
634 "Orientation enums not matched!");
635 static_assert(nsIScreen::ROTATION_90_DEG ==
636 DISPLAY_ORIENTATION_90,
637 "Orientation enums not matched!");
638 static_assert(nsIScreen::ROTATION_180_DEG ==
639 DISPLAY_ORIENTATION_180,
640 "Orientation enums not matched!");
641 static_assert(nsIScreen::ROTATION_270_DEG ==
642 DISPLAY_ORIENTATION_270,
643 "Orientation enums not matched!");
644
645 DisplayViewport viewport;
646 viewport.displayId = 0;
647 viewport.orientation = nsScreenGonk::GetRotation();
648 viewport.physicalRight = viewport.deviceWidth = gScreenBounds.width;
649 viewport.physicalBottom = viewport.deviceHeight = gScreenBounds.height;
650 if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
651 viewport.orientation == DISPLAY_ORIENTATION_270) {
652 viewport.logicalRight = gScreenBounds.height;
653 viewport.logicalBottom = gScreenBounds.width;
654 } else {
655 viewport.logicalRight = gScreenBounds.width;
656 viewport.logicalBottom = gScreenBounds.height;
657 }
658 mConfig.setDisplayInfo(false, viewport);
659 }
660
661 void GeckoInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig)
662 {
663 *outConfig = mConfig;
664 }
665
666
667 // GeckoInputDispatcher
668 void
669 GeckoInputDispatcher::dump(String8& dump)
670 {
671 }
672
673 static bool
674 isExpired(const UserInputData& data)
675 {
676 uint64_t timeNowMs =
677 nanosecsToMillisecs(systemTime(SYSTEM_TIME_MONOTONIC));
678 return (timeNowMs - data.timeMs) > kInputExpirationThresholdMs;
679 }
680
681 void
682 GeckoInputDispatcher::dispatchOnce()
683 {
684 UserInputData data;
685 {
686 MutexAutoLock lock(mQueueLock);
687 if (mEventQueue.empty())
688 return;
689 data = mEventQueue.front();
690 mEventQueue.pop();
691 if (!mEventQueue.empty())
692 gAppShell->NotifyNativeEvent();
693 }
694
695 switch (data.type) {
696 case UserInputData::MOTION_DATA: {
697 if (!mTouchDownCount) {
698 // No pending events, the filter state can be updated.
699 mTouchEventsFiltered = isExpired(data);
700 }
701
702 int32_t action = data.action & AMOTION_EVENT_ACTION_MASK;
703 int32_t index = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK;
704 index >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
705 switch (action) {
706 case AMOTION_EVENT_ACTION_DOWN:
707 case AMOTION_EVENT_ACTION_POINTER_DOWN:
708 if (!mTouchDown.hasBit(index)) {
709 mTouchDown.markBit(index);
710 mTouchDownCount++;
711 }
712 break;
713 case AMOTION_EVENT_ACTION_MOVE:
714 case AMOTION_EVENT_ACTION_HOVER_MOVE:
715 // No need to update the count on move.
716 break;
717 case AMOTION_EVENT_ACTION_UP:
718 case AMOTION_EVENT_ACTION_POINTER_UP:
719 case AMOTION_EVENT_ACTION_OUTSIDE:
720 case AMOTION_EVENT_ACTION_CANCEL:
721 if (mTouchDown.hasBit(index)) {
722 mTouchDown.clearBit(index);
723 mTouchDownCount--;
724 }
725 break;
726 default:
727 break;
728 }
729
730 if (mTouchEventsFiltered) {
731 return;
732 }
733
734 nsEventStatus status = nsEventStatus_eIgnore;
735 if (action != AMOTION_EVENT_ACTION_HOVER_MOVE) {
736 bool captured;
737 status = sendTouchEvent(data, &captured);
738 if (captured) {
739 return;
740 }
741 }
742
743 uint32_t msg;
744 switch (action) {
745 case AMOTION_EVENT_ACTION_DOWN:
746 msg = NS_MOUSE_BUTTON_DOWN;
747 break;
748 case AMOTION_EVENT_ACTION_POINTER_DOWN:
749 case AMOTION_EVENT_ACTION_POINTER_UP:
750 case AMOTION_EVENT_ACTION_MOVE:
751 case AMOTION_EVENT_ACTION_HOVER_MOVE:
752 msg = NS_MOUSE_MOVE;
753 break;
754 case AMOTION_EVENT_ACTION_OUTSIDE:
755 case AMOTION_EVENT_ACTION_CANCEL:
756 case AMOTION_EVENT_ACTION_UP:
757 msg = NS_MOUSE_BUTTON_UP;
758 break;
759 default:
760 msg = NS_EVENT_NULL;
761 break;
762 }
763 if (msg != NS_EVENT_NULL) {
764 sendMouseEvent(msg, data,
765 status != nsEventStatus_eConsumeNoDefault);
766 }
767 break;
768 }
769 case UserInputData::KEY_DATA: {
770 if (!mKeyDownCount) {
771 // No pending events, the filter state can be updated.
772 mKeyEventsFiltered = isExpired(data);
773 }
774
775 mKeyDownCount += (data.action == AKEY_EVENT_ACTION_DOWN) ? 1 : -1;
776 if (mKeyEventsFiltered) {
777 return;
778 }
779
780 sp<KeyCharacterMap> kcm = mEventHub->getKeyCharacterMap(data.deviceId);
781 KeyEventDispatcher dispatcher(data, kcm.get());
782 dispatcher.Dispatch();
783 break;
784 }
785 }
786 }
787
788 void
789 GeckoInputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs*)
790 {
791 }
792
793 void
794 GeckoInputDispatcher::notifyKey(const NotifyKeyArgs* args)
795 {
796 UserInputData data;
797 data.timeMs = nanosecsToMillisecs(args->eventTime);
798 data.type = UserInputData::KEY_DATA;
799 data.action = args->action;
800 data.flags = args->flags;
801 data.metaState = args->metaState;
802 data.deviceId = args->deviceId;
803 data.key.keyCode = args->keyCode;
804 data.key.scanCode = args->scanCode;
805 {
806 MutexAutoLock lock(mQueueLock);
807 mEventQueue.push(data);
808 }
809 gAppShell->NotifyNativeEvent();
810 }
811
812
813 void
814 GeckoInputDispatcher::notifyMotion(const NotifyMotionArgs* args)
815 {
816 UserInputData data;
817 data.timeMs = nanosecsToMillisecs(args->eventTime);
818 data.type = UserInputData::MOTION_DATA;
819 data.action = args->action;
820 data.flags = args->flags;
821 data.metaState = args->metaState;
822 data.deviceId = args->deviceId;
823 MOZ_ASSERT(args->pointerCount <= MAX_POINTERS);
824 data.motion.touchCount = args->pointerCount;
825 for (uint32_t i = 0; i < args->pointerCount; ++i) {
826 ::Touch& touch = data.motion.touches[i];
827 touch.id = args->pointerProperties[i].id;
828 memcpy(&touch.coords, &args->pointerCoords[i], sizeof(*args->pointerCoords));
829 }
830 {
831 MutexAutoLock lock(mQueueLock);
832 if (!mEventQueue.empty() &&
833 mEventQueue.back().type == UserInputData::MOTION_DATA &&
834 ((mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) ==
835 AMOTION_EVENT_ACTION_MOVE ||
836 (mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) ==
837 AMOTION_EVENT_ACTION_HOVER_MOVE))
838 mEventQueue.back() = data;
839 else
840 mEventQueue.push(data);
841 }
842 gAppShell->NotifyNativeEvent();
843 }
844
845
846
847 void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args)
848 {
849 if (!sDevInputAudioJack)
850 return;
851
852 bool needSwitchUpdate = false;
853
854 if (args->switchMask & (1 << SW_HEADPHONE_INSERT)) {
855 sHeadphoneState = (args->switchValues & (1 << SW_HEADPHONE_INSERT)) ?
856 AKEY_STATE_DOWN : AKEY_STATE_UP;
857 needSwitchUpdate = true;
858 }
859
860 if (args->switchMask & (1 << SW_MICROPHONE_INSERT)) {
861 sMicrophoneState = (args->switchValues & (1 << SW_MICROPHONE_INSERT)) ?
862 AKEY_STATE_DOWN : AKEY_STATE_UP;
863 needSwitchUpdate = true;
864 }
865
866 if (needSwitchUpdate)
867 updateHeadphoneSwitch();
868 }
869
870 void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args)
871 {
872 }
873
874 int32_t GeckoInputDispatcher::injectInputEvent(
875 const InputEvent* event,
876 int32_t injectorPid, int32_t injectorUid, int32_t syncMode,
877 int32_t timeoutMillis, uint32_t policyFlags)
878 {
879 return INPUT_EVENT_INJECTION_SUCCEEDED;
880 }
881
882 void
883 GeckoInputDispatcher::setInputWindows(const android::Vector<sp<InputWindowHandle> >& inputWindowHandles)
884 {
885 }
886
887 void
888 GeckoInputDispatcher::setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle)
889 {
890 }
891
892 void
893 GeckoInputDispatcher::setInputDispatchMode(bool enabled, bool frozen)
894 {
895 }
896
897 status_t
898 GeckoInputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
899 const sp<InputWindowHandle>& inputWindowHandle, bool monitor)
900 {
901 return OK;
902 }
903
904 status_t
905 GeckoInputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel)
906 {
907 return OK;
908 }
909
910 nsAppShell::nsAppShell()
911 : mNativeCallbackRequest(false)
912 , mEnableDraw(false)
913 , mHandlers()
914 {
915 gAppShell = this;
916 }
917
918 nsAppShell::~nsAppShell()
919 {
920 // mReaderThread and mEventHub will both be null if InitInputDevices
921 // is not called.
922 if (mReaderThread.get()) {
923 // We separate requestExit() and join() here so we can wake the EventHub's
924 // input loop, and stop it from polling for input events
925 mReaderThread->requestExit();
926 mEventHub->wake();
927
928 status_t result = mReaderThread->requestExitAndWait();
929 if (result)
930 LOG("Could not stop reader thread - %d", result);
931 }
932 gAppShell = nullptr;
933 }
934
935 nsresult
936 nsAppShell::Init()
937 {
938 nsresult rv = nsBaseAppShell::Init();
939 NS_ENSURE_SUCCESS(rv, rv);
940
941 epollfd = epoll_create(16);
942 NS_ENSURE_TRUE(epollfd >= 0, NS_ERROR_UNEXPECTED);
943
944 int ret = pipe2(signalfds, O_NONBLOCK);
945 NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED);
946
947 rv = AddFdHandler(signalfds[0], pipeHandler, "");
948 NS_ENSURE_SUCCESS(rv, rv);
949
950 InitGonkMemoryPressureMonitoring();
951
952 if (XRE_GetProcessType() == GeckoProcessType_Default) {
953 #ifdef MOZ_OMX_DECODER
954 android::MediaResourceManagerService::instantiate();
955 #endif
956 #if ANDROID_VERSION >= 18 && (defined(MOZ_OMX_DECODER) || defined(MOZ_B2G_CAMERA))
957 android::FakeSurfaceComposer::instantiate();
958 #endif
959 GonkPermissionService::instantiate();
960
961 // Causes the kernel timezone to be set, which in turn causes the
962 // timestamps on SD cards to have the local time rather than UTC time.
963 hal::SetTimezone(hal::GetTimezone());
964 }
965
966 nsCOMPtr<nsIObserverService> obsServ = GetObserverService();
967 if (obsServ) {
968 obsServ->AddObserver(this, "browser-ui-startup-complete", false);
969 obsServ->AddObserver(this, "network-connection-state-changed", false);
970 }
971
972 #ifdef MOZ_NUWA_PROCESS
973 // Make sure main thread was woken up after Nuwa fork.
974 NuwaAddConstructor((void (*)(void *))&NotifyEvent, nullptr);
975 #endif
976
977 // Delay initializing input devices until the screen has been
978 // initialized (and we know the resolution).
979 return rv;
980 }
981
982 NS_IMETHODIMP
983 nsAppShell::Observe(nsISupports* aSubject,
984 const char* aTopic,
985 const char16_t* aData)
986 {
987 if (!strcmp(aTopic, "network-connection-state-changed")) {
988 NS_ConvertUTF16toUTF8 type(aData);
989 if (!type.IsEmpty()) {
990 hal::NotifyNetworkChange(hal::NetworkInformation(atoi(type.get()), 0, 0));
991 }
992 return NS_OK;
993 } else if (!strcmp(aTopic, "browser-ui-startup-complete")) {
994 if (sDevInputAudioJack) {
995 sHeadphoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_HEADPHONE_INSERT);
996 sMicrophoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_MICROPHONE_INSERT);
997 updateHeadphoneSwitch();
998 }
999 mEnableDraw = true;
1000 NotifyEvent();
1001 return NS_OK;
1002 }
1003
1004 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
1005 }
1006
1007 NS_IMETHODIMP
1008 nsAppShell::Exit()
1009 {
1010 OrientationObserver::ShutDown();
1011 nsCOMPtr<nsIObserverService> obsServ = GetObserverService();
1012 if (obsServ) {
1013 obsServ->RemoveObserver(this, "browser-ui-startup-complete");
1014 obsServ->RemoveObserver(this, "network-connection-state-changed");
1015 }
1016 return nsBaseAppShell::Exit();
1017 }
1018
1019 void
1020 nsAppShell::InitInputDevices()
1021 {
1022 char value[PROPERTY_VALUE_MAX];
1023 property_get("ro.moz.devinputjack", value, "0");
1024 sDevInputAudioJack = !strcmp(value, "1");
1025 sHeadphoneState = AKEY_STATE_UNKNOWN;
1026 sMicrophoneState = AKEY_STATE_UNKNOWN;
1027
1028 mEventHub = new EventHub();
1029 mReaderPolicy = new GeckoInputReaderPolicy();
1030 mReaderPolicy->setDisplayInfo();
1031 mDispatcher = new GeckoInputDispatcher(mEventHub);
1032
1033 mReader = new InputReader(mEventHub, mReaderPolicy, mDispatcher);
1034 mReaderThread = new InputReaderThread(mReader);
1035
1036 status_t result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
1037 if (result) {
1038 LOG("Failed to initialize InputReader thread, bad things are going to happen...");
1039 }
1040 }
1041
1042 nsresult
1043 nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc,
1044 const char* deviceName)
1045 {
1046 epoll_event event = {
1047 EPOLLIN,
1048 { 0 }
1049 };
1050
1051 FdHandler *handler = mHandlers.AppendElement();
1052 handler->fd = fd;
1053 strncpy(handler->name, deviceName, sizeof(handler->name) - 1);
1054 handler->func = handlerFunc;
1055 event.data.u32 = mHandlers.Length() - 1;
1056 return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ?
1057 NS_ERROR_UNEXPECTED : NS_OK;
1058 }
1059
1060 void
1061 nsAppShell::ScheduleNativeEventCallback()
1062 {
1063 mNativeCallbackRequest = true;
1064 NotifyEvent();
1065 }
1066
1067 bool
1068 nsAppShell::ProcessNextNativeEvent(bool mayWait)
1069 {
1070 PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent");
1071 epoll_event events[16] = {{ 0 }};
1072
1073 int event_count;
1074 {
1075 PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent::Wait");
1076 if ((event_count = epoll_wait(epollfd, events, 16, mayWait ? -1 : 0)) <= 0)
1077 return true;
1078 }
1079
1080 for (int i = 0; i < event_count; i++)
1081 mHandlers[events[i].data.u32].run();
1082
1083 if (mDispatcher.get())
1084 mDispatcher->dispatchOnce();
1085
1086 // NativeEventCallback always schedules more if it needs it
1087 // so we can coalesce these.
1088 // See the implementation in nsBaseAppShell.cpp for more info
1089 if (mNativeCallbackRequest) {
1090 mNativeCallbackRequest = false;
1091 NativeEventCallback();
1092 }
1093
1094 if (gDrawRequest && mEnableDraw) {
1095 gDrawRequest = false;
1096 nsWindow::DoDraw();
1097 }
1098
1099 return true;
1100 }
1101
1102 void
1103 nsAppShell::NotifyNativeEvent()
1104 {
1105 write(signalfds[1], "w", 1);
1106 }
1107
1108 /* static */ void
1109 nsAppShell::NotifyScreenInitialized()
1110 {
1111 gAppShell->InitInputDevices();
1112
1113 // Getting the instance of OrientationObserver to initialize it.
1114 OrientationObserver::GetInstance();
1115 }
1116
1117 /* static */ void
1118 nsAppShell::NotifyScreenRotation()
1119 {
1120 gAppShell->mReaderPolicy->setDisplayInfo();
1121 gAppShell->mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
1122
1123 hal::NotifyScreenConfigurationChange(nsScreenGonk::GetConfiguration());
1124 }

mercurial