summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/iptseqpoll.cpp
blob: 8082b257e2f606db292c36e3e0da2a969b384c67 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// license:BSD-3-Clause
// copyright-holders:Vas Crabb,Aaron Giles

#include "emu.h"
#include "iptseqpoll.h"

#include <cassert>


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)
{
}


void input_sequence_poller::start(input_item_class itemclass)
{
	m_sequence.reset();
	do_start(itemclass);
}


void input_sequence_poller::start(input_item_class itemclass, 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);
}


bool input_sequence_poller::poll()
{
	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];

	input_code newcode;
	if (ITEM_CLASS_SWITCH == m_class)
	{
		// 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)
			{
				// 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();
			}
		}
	}

	// 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)
	{
		m_sequence += newcode;
		m_last_ticks = newticks;
		m_modified = true;
	}

	// 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)
{
	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;

	// wait for any inputs that are already active to be released
	m_manager.reset_polling();
	for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; )
		dummycode = (ITEM_CLASS_SWITCH == itemclass) ? m_manager.poll_switches() : m_manager.poll_axes();
}