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

Commit 1b217913 authored by Harry Cutts's avatar Harry Cutts
Browse files

TouchpadInputMapper: Add gesture property provider

Gesture properties are configurable parameters used by the gestures
library [0]. These will be used for per-device tuning and to implement
various touchpad settings. The property provider gives Android an
interface with which to get and set property values.

[0]: https://chromium.googlesource.com/chromiumos/platform/gestures/+/refs/heads/main/docs/gesture_properties.md

Bug: 251196347
Test: atest inputflinger_tests
Test: on device with some hacky test code
Change-Id: I7723dcbabf29e13c8ab0907c71e5dd1faa766b45
parent 808ff1d4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ filegroup {
        "mapper/gestures/GestureConverter.cpp",
        "mapper/gestures/GesturesLogging.cpp",
        "mapper/gestures/HardwareStateConverter.cpp",
        "mapper/gestures/PropertyProvider.cpp",
    ],
}

+12 −2
Original line number Diff line number Diff line
@@ -96,11 +96,12 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
    mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
    mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
    // Even though we don't explicitly delete copy/move semantics, it's safe to
    // give away a pointer to TouchpadInputMapper here because
    // give away pointers to TouchpadInputMapper and its members here because
    // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
    // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
    mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider),
                                         &mPropertyProvider);
    mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
    // TODO(b/251196347): set a property provider, so we can change gesture properties.
    // TODO(b/251196347): set a timer provider, so the library can use timers.
}

@@ -108,6 +109,15 @@ TouchpadInputMapper::~TouchpadInputMapper() {
    if (mPointerController != nullptr) {
        mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
    }

    // The gesture interpreter's destructor will call its property provider's free function for all
    // gesture properties, in this case calling PropertyProvider::freeProperty using a raw pointer
    // to mPropertyProvider. Depending on the declaration order in TouchpadInputMapper.h, this may
    // happen after mPropertyProvider has been destructed, causing allocation errors. Depending on
    // declaration order to avoid crashes seems rather fragile, so explicitly clear the property
    // provider here to ensure all the freeProperty calls happen before mPropertyProvider is
    // destructed.
    mGestureInterpreter->SetPropProvider(nullptr, nullptr);
}

uint32_t TouchpadInputMapper::getSources() const {
+3 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "NotifyArgs.h"
#include "gestures/GestureConverter.h"
#include "gestures/HardwareStateConverter.h"
#include "gestures/PropertyProvider.h"

#include "include/gestures.h"

@@ -57,6 +58,8 @@ private:
            mGestureInterpreter;
    std::shared_ptr<PointerControllerInterface> mPointerController;

    PropertyProvider mPropertyProvider;

    HardwareStateConverter mStateConverter;
    GestureConverter mGestureConverter;

+201 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include "../Macros.h"

#include "gestures/PropertyProvider.h"

#include <algorithm>
#include <utility>

#include <ftl/enum.h>
#include <log/log_main.h>

namespace android {

namespace {

GesturesProp* createInt(void* data, const char* name, int* loc, size_t count, const int* init) {
    return static_cast<PropertyProvider*>(data)->createIntArrayProperty(name, loc, count, init);
}

GesturesProp* createBool(void* data, const char* name, GesturesPropBool* loc, size_t count,
                         const GesturesPropBool* init) {
    return static_cast<PropertyProvider*>(data)->createBoolArrayProperty(name, loc, count, init);
}

GesturesProp* createString(void* data, const char* name, const char** loc, const char* const init) {
    return static_cast<PropertyProvider*>(data)->createStringProperty(name, loc, init);
}

GesturesProp* createReal(void* data, const char* name, double* loc, size_t count,
                         const double* init) {
    return static_cast<PropertyProvider*>(data)->createRealArrayProperty(name, loc, count, init);
}

void registerHandlers(void* data, GesturesProp* prop, void* handlerData,
                      GesturesPropGetHandler getter, GesturesPropSetHandler setter) {
    prop->registerHandlers(handlerData, getter, setter);
}

void freeProperty(void* data, GesturesProp* prop) {
    static_cast<PropertyProvider*>(data)->freeProperty(prop);
}

} // namespace

const GesturesPropProvider gesturePropProvider = {
        .create_int_fn = createInt,
        .create_bool_fn = createBool,
        .create_string_fn = createString,
        .create_real_fn = createReal,
        .register_handlers_fn = registerHandlers,
        .free_fn = freeProperty,
};

bool PropertyProvider::hasProperty(const std::string name) const {
    return mProperties.find(name) != mProperties.end();
}

GesturesProp& PropertyProvider::getProperty(const std::string name) {
    return mProperties.at(name);
}

GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc,
                                                       size_t count, const int* init) {
    const auto [it, inserted] =
            mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
    LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
    return &it->second;
}

GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name,
                                                        GesturesPropBool* loc, size_t count,
                                                        const GesturesPropBool* init) {
    const auto [it, inserted] =
            mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
    LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
    return &it->second;
}

GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc,
                                                        size_t count, const double* init) {
    const auto [it, inserted] =
            mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
    LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
    return &it->second;
}

GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc,
                                                     const char* const init) {
    const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)});
    LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
    return &it->second;
}

void PropertyProvider::freeProperty(GesturesProp* prop) {
    mProperties.erase(prop->getName());
}

} // namespace android

template <typename T>
GesturesProp::GesturesProp(std::string name, T* dataPointer, size_t count, const T* initialValues)
      : mName(name), mCount(count), mDataPointer(dataPointer) {
    std::copy_n(initialValues, count, dataPointer);
}

GesturesProp::GesturesProp(std::string name, const char** dataPointer,
                           const char* const initialValue)
      : mName(name), mCount(1), mDataPointer(dataPointer) {
    *(std::get<const char**>(mDataPointer)) = initialValue;
}

void GesturesProp::registerHandlers(void* handlerData, GesturesPropGetHandler getter,
                                    GesturesPropSetHandler setter) {
    mHandlerData = handlerData;
    mGetter = getter;
    mSetter = setter;
}

std::vector<int> GesturesProp::getIntValues() const {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<int*>(mDataPointer),
                        "Attempt to read ints from \"%s\" gesture property.", mName.c_str());
    return getValues<int, int>(std::get<int*>(mDataPointer));
}

std::vector<bool> GesturesProp::getBoolValues() const {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<GesturesPropBool*>(mDataPointer),
                        "Attempt to read bools from \"%s\" gesture property.", mName.c_str());
    return getValues<bool, GesturesPropBool>(std::get<GesturesPropBool*>(mDataPointer));
}

std::vector<double> GesturesProp::getRealValues() const {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<double*>(mDataPointer),
                        "Attempt to read reals from \"%s\" gesture property.", mName.c_str());
    return getValues<double, double>(std::get<double*>(mDataPointer));
}

std::string GesturesProp::getStringValue() const {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<const char**>(mDataPointer),
                        "Attempt to read a string from \"%s\" gesture property.", mName.c_str());
    if (mGetter != nullptr) {
        mGetter(mHandlerData);
    }
    return std::string(*std::get<const char**>(mDataPointer));
}

void GesturesProp::setBoolValues(const std::vector<bool>& values) {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<GesturesPropBool*>(mDataPointer),
                        "Attempt to write bools to \"%s\" gesture property.", mName.c_str());
    setValues(std::get<GesturesPropBool*>(mDataPointer), values);
}

void GesturesProp::setIntValues(const std::vector<int>& values) {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<int*>(mDataPointer),
                        "Attempt to write ints to \"%s\" gesture property.", mName.c_str());
    setValues(std::get<int*>(mDataPointer), values);
}

void GesturesProp::setRealValues(const std::vector<double>& values) {
    LOG_ALWAYS_FATAL_IF(!std::holds_alternative<double*>(mDataPointer),
                        "Attempt to write reals to \"%s\" gesture property.", mName.c_str());
    setValues(std::get<double*>(mDataPointer), values);
}

template <typename T, typename U>
const std::vector<T> GesturesProp::getValues(U* dataPointer) const {
    if (mGetter != nullptr) {
        mGetter(mHandlerData);
    }
    std::vector<T> values;
    values.reserve(mCount);
    for (size_t i = 0; i < mCount; i++) {
        values.push_back(dataPointer[i]);
    }
    return values;
}

template <typename T, typename U>
void GesturesProp::setValues(T* dataPointer, const std::vector<U>& values) {
    LOG_ALWAYS_FATAL_IF(values.size() != mCount,
                        "Attempt to write %zu values to \"%s\" gesture property, which holds %zu.",
                        values.size(), mName.c_str(), mCount);
    std::copy(values.begin(), values.end(), dataPointer);
    if (mSetter != nullptr) {
        mSetter(mHandlerData);
    }
}
+97 −0
Original line number Diff line number Diff line
/*
 * 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 <map>
#include <string>
#include <variant>
#include <vector>

#include "include/gestures.h"

namespace android {

// Struct containing functions that wrap PropertyProvider in a C-compatible interface.
extern const GesturesPropProvider gesturePropProvider;

// Implementation of a gestures library property provider, which provides configuration parameters.
class PropertyProvider {
public:
    bool hasProperty(const std::string name) const;
    GesturesProp& getProperty(const std::string name);

    // Methods to be called by the gestures library:
    GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count,
                                         const int* init);
    GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc,
                                          size_t count, const GesturesPropBool* init);
    GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count,
                                          const double* init);
    GesturesProp* createStringProperty(const std::string name, const char** loc,
                                       const char* const init);

    void freeProperty(GesturesProp* prop);

private:
    std::map<std::string, GesturesProp> mProperties;
};

} // namespace android

// Represents a single gesture property.
//
// Pointers to this struct will be used by the gestures library (though it can never deference
// them). The library's API requires this to be in the top-level namespace.
struct GesturesProp {
public:
    template <typename T>
    GesturesProp(std::string name, T* dataPointer, size_t count, const T* initialValues);
    GesturesProp(std::string name, const char** dataPointer, const char* const initialValue);

    std::string getName() const { return mName; }

    size_t getCount() const { return mCount; }

    void registerHandlers(void* handlerData, GesturesPropGetHandler getter,
                          GesturesPropSetHandler setter);

    std::vector<int> getIntValues() const;
    std::vector<bool> getBoolValues() const;
    std::vector<double> getRealValues() const;
    std::string getStringValue() const;

    void setIntValues(const std::vector<int>& values);
    void setBoolValues(const std::vector<bool>& values);
    void setRealValues(const std::vector<double>& values);
    // Setting string values isn't supported since we don't have a use case yet and the memory
    // management adds additional complexity.

private:
    // Two type parameters are required for these methods, rather than one, due to the gestures
    // library using its own bool type.
    template <typename T, typename U>
    const std::vector<T> getValues(U* dataPointer) const;
    template <typename T, typename U>
    void setValues(T* dataPointer, const std::vector<U>& values);

    std::string mName;
    size_t mCount;
    std::variant<int*, GesturesPropBool*, const char**, double*> mDataPointer;
    void* mHandlerData = nullptr;
    GesturesPropGetHandler mGetter = nullptr;
    GesturesPropSetHandler mSetter = nullptr;
};
Loading