blob: 939529f7740316fb32649f6672e91de6389a57b1 [file] [log] [blame]
/*
* 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 <android-base/thread_annotations.h>
#include <gui/WindowInfosListener.h>
#include <type_traits>
#include <unordered_set>
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 <typename Factory>
struct ConstructorDelegate {
constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {}
using ConstructedType = std::invoke_result_t<const Factory&>;
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<DisplayViewport>& viewports) = 0;
virtual std::optional<DisplayViewport> 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<std::unique_ptr<SpriteIcon>, 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<DisplayViewport>& viewports) override;
std::optional<DisplayViewport> 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<std::unique_ptr<SpriteIcon>, 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<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
displayTopology);
void dump(std::string& dump) override;
private:
using PointerDisplayChange =
std::optional<std::tuple<ui::LogicalDisplayId /*displayId*/, vec2 /*cursorPosition*/>>;
// 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<ui::LogicalDisplayId /*displayId*/, PointerControllerInterface&>
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<ui::LogicalDisplayId>& privacySensitiveDisplays)
REQUIRES(getLock());
void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
REQUIRES(getLock());
void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
std::optional<std::pair<const DisplayViewport*, float /*offset*/>> 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<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> 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<gui::WindowInfo>& windowInfos)
REQUIRES(mLock);
std::unordered_set<ui::LogicalDisplayId> 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<ui::LogicalDisplayId /*displayId*/> mPrivacySensitiveDisplays
GUARDED_BY(mLock);
};
using ControllerConstructor =
ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
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<ui::LogicalDisplayId, std::shared_ptr<PointerControllerInterface>>
mMousePointersByDisplay GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
GUARDED_BY(getLock());
ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
bool mShowTouchesEnabled GUARDED_BY(getLock());
bool mStylusPointerIconEnabled GUARDED_BY(getLock());
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
protected:
using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>(
const sp<android::gui::WindowInfosListener>&)>;
using WindowListenerUnregisterConsumer =
std::function<void(const sp<android::gui::WindowInfosListener>&)>;
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<PointerChoreographerDisplayInfoListener> mWindowInfoListener;
const WindowListenerRegisterConsumer mRegisterListener;
const WindowListenerUnregisterConsumer mUnregisterListener;
};
} // namespace android