1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria
3 /***************************************************************************
4 
5     uiinput.cpp
6 
7     Internal MAME user interface input state.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "uiinput.h"
13 #include "render.h"
14 
15 
16 /***************************************************************************
17     CONSTANTS
18 ***************************************************************************/
19 
20 enum
21 {
22 	SEQ_PRESSED_FALSE = 0,      /* not pressed */
23 	SEQ_PRESSED_TRUE,           /* pressed */
24 	SEQ_PRESSED_RESET           /* reset -- converted to false once detected as not pressed */
25 };
26 
27 
28 //**************************************************************************
29 //  UI INPUT MANAGER
30 //**************************************************************************
31 
32 //-------------------------------------------------
33 //  ui_input_manager - constructor
34 //-------------------------------------------------
35 
ui_input_manager(running_machine & machine)36 ui_input_manager::ui_input_manager(running_machine &machine)
37 	: m_machine(machine)
38 	, m_presses_enabled(true)
39 	, m_current_mouse_target(nullptr)
40 	, m_current_mouse_x(-1)
41 	, m_current_mouse_y(-1)
42 	, m_current_mouse_down(false)
43 	, m_current_mouse_field(nullptr)
44 	, m_events_start(0)
45 	, m_events_end(0)
46 {
47 	std::fill(std::begin(m_next_repeat), std::end(m_next_repeat), 0);
48 	std::fill(std::begin(m_seqpressed), std::end(m_seqpressed), 0);
49 
50 	// add a frame callback to poll inputs
51 	machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&ui_input_manager::frame_update, this));
52 }
53 
54 
55 
56 /***************************************************************************
57     EVENT HANDLING
58 ***************************************************************************/
59 
60 /*-------------------------------------------------
61     frame_update - looks through pressed
62     input as per events pushed our way and posts
63     corresponding IPT_UI_* events
64 -------------------------------------------------*/
65 
frame_update()66 void ui_input_manager::frame_update()
67 {
68 	// update the state of all the UI keys
69 	for (ioport_type code = ioport_type(IPT_UI_FIRST + 1); code < IPT_UI_LAST; ++code)
70 	{
71 		if (m_presses_enabled)
72 		{
73 			bool pressed = machine().ioport().type_pressed(code);
74 			if (!pressed || m_seqpressed[code] != SEQ_PRESSED_RESET)
75 				m_seqpressed[code] = pressed;
76 		}
77 		else
78 		{
79 			// UI key presses are disabled
80 			m_seqpressed[code] = false;
81 		}
82 	}
83 
84 	// perform mouse hit testing
85 	ioport_field *mouse_field = m_current_mouse_down ? find_mouse_field() : nullptr;
86 	if (m_current_mouse_field != mouse_field)
87 	{
88 		// clear the old field if there was one
89 		if (m_current_mouse_field != nullptr)
90 			m_current_mouse_field->set_value(0);
91 
92 		// set the new field if it exists and isn't already being pressed
93 		if (mouse_field != nullptr && !mouse_field->digital_value())
94 			mouse_field->set_value(1);
95 
96 		// update internal state
97 		m_current_mouse_field = mouse_field;
98 	}
99 }
100 
101 
102 /*-------------------------------------------------
103     push_event - pushes a single event
104     onto the queue
105 -------------------------------------------------*/
106 
push_event(ui_event evt)107 bool ui_input_manager::push_event(ui_event evt)
108 {
109 	// some pre-processing (this is an icky place to do this stuff!)
110 	switch (evt.event_type)
111 	{
112 		case ui_event::type::MOUSE_MOVE:
113 			m_current_mouse_target = evt.target;
114 			m_current_mouse_x = evt.mouse_x;
115 			m_current_mouse_y = evt.mouse_y;
116 			break;
117 
118 		case ui_event::type::MOUSE_LEAVE:
119 			if (m_current_mouse_target == evt.target)
120 			{
121 				m_current_mouse_target = nullptr;
122 				m_current_mouse_x = -1;
123 				m_current_mouse_y = -1;
124 			}
125 			break;
126 
127 		case ui_event::type::MOUSE_DOWN:
128 			m_current_mouse_down = true;
129 			break;
130 
131 		case ui_event::type::MOUSE_UP:
132 			m_current_mouse_down = false;
133 			break;
134 
135 		default:
136 			/* do nothing */
137 			break;
138 	}
139 
140 	// is the queue filled up?
141 	if ((m_events_end + 1) % ARRAY_LENGTH(m_events) == m_events_start)
142 		return false;
143 
144 	m_events[m_events_end++] = evt;
145 	m_events_end %= ARRAY_LENGTH(m_events);
146 	return true;
147 }
148 
149 
150 /*-------------------------------------------------
151     pop_event - pops an event off of the queue
152 -------------------------------------------------*/
153 
pop_event(ui_event * evt)154 bool ui_input_manager::pop_event(ui_event *evt)
155 {
156 	if (m_events_start != m_events_end)
157 	{
158 		*evt = m_events[m_events_start++];
159 		m_events_start %= ARRAY_LENGTH(m_events);
160 		return true;
161 	}
162 	else
163 	{
164 		memset(evt, 0, sizeof(*evt));
165 		return false;
166 	}
167 }
168 
169 
170 /*-------------------------------------------------
171     reset - clears all outstanding events
172     and resets the sequence states
173 -------------------------------------------------*/
174 
reset()175 void ui_input_manager::reset()
176 {
177 	int code;
178 
179 	m_events_start = 0;
180 	m_events_end = 0;
181 	for (code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
182 	{
183 		m_seqpressed[code] = SEQ_PRESSED_RESET;
184 		m_next_repeat[code] = 0;
185 	}
186 }
187 
188 
189 /*-------------------------------------------------
190     find_mouse - retrieves the current
191     location of the mouse
192 -------------------------------------------------*/
193 
find_mouse(s32 * x,s32 * y,bool * button) const194 render_target *ui_input_manager::find_mouse(s32 *x, s32 *y, bool *button) const
195 {
196 	if (x != nullptr)
197 		*x = m_current_mouse_x;
198 	if (y != nullptr)
199 		*y = m_current_mouse_y;
200 	if (button != nullptr)
201 		*button = m_current_mouse_down;
202 	return m_current_mouse_target;
203 }
204 
205 
206 /*-------------------------------------------------
207     find_mouse_field - retrieves the input field
208     the mouse is currently pointing at
209 -------------------------------------------------*/
210 
find_mouse_field() const211 ioport_field *ui_input_manager::find_mouse_field() const
212 {
213 	// map the point and determine what was hit
214 	if (m_current_mouse_target != nullptr)
215 	{
216 		ioport_port *port = nullptr;
217 		ioport_value mask;
218 		float x, y;
219 		if (m_current_mouse_target->map_point_input(m_current_mouse_x, m_current_mouse_y, port, mask, x, y))
220 		{
221 			if (port != nullptr)
222 				return port->field(mask);
223 		}
224 	}
225 	return nullptr;
226 }
227 
228 
229 
230 /***************************************************************************
231     USER INTERFACE SEQUENCE READING
232 ***************************************************************************/
233 
234 /*-------------------------------------------------
235     pressed - return true if a key down
236     for the given user interface sequence is
237     detected
238 -------------------------------------------------*/
239 
pressed(int code)240 bool ui_input_manager::pressed(int code)
241 {
242 	return pressed_repeat(code, 0);
243 }
244 
245 
246 /*-------------------------------------------------
247     pressed_repeat - return true if a key
248     down for the given user interface sequence is
249     detected, or if autorepeat at the given speed
250     is triggered
251 -------------------------------------------------*/
252 
pressed_repeat(int code,int speed)253 bool ui_input_manager::pressed_repeat(int code, int speed)
254 {
255 	bool pressed;
256 
257 g_profiler.start(PROFILER_INPUT);
258 
259 	/* get the status of this key (assumed to be only in the defaults) */
260 	assert(code >= IPT_UI_CONFIGURE && code <= IPT_OSD_16);
261 	pressed = (m_seqpressed[code] == SEQ_PRESSED_TRUE);
262 
263 	/* if down, handle it specially */
264 	if (pressed)
265 	{
266 		osd_ticks_t tps = osd_ticks_per_second();
267 
268 		/* if this is the first press, set a 3x delay and leave pressed = 1 */
269 		if (m_next_repeat[code] == 0)
270 			m_next_repeat[code] = osd_ticks() + 3 * speed * tps / 60;
271 
272 		/* if this is an autorepeat case, set a 1x delay and leave pressed = 1 */
273 		else if (speed > 0 && (osd_ticks() + tps - m_next_repeat[code]) >= tps)
274 		{
275 			// In the autorepeatcase, we need to double check the key is still pressed
276 			// as there can be a delay between the key polling and our processing of the event
277 			m_seqpressed[code] = machine().ioport().type_pressed(ioport_type(code));
278 			pressed = (m_seqpressed[code] == SEQ_PRESSED_TRUE);
279 			if (pressed)
280 				m_next_repeat[code] += 1 * speed * tps / 60;
281 		}
282 
283 		/* otherwise, reset pressed = 0 */
284 		else
285 			pressed = false;
286 	}
287 
288 	/* if we're not pressed, reset the memory field */
289 	else
290 		m_next_repeat[code] = 0;
291 
292 g_profiler.stop();
293 
294 	return pressed;
295 }
296 
297 /*-------------------------------------------------
298     push_mouse_move_event - pushes a mouse
299     move event to the specified render_target
300 -------------------------------------------------*/
301 
push_mouse_move_event(render_target * target,s32 x,s32 y)302 void ui_input_manager::push_mouse_move_event(render_target* target, s32 x, s32 y)
303 {
304 	ui_event event = { ui_event::type::NONE };
305 	event.event_type = ui_event::type::MOUSE_MOVE;
306 	event.target = target;
307 	event.mouse_x = x;
308 	event.mouse_y = y;
309 	push_event(event);
310 }
311 
312 /*-------------------------------------------------
313     push_mouse_leave_event - pushes a
314     mouse leave event to the specified render_target
315 -------------------------------------------------*/
316 
push_mouse_leave_event(render_target * target)317 void ui_input_manager::push_mouse_leave_event(render_target* target)
318 {
319 	ui_event event = { ui_event::type::NONE };
320 	event.event_type = ui_event::type::MOUSE_LEAVE;
321 	event.target = target;
322 	push_event(event);
323 }
324 
325 /*-------------------------------------------------
326     push_mouse_down_event - pushes a mouse
327     down event to the specified render_target
328 -------------------------------------------------*/
329 
push_mouse_down_event(render_target * target,s32 x,s32 y)330 void ui_input_manager::push_mouse_down_event(render_target* target, s32 x, s32 y)
331 {
332 	ui_event event = { ui_event::type::NONE };
333 	event.event_type = ui_event::type::MOUSE_DOWN;
334 	event.target = target;
335 	event.mouse_x = x;
336 	event.mouse_y = y;
337 	push_event(event);
338 }
339 
340 /*-------------------------------------------------
341     push_mouse_down_event - pushes a mouse
342     down event to the specified render_target
343 -------------------------------------------------*/
344 
push_mouse_up_event(render_target * target,s32 x,s32 y)345 void ui_input_manager::push_mouse_up_event(render_target* target, s32 x, s32 y)
346 {
347 	ui_event event = { ui_event::type::NONE };
348 	event.event_type = ui_event::type::MOUSE_UP;
349 	event.target = target;
350 	event.mouse_x = x;
351 	event.mouse_y = y;
352 	push_event(event);
353 }
354 
355 /*-------------------------------------------------
356 push_mouse_down_event - pushes a mouse
357 down event to the specified render_target
358 -------------------------------------------------*/
359 
push_mouse_rdown_event(render_target * target,s32 x,s32 y)360 void ui_input_manager::push_mouse_rdown_event(render_target* target, s32 x, s32 y)
361 {
362 	ui_event event = { ui_event::type::NONE };
363 	event.event_type = ui_event::type::MOUSE_RDOWN;
364 	event.target = target;
365 	event.mouse_x = x;
366 	event.mouse_y = y;
367 	push_event(event);
368 }
369 
370 /*-------------------------------------------------
371 push_mouse_down_event - pushes a mouse
372 down event to the specified render_target
373 -------------------------------------------------*/
374 
push_mouse_rup_event(render_target * target,s32 x,s32 y)375 void ui_input_manager::push_mouse_rup_event(render_target* target, s32 x, s32 y)
376 {
377 	ui_event event = { ui_event::type::NONE };
378 	event.event_type = ui_event::type::MOUSE_RUP;
379 	event.target = target;
380 	event.mouse_x = x;
381 	event.mouse_y = y;
382 	push_event(event);
383 }
384 
385 /*-------------------------------------------------
386     push_mouse_double_click_event - pushes
387     a mouse double-click event to the specified
388     render_target
389 -------------------------------------------------*/
push_mouse_double_click_event(render_target * target,s32 x,s32 y)390 void ui_input_manager::push_mouse_double_click_event(render_target* target, s32 x, s32 y)
391 {
392 	ui_event event = { ui_event::type::NONE };
393 	event.event_type = ui_event::type::MOUSE_DOUBLE_CLICK;
394 	event.target = target;
395 	event.mouse_x = x;
396 	event.mouse_y = y;
397 	push_event(event);
398 }
399 
400 /*-------------------------------------------------
401     push_char_event - pushes a char event
402     to the specified render_target
403 -------------------------------------------------*/
push_char_event(render_target * target,char32_t ch)404 void ui_input_manager::push_char_event(render_target* target, char32_t ch)
405 {
406 	ui_event event = { ui_event::type::NONE };
407 	event.event_type = ui_event::type::IME_CHAR;
408 	event.target = target;
409 	event.ch = ch;
410 	push_event(event);
411 }
412 
413 /*-------------------------------------------------
414     push_mouse_wheel_event - pushes a mouse
415     wheel event to the specified render_target
416 -------------------------------------------------*/
417 
push_mouse_wheel_event(render_target * target,s32 x,s32 y,short delta,int ucNumLines)418 void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int ucNumLines)
419 {
420 	ui_event event = { ui_event::type::NONE };
421 	event.event_type = ui_event::type::MOUSE_WHEEL;
422 	event.target = target;
423 	event.mouse_x = x;
424 	event.mouse_y = y;
425 	event.zdelta = delta;
426 	event.num_lines = ucNumLines;
427 	push_event(event);
428 }
429 
430 /*-------------------------------------------------
431     mark_all_as_pressed - marks all buttons
432     as if they were already pressed once
433 -------------------------------------------------*/
mark_all_as_pressed()434 void ui_input_manager::mark_all_as_pressed()
435 {
436 	for (int code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
437 		m_next_repeat[code] = osd_ticks();
438 }
439