blob: 71dee049527ba22a79cd488812045841b783374f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_SCENIC_LIB_INPUT_GESTURE_ARENA_H_
#define SRC_UI_SCENIC_LIB_INPUT_GESTURE_ARENA_H_
#include <lib/fit/function.h>
#include <deque>
#include <map>
#include <unordered_map>
#include <vector>
#include "src/ui/scenic/lib/input/gesture_contender.h"
namespace scenic_impl::input {
struct ContestResults {
std::optional<ContenderId> winner;
std::vector<ContenderId> losers;
bool end_of_contest = false;
};
// Class for deciding Gesture Disambiguation contests.
// On construction the arena takes a list of all the clients contending, in priority order, for the
// stream. The arena then receives responses for the every contender, and uses these in
// combination with priority to decide the owner ("winner") of the stream.
//
// Intended use:
// InputSystem creates one GestureArena every time a new stream begins, designating contenders
// for that stream at construction.
// Each time a new set of events arrives for the stream InputSystem should call UpdateStream() with
// the number of new events as well a bool telling the arena whether there will be any more events.
// RecordResponse() should be called once for each event for every contender (until the contender
// has been designated either a winner or a loser). With every call the arena makes an attempt at
// determining a winner, returning a ContestResults struct containing any new results from
// the contest.
// After the contest ends the arena can be kept around to track stream and winner state, but no more
// calls to RecordResponse() should be made.
class GestureArena {
public:
// Priority of each contender (only used internally). The lowest number equals highest priority.
using Priority = int64_t;
static constexpr Priority kInvalidPriority = std::numeric_limits<Priority>::min();
struct Contender {
ContenderId id = kInvalidContenderId;
Priority priority = kInvalidPriority;
GestureResponse response_status = GestureResponse::kUndefined;
};
// |contenders| should have no duplicates and be in priority order from highest to lowest.
explicit GestureArena(std::vector<ContenderId> contenders);
~GestureArena() = default;
// Update the stream with new messages. |length| denotes how many new messages were added to the
// stream, while |is_last_message| denotes whether the stream ended with the last message in
// the sequence.
void UpdateStream(uint64_t added_length, bool is_last_message);
// To be called whenever a contender has a new set of responses. The responses should be
// chronologically ordered, with the earliest response first.
// To remove a contender, pass in a NO response.
ContestResults RecordResponse(ContenderId contender_id,
const std::vector<GestureResponse>& responses);
// Returns a vector of all remaining contenders.
std::vector<ContenderId> contenders() const;
bool stream_has_ended() const { return stream_has_ended_; }
bool contest_has_ended() const { return contest_has_ended_; }
bool contains(ContenderId contender_id) const { return contenders_.count(contender_id) > 0; }
private:
void AddResponse(ContenderId contender_id, GestureResponse response);
// See if the current state can determine a winner.
std::optional<ContenderId> TryResolve();
// Removes contender |contender_id| from the arena.
// Should only be called once per contender.
void RemoveContender(ContenderId contender_id);
// Makes |id| the only remaining contender and return the resulting ContestResults.
ContestResults SetWinner(ContenderId id);
// All current contenders.
std::unordered_map<ContenderId, Contender> contenders_;
std::unordered_map<Priority, ContenderId> priority_to_id_;
uint64_t stream_length_ = 0;
bool stream_has_ended_ = false;
bool contest_has_ended_ = false;
// A double ended queue that collects responses from all contenders in chronological order.
// Each item in the queue is a map of one response from every client, ordered by client priority
// (highest priority to lowest).
// When the map at the front of the queue has a response from every contender (oldest full map),
// we inspect that map to determine how the contest should progress and then drop that map from
// the deque.
// - Mid contest. We append a new map to the queue for event's set of responses.
// - Sweep. If a contender has Hold, an updated response is substituted directly into the final
// response map (we don't append another response map).
std::deque<std::map<Priority, GestureResponse>> responses_;
// The stream index the frontmost item in |responses_| maps to.
size_t index_of_current_responses_ = 0;
};
} // namespace scenic_impl::input
#endif // SRC_UI_SCENIC_LIB_INPUT_GESTURE_ARENA_H_