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

Commit da10dd34 authored by Byoungho Jung's avatar Byoungho Jung Committed by Prabir Pradhan
Browse files

Pointer icon refactor for mouse

When PointerChoreographer is enabled, CursorInputMapper no longer
depends on the legacy PointerController. PointerChoreographer is
responsible for accumulating movements, fading/unfading pointers,
and deciding display/coordinates.

Test: atest inputflinger_tests
Bug: 293587049

Change-Id: I3a4fa4ab260673076d95dbcb0832d13d0fc34c75
parent 0a1d7be4
Loading
Loading
Loading
Loading
+197 −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,70 @@ 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) << "Wrong number of pointers " << args.dump();
    }

    const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
    auto it = mMousePointersByDisplay.find(displayId);
    if (it == mMousePointersByDisplay.end()) {
        it = mMousePointersByDisplay
                     .insert({displayId,
                              mPolicy.createPointerController(
                                      PointerControllerInterface::ControllerType::MOUSE)})
                     .first;
        if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
            it->second->setDisplayViewport(*viewport);
        }
        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 +143,123 @@ 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& item) {
        if (mouseDisplaysToKeep.find(item.first) == mouseDisplaysToKeep.end()) {
            item.second->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()) {
        displayIdToNotify = it->second->getDisplayId();
        cursorPosition = it->second->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) {
        int32_t displayId = viewport.displayId;
        if (const auto it = mMousePointersByDisplay.find(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};
}

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

#include <android-base/thread_annotations.h>

namespace android {

/**
@@ -30,6 +32,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 +53,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 +72,27 @@ 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);

    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
+6 −0
Original line number Diff line number Diff line
@@ -451,6 +451,12 @@ public:

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

    /* Gets the viewport of a particular display. 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> getViewportForPointerDevice(
            int32_t associatedDisplayId) = 0;
};

} // namespace android
+4 −1
Original line number Diff line number Diff line
@@ -38,7 +38,10 @@ 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;

    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