diff options
Diffstat (limited to 'src/frontend/mame/iptseqpoll.cpp')
-rw-r--r-- | src/frontend/mame/iptseqpoll.cpp | 508 |
1 files changed, 439 insertions, 69 deletions
diff --git a/src/frontend/mame/iptseqpoll.cpp b/src/frontend/mame/iptseqpoll.cpp index 8082b257e2f..7808256c190 100644 --- a/src/frontend/mame/iptseqpoll.cpp +++ b/src/frontend/mame/iptseqpoll.cpp @@ -1,88 +1,396 @@ // 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 : +input_code_poller::input_code_poller(input_manager &manager) noexcept : m_manager(manager), + m_axis_memory(), + m_switch_memory() +{ +} + + +input_code_poller::~input_code_poller() +{ +} + + +void input_code_poller::reset() +{ + // iterate over device classes and devices + m_axis_memory.clear(); + m_switch_memory.clear(); + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + if (devclass.enabled()) + { + 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)) + m_axis_memory.emplace_back(item, m_manager.code_value(item->code())); + } + } + } + } + } + std::sort(m_axis_memory.begin(), m_axis_memory.end()); +} + + +bool input_code_poller::code_pressed_once(input_code code, bool moved) +{ + // 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 || !moved) + 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), + m_axis_active() +{ +} + + +void axis_code_poller::reset() +{ + input_code_poller::reset(); + m_axis_active.clear(); + m_axis_active.resize(m_axis_memory.size(), false); +} + + +input_code axis_code_poller::poll() +{ + // iterate over the axis items we found + for (std::size_t i = 0; m_axis_memory.size() > i; ++i) + { + auto &memory = m_axis_memory[i]; + input_code code = memory.first->code(); + if (!memory.first->check_axis(code.item_modifier(), memory.second)) + { + m_axis_active[i] = false; + } + else if (!m_axis_active[i]) + { + if (code.item_class() == ITEM_CLASS_ABSOLUTE) + { + m_axis_active[i] = true; + } + else + { + // can only cycle modifiers on a relative item with append + m_axis_memory.erase(m_axis_memory.begin() + i); + m_axis_active.erase(m_axis_active.begin() + i); + } + if (!m_manager.device_class(memory.first->device().devclass()).multi()) + code.set_device_index(0); + return code; + } + } + + // iterate over device classes and devices, skipping disabled classes + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + if (!devclass.enabled()) + continue; + + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (!device) + continue; + + // 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) + continue; + + input_code code = item->code(); + if (item->itemclass() == ITEM_CLASS_SWITCH) + { + // item is natively a switch, poll it + if (code_pressed_once(code, true)) + return code; + else + continue; + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +switch_code_poller::switch_code_poller(input_manager &manager) noexcept : + input_code_poller(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) + { + input_class &devclass(m_manager.device_class(classno)); + if (!devclass.enabled()) + continue; + + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (!device) + continue; + + // 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) + continue; + + input_code code = item->code(); + if (item->itemclass() == ITEM_CLASS_SWITCH) + { + // item is natively a switch, poll it + if (code_pressed_once(code, true)) + return code; + else + continue; + } + + auto const memory(std::lower_bound( + m_axis_memory.begin(), + m_axis_memory.end(), + item, + [] (auto const &x, auto const &y) { return x.first < y; })); + if ((m_axis_memory.end() == memory) || (item != memory->first)) + continue; + + // poll axes digitally + bool const moved(item->check_axis(code.item_modifier(), memory->second)); + 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, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_RIGHT); + if (code_pressed_once(code, moved)) + 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, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_DOWN); + if (code_pressed_once(code, moved)) + return code; + } + else + { + // any other axis, check with pos/neg modifiers + code.set_item_modifier(ITEM_MODIFIER_POS); + if (code_pressed_once(code, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_NEG); + if (code_pressed_once(code, moved)) + return code; + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +keyboard_code_poller::keyboard_code_poller(input_manager &manager) noexcept : + input_code_poller(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, true)) + return code; + } + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +input_sequence_poller::input_sequence_poller() noexcept : m_sequence(), - m_class(ITEM_CLASS_INVALID), m_last_ticks(0), m_modified(false) { } -void input_sequence_poller::start(input_item_class itemclass) +input_sequence_poller::~input_sequence_poller() +{ +} + + +void input_sequence_poller::start() { + // start with an empty sequence m_sequence.reset(); - do_start(itemclass); + + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); } -void input_sequence_poller::start(input_item_class itemclass, input_seq const &startseq) +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; - do_start(itemclass); + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); } bool input_sequence_poller::poll() { - assert((ITEM_CLASS_SWITCH == m_class) || (ITEM_CLASS_ABSOLUTE == m_class) || (ITEM_CLASS_RELATIVE == m_class)); + // 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) + { + m_sequence += newcode; + m_last_ticks = newticks; + m_modified = true; + } + + // if we've recorded at least one item and one second has passed, we're done + if (m_last_ticks && ((m_last_ticks + osd_ticks_per_second()) < newticks)) + return true; + + // return false to indicate we are still polling + return false; +} + + + +axis_sequence_poller::axis_sequence_poller(input_manager &manager) noexcept : + input_sequence_poller(), + m_code_poller(manager) +{ +} + + +void axis_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 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(); - input_code newcode; - if (ITEM_CLASS_SWITCH == m_class) + // if not empty, see if it's the same control again to cycle modifiers + if ((INPUT_CODE_INVALID != newcode) && curlen) { - // switch case: see if we have a new code to process - newcode = m_manager.poll_switches(); - 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; - } - } - } - else - { - // 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_code last_nomodifier = lastcode; + last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); + if (newcode == last_nomodifier) { - input_code last_nomodifier = lastcode; - last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); - if (newcode == last_nomodifier) + if (ITEM_CLASS_ABSOLUTE == newclass) { // increment the modifier, wrapping back to none switch (lastcode.item_modifier()) @@ -93,8 +401,11 @@ bool input_sequence_poller::poll() case ITEM_MODIFIER_POS: newcode.set_item_modifier(ITEM_MODIFIER_NEG); break; - default: case ITEM_MODIFIER_NEG: + newcode.set_item_modifier(ITEM_MODIFIER_REVERSE); + break; + default: + case ITEM_MODIFIER_REVERSE: newcode.set_item_modifier(ITEM_MODIFIER_NONE); break; } @@ -104,41 +415,100 @@ bool input_sequence_poller::poll() m_sequence.backspace(); m_sequence.backspace(); } + else if (ITEM_CLASS_RELATIVE == newclass) + { + // increment the modifier, wrapping back to none + switch (lastcode.item_modifier()) + { + case ITEM_MODIFIER_NONE: + newcode.set_item_modifier(ITEM_MODIFIER_REVERSE); + break; + default: + case ITEM_MODIFIER_REVERSE: + 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(); + } + else if (!has_or && (ITEM_CLASS_SWITCH == newclass)) + { + // 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; + } + } + else if (!has_or && (ITEM_CLASS_SWITCH == newclass)) + { + // ignore switches following axes + if (ITEM_CLASS_SWITCH != lastcode.item_class()) + { + // hack to stop it timing out so user can cancel + m_sequence.backspace(); + newcode = lastcode; + } } } - // if we got a new code to append it, append it and reset the timer - osd_ticks_t const newticks = osd_ticks(); - if (INPUT_CODE_INVALID != newcode) + // hack to stop it timing out before assigning an axis + if (ITEM_CLASS_SWITCH == newcode.item_class()) { m_sequence += newcode; - m_last_ticks = newticks; - m_modified = true; + set_modified(); + return INPUT_CODE_INVALID; } - - // 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)) + else { - m_class = ITEM_CLASS_INVALID; - return true; + return newcode; } - - // return false to indicate we are still polling - return false; } -void input_sequence_poller::do_start(input_item_class itemclass) + +switch_sequence_poller::switch_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 switch_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 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; } |