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