1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-15 05:31:30 +00:00

Roll formatting and const tweaks into Inputs.

This commit is contained in:
Thomas Harte 2024-11-30 18:57:56 -05:00
parent 9d87296316
commit 7248470950
34 changed files with 464 additions and 465 deletions

View File

@ -115,6 +115,6 @@ void MultiConfigurable::set_options(const std::unique_ptr<Reflection::Struct> &s
options->apply();
}
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() {
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() const {
return std::make_unique<MultiStruct>(devices_);
}

View File

@ -28,7 +28,7 @@ public:
// Below is the standard Configurable::Device interface; see there for documentation.
void set_options(const std::unique_ptr<Reflection::Struct> &) final;
std::unique_ptr<Reflection::Struct> get_options() final;
std::unique_ptr<Reflection::Struct> get_options() const final;
private:
std::vector<Configurable::Device *> devices_;

View File

@ -65,126 +65,126 @@ template <> struct TaskQueueStorage<void> {
called once per action.
*/
template <bool perform_automatically, bool start_immediately = true, typename Performer = void> class AsyncTaskQueue: public TaskQueueStorage<Performer> {
public:
template <typename... Args> AsyncTaskQueue(Args&&... args) :
TaskQueueStorage<Performer>(std::forward<Args>(args)...) {
if constexpr (start_immediately) {
start();
}
public:
template <typename... Args> AsyncTaskQueue(Args&&... args) :
TaskQueueStorage<Performer>(std::forward<Args>(args)...) {
if constexpr (start_immediately) {
start();
}
}
/// Enqueus @c post_action to be performed asynchronously at some point
/// in the future. If @c perform_automatically is @c true then the action
/// will be performed as soon as possible. Otherwise it will sit unsheculed until
/// a call to @c perform().
///
/// Actions may be elided.
///
/// If this TaskQueue has a @c Performer then the action will be performed
/// on the same thread as the performer, after the performer has been updated
/// to 'now'.
void enqueue(const std::function<void(void)> &post_action) {
std::lock_guard guard(condition_mutex_);
actions_.push_back(post_action);
/// Enqueus @c post_action to be performed asynchronously at some point
/// in the future. If @c perform_automatically is @c true then the action
/// will be performed as soon as possible. Otherwise it will sit unsheculed until
/// a call to @c perform().
///
/// Actions may be elided.
///
/// If this TaskQueue has a @c Performer then the action will be performed
/// on the same thread as the performer, after the performer has been updated
/// to 'now'.
void enqueue(const std::function<void(void)> &post_action) {
std::lock_guard guard(condition_mutex_);
actions_.push_back(post_action);
if constexpr (perform_automatically) {
condition_.notify_all();
}
}
/// Causes any enqueued actions that are not yet scheduled to be scheduled.
void perform() {
if(actions_.empty()) {
return;
}
if constexpr (perform_automatically) {
condition_.notify_all();
}
}
/// Permanently stops this task queue, blocking until that has happened.
/// All pending actions will be performed first.
///
/// The queue cannot be restarted; this is a destructive action.
void stop() {
if(thread_.joinable()) {
should_quit_ = true;
enqueue([] {});
if constexpr (!perform_automatically) {
perform();
}
thread_.join();
}
/// Causes any enqueued actions that are not yet scheduled to be scheduled.
void perform() {
if(actions_.empty()) {
return;
}
condition_.notify_all();
}
/// Starts the queue if it has never been started before.
///
/// This is not guaranteed safely to restart a stopped queue.
void start() {
thread_ = std::thread{
[this] {
ActionVector actions;
// Continue until told to quit.
while(!should_quit_) {
// Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_);
while(actions_.empty() && !should_quit_) {
condition_.wait(lock);
}
std::swap(actions, actions_);
lock.unlock();
// Update to now (which is possibly a no-op).
TaskQueueStorage<Performer>::update();
// Perform the actions and destroy them.
for(const auto &action: actions) {
action();
}
actions.clear();
}
}
};
}
/// Schedules any remaining unscheduled work, then blocks synchronously
/// until all scheduled work has been performed.
void flush() {
std::mutex flush_mutex;
std::condition_variable flush_condition;
bool has_run = false;
std::unique_lock lock(flush_mutex);
enqueue([&flush_mutex, &flush_condition, &has_run] () {
std::unique_lock inner_lock(flush_mutex);
has_run = true;
flush_condition.notify_all();
});
/// Permanently stops this task queue, blocking until that has happened.
/// All pending actions will be performed first.
///
/// The queue cannot be restarted; this is a destructive action.
void stop() {
if(thread_.joinable()) {
should_quit_ = true;
enqueue([] {});
if constexpr (!perform_automatically) {
perform();
}
thread_.join();
}
}
flush_condition.wait(lock, [&has_run] { return has_run; });
/// Starts the queue if it has never been started before.
///
/// This is not guaranteed safely to restart a stopped queue.
void start() {
thread_ = std::thread{
[this] {
ActionVector actions;
// Continue until told to quit.
while(!should_quit_) {
// Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_);
while(actions_.empty() && !should_quit_) {
condition_.wait(lock);
}
std::swap(actions, actions_);
lock.unlock();
// Update to now (which is possibly a no-op).
TaskQueueStorage<Performer>::update();
// Perform the actions and destroy them.
for(const auto &action: actions) {
action();
}
actions.clear();
}
}
};
}
/// Schedules any remaining unscheduled work, then blocks synchronously
/// until all scheduled work has been performed.
void flush() {
std::mutex flush_mutex;
std::condition_variable flush_condition;
bool has_run = false;
std::unique_lock lock(flush_mutex);
enqueue([&flush_mutex, &flush_condition, &has_run] () {
std::unique_lock inner_lock(flush_mutex);
has_run = true;
flush_condition.notify_all();
});
if constexpr (!perform_automatically) {
perform();
}
~AsyncTaskQueue() {
stop();
}
flush_condition.wait(lock, [&has_run] { return has_run; });
}
private:
// The list of actions waiting be performed. These will be elided,
// increasing their latency, if the emulation thread falls behind.
using ActionVector = std::vector<std::function<void(void)>>;
ActionVector actions_;
~AsyncTaskQueue() {
stop();
}
// Necessary synchronisation parts.
std::atomic<bool> should_quit_ = false;
std::mutex condition_mutex_;
std::condition_variable condition_;
private:
// The list of actions waiting be performed. These will be elided,
// increasing their latency, if the emulation thread falls behind.
using ActionVector = std::vector<std::function<void(void)>>;
ActionVector actions_;
// Ensure the thread isn't constructed until after the mutex
// and condition variable.
std::thread thread_;
// Necessary synchronisation parts.
std::atomic<bool> should_quit_ = false;
std::mutex condition_mutex_;
std::condition_variable condition_;
// Ensure the thread isn't constructed until after the mutex
// and condition variable.
std::thread thread_;
};
}

View File

@ -25,7 +25,7 @@ struct Device {
virtual void set_options(const std::unique_ptr<Reflection::Struct> &options) = 0;
/// @returns An options object
virtual std::unique_ptr<Reflection::Struct> get_options() = 0;
virtual std::unique_ptr<Reflection::Struct> get_options() const = 0;
};
/*!

View File

@ -26,37 +26,37 @@ ReflectableEnum(Display,
//===
template <typename Owner> class DisplayOption {
public:
Configurable::Display output;
DisplayOption(Configurable::Display output) : output(output) {}
public:
Configurable::Display output;
DisplayOption(Configurable::Display output) : output(output) {}
protected:
void declare_display_option() {
static_cast<Owner *>(this)->declare(&output, "output");
AnnounceEnumNS(Configurable, Display);
}
protected:
void declare_display_option() {
static_cast<Owner *>(this)->declare(&output, "output");
AnnounceEnumNS(Configurable, Display);
}
};
template <typename Owner> class QuickloadOption {
public:
bool quickload;
QuickloadOption(bool quickload) : quickload(quickload) {}
public:
bool quickload;
QuickloadOption(bool quickload) : quickload(quickload) {}
protected:
void declare_quickload_option() {
static_cast<Owner *>(this)->declare(&quickload, "quickload");
}
protected:
void declare_quickload_option() {
static_cast<Owner *>(this)->declare(&quickload, "quickload");
}
};
template <typename Owner> class QuickbootOption {
public:
bool quickboot;
QuickbootOption(bool quickboot) : quickboot(quickboot) {}
public:
bool quickboot;
QuickbootOption(bool quickboot) : quickboot(quickboot) {}
protected:
void declare_quickboot_option() {
static_cast<Owner *>(this)->declare(&quickboot, "quickboot");
}
protected:
void declare_quickboot_option() {
static_cast<Owner *>(this)->declare(&quickboot, "quickboot");
}
};
}

View File

@ -18,137 +18,137 @@ namespace Inputs {
machine to toggle states, while an interested party either observes or polls.
*/
class Joystick {
public:
virtual ~Joystick() = default;
public:
virtual ~Joystick() = default;
/*!
Defines a single input, any individually-measured thing a fire button or
other digital control, an analogue axis, or a button with a symbol on it.
*/
struct Input {
/// Defines the broad type of the input.
enum Type {
// Half-axis inputs.
Up, Down, Left, Right,
// Full-axis inputs.
Horizontal, Vertical,
// Fire buttons.
Fire,
// Other labelled keys.
Key,
/*!
Defines a single input, any individually-measured thing a fire button or
other digital control, an analogue axis, or a button with a symbol on it.
*/
struct Input {
/// Defines the broad type of the input.
enum Type {
// Half-axis inputs.
Up, Down, Left, Right,
// Full-axis inputs.
Horizontal, Vertical,
// Fire buttons.
Fire,
// Other labelled keys.
Key,
// The maximum value this enum can contain.
Max = Key
};
const Type type;
bool is_digital_axis() const {
return type < Type::Horizontal;
}
bool is_analogue_axis() const {
return type >= Type::Horizontal && type < Type::Fire;
}
bool is_axis() const {
return type < Type::Fire;
}
bool is_button() const {
return type >= Type::Fire;
}
enum Precision {
Analogue, Digital
};
Precision precision() const {
return is_analogue_axis() ? Precision::Analogue : Precision::Digital;
}
/*!
Holds extra information pertaining to the input.
@c Type::Key inputs declare the symbol printed on them.
All other types of input have an associated index, indicating whether they
are the zeroth, first, second, third, etc of those things. E.g. a joystick
may have two fire buttons, which will be buttons 0 and 1.
*/
union Info {
struct {
size_t index;
} control;
struct {
wchar_t symbol;
} key;
};
Info info;
// TODO: Find a way to make the above safely const; may mean not using a union.
Input(Type type, size_t index = 0) :
type(type) {
info.control.index = index;
}
Input(wchar_t symbol) : type(Key) {
info.key.symbol = symbol;
}
bool operator == (const Input &rhs) {
if(rhs.type != type) return false;
if(rhs.type == Key) {
return rhs.info.key.symbol == info.key.symbol;
} else {
return rhs.info.control.index == info.control.index;
}
}
// The maximum value this enum can contain.
Max = Key
};
const Type type;
/// @returns The list of all inputs defined on this joystick.
virtual const std::vector<Input> &get_inputs() = 0;
bool is_digital_axis() const {
return type < Type::Horizontal;
}
bool is_analogue_axis() const {
return type >= Type::Horizontal && type < Type::Fire;
}
bool is_axis() const {
return type < Type::Fire;
}
bool is_button() const {
return type >= Type::Fire;
}
/*!
Sets the digital value of @c input. This may have direct effect or
influence an analogue value; e.g. if the caller declares that ::Left is
active but this joystick has only an analogue horizontal axis, this will
cause a change to that analogue value.
*/
virtual void set_input(const Input &input, bool is_active) = 0;
/*!
Sets the analogue value of @c input. If the input is actually digital,
or if there is a digital input with a corresponding meaning (e.g. ::Left
versus the horizontal axis), this may cause a digital input to be set.
@c value should be in the range [0.0, 1.0].
*/
virtual void set_input(const Input &input, float value) = 0;
/*!
Sets all inputs to their resting state.
*/
virtual void reset_all_inputs() {
for(const auto &input: get_inputs()) {
if(input.precision() == Input::Precision::Digital)
set_input(input, false);
else
set_input(input, 0.5f);
}
enum Precision {
Analogue, Digital
};
Precision precision() const {
return is_analogue_axis() ? Precision::Analogue : Precision::Digital;
}
/*!
Gets the number of input fire buttons.
Holds extra information pertaining to the input.
This is cached by default, but it's virtual so overridable.
@c Type::Key inputs declare the symbol printed on them.
All other types of input have an associated index, indicating whether they
are the zeroth, first, second, third, etc of those things. E.g. a joystick
may have two fire buttons, which will be buttons 0 and 1.
*/
virtual int get_number_of_fire_buttons() {
if(number_of_buttons_ >= 0) return number_of_buttons_;
union Info {
struct {
size_t index;
} control;
struct {
wchar_t symbol;
} key;
};
Info info;
// TODO: Find a way to make the above safely const; may mean not using a union.
number_of_buttons_ = 0;
for(const auto &input: get_inputs()) {
if(input.type == Input::Type::Fire) ++number_of_buttons_;
}
return number_of_buttons_;
Input(const Type type, const size_t index = 0) :
type(type) {
info.control.index = index;
}
Input(const wchar_t symbol) : type(Key) {
info.key.symbol = symbol;
}
private:
int number_of_buttons_ = -1;
bool operator == (const Input &rhs) {
if(rhs.type != type) return false;
if(rhs.type == Key) {
return rhs.info.key.symbol == info.key.symbol;
} else {
return rhs.info.control.index == info.control.index;
}
}
};
/// @returns The list of all inputs defined on this joystick.
virtual const std::vector<Input> &get_inputs() = 0;
/*!
Sets the digital value of @c input. This may have direct effect or
influence an analogue value; e.g. if the caller declares that ::Left is
active but this joystick has only an analogue horizontal axis, this will
cause a change to that analogue value.
*/
virtual void set_input(const Input &input, bool is_active) = 0;
/*!
Sets the analogue value of @c input. If the input is actually digital,
or if there is a digital input with a corresponding meaning (e.g. ::Left
versus the horizontal axis), this may cause a digital input to be set.
@c value should be in the range [0.0, 1.0].
*/
virtual void set_input(const Input &input, float value) = 0;
/*!
Sets all inputs to their resting state.
*/
virtual void reset_all_inputs() {
for(const auto &input: get_inputs()) {
if(input.precision() == Input::Precision::Digital)
set_input(input, false);
else
set_input(input, 0.5f);
}
}
/*!
Gets the number of input fire buttons.
This is cached by default, but it's virtual so overridable.
*/
virtual int get_number_of_fire_buttons() {
if(number_of_buttons_ >= 0) return number_of_buttons_;
number_of_buttons_ = 0;
for(const auto &input: get_inputs()) {
if(input.type == Input::Type::Fire) ++number_of_buttons_;
}
return number_of_buttons_;
}
private:
int number_of_buttons_ = -1;
};
/*!
@ -157,79 +157,79 @@ class Joystick {
promised analogue <-> digital mapping of Joystick.
*/
class ConcreteJoystick: public Joystick {
public:
ConcreteJoystick(const std::vector<Input> &inputs) : inputs_(inputs) {
// Size and populate stick_types_, which is used for digital <-> analogue conversion.
for(const auto &input: inputs_) {
const bool is_digital_axis = input.is_digital_axis();
const bool is_analogue_axis = input.is_analogue_axis();
if(is_digital_axis || is_analogue_axis) {
const size_t required_size = size_t(input.info.control.index+1);
if(stick_types_.size() < required_size) {
stick_types_.resize(required_size);
}
stick_types_[size_t(input.info.control.index)] = is_digital_axis ? StickType::Digital : StickType::Analogue;
public:
ConcreteJoystick(const std::vector<Input> &inputs) : inputs_(inputs) {
// Size and populate stick_types_, which is used for digital <-> analogue conversion.
for(const auto &input: inputs_) {
const bool is_digital_axis = input.is_digital_axis();
const bool is_analogue_axis = input.is_analogue_axis();
if(is_digital_axis || is_analogue_axis) {
const size_t required_size = size_t(input.info.control.index+1);
if(stick_types_.size() < required_size) {
stick_types_.resize(required_size);
}
stick_types_[size_t(input.info.control.index)] = is_digital_axis ? StickType::Digital : StickType::Analogue;
}
}
}
const std::vector<Input> &get_inputs() final {
return inputs_;
const std::vector<Input> &get_inputs() final {
return inputs_;
}
void set_input(const Input &input, const bool is_active) final {
// If this is a digital setting to a digital property, just pass it along.
if(input.is_button() || stick_types_[input.info.control.index] == StickType::Digital) {
did_set_input(input, is_active);
return;
}
void set_input(const Input &input, bool is_active) final {
// If this is a digital setting to a digital property, just pass it along.
if(input.is_button() || stick_types_[input.info.control.index] == StickType::Digital) {
did_set_input(input, is_active);
return;
}
// Otherwise this is logically to an analogue axis; for now just use some
// convenient hard-coded values. TODO: make these a function of time.
using Type = Joystick::Input::Type;
switch(input.type) {
default: did_set_input(input, is_active ? 1.0f : 0.0f); break;
case Type::Left: did_set_input(Input(Type::Horizontal, input.info.control.index), is_active ? 0.1f : 0.5f); break;
case Type::Right: did_set_input(Input(Type::Horizontal, input.info.control.index), is_active ? 0.9f : 0.5f); break;
case Type::Up: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.1f : 0.5f); break;
case Type::Down: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.9f : 0.5f); break;
}
}
// Otherwise this is logically to an analogue axis; for now just use some
// convenient hard-coded values. TODO: make these a function of time.
using Type = Joystick::Input::Type;
switch(input.type) {
default: did_set_input(input, is_active ? 1.0f : 0.0f); break;
case Type::Left: did_set_input(Input(Type::Horizontal, input.info.control.index), is_active ? 0.1f : 0.5f); break;
case Type::Right: did_set_input(Input(Type::Horizontal, input.info.control.index), is_active ? 0.9f : 0.5f); break;
case Type::Up: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.1f : 0.5f); break;
case Type::Down: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.9f : 0.5f); break;
}
void set_input(const Input &input, const float value) final {
// If this is an analogue setting to an analogue property, just pass it along.
if(!input.is_button() && stick_types_[input.info.control.index] == StickType::Analogue) {
did_set_input(input, value);
return;
}
void set_input(const Input &input, float value) final {
// If this is an analogue setting to an analogue property, just pass it along.
if(!input.is_button() && stick_types_[input.info.control.index] == StickType::Analogue) {
did_set_input(input, value);
return;
}
// Otherwise apply a threshold test to convert to digital, with remapping from axes to digital inputs.
using Type = Joystick::Input::Type;
switch(input.type) {
default: did_set_input(input, value > 0.5f); break;
case Type::Horizontal:
did_set_input(Input(Type::Left, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Right, input.info.control.index), value >= 0.75f);
break;
case Type::Vertical:
did_set_input(Input(Type::Up, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Down, input.info.control.index), value >= 0.75f);
break;
}
// Otherwise apply a threshold test to convert to digital, with remapping from axes to digital inputs.
using Type = Joystick::Input::Type;
switch(input.type) {
default: did_set_input(input, value > 0.5f); break;
case Type::Horizontal:
did_set_input(Input(Type::Left, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Right, input.info.control.index), value >= 0.75f);
break;
case Type::Vertical:
did_set_input(Input(Type::Up, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Down, input.info.control.index), value >= 0.75f);
break;
}
}
protected:
virtual void did_set_input([[maybe_unused]] const Input &input, [[maybe_unused]] float value) {}
virtual void did_set_input([[maybe_unused]] const Input &input, [[maybe_unused]] bool value) {}
protected:
virtual void did_set_input([[maybe_unused]] const Input &input, [[maybe_unused]] float value) {}
virtual void did_set_input([[maybe_unused]] const Input &input, [[maybe_unused]] bool value) {}
private:
const std::vector<Input> inputs_;
private:
const std::vector<Input> inputs_;
enum class StickType {
Digital,
Analogue
};
std::vector<StickType> stick_types_;
enum class StickType {
Digital,
Analogue
};
std::vector<StickType> stick_types_;
};
}

View File

@ -21,7 +21,7 @@ Keyboard::Keyboard(const std::set<Key> &essential_modifiers) : essential_modifie
Keyboard::Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers) :
observed_keys_(observed_keys), essential_modifiers_(essential_modifiers), is_exclusive_(false) {}
bool Keyboard::set_key_pressed(Key key, char, bool is_pressed, bool) {
bool Keyboard::set_key_pressed(const Key key, const char, const bool is_pressed, bool) {
const size_t key_offset = size_t(key);
if(key_offset >= key_states_.size()) {
key_states_.resize(key_offset+1, false);
@ -41,7 +41,7 @@ void Keyboard::reset_all_keys() {
if(delegate_) delegate_->reset_all_keys(this);
}
void Keyboard::set_delegate(Delegate *delegate) {
void Keyboard::set_delegate(Delegate *const delegate) {
delegate_ = delegate;
}

View File

@ -19,73 +19,73 @@ namespace Inputs {
machine to toggle states, while an interested party either observes or polls.
*/
class Keyboard {
public:
enum class Key {
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause,
BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, Backspace,
Tab, Q, W, E, R, T, Y, U, I, O, P, OpenSquareBracket, CloseSquareBracket, Backslash,
CapsLock, A, S, D, F, G, H, J, K, L, Semicolon, Quote, Hash, Enter,
LeftShift, Z, X, C, V, B, N, M, Comma, FullStop, ForwardSlash, RightShift,
LeftControl, LeftOption, LeftMeta, Space, RightMeta, RightOption, RightControl,
Left, Right, Up, Down,
Insert, Home, PageUp, Delete, End, PageDown,
NumLock, KeypadSlash, KeypadAsterisk, KeypadDelete,
Keypad7, Keypad8, Keypad9, KeypadPlus,
Keypad4, Keypad5, Keypad6, KeypadMinus,
Keypad1, Keypad2, Keypad3, KeypadEnter,
Keypad0, KeypadDecimalPoint, KeypadEquals,
Help,
public:
enum class Key {
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause,
BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, Backspace,
Tab, Q, W, E, R, T, Y, U, I, O, P, OpenSquareBracket, CloseSquareBracket, Backslash,
CapsLock, A, S, D, F, G, H, J, K, L, Semicolon, Quote, Hash, Enter,
LeftShift, Z, X, C, V, B, N, M, Comma, FullStop, ForwardSlash, RightShift,
LeftControl, LeftOption, LeftMeta, Space, RightMeta, RightOption, RightControl,
Left, Right, Up, Down,
Insert, Home, PageUp, Delete, End, PageDown,
NumLock, KeypadSlash, KeypadAsterisk, KeypadDelete,
Keypad7, Keypad8, Keypad9, KeypadPlus,
Keypad4, Keypad5, Keypad6, KeypadMinus,
Keypad1, Keypad2, Keypad3, KeypadEnter,
Keypad0, KeypadDecimalPoint, KeypadEquals,
Help,
Max = Help
};
Max = Help
};
/// Constructs a Keyboard that declares itself to observe all keys.
Keyboard(const std::set<Key> &essential_modifiers = {});
/// Constructs a Keyboard that declares itself to observe all keys.
Keyboard(const std::set<Key> &essential_modifiers = {});
/// Constructs a Keyboard that declares itself to observe only members of @c observed_keys.
Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers);
/// Constructs a Keyboard that declares itself to observe only members of @c observed_keys.
Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers);
virtual ~Keyboard() = default;
virtual ~Keyboard() = default;
// Host interface.
// Host interface.
/// @returns @c true if the key press affects the machine; @c false otherwise.
virtual bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat);
virtual void reset_all_keys();
/// @returns @c true if the key press affects the machine; @c false otherwise.
virtual bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat);
virtual void reset_all_keys();
/// @returns a set of all Keys that this keyboard responds to.
virtual const std::set<Key> &observed_keys() const;
/// @returns a set of all Keys that this keyboard responds to.
virtual const std::set<Key> &observed_keys() const;
/// @returns the list of modifiers that this keyboard considers 'essential' (i.e. both mapped and highly used).
virtual const std::set<Inputs::Keyboard::Key> &get_essential_modifiers() const;
/// @returns the list of modifiers that this keyboard considers 'essential' (i.e. both mapped and highly used).
virtual const std::set<Inputs::Keyboard::Key> &get_essential_modifiers() const;
/*!
@returns @c true if this keyboard, on its original machine, looked
like a complete keyboard i.e. if a user would expect this keyboard
to be the only thing a real keyboard maps to.
/*!
@returns @c true if this keyboard, on its original machine, looked
like a complete keyboard i.e. if a user would expect this keyboard
to be the only thing a real keyboard maps to.
So this would be true of something like the Amstrad CPC, which has a full
keyboard, but it would be false of something like the Sega Master System
which has some buttons that you'd expect an emulator to map to its host
keyboard but which does not offer a full keyboard.
*/
virtual bool is_exclusive() const;
So this would be true of something like the Amstrad CPC, which has a full
keyboard, but it would be false of something like the Sega Master System
which has some buttons that you'd expect an emulator to map to its host
keyboard but which does not offer a full keyboard.
*/
virtual bool is_exclusive() const;
// Delegate interface.
struct Delegate {
virtual bool keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0;
virtual void reset_all_keys(Keyboard *keyboard) = 0;
};
void set_delegate(Delegate *delegate);
bool get_key_state(Key key) const;
// Delegate interface.
struct Delegate {
virtual bool keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0;
virtual void reset_all_keys(Keyboard *keyboard) = 0;
};
void set_delegate(Delegate *);
bool get_key_state(Key) const;
private:
std::set<Key> observed_keys_;
const std::set<Key> essential_modifiers_;
const bool is_exclusive_ = true;
private:
std::set<Key> observed_keys_;
const std::set<Key> essential_modifiers_;
const bool is_exclusive_ = true;
std::vector<bool> key_states_;
Delegate *delegate_ = nullptr;
std::vector<bool> key_states_;
Delegate *delegate_ = nullptr;
};
}

View File

@ -19,12 +19,12 @@ class Mouse {
/*!
Indicates a movement of the mouse.
*/
virtual void move([[maybe_unused]] int x, [[maybe_unused]] int y) {}
virtual void move([[maybe_unused]] const int x, [[maybe_unused]] const int y) {}
/*!
@returns the number of buttons on this mouse.
*/
virtual int get_number_of_buttons() {
virtual int get_number_of_buttons() const {
return 1;
}
@ -33,7 +33,7 @@ class Mouse {
The intention is that @c index be semantic, not positional:
0 for the primary button, 1 for the secondary, 2 for the tertiary, etc.
*/
virtual void set_button_pressed([[maybe_unused]] int index, [[maybe_unused]] bool is_pressed) {}
virtual void set_button_pressed([[maybe_unused]] const int index, [[maybe_unused]] const bool is_pressed) {}
/*!
Releases all depressed buttons.

View File

@ -26,95 +26,95 @@ namespace Inputs {
channels below. This is intended to be fixed.
*/
class QuadratureMouse: public Mouse {
public:
QuadratureMouse(int number_of_buttons) :
number_of_buttons_(number_of_buttons) {}
public:
QuadratureMouse(const int number_of_buttons) :
number_of_buttons_(number_of_buttons) {}
/*
Inputs, to satisfy the Mouse interface.
*/
void move(int x, int y) final {
// Accumulate all provided motion.
axes_[0] += x;
axes_[1] += y;
}
/*
Inputs, to satisfy the Mouse interface.
*/
void move(const int x, const int y) final {
// Accumulate all provided motion.
axes_[0] += x;
axes_[1] += y;
}
int get_number_of_buttons() final {
return number_of_buttons_;
}
int get_number_of_buttons() const final {
return number_of_buttons_;
}
void set_button_pressed(int index, bool is_pressed) final {
if(is_pressed)
button_flags_ |= (1 << index);
else
button_flags_ &= ~(1 << index);
}
void set_button_pressed(const int index, const bool is_pressed) final {
if(is_pressed)
button_flags_ |= (1 << index);
else
button_flags_ &= ~(1 << index);
}
void reset_all_buttons() final {
button_flags_ = 0;
}
void reset_all_buttons() final {
button_flags_ = 0;
}
/*
Outputs.
*/
/*
Outputs.
*/
/*!
Applies a single step from the current accumulated mouse movement, which
might involve the mouse moving right, or left, or not at all.
*/
void prepare_step() {
for(int axis = 0; axis < 2; ++axis) {
// Do nothing if there's no motion to communicate.
const int axis_value = axes_[axis];
if(!axis_value) continue;
/*!
Applies a single step from the current accumulated mouse movement, which
might involve the mouse moving right, or left, or not at all.
*/
void prepare_step() {
for(int axis = 0; axis < 2; ++axis) {
// Do nothing if there's no motion to communicate.
const int axis_value = axes_[axis];
if(!axis_value) continue;
// Toggle the primary channel and set the secondary for
// negative motion. At present the y axis signals the
// secondary channel the opposite way around from the
// primary.
primaries_[axis] ^= 1;
secondaries_[axis] = primaries_[axis] ^ axis;
if(axis_value > 0) {
-- axes_[axis];
secondaries_[axis] ^= 1; // Switch to positive motion.
} else {
++ axes_[axis];
}
// Toggle the primary channel and set the secondary for
// negative motion. At present the y axis signals the
// secondary channel the opposite way around from the
// primary.
primaries_[axis] ^= 1;
secondaries_[axis] = primaries_[axis] ^ axis;
if(axis_value > 0) {
-- axes_[axis];
secondaries_[axis] ^= 1; // Switch to positive motion.
} else {
++ axes_[axis];
}
}
}
/*!
@returns the two quadrature channels bit 0 is the 'primary' channel
(i.e. the one that can be monitored to observe velocity) and
bit 1 is the 'secondary' (i.e. that which can be queried to
observe direction).
*/
int get_channel(int axis) {
return primaries_[axis] | (secondaries_[axis] << 1);
}
/*!
@returns the two quadrature channels bit 0 is the 'primary' channel
(i.e. the one that can be monitored to observe velocity) and
bit 1 is the 'secondary' (i.e. that which can be queried to
observe direction).
*/
int get_channel(int axis) const {
return primaries_[axis] | (secondaries_[axis] << 1);
}
/*!
@returns a bit mask of the currently pressed buttons.
*/
int get_button_mask() {
return button_flags_;
}
/*!
@returns a bit mask of the currently pressed buttons.
*/
int get_button_mask() const {
return button_flags_;
}
/*!
@returns @c true if any mouse motion is waiting to be communicated;
@c false otherwise.
*/
bool has_steps() {
return axes_[0] || axes_[1];
}
/*!
@returns @c true if any mouse motion is waiting to be communicated;
@c false otherwise.
*/
bool has_steps() const {
return axes_[0] || axes_[1];
}
private:
const int number_of_buttons_ = 0;
std::atomic<int> button_flags_{0};
std::atomic<int> axes_[2]{0, 0};
private:
const int number_of_buttons_ = 0;
std::atomic<int> button_flags_{0};
std::atomic<int> axes_[2]{0, 0};
int primaries_[2] = {0, 0};
int secondaries_[2] = {0, 0};
int primaries_[2] = {0, 0};
int secondaries_[2] = {0, 0};
};
}

View File

@ -507,7 +507,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->quickload = accelerate_loading_;
return options;

View File

@ -314,7 +314,7 @@ private:
keyboard_.mouse_y_ += y;
}
int get_number_of_buttons() override {
int get_number_of_buttons() const override {
return 3;
}

View File

@ -589,7 +589,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
options->quickload = allow_fast_tape_hack_;

View File

@ -14,7 +14,7 @@ using namespace Amiga;
// MARK: - Mouse.
int Mouse::get_number_of_buttons() {
int Mouse::get_number_of_buttons() const {
return 2;
}

View File

@ -27,7 +27,7 @@ class Mouse: public Inputs::Mouse, public MouseJoystickInput {
uint8_t get_cia_button() const final;
private:
int get_number_of_buttons() final;
int get_number_of_buttons() const final;
void set_button_pressed(int, bool) final;
void reset_all_buttons() final;
void move(int, int) final;

View File

@ -1187,7 +1187,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
options->quickload = allow_fast_tape_hack_;

View File

@ -58,7 +58,7 @@ void Mouse::move(int x, int y) {
post_service_request();
}
int Mouse::get_number_of_buttons() {
int Mouse::get_number_of_buttons() const {
return 2;
}

View File

@ -21,7 +21,7 @@ class Mouse: public ReactiveDevice, public Inputs::Mouse {
void perform_command(const Command &command) override;
void move(int x, int y) override;
int get_number_of_buttons() override;
int get_number_of_buttons() const override;
void set_button_pressed(int index, bool is_pressed) override;
void reset_all_buttons() override;

View File

@ -1043,7 +1043,7 @@ template <Analyser::Static::AppleII::Target::Model model, bool has_mockingboard>
}
// MARK:: Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
options->use_square_pixels = video_.get_use_square_pixels();

View File

@ -55,11 +55,10 @@ void VideoBase::set_use_square_pixels(bool use_square_pixels) {
crt_.set_aspect_ratio(4.0f / 3.0f);
}
}
bool VideoBase::get_use_square_pixels() {
bool VideoBase::get_use_square_pixels() const {
return use_square_pixels_;
}
void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
crt_.set_scan_target(scan_target);
}

View File

@ -50,7 +50,7 @@ class VideoBase: public VideoSwitches<Cycles> {
/// Sets whether the current CRT should be recalibrated away from normative NTSC
/// to produce square pixels in 40-column text mode.
void set_use_square_pixels(bool);
bool get_use_square_pixels();
bool get_use_square_pixels() const;
protected:
Outputs::CRT::CRT crt_;

View File

@ -502,7 +502,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->quickboot = quickboot_;
return options;

View File

@ -692,7 +692,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
return options;

View File

@ -427,7 +427,7 @@ void IntelligentKeyboard::move(int x, int y) {
mouse_movement_[1] += y;
}
int IntelligentKeyboard::get_number_of_buttons() {
int IntelligentKeyboard::get_number_of_buttons() const {
return 2;
}

View File

@ -105,7 +105,7 @@ class IntelligentKeyboard:
// Inputs::Mouse.
void move(int x, int y) final;
int get_number_of_buttons() final;
int get_number_of_buttons() const final;
void set_button_pressed(int index, bool is_pressed) final;
void reset_all_buttons() final;

View File

@ -357,7 +357,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
return options;

View File

@ -678,7 +678,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options() final {
std::unique_ptr<Reflection::Struct> get_options() const final {
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
options->output = get_video_signal_configurable();
options->quickload = allow_fast_tape_hack_;

View File

@ -734,7 +734,7 @@ template <bool has_disk_controller, bool is_6mhz> class ConcreteMachine:
}
// MARK: - Configuration options.