/* * Copyright 2023 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. */ #pragma once #include "InputListener.h" #include "NotifyArgs.h" #include "PointerChoreographerPolicyInterface.h" #include #include #include #include namespace android { struct SpriteIcon; /** * A helper class that wraps a factory method that acts as a constructor for the type returned * by the factory method. */ template struct ConstructorDelegate { constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {} using ConstructedType = std::invoke_result_t; constexpr operator ConstructedType() const { return mFactory(); } Factory mFactory; }; /** * PointerChoreographer manages the icons shown by the system for input interactions. * This includes showing the mouse cursor, stylus hover icons, and touch spots. * It is responsible for accumulating the location of the mouse cursor, and populating * the cursor position for incoming events, if necessary. */ class PointerChoreographerInterface : public InputListenerInterface { public: /** * Set the display that pointers, like the mouse cursor and drawing tablets, * should be drawn on. */ virtual void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) = 0; virtual void setDisplayViewports(const std::vector& viewports) = 0; virtual std::optional getViewportForPointerDevice( ui::LogicalDisplayId associatedDisplayId = ui::LogicalDisplayId::INVALID) = 0; virtual vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId) = 0; virtual void setShowTouchesEnabled(bool enabled) = 0; virtual void setStylusPointerIconEnabled(bool enabled) = 0; /** * Set the icon that is shown for the given pointer. The request may fail in some cases, such * as if the device or display was removed, or if the cursor was moved to a different display. * Returns true if the icon was changed successfully, false otherwise. */ virtual bool setPointerIcon(std::variant, PointerIconStyle> icon, ui::LogicalDisplayId displayId, DeviceId deviceId) = 0; /** * Set whether pointer icons for mice, touchpads, and styluses should be visible on the * given display. */ virtual void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) = 0; /** * Used by Dispatcher to notify changes in the current focused display. */ virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0; /** * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; }; class PointerChoreographer : public PointerChoreographerInterface { public: explicit PointerChoreographer(InputListenerInterface& listener, PointerChoreographerPolicyInterface&); ~PointerChoreographer() override; void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) override; void setDisplayViewports(const std::vector& viewports) override; std::optional getViewportForPointerDevice( ui::LogicalDisplayId associatedDisplayId) override; vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId) override; void setShowTouchesEnabled(bool enabled) override; void setStylusPointerIconEnabled(bool enabled) override; bool setPointerIcon(std::variant, PointerIconStyle> icon, ui::LogicalDisplayId displayId, DeviceId deviceId) override; void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override; void setFocusedDisplay(ui::LogicalDisplayId displayId) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; void notifyMotion(const NotifyMotionArgs& args) override; void notifySwitch(const NotifySwitchArgs& args) override; void notifySensor(const NotifySensorArgs& args) override; void notifyVibratorState(const NotifyVibratorStateArgs& args) override; void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; // TODO(b/362719483) remove these when real topology is available enum class DisplayPosition { RIGHT, TOP, LEFT, BOTTOM, ftl_last = BOTTOM, }; struct AdjacentDisplay { ui::LogicalDisplayId displayId; DisplayPosition position; float offsetPx; }; void setDisplayTopology( const std::unordered_map>& displayTopology); void dump(std::string& dump) override; private: using PointerDisplayChange = std::optional>; // PointerChoreographer's DisplayInfoListener can outlive the PointerChoreographer because when // the listener is registered and called from display thread, a strong pointer to the listener // (which can extend its lifecycle) is given away. // If we use two locks it can also cause deadlocks due to race in acquiring them between the // display and reader thread. // To avoid these problems we use DisplayInfoListener's lock in PointerChoreographer. std::mutex& getLock() const; [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(getLock()); [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(getLock()); const DisplayViewport* findViewportByIdLocked(ui::LogicalDisplayId displayId) const REQUIRES(getLock()); ui::LogicalDisplayId getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const REQUIRES(getLock()); std::pair ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(getLock()); InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(getLock()); bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(getLock()); void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock()); NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock()); void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock()); void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock()); void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock()); void processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs, PointerControllerInterface& pc) REQUIRES(getLock()); void processDeviceReset(const NotifyDeviceResetArgs& args); void onControllerAddedOrRemovedLocked() REQUIRES(getLock()); void onPrivacySensitiveDisplaysChangedLocked( const std::unordered_set& privacySensitiveDisplays) REQUIRES(getLock()); void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta) REQUIRES(getLock()); void populateFakeDisplayTopologyLocked(const std::vector& displayInfos) REQUIRES(getLock()); std::optional> findDestinationDisplayLocked( const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary, float cursorOffset) const REQUIRES(getLock()); static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport, float pointerOffset, DisplayPosition sourceBoundary); std::unordered_map> mTopology GUARDED_BY(getLock()); /* This listener keeps tracks of visible privacy sensitive displays and updates the * choreographer if there are any changes. * * Listener uses mListenerLock to guard all private data as choreographer and SurfaceComposer * both can call into the listener. To prevent deadlocks Choreographer can call listener with * its lock held, but listener must not call choreographer with its lock. */ class PointerChoreographerDisplayInfoListener : public gui::WindowInfosListener { public: explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc) : mPointerChoreographer(pc){}; void onWindowInfosChanged(const gui::WindowInfosUpdate&) override; void setInitialDisplayInfosLocked(const std::vector& windowInfos) REQUIRES(mLock); std::unordered_set getPrivacySensitiveDisplaysLocked() REQUIRES(mLock); void onPointerChoreographerDestroyed(); // This lock is also used by PointerChoreographer. See PointerChoreographer::getLock(). std::mutex mLock; private: PointerChoreographer* mPointerChoreographer GUARDED_BY(mLock); std::unordered_set mPrivacySensitiveDisplays GUARDED_BY(mLock); }; using ControllerConstructor = ConstructorDelegate()>>; ControllerConstructor mTouchControllerConstructor GUARDED_BY(getLock()); ControllerConstructor getMouseControllerConstructor(ui::LogicalDisplayId displayId) REQUIRES(getLock()); ControllerConstructor getStylusControllerConstructor(ui::LogicalDisplayId displayId) REQUIRES(getLock()); InputListenerInterface& mNextListener; PointerChoreographerPolicyInterface& mPolicy; std::map> mMousePointersByDisplay GUARDED_BY(getLock()); std::map> mTouchPointersByDevice GUARDED_BY(getLock()); std::map> mStylusPointersByDevice GUARDED_BY(getLock()); std::map> mDrawingTabletPointersByDevice GUARDED_BY(getLock()); ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock()); ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock()); std::vector mInputDeviceInfos GUARDED_BY(getLock()); std::set mMouseDevices GUARDED_BY(getLock()); std::vector mViewports GUARDED_BY(getLock()); bool mShowTouchesEnabled GUARDED_BY(getLock()); bool mStylusPointerIconEnabled GUARDED_BY(getLock()); std::set mDisplaysWithPointersHidden; ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock()); protected: using WindowListenerRegisterConsumer = std::function( const sp&)>; using WindowListenerUnregisterConsumer = std::function&)>; explicit PointerChoreographer(InputListenerInterface& listener, PointerChoreographerPolicyInterface&, const WindowListenerRegisterConsumer& registerListener, const WindowListenerUnregisterConsumer& unregisterListener); private: // WindowInfoListener object should always exist while PointerChoreographer exists, because we // need to use the lock from it. But we don't always need to register the listener. bool mIsWindowInfoListenerRegistered GUARDED_BY(getLock()); const sp mWindowInfoListener; const WindowListenerRegisterConsumer mRegisterListener; const WindowListenerUnregisterConsumer mUnregisterListener; }; } // namespace android