Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d64ad04 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge changes from topic "pointer-icon-refactor-mouse" into main

* changes:
  Address internal comments: Pointer icon refactor for mouse
  Pointer icon refactor for mouse
parents 2891b69e 1976760e
Loading
Loading
Loading
Loading
+212 −2
Original line number Diff line number Diff line
@@ -16,17 +16,37 @@

#define LOG_TAG "PointerChoreographer"

#include <android-base/logging.h>
#include <input/PrintTools.h>

#include "PointerChoreographer.h"

#define INDENT "  "

namespace android {

namespace {
bool isFromMouse(const NotifyMotionArgs& args) {
    return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
            args.pointerProperties[0].toolType == ToolType::MOUSE;
}

} // namespace

// --- PointerChoreographer ---

PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
                                           PointerChoreographerPolicyInterface& policy)
      : mNextListener(listener) {}
      : mNextListener(listener),
        mPolicy(policy),
        mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
        mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {}

void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
    std::scoped_lock _l(mLock);

    mInputDeviceInfos = args.inputDeviceInfos;
    updatePointerControllersLocked();
    mNextListener.notify(args);
}

@@ -39,7 +59,67 @@ void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
}

void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
    mNextListener.notify(args);
    NotifyMotionArgs newArgs = processMotion(args);

    mNextListener.notify(newArgs);
}

NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
    std::scoped_lock _l(mLock);

    if (isFromMouse(args)) {
        return processMouseEventLocked(args);
    } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
        return processTouchscreenEventLocked(args);
    }
    return args;
}

NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
    if (args.getPointerCount() != 1) {
        LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
                   << args.dump();
    }

    const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);

    // Get the mouse pointer controller for the display, or create one if it doesn't exist.
    auto [it, emplaced] =
            mMousePointersByDisplay.try_emplace(displayId,
                                                getMouseControllerConstructor(displayId));
    if (emplaced) {
        notifyPointerDisplayIdChangedLocked();
    }

    PointerControllerInterface& pc = *it->second;

    const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
    const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
    pc.move(deltaX, deltaY);
    pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);

    const auto [x, y] = pc.getPosition();
    NotifyMotionArgs newArgs(args);
    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
    newArgs.xCursorPosition = x;
    newArgs.yCursorPosition = y;
    newArgs.displayId = displayId;
    return newArgs;
}

/**
 * When screen is touched, fade the mouse pointer on that display. We only call fade for
 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
 * mouse device keeps moving and unfades the cursor.
 * For touch events, we do not need to populate the cursor position.
 */
NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) {
    if (const auto it = mMousePointersByDisplay.find(args.displayId);
        it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
        it->second->fade(PointerControllerInterface::Transition::GRADUAL);
    }
    return args;
}

void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
@@ -60,11 +140,141 @@ void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args)

void PointerChoreographer::notifyPointerCaptureChanged(
        const NotifyPointerCaptureChangedArgs& args) {
    if (args.request.enable) {
        std::scoped_lock _l(mLock);
        for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
            mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
        }
    }
    mNextListener.notify(args);
}

void PointerChoreographer::dump(std::string& dump) {
    std::scoped_lock _l(mLock);

    dump += "PointerChoreographer:\n";

    dump += INDENT "MousePointerControllers:\n";
    for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
        std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
        dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
    }
    dump += "\n";
}

const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
    for (auto& viewport : mViewports) {
        if (viewport.displayId == displayId) {
            return &viewport;
        }
    }
    return nullptr;
}

int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
    return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
}

void PointerChoreographer::updatePointerControllersLocked() {
    std::set<int32_t /*displayId*/> mouseDisplaysToKeep;

    // Mark the displayIds or deviceIds of PointerControllers currently needed.
    for (const auto& info : mInputDeviceInfos) {
        const uint32_t sources = info.getSources();
        if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
            isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
            const int32_t resolvedDisplayId =
                    getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
            mouseDisplaysToKeep.insert(resolvedDisplayId);
        }
    }

    // Remove PointerControllers no longer needed.
    // This has the side-effect of fading pointers or clearing spots before removal.
    std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
        auto& [displayId, controller] = pair;
        if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) {
            controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
            return true;
        }
        return false;
    });

    // Notify the policy if there's a change on the pointer display ID.
    notifyPointerDisplayIdChangedLocked();
}

void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
    int32_t displayIdToNotify = ADISPLAY_ID_NONE;
    FloatPoint cursorPosition = {0, 0};
    if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
        it != mMousePointersByDisplay.end()) {
        const auto& pointerController = it->second;
        // Use the displayId from the pointerController, because it accurately reflects whether
        // the viewport has been added for that display. Otherwise, we would have to check if
        // the viewport exists separately.
        displayIdToNotify = pointerController->getDisplayId();
        cursorPosition = pointerController->getPosition();
    }

    if (mNotifiedPointerDisplayId == displayIdToNotify) {
        return;
    }
    mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
    mNotifiedPointerDisplayId = displayIdToNotify;
}

void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
    std::scoped_lock _l(mLock);

    mDefaultMouseDisplayId = displayId;
    updatePointerControllersLocked();
}

void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
    std::scoped_lock _l(mLock);
    for (const auto& viewport : viewports) {
        if (const auto it = mMousePointersByDisplay.find(viewport.displayId);
            it != mMousePointersByDisplay.end()) {
            it->second->setDisplayViewport(viewport);
        }
    }
    mViewports = viewports;
    notifyPointerDisplayIdChangedLocked();
}

std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
        int32_t associatedDisplayId) {
    std::scoped_lock _l(mLock);
    const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
    if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
        return *viewport;
    }
    return std::nullopt;
}

FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
    std::scoped_lock _l(mLock);
    const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
    if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
        it != mMousePointersByDisplay.end()) {
        return it->second->getPosition();
    }
    return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
}

PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
        int32_t displayId) {
    std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
            [this, displayId]() REQUIRES(mLock) {
                auto pc = mPolicy.createPointerController(
                        PointerControllerInterface::ControllerType::MOUSE);
                if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
                    pc->setDisplayViewport(*viewport);
                }
                return pc;
            };
    return ConstructorDelegate(std::move(ctor));
}

} // namespace android
+56 −0
Original line number Diff line number Diff line
@@ -20,8 +20,25 @@
#include "NotifyArgs.h"
#include "PointerChoreographerPolicyInterface.h"

#include <android-base/thread_annotations.h>
#include <type_traits>

namespace android {

/**
 * 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.
@@ -30,6 +47,15 @@ namespace android {
 */
class PointerChoreographerInterface : public InputListenerInterface {
public:
    /**
     * Set the display that pointers, like the mouse cursor and drawing tablets,
     * should be drawn on.
     */
    virtual void setDefaultMouseDisplayId(int32_t displayId) = 0;
    virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0;
    virtual std::optional<DisplayViewport> getViewportForPointerDevice(
            int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
    virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
    /**
     * This method may be called on any thread (usually by the input manager on a binder thread).
     */
@@ -42,6 +68,12 @@ public:
                                  PointerChoreographerPolicyInterface&);
    ~PointerChoreographer() override = default;

    void setDefaultMouseDisplayId(int32_t displayId) override;
    void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
    std::optional<DisplayViewport> getViewportForPointerDevice(
            int32_t associatedDisplayId) override;
    FloatPoint getMouseCursorPosition(int32_t displayId) override;

    void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
    void notifyKey(const NotifyKeyArgs& args) override;
@@ -55,7 +87,31 @@ public:
    void dump(std::string& dump) override;

private:
    void updatePointerControllersLocked() REQUIRES(mLock);
    void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
    const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
    int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);

    NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
    NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
    NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);

    using ControllerConstructor =
            ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
    ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);

    std::mutex mLock;

    InputListenerInterface& mNextListener;
    PointerChoreographerPolicyInterface& mPolicy;

    std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
            GUARDED_BY(mLock);

    int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
    int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
    std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
    std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
};

} // namespace android
+9 −0
Original line number Diff line number Diff line
@@ -451,6 +451,15 @@ public:

    /* Returns true if any InputConnection is currently active. */
    virtual bool isInputMethodConnectionActive() = 0;

    /* Gets the viewport of a particular display that the pointer device is associated with. If
     * the pointer device is not associated with any display, it should ADISPLAY_IS_NONE to get
     * the viewport that should be used. The device should get a new viewport using this method
     * every time there is a display configuration change. The logical bounds of the viewport should
     * be used as the range of possible values for pointing devices, like mice and touchpads.
     */
    virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
            int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
};

} // namespace android
+10 −1
Original line number Diff line number Diff line
@@ -38,7 +38,16 @@ public:
     * library, libinputservice, that has the additional dependencies. The PointerController
     * will be mocked when testing PointerChoreographer.
     */
    virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0;
    virtual std::shared_ptr<PointerControllerInterface> createPointerController(
            PointerControllerInterface::ControllerType type) = 0;

    /**
     * Notifies the policy that the default pointer displayId has changed. PointerChoreographer is
     * the single source of truth for all pointers on screen.
     * @param displayId The updated display on which the mouse cursor is shown
     * @param position The new position of the mouse cursor on the logical display
     */
    virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0;
};

} // namespace android
+16 −0
Original line number Diff line number Diff line
@@ -52,6 +52,22 @@ protected:
    virtual ~PointerControllerInterface() { }

public:
    /**
     * Enum used to differentiate various types of PointerControllers for the transition to
     * using PointerChoreographer.
     *
     * TODO(b/293587049): Refactor the PointerController class into different controller types.
     */
    enum class ControllerType {
        // The PointerController that is responsible for drawing all icons.
        LEGACY,
        // Represents a single mouse pointer.
        MOUSE,
    };

    /* Dumps the state of the pointer controller. */
    virtual std::string dump() = 0;

    /* Gets the bounds of the region that the pointer can traverse.
     * Returns true if the bounds are available. */
    virtual std::optional<FloatRect> getBounds() const = 0;
Loading