diff options
Diffstat (limited to 'src/frontend/mame/iptseqpoll.cpp')
-rw-r--r-- | src/frontend/mame/iptseqpoll.cpp | 437 |
1 files changed, 356 insertions, 81 deletions
diff --git a/src/frontend/mame/iptseqpoll.cpp b/src/frontend/mame/iptseqpoll.cpp index 8082b257e2f..145c64f033a 100644 --- a/src/frontend/mame/iptseqpoll.cpp +++ b/src/frontend/mame/iptseqpoll.cpp @@ -1,113 +1,299 @@ // license:BSD-3-Clause -// copyright-holders:Vas Crabb,Aaron Giles +// copyright-holders:Vas Crabb, Aaron Giles #include "emu.h" #include "iptseqpoll.h" +#include "inputdev.h" + #include <cassert> +#include <algorithm> -input_sequence_poller::input_sequence_poller(input_manager &manager) noexcept : - m_manager(manager), - m_sequence(), - m_class(ITEM_CLASS_INVALID), - m_last_ticks(0), - m_modified(false) +input_code_poller::input_code_poller(input_manager &manager) noexcept : + m_manager(manager) { } -void input_sequence_poller::start(input_item_class itemclass) +input_code_poller::~input_code_poller() { - m_sequence.reset(); - do_start(itemclass); } -void input_sequence_poller::start(input_item_class itemclass, input_seq const &startseq) +void input_code_poller::reset() { - // grab the starting sequence to append to, and append an OR if it isn't empty - m_sequence = startseq; - if (input_seq::end_code != m_sequence[0]) - m_sequence += input_seq::or_code; + // iterate over device classes and devices + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + // for any non-switch items, set memory to the current value + input_device_item *const item(device->item(itemid)); + if (item && (item->itemclass() != ITEM_CLASS_SWITCH)) + item->set_memory(m_manager.code_value(item->code())); + } + } + } + } +} + + - do_start(itemclass); +switch_code_poller_base::switch_code_poller_base(input_manager &manager) noexcept : + input_code_poller(manager), + m_switch_memory() +{ } -bool input_sequence_poller::poll() +void switch_code_poller_base::reset() { - assert((ITEM_CLASS_SWITCH == m_class) || (ITEM_CLASS_ABSOLUTE == m_class) || (ITEM_CLASS_RELATIVE == m_class)); - int const curlen = m_sequence.length(); - input_code lastcode = m_sequence[curlen - 1]; + m_switch_memory.clear(); + input_code_poller::reset(); +} + + +bool switch_code_poller_base::code_pressed_once(input_code code) +{ + // look for the code in the memory + bool const pressed(m_manager.code_pressed(code)); + auto const found(std::lower_bound(m_switch_memory.begin(), m_switch_memory.end(), code)); + if ((m_switch_memory.end() != found) && (*found == code)) + { + // if no longer pressed, clear entry + if (!pressed) + m_switch_memory.erase(found); + + // always return false + return false; + } + + // if we get here, we were not previously pressed; if still not pressed, return false + if (!pressed) + return false; + + // otherwise, add the code to the memory and return true + m_switch_memory.emplace(found, code); + return true; +} + + + +axis_code_poller::axis_code_poller(input_manager &manager) noexcept : + input_code_poller(manager) +{ +} + - input_code newcode; - if (ITEM_CLASS_SWITCH == m_class) +input_code axis_code_poller::poll() +{ + // iterate over device classes and devices, skipping disabled classes + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) { - // switch case: see if we have a new code to process - newcode = m_manager.poll_switches(); - if (INPUT_CODE_INVALID != newcode) + input_class &devclass(m_manager.device_class(classno)); + if (devclass.enabled()) { - // if code is duplicate, toggle the NOT state on the code - if (curlen && (newcode == lastcode)) + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) { - // back up over the existing code - m_sequence.backspace(); - - // if there was a NOT preceding it, delete it as well, otherwise append a fresh one - if (m_sequence[curlen - 2] == input_seq::not_code) - m_sequence.backspace(); - else - m_sequence += input_seq::not_code; + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item(device->item(itemid)); + if (item && (item->itemclass() != ITEM_CLASS_SWITCH)) + { + input_code const code = item->code(); + if (item->check_axis(code.item_modifier())) + return code; + } + } + } } } } - else + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +switch_code_poller::switch_code_poller(input_manager &manager) noexcept : + switch_code_poller_base(manager) +{ +} + + +input_code switch_code_poller::poll() +{ + // iterate over device classes and devices, skipping disabled classes + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) { - // absolute/relative case: see if we have an analog change of sufficient amount - bool const has_or = input_seq::or_code == lastcode; - if (has_or) - lastcode = m_sequence[curlen - 2]; - newcode = m_manager.poll_axes(); - - // if the last code doesn't match absolute/relative of this code, ignore the new one - input_item_class const lastclass = lastcode.item_class(); - input_item_class const newclass = newcode.item_class(); - if (((ITEM_CLASS_ABSOLUTE == lastclass) && (ITEM_CLASS_ABSOLUTE != newclass)) || - ((ITEM_CLASS_RELATIVE == lastclass) && (ITEM_CLASS_RELATIVE != newclass))) - newcode = INPUT_CODE_INVALID; - - // if the new code is valid, check for half-axis toggles on absolute controls - if ((INPUT_CODE_INVALID != newcode) && curlen && (ITEM_CLASS_ABSOLUTE == newclass)) + input_class &devclass(m_manager.device_class(classno)); + if (devclass.enabled()) { - input_code last_nomodifier = lastcode; - last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); - if (newcode == last_nomodifier) + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) { - // increment the modifier, wrapping back to none - switch (lastcode.item_modifier()) + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) { - case ITEM_MODIFIER_NONE: - newcode.set_item_modifier(ITEM_MODIFIER_POS); - break; - case ITEM_MODIFIER_POS: - newcode.set_item_modifier(ITEM_MODIFIER_NEG); - break; - default: - case ITEM_MODIFIER_NEG: - newcode.set_item_modifier(ITEM_MODIFIER_NONE); - break; + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item(device->item(itemid)); + if (item) + { + input_code code = item->code(); + if (item->itemclass() == ITEM_CLASS_SWITCH) + { + // item is natively a switch, poll it + if (code_pressed_once(code)) + return code; + } + else if (item->check_axis(code.item_modifier())) + { + // poll axes digitally + code.set_item_class(ITEM_CLASS_SWITCH); + if ((classno == DEVICE_CLASS_JOYSTICK) && (code.item_id() == ITEM_ID_XAXIS)) + { + // joystick X axis - check with left/right modifiers + code.set_item_modifier(ITEM_MODIFIER_LEFT); + if (code_pressed_once(code)) + return code; + code.set_item_modifier(ITEM_MODIFIER_RIGHT); + if (code_pressed_once(code)) + return code; + } + else if ((classno == DEVICE_CLASS_JOYSTICK) && (code.item_id() == ITEM_ID_YAXIS)) + { + // if this is a joystick Y axis, check with up/down modifiers + code.set_item_modifier(ITEM_MODIFIER_UP); + if (code_pressed_once(code)) + return code; + code.set_item_modifier(ITEM_MODIFIER_DOWN); + if (code_pressed_once(code)) + return code; + } + else + { + // any other axis, check with pos/neg modifiers + code.set_item_modifier(ITEM_MODIFIER_POS); + if (code_pressed_once(code)) + return code; + code.set_item_modifier(ITEM_MODIFIER_NEG); + if (code_pressed_once(code)) + return code; + } + } + } + } } + } + } + } - // back up over the previous code so we can re-append - if (has_or) - m_sequence.backspace(); - m_sequence.backspace(); + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +keyboard_code_poller::keyboard_code_poller(input_manager &manager) noexcept : + switch_code_poller_base(manager) +{ +} + + +input_code keyboard_code_poller::poll() +{ + // iterate over devices in keyboard class + input_class &devclass = m_manager.device_class(DEVICE_CLASS_KEYBOARD); + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item = device->item(itemid); + if (item && (item->itemclass() == ITEM_CLASS_SWITCH)) + { + input_code const code = item->code(); + if (code_pressed_once(code)) + return code; + } + } } } } + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +input_sequence_poller::input_sequence_poller() noexcept : + m_sequence(), + m_last_ticks(0), + m_modified(false) +{ +} + + +input_sequence_poller::~input_sequence_poller() +{ +} + + +void input_sequence_poller::start() +{ + // start with an empty sequence + m_sequence.reset(); + + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); +} + + +void input_sequence_poller::start(input_seq const &startseq) +{ + // grab the starting sequence to append to, and append an OR if it isn't empty + m_sequence = startseq; + if (input_seq::end_code != m_sequence[0]) + m_sequence += input_seq::or_code; + + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); +} + + +bool input_sequence_poller::poll() +{ // if we got a new code to append it, append it and reset the timer + input_code const newcode = do_poll(); osd_ticks_t const newticks = osd_ticks(); if (INPUT_CODE_INVALID != newcode) { @@ -118,27 +304,116 @@ bool input_sequence_poller::poll() // if we're recorded at least one item and 2/3 of a second has passed, we're done if (m_last_ticks && ((m_last_ticks + (osd_ticks_per_second() * 2 / 3)) < newticks)) - { - m_class = ITEM_CLASS_INVALID; return true; - } // return false to indicate we are still polling return false; } -void input_sequence_poller::do_start(input_item_class itemclass) + +axis_sequence_poller::axis_sequence_poller(input_manager &manager) noexcept : + input_sequence_poller(), + m_code_poller(manager) { - assert((ITEM_CLASS_SWITCH == itemclass) || (ITEM_CLASS_ABSOLUTE == itemclass) || (ITEM_CLASS_RELATIVE == itemclass)); +} - // reset the recording count and the clock - m_class = itemclass; - m_last_ticks = 0; - m_modified = false; +void axis_sequence_poller::do_start() +{ // wait for any inputs that are already active to be released - m_manager.reset_polling(); + m_code_poller.reset(); for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; ) - dummycode = (ITEM_CLASS_SWITCH == itemclass) ? m_manager.poll_switches() : m_manager.poll_axes(); + dummycode = m_code_poller.poll(); +} + + +input_code axis_sequence_poller::do_poll() +{ + // absolute/relative case: see if we have an analog change of sufficient amount + int const curlen = m_sequence.length(); + input_code lastcode = m_sequence[curlen - 1]; + bool const has_or = input_seq::or_code == lastcode; + if (has_or) + lastcode = m_sequence[curlen - 2]; + input_code newcode = m_code_poller.poll(); + + // if the last code doesn't match absolute/relative of this code, ignore the new one + input_item_class const lastclass = lastcode.item_class(); + input_item_class const newclass = newcode.item_class(); + if (((ITEM_CLASS_ABSOLUTE == lastclass) && (ITEM_CLASS_ABSOLUTE != newclass)) || + ((ITEM_CLASS_RELATIVE == lastclass) && (ITEM_CLASS_RELATIVE != newclass))) + newcode = INPUT_CODE_INVALID; + + // if the new code is valid, check for half-axis toggles on absolute controls + if ((INPUT_CODE_INVALID != newcode) && curlen && (ITEM_CLASS_ABSOLUTE == newclass)) + { + input_code last_nomodifier = lastcode; + last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); + if (newcode == last_nomodifier) + { + // increment the modifier, wrapping back to none + switch (lastcode.item_modifier()) + { + case ITEM_MODIFIER_NONE: + newcode.set_item_modifier(ITEM_MODIFIER_POS); + break; + case ITEM_MODIFIER_POS: + newcode.set_item_modifier(ITEM_MODIFIER_NEG); + break; + default: + case ITEM_MODIFIER_NEG: + newcode.set_item_modifier(ITEM_MODIFIER_NONE); + break; + } + + // back up over the previous code so we can re-append + if (has_or) + m_sequence.backspace(); + m_sequence.backspace(); + } + } + return newcode; +} + + + +switch_sequence_poller::switch_sequence_poller(input_manager &manager) noexcept : + input_sequence_poller(), + m_code_poller(manager) +{ +} + + +void switch_sequence_poller::do_start() +{ + // wait for any inputs that are already active to be released + m_code_poller.reset(); + for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; ) + dummycode = m_code_poller.poll(); +} + + +input_code switch_sequence_poller::do_poll() +{ + // switch case: see if we have a new code to process + int const curlen = m_sequence.length(); + input_code lastcode = m_sequence[curlen - 1]; + input_code newcode = m_code_poller.poll(); + if (INPUT_CODE_INVALID != newcode) + { + // if code is duplicate, toggle the NOT state on the code + if (curlen && (newcode == lastcode)) + { + // back up over the existing code + m_sequence.backspace(); + + // if there was a NOT preceding it, delete it as well, otherwise append a fresh one + if (m_sequence[curlen - 2] == input_seq::not_code) + m_sequence.backspace(); + else + m_sequence += input_seq::not_code; + } + } + return newcode; } |