summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/input')
-rw-r--r--src/osd/modules/input/assignmenthelper.cpp592
-rw-r--r--src/osd/modules/input/assignmenthelper.h118
-rw-r--r--src/osd/modules/input/input_common.cpp400
-rw-r--r--src/osd/modules/input/input_common.h504
-rw-r--r--src/osd/modules/input/input_dinput.cpp1540
-rw-r--r--src/osd/modules/input/input_dinput.h331
-rw-r--r--src/osd/modules/input/input_mac.cpp13
-rw-r--r--src/osd/modules/input/input_module.h20
-rw-r--r--src/osd/modules/input/input_none.cpp72
-rw-r--r--src/osd/modules/input/input_rawinput.cpp1028
-rw-r--r--src/osd/modules/input/input_sdl.cpp2751
-rw-r--r--src/osd/modules/input/input_sdlcommon.cpp318
-rw-r--r--src/osd/modules/input/input_sdlcommon.h204
-rw-r--r--src/osd/modules/input/input_uwp.cpp649
-rw-r--r--src/osd/modules/input/input_win32.cpp622
-rw-r--r--src/osd/modules/input/input_wincommon.h40
-rw-r--r--src/osd/modules/input/input_windows.cpp130
-rw-r--r--src/osd/modules/input/input_windows.h75
-rw-r--r--src/osd/modules/input/input_winhybrid.cpp351
-rw-r--r--src/osd/modules/input/input_x11.cpp678
-rw-r--r--src/osd/modules/input/input_xinput.cpp3164
-rw-r--r--src/osd/modules/input/input_xinput.h142
22 files changed, 9290 insertions, 4452 deletions
diff --git a/src/osd/modules/input/assignmenthelper.cpp b/src/osd/modules/input/assignmenthelper.cpp
new file mode 100644
index 00000000000..c044d4b7aa7
--- /dev/null
+++ b/src/osd/modules/input/assignmenthelper.cpp
@@ -0,0 +1,592 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+//============================================================
+//
+// assignmenthelper.cpp - input assignment setup helper
+//
+//============================================================
+
+#include "assignmenthelper.h"
+
+#include "interface/inputseq.h"
+
+#include "inpttype.h"
+
+
+namespace osd {
+
+bool joystick_assignment_helper::add_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type fieldtype,
+ input_seq_type seqtype,
+ input_item_class itemclass,
+ input_item_modifier modifier,
+ std::initializer_list<input_item_id> items)
+{
+ for (input_item_id item : items)
+ {
+ if (ITEM_ID_INVALID != item)
+ {
+ assignments.emplace_back(
+ fieldtype,
+ seqtype,
+ input_seq(input_code(DEVICE_CLASS_JOYSTICK, 0, itemclass, modifier, item)));
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool joystick_assignment_helper::add_button_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field_type,
+ std::initializer_list<input_item_id> items)
+{
+ return add_assignment(
+ assignments,
+ field_type,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_SWITCH,
+ ITEM_MODIFIER_NONE,
+ items);
+}
+
+
+bool joystick_assignment_helper::add_button_pair_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id button1,
+ input_item_id button2)
+{
+ if ((ITEM_ID_INVALID == button1) || (ITEM_ID_INVALID == button2))
+ return false;
+
+ assignments.emplace_back(
+ field1,
+ SEQ_TYPE_STANDARD,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button1));
+ assignments.emplace_back(
+ field2,
+ SEQ_TYPE_STANDARD,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button2));
+ return true;
+}
+
+
+bool joystick_assignment_helper::add_axis_inc_dec_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field_type,
+ input_item_id button_dec,
+ input_item_id button_inc)
+{
+ if ((ITEM_ID_INVALID == button_dec) || (ITEM_ID_INVALID == button_inc))
+ return false;
+
+ assignments.emplace_back(
+ field_type,
+ SEQ_TYPE_DECREMENT,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button_dec));
+ assignments.emplace_back(
+ field_type,
+ SEQ_TYPE_INCREMENT,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button_inc));
+ return true;
+}
+
+
+bool joystick_assignment_helper::add_axis_pair_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id axis)
+{
+ if (ITEM_ID_INVALID == axis)
+ return false;
+
+ assignments.emplace_back(
+ field1,
+ SEQ_TYPE_STANDARD,
+ make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == axis) ? ITEM_MODIFIER_LEFT : (ITEM_ID_YAXIS == axis) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG,
+ axis));
+ assignments.emplace_back(
+ field2,
+ SEQ_TYPE_STANDARD,
+ make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == axis) ? ITEM_MODIFIER_RIGHT : (ITEM_ID_YAXIS == axis) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS,
+ axis));
+ return true;
+}
+
+
+bool joystick_assignment_helper::consume_button_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &button1,
+ input_item_id &button2)
+{
+ if (!add_button_pair_assignment(assignments, field1, field2, button1, button2))
+ return false;
+
+ button1 = ITEM_ID_INVALID;
+ button2 = ITEM_ID_INVALID;
+ return true;
+}
+
+
+bool joystick_assignment_helper::consume_trigger_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &axis1,
+ input_item_id &axis2)
+{
+ if ((ITEM_ID_INVALID == axis1) || (ITEM_ID_INVALID == axis2))
+ return false;
+
+ assignments.emplace_back(
+ field1,
+ SEQ_TYPE_STANDARD,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis1));
+ assignments.emplace_back(
+ field2,
+ SEQ_TYPE_STANDARD,
+ make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis2));
+ axis1 = ITEM_ID_INVALID;
+ axis2 = ITEM_ID_INVALID;
+ return true;
+}
+
+
+bool joystick_assignment_helper::consume_axis_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &axis)
+{
+ if (!add_axis_pair_assignment(assignments, field1, field2, axis))
+ return false;
+
+ axis = ITEM_ID_INVALID;
+ return true;
+}
+
+
+void joystick_assignment_helper::add_directional_assignments(
+ input_device::assignment_vector &assignments,
+ input_item_id xaxis,
+ input_item_id yaxis,
+ input_item_id leftswitch,
+ input_item_id rightswitch,
+ input_item_id upswitch,
+ input_item_id downswitch)
+{
+ // see if we have complementary pairs of directional switches
+ bool const hswitches = (ITEM_ID_INVALID != leftswitch) && (ITEM_ID_INVALID != rightswitch);
+ bool const vswitches = (ITEM_ID_INVALID != upswitch) && (ITEM_ID_INVALID != downswitch);
+
+ // use X axis if present
+ if (ITEM_ID_INVALID != xaxis)
+ {
+ // use this for horizontal axis movement
+ input_seq const xseq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, xaxis));
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, xseq);
+
+ // use it for the main left/right control, too
+ input_seq leftseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_XAXIS == xaxis) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG, xaxis));
+ input_seq rightseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_XAXIS == xaxis) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS, xaxis));
+ if (ITEM_ID_INVALID != leftswitch)
+ {
+ leftseq += input_seq::or_code;
+ leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch);
+ }
+ if (ITEM_ID_INVALID != rightswitch)
+ {
+ rightseq += input_seq::or_code;
+ rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch);
+ }
+ assignments.emplace_back(IPT_JOYSTICK_LEFT, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_JOYSTICK_RIGHT, SEQ_TYPE_STANDARD, rightseq);
+
+ // use for vertical navigation if there's no Y axis, or horizontal otherwise
+ if (ITEM_ID_INVALID != yaxis)
+ {
+ // if left/right are both present but not both up/down, they'll be taken for vertical navigation
+ if (hswitches && !vswitches)
+ {
+ leftseq.backspace();
+ if (ITEM_ID_INVALID != upswitch)
+ leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch);
+ else
+ leftseq.backspace();
+
+ rightseq.backspace();
+ if (ITEM_ID_INVALID != downswitch)
+ rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch);
+ else
+ rightseq.backspace();
+ }
+ assignments.emplace_back(IPT_UI_LEFT, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_RIGHT, SEQ_TYPE_STANDARD, rightseq);
+ }
+ else
+ {
+ // prefer D-pad up/down for vertical navigation if present
+ if (!hswitches || vswitches)
+ {
+ while (leftseq.length() > 1)
+ leftseq.backspace();
+ if (ITEM_ID_INVALID != upswitch)
+ {
+ leftseq += input_seq::or_code;
+ leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch);
+ }
+
+ while (rightseq.length() > 1)
+ rightseq.backspace();
+ if (ITEM_ID_INVALID != downswitch)
+ {
+ rightseq += input_seq::or_code;
+ rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch);
+ }
+ }
+ assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, rightseq);
+ }
+ }
+ else
+ {
+ // without a primary analog X axis, we still want D-pad left/right controls if possible
+ add_button_assignment(assignments, IPT_JOYSTICK_LEFT, { leftswitch });
+ add_button_assignment(assignments, IPT_JOYSTICK_RIGHT, { rightswitch });
+
+ // vertical navigation gets first pick on directional controls
+ add_button_assignment(assignments, IPT_UI_LEFT, { (hswitches && !vswitches) ? upswitch : leftswitch });
+ add_button_assignment(assignments, IPT_UI_RIGHT, { (hswitches && !vswitches) ? downswitch : rightswitch });
+ }
+
+ // use Y axis if present
+ if (ITEM_ID_INVALID != yaxis)
+ {
+ // use this for vertical axis movement
+ input_seq const yseq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, yaxis));
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, yseq);
+
+ // use it for the main up/down control, too
+ input_seq upseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_YAXIS == yaxis) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG, yaxis));
+ input_seq downseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_YAXIS == yaxis) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS, yaxis));
+ if (ITEM_ID_INVALID != upswitch)
+ {
+ upseq += input_seq::or_code;
+ upseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch);
+ }
+ if (ITEM_ID_INVALID != downswitch)
+ {
+ downseq += input_seq::or_code;
+ downseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch);
+ }
+ assignments.emplace_back(IPT_JOYSTICK_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_JOYSTICK_DOWN, SEQ_TYPE_STANDARD, downseq);
+
+ // if available, this is used for vertical navigation
+ if (hswitches && !vswitches)
+ {
+ if (upseq.length() > 1)
+ upseq.backspace();
+ else
+ upseq += input_seq::or_code;
+ upseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch);
+
+ if (downseq.length() > 1)
+ downseq.backspace();
+ else
+ downseq += input_seq::or_code;
+ downseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch);
+ }
+ assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, downseq);
+ }
+ else
+ {
+ // without a primary analog Y axis, we still want D-pad up/down controls if possible
+ add_button_assignment(assignments, IPT_JOYSTICK_UP, { upswitch });
+ add_button_assignment(assignments, IPT_JOYSTICK_DOWN, { downswitch });
+
+ // vertical navigation may be assigned to X axis if Y axis is not present
+ bool const dpadflip = (ITEM_ID_INVALID != xaxis) == (!hswitches || vswitches);
+ add_button_assignment(
+ assignments,
+ (ITEM_ID_INVALID != xaxis) ? IPT_UI_LEFT : IPT_UI_UP,
+ { dpadflip ? leftswitch : upswitch });
+ add_button_assignment(
+ assignments,
+ (ITEM_ID_INVALID != xaxis) ? IPT_UI_RIGHT : IPT_UI_DOWN,
+ { dpadflip ? rightswitch : downswitch });
+ }
+
+ // if we're missing either primary axis, fall back to D-pad for analog increment/decrement
+ if ((ITEM_ID_INVALID == xaxis) || (ITEM_ID_INVALID == yaxis))
+ {
+ if (ITEM_ID_INVALID != leftswitch)
+ {
+ input_seq const leftseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch));
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_DECREMENT, leftseq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_DECREMENT, leftseq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_DECREMENT, leftseq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_DECREMENT, leftseq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_DECREMENT, leftseq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_DECREMENT, leftseq);
+ }
+ if (ITEM_ID_INVALID != rightswitch)
+ {
+ input_seq const rightseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch));
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_INCREMENT, rightseq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_INCREMENT, rightseq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_INCREMENT, rightseq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_INCREMENT, rightseq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_INCREMENT, rightseq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_INCREMENT, rightseq);
+ }
+ if (ITEM_ID_INVALID != upswitch)
+ {
+ input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch));
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_DECREMENT, upseq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_DECREMENT, upseq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_DECREMENT, upseq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_DECREMENT, upseq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_DECREMENT, upseq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_DECREMENT, upseq);
+ }
+ if (ITEM_ID_INVALID != downswitch)
+ {
+ input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch));
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_INCREMENT, downseq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_INCREMENT, downseq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_INCREMENT, downseq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_INCREMENT, downseq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_INCREMENT, downseq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_INCREMENT, downseq);
+ }
+ }
+}
+
+
+void joystick_assignment_helper::add_twin_stick_assignments(
+ input_device::assignment_vector &assignments,
+ input_item_id leftx,
+ input_item_id lefty,
+ input_item_id rightx,
+ input_item_id righty,
+ input_item_id leftleft,
+ input_item_id leftright,
+ input_item_id leftup,
+ input_item_id leftdown,
+ input_item_id rightleft,
+ input_item_id rightright,
+ input_item_id rightup,
+ input_item_id rightdown)
+{
+ // we'll add these at the end if they aren't empty
+ input_seq leftleftseq, leftrightseq, leftupseq, leftdownseq;
+ input_seq rightleftseq, rightrightseq, rightupseq, rightdownseq;
+
+ // only use axes if there are at least two axes in the same orientation
+ bool const useaxes =
+ ((ITEM_ID_INVALID != leftx) && (ITEM_ID_INVALID != rightx)) ||
+ ((ITEM_ID_INVALID != lefty) && (ITEM_ID_INVALID != righty));
+ if (useaxes)
+ {
+ // left stick
+ if (ITEM_ID_INVALID != leftx)
+ {
+ leftleftseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == leftx) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG,
+ leftx);
+ leftrightseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == leftx) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS,
+ leftx);
+ }
+ if (ITEM_ID_INVALID != lefty)
+ {
+ leftupseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_YAXIS == lefty) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG,
+ lefty);
+ leftdownseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_YAXIS == lefty) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS,
+ lefty);
+ }
+
+ // right stick
+ if (ITEM_ID_INVALID != rightx)
+ {
+ rightleftseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == rightx) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG,
+ rightx);
+ rightrightseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_XAXIS == rightx) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS,
+ rightx);
+ }
+ if (ITEM_ID_INVALID != righty)
+ {
+ rightupseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_YAXIS == righty) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG,
+ righty);
+ rightdownseq += make_code(
+ ITEM_CLASS_SWITCH,
+ (ITEM_ID_YAXIS == righty) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS,
+ righty);
+ }
+ }
+
+ // only use switches if we have at least one pair of matching opposing directions
+ bool const lefth = (ITEM_ID_INVALID != leftleft) && (ITEM_ID_INVALID != leftright);
+ bool const leftv = (ITEM_ID_INVALID != leftup) && (ITEM_ID_INVALID != leftdown);
+ bool const righth = (ITEM_ID_INVALID != rightleft) && (ITEM_ID_INVALID != rightright);
+ bool const rightv = (ITEM_ID_INVALID != rightup) && (ITEM_ID_INVALID != rightdown);
+ if ((lefth && righth) || (leftv && rightv))
+ {
+ // left stick
+ if (ITEM_ID_INVALID != leftleft)
+ {
+ if (!leftleftseq.empty())
+ leftleftseq += input_seq::or_code;
+ leftleftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftleft);
+ }
+ if (ITEM_ID_INVALID != leftright)
+ {
+ if (!leftrightseq.empty())
+ leftrightseq += input_seq::or_code;
+ leftrightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftright);
+ }
+ if (ITEM_ID_INVALID != leftup)
+ {
+ if (!leftupseq.empty())
+ leftupseq += input_seq::or_code;
+ leftupseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftup);
+ }
+ if (ITEM_ID_INVALID != leftdown)
+ {
+ if (!leftdownseq.empty())
+ leftdownseq += input_seq::or_code;
+ leftdownseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftdown);
+ }
+
+ // right stick
+ if (ITEM_ID_INVALID != rightleft)
+ {
+ if (!rightleftseq.empty())
+ rightleftseq += input_seq::or_code;
+ rightleftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightleft);
+ }
+ if (ITEM_ID_INVALID != rightright)
+ {
+ if (!rightrightseq.empty())
+ rightrightseq += input_seq::or_code;
+ rightrightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightright);
+ }
+ if (ITEM_ID_INVALID != rightup)
+ {
+ if (!rightupseq.empty())
+ rightupseq += input_seq::or_code;
+ rightupseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightup);
+ }
+ if (ITEM_ID_INVALID != rightdown)
+ {
+ if (!rightdownseq.empty())
+ rightdownseq += input_seq::or_code;
+ rightdownseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightdown);
+ }
+ }
+
+ // now add collected assignments
+ if (!leftleftseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKLEFT_LEFT, SEQ_TYPE_STANDARD, leftleftseq);
+ if (!leftrightseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKLEFT_RIGHT, SEQ_TYPE_STANDARD, leftrightseq);
+ if (!leftupseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKLEFT_UP, SEQ_TYPE_STANDARD, leftupseq);
+ if (!leftdownseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKLEFT_DOWN, SEQ_TYPE_STANDARD, leftdownseq);
+ if (!rightleftseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_LEFT, SEQ_TYPE_STANDARD, rightleftseq);
+ if (!rightrightseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_RIGHT, SEQ_TYPE_STANDARD, rightrightseq);
+ if (!rightupseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_UP, SEQ_TYPE_STANDARD, rightupseq);
+ if (!rightdownseq.empty())
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_DOWN, SEQ_TYPE_STANDARD, rightdownseq);
+}
+
+
+void joystick_assignment_helper::choose_primary_stick(
+ input_item_id (&stickaxes)[2][2],
+ input_item_id leftx,
+ input_item_id lefty,
+ input_item_id rightx,
+ input_item_id righty)
+{
+ if ((ITEM_ID_INVALID != leftx) && (ITEM_ID_INVALID != lefty))
+ {
+ // left stick has both axes, make it primary
+ stickaxes[0][0] = leftx;
+ stickaxes[0][1] = lefty;
+ stickaxes[1][0] = rightx;
+ stickaxes[1][1] = righty;
+ }
+ else if ((ITEM_ID_INVALID != rightx) && (ITEM_ID_INVALID != righty))
+ {
+ // right stick has both axes, make it primary
+ stickaxes[0][0] = rightx;
+ stickaxes[0][1] = righty;
+ stickaxes[1][0] = leftx;
+ stickaxes[1][1] = lefty;
+ }
+ else if (ITEM_ID_INVALID != leftx)
+ {
+ // degenerate case - left X and possibly right X or Y
+ stickaxes[0][0] = leftx;
+ stickaxes[0][1] = righty;
+ stickaxes[1][0] = rightx;
+ stickaxes[1][1] = ITEM_ID_INVALID;
+ }
+ else if ((ITEM_ID_INVALID != rightx) || (ITEM_ID_INVALID != lefty))
+ {
+ // degenerate case - right X and possibly left Y, or one or two Y axes
+ stickaxes[0][0] = rightx;
+ stickaxes[0][1] = lefty;
+ stickaxes[1][0] = ITEM_ID_INVALID;
+ stickaxes[1][1] = righty;
+ }
+ else
+ {
+ // degenerate case - one Y axis at most
+ stickaxes[0][0] = ITEM_ID_INVALID;
+ stickaxes[0][1] = righty;
+ stickaxes[1][0] = ITEM_ID_INVALID;
+ stickaxes[1][1] = ITEM_ID_INVALID;
+ }
+}
+
+} // namespace osd
diff --git a/src/osd/modules/input/assignmenthelper.h b/src/osd/modules/input/assignmenthelper.h
new file mode 100644
index 00000000000..ff5a9d34072
--- /dev/null
+++ b/src/osd/modules/input/assignmenthelper.h
@@ -0,0 +1,118 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+//============================================================
+//
+// assignmenthelper.h - input assignment setup helper
+//
+//============================================================
+#ifndef MAME_OSD_INPUT_ASSIGNMENTHELPER_H
+#define MAME_OSD_INPUT_ASSIGNMENTHELPER_H
+
+#pragma once
+
+#include "interface/inputcode.h"
+#include "interface/inputdev.h"
+
+#include <initializer_list>
+
+
+namespace osd {
+
+class joystick_assignment_helper
+{
+protected:
+ static constexpr input_code make_code(
+ input_item_class itemclass,
+ input_item_modifier modifier,
+ input_item_id item)
+ {
+ return input_code(DEVICE_CLASS_JOYSTICK, 0, itemclass, modifier, item);
+ }
+
+ static bool add_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type fieldtype,
+ input_seq_type seqtype,
+ input_item_class itemclass,
+ input_item_modifier modifier,
+ std::initializer_list<input_item_id> items);
+
+ static bool add_button_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field_type,
+ std::initializer_list<input_item_id> items);
+
+ static bool add_button_pair_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id button1,
+ input_item_id button2);
+
+ static bool add_axis_inc_dec_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field_type,
+ input_item_id button_dec,
+ input_item_id button_inc);
+
+ static bool add_axis_pair_assignment(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id axis);
+
+ static bool consume_button_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &button1,
+ input_item_id &button2);
+
+ static bool consume_trigger_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &axis1,
+ input_item_id &axis2);
+
+ static bool consume_axis_pair(
+ input_device::assignment_vector &assignments,
+ ioport_type field1,
+ ioport_type field2,
+ input_item_id &axis);
+
+ static void add_directional_assignments(
+ input_device::assignment_vector &assignments,
+ input_item_id xaxis,
+ input_item_id yaxis,
+ input_item_id leftswitch,
+ input_item_id rightswitch,
+ input_item_id upswitch,
+ input_item_id downswitch);
+
+ static void add_twin_stick_assignments(
+ input_device::assignment_vector &assignments,
+ input_item_id leftx,
+ input_item_id lefty,
+ input_item_id rightx,
+ input_item_id righty,
+ input_item_id leftleft,
+ input_item_id leftright,
+ input_item_id leftup,
+ input_item_id leftdown,
+ input_item_id rightleft,
+ input_item_id rightright,
+ input_item_id rightup,
+ input_item_id rightdown);
+
+ static void choose_primary_stick(
+ input_item_id (&stickaxes)[2][2],
+ input_item_id leftx,
+ input_item_id lefty,
+ input_item_id rightx,
+ input_item_id righty);
+};
+
+} // namespace osd
+
+#endif // MAME_OSD_INPUT_ASSIGNMENTHELPER_H
diff --git a/src/osd/modules/input/input_common.cpp b/src/osd/modules/input/input_common.cpp
index ab51b6bbe34..f3de3ca186e 100644
--- a/src/osd/modules/input/input_common.cpp
+++ b/src/osd/modules/input/input_common.cpp
@@ -8,201 +8,202 @@
//
//============================================================
-#include "input_module.h"
-#include "modules/lib/osdobj_common.h"
-
-#include <memory>
-
-// MAME headers
-#include "emu.h"
+#include "input_common.h"
-// winnt.h defines this
-#ifdef DELETE
-#undef DELETE
-#endif
+#include "modules/lib/osdobj_common.h"
-#include "input_common.h"
+#include "emu.h" // so we can get an input manager from the running machine
//============================================================
// Keyboard translation table
//============================================================
-#if defined(OSD_WINDOWS)
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
#include <windows.h>
-#define KEY_TRANS_ENTRY0(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii, UI) { ITEM_ID_##mame, KEY_ ## disc, virtual, ascii, "ITEM_ID_"#mame, (char *) UI }
-#define KEY_TRANS_ENTRY1(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii) { ITEM_ID_##mame, KEY_ ## disc, virtual, ascii, "ITEM_ID_"#mame, (char*) #mame }
-#elif defined(OSD_SDL)
-// SDL include
+#include <dinput.h>
+#define DIK_UNKNOWN 0xffff // intentionally impossible
+#define KEY_TRANS_WIN32(disc, virtual) DIK_##disc, virtual,
+#else
+#define KEY_TRANS_WIN32(disc, virtual)
+#endif
+
+#if defined(OSD_SDL) || defined(SDLMAME_WIN32)
#include <SDL2/SDL.h>
-#define KEY_TRANS_ENTRY0(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii, UI) { ITEM_ID_##mame, SDL_SCANCODE_ ## sdlsc, ascii, "ITEM_ID_"#mame, (char *) UI }
-#define KEY_TRANS_ENTRY1(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii) { ITEM_ID_##mame, SDL_SCANCODE_ ## sdlsc, ascii, "ITEM_ID_"#mame, (char*) #mame }
-#elif defined(OSD_UWP)
-#define KEY_TRANS_ENTRY0(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii, UI) { ITEM_ID_##mame, KEY_ ## disc, Windows::System::VirtualKey:: ## uwp, ascii, "ITEM_ID_"#mame, (char *) UI }
-#define KEY_TRANS_ENTRY1(mame, sdlsc, sdlkey, disc, virtual, uwp, ascii) { ITEM_ID_##mame, KEY_ ## disc, Windows::System::VirtualKey:: ## uwp, ascii, "ITEM_ID_"#mame, (char*) #mame }
+#define KEY_TRANS_SDL(sdlsc) SDL_SCANCODE_##sdlsc,
#else
-// osd mini
+#define KEY_TRANS_SDL(sdlsc)
+#endif
+
+#define KEY_TRANS_ENTRY0(mame, sdlsc, disc, virtual, ascii, UI) { ITEM_ID_##mame, KEY_TRANS_SDL(sdlsc) KEY_TRANS_WIN32(disc, virtual) ascii, "ITEM_ID_"#mame, UI }
+#define KEY_TRANS_ENTRY1(mame, sdlsc, disc, virtual, ascii) { ITEM_ID_##mame, KEY_TRANS_SDL(sdlsc) KEY_TRANS_WIN32(disc, virtual) ascii, "ITEM_ID_"#mame, #mame }
+
+// winnt.h defines this
+#ifdef DELETE
+#undef DELETE
#endif
-// FIXME: sdl_key can be removed from the table below. It is no longer used.
+#if defined(OSD_WINDOWS) || defined(OSD_SDL)
-#if defined(OSD_WINDOWS) || defined(OSD_SDL) || defined(OSD_UWP)
key_trans_entry keyboard_trans_table::s_default_table[] =
{
- // MAME key sdl scancode sdl key di scancode virtual key uwp vkey ascii ui
- KEY_TRANS_ENTRY0(ESC, ESCAPE, ESCAPE, ESCAPE, VK_ESCAPE, Escape, 27, "ESCAPE"),
- KEY_TRANS_ENTRY1(1, 1, 1, 1, '1', Number1, '1'),
- KEY_TRANS_ENTRY1(2, 2, 2, 2, '2', Number2, '2'),
- KEY_TRANS_ENTRY1(3, 3, 3, 3, '3', Number3, '3'),
- KEY_TRANS_ENTRY1(4, 4, 4, 4, '4', Number4, '4'),
- KEY_TRANS_ENTRY1(5, 5, 5, 5, '5', Number5, '5'),
- KEY_TRANS_ENTRY1(6, 6, 6, 6, '6', Number6, '6'),
- KEY_TRANS_ENTRY1(7, 7, 7, 7, '7', Number7, '7'),
- KEY_TRANS_ENTRY1(8, 8, 8, 8, '8', Number8, '8'),
- KEY_TRANS_ENTRY1(9, 9, 9, 9, '9', Number9, '9'),
- KEY_TRANS_ENTRY1(0, 0, 0, 0, '0', Number0, '0'),
- KEY_TRANS_ENTRY1(MINUS, MINUS, MINUS, MINUS, VK_OEM_MINUS, None, '-'),
- KEY_TRANS_ENTRY1(EQUALS, EQUALS, EQUALS, EQUALS, VK_OEM_PLUS, None, '='),
- KEY_TRANS_ENTRY1(BACKSPACE, BACKSPACE, BACKSPACE, BACK, VK_BACK, Back, 8),
- KEY_TRANS_ENTRY1(TAB, TAB, TAB, TAB, VK_TAB, Tab, 9),
- KEY_TRANS_ENTRY1(Q, Q, q, Q, 'Q', Q, 'Q'),
- KEY_TRANS_ENTRY1(W, W, w, W, 'W', W, 'W'),
- KEY_TRANS_ENTRY1(E, E, e, E, 'E', E, 'E'),
- KEY_TRANS_ENTRY1(R, R, r, R, 'R', R, 'R'),
- KEY_TRANS_ENTRY1(T, T, t, T, 'T', T, 'T'),
- KEY_TRANS_ENTRY1(Y, Y, y, Y, 'Y', Y, 'Y'),
- KEY_TRANS_ENTRY1(U, U, u, U, 'U', U, 'U'),
- KEY_TRANS_ENTRY1(I, I, i, I, 'I', I, 'I'),
- KEY_TRANS_ENTRY1(O, O, o, O, 'O', O, 'O'),
- KEY_TRANS_ENTRY1(P, P, p, P, 'P', P, 'P'),
- KEY_TRANS_ENTRY1(OPENBRACE, LEFTBRACKET, LEFTBRACKET, LBRACKET, VK_OEM_4, None, '['),
- KEY_TRANS_ENTRY1(CLOSEBRACE, RIGHTBRACKET, RIGHTBRACKET, RBRACKET, VK_OEM_6, None, ']'),
- KEY_TRANS_ENTRY0(ENTER, RETURN, RETURN, RETURN, VK_RETURN, Enter, 13, "RETURN"),
- KEY_TRANS_ENTRY1(LCONTROL, LCTRL, LCTRL, LCONTROL, VK_LCONTROL, LeftControl, 0),
- KEY_TRANS_ENTRY1(A, A, a, A, 'A', A, 'A'),
- KEY_TRANS_ENTRY1(S, S, s, S, 'S', S, 'S'),
- KEY_TRANS_ENTRY1(D, D, d, D, 'D', D, 'D'),
- KEY_TRANS_ENTRY1(F, F, f, F, 'F', F, 'F'),
- KEY_TRANS_ENTRY1(G, G, g, G, 'G', G, 'G'),
- KEY_TRANS_ENTRY1(H, H, h, H, 'H', H, 'H'),
- KEY_TRANS_ENTRY1(J, J, j, J, 'J', J, 'J'),
- KEY_TRANS_ENTRY1(K, K, k, K, 'K', K, 'K'),
- KEY_TRANS_ENTRY1(L, L, l, L, 'L', L, 'L'),
- KEY_TRANS_ENTRY1(COLON, SEMICOLON, SEMICOLON, SEMICOLON, VK_OEM_1, None, ';'),
- KEY_TRANS_ENTRY1(QUOTE, APOSTROPHE, QUOTE, APOSTROPHE, VK_OEM_7, None, '\''),
- KEY_TRANS_ENTRY1(TILDE, GRAVE, BACKQUOTE, GRAVE, VK_OEM_3, None, '`'),
- KEY_TRANS_ENTRY1(LSHIFT, LSHIFT, LSHIFT, LSHIFT, VK_LSHIFT, LeftShift, 0),
- KEY_TRANS_ENTRY1(BACKSLASH, BACKSLASH, BACKSLASH, BACKSLASH, VK_OEM_5, None, '\\'),
-// KEY_TRANS_ENTRY1(BACKSLASH2, NONUSHASH, UNKNOWN, OEM_102, VK_OEM_102, None, '<'),
+ // MAME key SDL scancode DI scancode virtual key ASCII UI
+ KEY_TRANS_ENTRY0(ESC, ESCAPE, ESCAPE, VK_ESCAPE, 27, "ESCAPE"),
+ KEY_TRANS_ENTRY1(1, 1, 1, '1', '1'),
+ KEY_TRANS_ENTRY1(2, 2, 2, '2', '2'),
+ KEY_TRANS_ENTRY1(3, 3, 3, '3', '3'),
+ KEY_TRANS_ENTRY1(4, 4, 4, '4', '4'),
+ KEY_TRANS_ENTRY1(5, 5, 5, '5', '5'),
+ KEY_TRANS_ENTRY1(6, 6, 6, '6', '6'),
+ KEY_TRANS_ENTRY1(7, 7, 7, '7', '7'),
+ KEY_TRANS_ENTRY1(8, 8, 8, '8', '8'),
+ KEY_TRANS_ENTRY1(9, 9, 9, '9', '9'),
+ KEY_TRANS_ENTRY1(0, 0, 0, '0', '0'),
+ KEY_TRANS_ENTRY1(MINUS, MINUS, MINUS, VK_OEM_MINUS, '-'),
+ KEY_TRANS_ENTRY1(EQUALS, EQUALS, EQUALS, VK_OEM_PLUS, '='),
+ KEY_TRANS_ENTRY1(BACKSPACE, BACKSPACE, BACK, VK_BACK, 8),
+ KEY_TRANS_ENTRY1(TAB, TAB, TAB, VK_TAB, 9),
+ KEY_TRANS_ENTRY1(Q, Q, Q, 'Q', 'Q'),
+ KEY_TRANS_ENTRY1(W, W, W, 'W', 'W'),
+ KEY_TRANS_ENTRY1(E, E, E, 'E', 'E'),
+ KEY_TRANS_ENTRY1(R, R, R, 'R', 'R'),
+ KEY_TRANS_ENTRY1(T, T, T, 'T', 'T'),
+ KEY_TRANS_ENTRY1(Y, Y, Y, 'Y', 'Y'),
+ KEY_TRANS_ENTRY1(U, U, U, 'U', 'U'),
+ KEY_TRANS_ENTRY1(I, I, I, 'I', 'I'),
+ KEY_TRANS_ENTRY1(O, O, O, 'O', 'O'),
+ KEY_TRANS_ENTRY1(P, P, P, 'P', 'P'),
+ KEY_TRANS_ENTRY1(OPENBRACE, LEFTBRACKET, LBRACKET, VK_OEM_4, '['),
+ KEY_TRANS_ENTRY1(CLOSEBRACE, RIGHTBRACKET, RBRACKET, VK_OEM_6, ']'),
+ KEY_TRANS_ENTRY0(ENTER, RETURN, RETURN, VK_RETURN, 13, "RETURN"),
+ KEY_TRANS_ENTRY1(LCONTROL, LCTRL, LCONTROL, VK_LCONTROL, 0),
+ KEY_TRANS_ENTRY1(A, A, A, 'A', 'A'),
+ KEY_TRANS_ENTRY1(S, S, S, 'S', 'S'),
+ KEY_TRANS_ENTRY1(D, D, D, 'D', 'D'),
+ KEY_TRANS_ENTRY1(F, F, F, 'F', 'F'),
+ KEY_TRANS_ENTRY1(G, G, G, 'G', 'G'),
+ KEY_TRANS_ENTRY1(H, H, H, 'H', 'H'),
+ KEY_TRANS_ENTRY1(J, J, J, 'J', 'J'),
+ KEY_TRANS_ENTRY1(K, K, K, 'K', 'K'),
+ KEY_TRANS_ENTRY1(L, L, L, 'L', 'L'),
+ KEY_TRANS_ENTRY1(COLON, SEMICOLON, SEMICOLON, VK_OEM_1, ';'),
+ KEY_TRANS_ENTRY1(QUOTE, APOSTROPHE, APOSTROPHE, VK_OEM_7, '\''),
+ KEY_TRANS_ENTRY1(TILDE, GRAVE, GRAVE, VK_OEM_3, '`'),
+ KEY_TRANS_ENTRY1(LSHIFT, LSHIFT, LSHIFT, VK_LSHIFT, 0),
+ KEY_TRANS_ENTRY1(BACKSLASH, BACKSLASH, BACKSLASH, VK_OEM_5, '\\'),
+// KEY_TRANS_ENTRY1(BACKSLASH2, NONUSHASH, UNKNOWN, OEM_102, VK_OEM_102, '<'),
// This is the additional key that ISO keyboards have over ANSI ones, located between left shift and Y.
- KEY_TRANS_ENTRY1(BACKSLASH2, NONUSBACKSLASH, UNKNOWN, OEM_102, VK_OEM_102, None, '<'),
- KEY_TRANS_ENTRY1(Z, Z, z, Z, 'Z', Z, 'Z'),
- KEY_TRANS_ENTRY1(X, X, x, X, 'X', X, 'X'),
- KEY_TRANS_ENTRY1(C, C, c, C, 'C', C, 'C'),
- KEY_TRANS_ENTRY1(V, V, v, V, 'V', V, 'V'),
- KEY_TRANS_ENTRY1(B, B, b, B, 'B', B, 'B'),
- KEY_TRANS_ENTRY1(N, N, n, N, 'N', N, 'N'),
- KEY_TRANS_ENTRY1(M, M, m, M, 'M', M, 'M'),
- KEY_TRANS_ENTRY1(COMMA, COMMA, COMMA, COMMA, VK_OEM_COMMA, None, ','),
- KEY_TRANS_ENTRY1(STOP, PERIOD, PERIOD, PERIOD, VK_OEM_PERIOD, None, '.'),
- KEY_TRANS_ENTRY1(SLASH, SLASH, SLASH, SLASH, VK_OEM_2, None, '/'),
- KEY_TRANS_ENTRY1(RSHIFT, RSHIFT, RSHIFT, RSHIFT, VK_RSHIFT, RightShift, 0),
- KEY_TRANS_ENTRY1(ASTERISK, KP_MULTIPLY, KP_MULTIPLY, MULTIPLY, VK_MULTIPLY, Multiply, '*'),
- KEY_TRANS_ENTRY1(LALT, LALT, LALT, LMENU, VK_LMENU, LeftMenu, 0),
- KEY_TRANS_ENTRY1(SPACE, SPACE, SPACE, SPACE, VK_SPACE, Space, ' '),
- KEY_TRANS_ENTRY1(CAPSLOCK, CAPSLOCK, CAPSLOCK, CAPITAL, VK_CAPITAL, CapitalLock, 0),
- KEY_TRANS_ENTRY1(F1, F1, F1, F1, VK_F1, F1, 0),
- KEY_TRANS_ENTRY1(F2, F2, F2, F2, VK_F2, F2, 0),
- KEY_TRANS_ENTRY1(F3, F3, F3, F3, VK_F3, F3, 0),
- KEY_TRANS_ENTRY1(F4, F4, F4, F4, VK_F4, F4, 0),
- KEY_TRANS_ENTRY1(F5, F5, F5, F5, VK_F5, F5, 0),
- KEY_TRANS_ENTRY1(F6, F6, F6, F6, VK_F6, F6, 0),
- KEY_TRANS_ENTRY1(F7, F7, F7, F7, VK_F7, F7, 0),
- KEY_TRANS_ENTRY1(F8, F8, F8, F8, VK_F8, F8, 0),
- KEY_TRANS_ENTRY1(F9, F9, F9, F9, VK_F9, F9, 0),
- KEY_TRANS_ENTRY1(F10, F10, F10, F10, VK_F10, F10, 0),
- KEY_TRANS_ENTRY1(NUMLOCK, NUMLOCKCLEAR, NUMLOCKCLEAR, NUMLOCK, VK_NUMLOCK, NumberKeyLock, 0),
- KEY_TRANS_ENTRY1(SCRLOCK, SCROLLLOCK, SCROLLLOCK, SCROLL, VK_SCROLL, Scroll, 0),
- KEY_TRANS_ENTRY1(7_PAD, KP_7, KP_7, NUMPAD7, VK_NUMPAD7, NumberPad7, 0),
- KEY_TRANS_ENTRY1(8_PAD, KP_8, KP_8, NUMPAD8, VK_NUMPAD8, NumberPad8, 0),
- KEY_TRANS_ENTRY1(9_PAD, KP_9, KP_9, NUMPAD9, VK_NUMPAD9, NumberPad9, 0),
- KEY_TRANS_ENTRY1(MINUS_PAD, KP_MINUS, KP_MINUS, SUBTRACT, VK_SUBTRACT, Subtract, 0),
- KEY_TRANS_ENTRY1(4_PAD, KP_4, KP_4, NUMPAD4, VK_NUMPAD4, NumberPad4, 0),
- KEY_TRANS_ENTRY1(5_PAD, KP_5, KP_5, NUMPAD5, VK_NUMPAD5, NumberPad5, 0),
- KEY_TRANS_ENTRY1(6_PAD, KP_6, KP_6, NUMPAD6, VK_NUMPAD6, NumberPad6, 0),
- KEY_TRANS_ENTRY1(PLUS_PAD, KP_PLUS, KP_PLUS, ADD, VK_ADD, Add, 0),
- KEY_TRANS_ENTRY1(1_PAD, KP_1, KP_1, NUMPAD1, VK_NUMPAD1, NumberPad1, 0),
- KEY_TRANS_ENTRY1(2_PAD, KP_2, KP_2, NUMPAD2, VK_NUMPAD2, NumberPad2, 0),
- KEY_TRANS_ENTRY1(3_PAD, KP_3, KP_3, NUMPAD3, VK_NUMPAD3, NumberPad3, 0),
- KEY_TRANS_ENTRY1(0_PAD, KP_0, KP_0, NUMPAD0, VK_NUMPAD0, NumberPad0, 0),
- KEY_TRANS_ENTRY1(DEL_PAD, KP_PERIOD, KP_PERIOD, DECIMAL, VK_DECIMAL, Decimal, 0),
- KEY_TRANS_ENTRY1(F11, F11, F11, F11, VK_F11, F11, 0),
- KEY_TRANS_ENTRY1(F12, F12, F12, F12, VK_F12, F12, 0),
- KEY_TRANS_ENTRY1(F13, F13, F13, F13, VK_F13, F13, 0),
- KEY_TRANS_ENTRY1(F14, F14, F14, F14, VK_F14, F14, 0),
- KEY_TRANS_ENTRY1(F15, F15, F15, F15, VK_F15, F15, 0),
- KEY_TRANS_ENTRY1(ENTER_PAD, KP_ENTER, KP_ENTER, NUMPADENTER, VK_RETURN, None, 0),
- KEY_TRANS_ENTRY1(RCONTROL, RCTRL, RCTRL, RCONTROL, VK_RCONTROL, RightControl, 0),
- KEY_TRANS_ENTRY1(SLASH_PAD, KP_DIVIDE, KP_DIVIDE, DIVIDE, VK_DIVIDE, Divide, 0),
- KEY_TRANS_ENTRY1(PRTSCR, PRINTSCREEN, PRINTSCREEN, SYSRQ, 0, Print, 0),
- KEY_TRANS_ENTRY1(RALT, RALT, RALT, RMENU, VK_RMENU, RightMenu, 0),
- KEY_TRANS_ENTRY1(HOME, HOME, HOME, HOME, VK_HOME, Home, 0),
- KEY_TRANS_ENTRY1(UP, UP, UP, UP, VK_UP, Up, 0),
- KEY_TRANS_ENTRY1(PGUP, PAGEUP, PAGEUP, PRIOR, VK_PRIOR, PageUp, 0),
- KEY_TRANS_ENTRY1(LEFT, LEFT, LEFT, LEFT, VK_LEFT, Left, 0),
- KEY_TRANS_ENTRY1(RIGHT, RIGHT, RIGHT, RIGHT, VK_RIGHT, Right, 0),
- KEY_TRANS_ENTRY1(END, END, END, END, VK_END, End, 0),
- KEY_TRANS_ENTRY1(DOWN, DOWN, DOWN, DOWN, VK_DOWN, Down, 0),
- KEY_TRANS_ENTRY1(PGDN, PAGEDOWN, PAGEDOWN, NEXT, VK_NEXT, PageDown, 0),
- KEY_TRANS_ENTRY1(INSERT, INSERT, INSERT, INSERT, VK_INSERT, Insert, 0),
- KEY_TRANS_ENTRY0(DEL, DELETE, DELETE, DELETE, VK_DELETE, Delete, 0, "DELETE"),
- KEY_TRANS_ENTRY1(LWIN, LGUI, LGUI, LWIN, VK_LWIN, LeftWindows, 0),
- KEY_TRANS_ENTRY1(RWIN, RGUI, RGUI, RWIN, VK_RWIN, RightWindows, 0),
- KEY_TRANS_ENTRY1(MENU, MENU, MENU, APPS, VK_APPS, Menu, 0),
- KEY_TRANS_ENTRY1(PAUSE, PAUSE, PAUSE, PAUSE, VK_PAUSE, Pause, 0),
- KEY_TRANS_ENTRY0(CANCEL, CANCEL, CANCEL, UNKNOWN, 0, Cancel, 0, "CANCEL"),
- KEY_TRANS_ENTRY1(BS_PAD, KP_BACKSPACE, KP_BACKSPACE, UNKNOWN, 0, None, 0),
- KEY_TRANS_ENTRY1(TAB_PAD, KP_TAB, KP_TAB, UNKNOWN, 0, None, 0),
- KEY_TRANS_ENTRY1(00_PAD, KP_00, KP_00, UNKNOWN, 0, None, 0),
- KEY_TRANS_ENTRY1(000_PAD, KP_000, KP_000, UNKNOWN, 0, None, 0),
- KEY_TRANS_ENTRY1(COMMA_PAD, KP_COMMA, KP_COMMA, NUMPADCOMMA, 0, None, 0),
- KEY_TRANS_ENTRY1(EQUALS_PAD, KP_EQUALS, KP_EQUALS, NUMPADEQUALS, 0, None, 0),
-
- // New keys introduced in Windows 2000. These have no MAME codes to
- // preserve compatibility with old config files that may refer to them
- // as e.g. FORWARD instead of e.g. KEYCODE_WEBFORWARD. They need table
- // entries anyway because otherwise they aren't recognized when
- // GetAsyncKeyState polling is used (as happens currently when MAME is
- // paused). Some codes are missing because the mapping to vkey codes
- // isn't clear, and MapVirtualKey is no help.
- KEY_TRANS_ENTRY1(OTHER_SWITCH, MUTE, MUTE, MUTE, VK_VOLUME_MUTE, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEDOWN, VOLUMEDOWN, VOLUMEDOWN, VK_VOLUME_DOWN, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEUP, VOLUMEUP, VOLUMEUP, VK_VOLUME_UP, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_HOME, AC_HOME, WEBHOME, VK_BROWSER_HOME, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_SEARCH, AC_SEARCH, WEBSEARCH, VK_BROWSER_SEARCH, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BOOKMARKS, AC_BOOKMARKS, WEBFAVORITES, VK_BROWSER_FAVORITES, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_REFRESH, AC_REFRESH, WEBREFRESH, VK_BROWSER_REFRESH, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_STOP, AC_STOP, WEBSTOP, VK_BROWSER_STOP, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_FORWARD, AC_FORWARD, WEBFORWARD, VK_BROWSER_FORWARD, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BACK, AC_BACK, WEBBACK, VK_BROWSER_BACK, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, MAIL, MAIL, MAIL, VK_LAUNCH_MAIL, None, 0),
- KEY_TRANS_ENTRY1(OTHER_SWITCH, MEDIASELECT, MEDIASELECT, MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, None, 0),
- KEY_TRANS_ENTRY0(INVALID, UNKNOWN, UNKNOWN, ESCAPE, 0, None, 0, "INVALID")
+ KEY_TRANS_ENTRY1(BACKSLASH2, NONUSBACKSLASH, OEM_102, VK_OEM_102, '<'),
+ KEY_TRANS_ENTRY1(Z, Z, Z, 'Z', 'Z'),
+ KEY_TRANS_ENTRY1(X, X, X, 'X', 'X'),
+ KEY_TRANS_ENTRY1(C, C, C, 'C', 'C'),
+ KEY_TRANS_ENTRY1(V, V, V, 'V', 'V'),
+ KEY_TRANS_ENTRY1(B, B, B, 'B', 'B'),
+ KEY_TRANS_ENTRY1(N, N, N, 'N', 'N'),
+ KEY_TRANS_ENTRY1(M, M, M, 'M', 'M'),
+ KEY_TRANS_ENTRY1(COMMA, COMMA, COMMA, VK_OEM_COMMA, ','),
+ KEY_TRANS_ENTRY1(STOP, PERIOD, PERIOD, VK_OEM_PERIOD, '.'),
+ KEY_TRANS_ENTRY1(SLASH, SLASH, SLASH, VK_OEM_2, '/'),
+ KEY_TRANS_ENTRY1(RSHIFT, RSHIFT, RSHIFT, VK_RSHIFT, 0),
+ KEY_TRANS_ENTRY1(ASTERISK, KP_MULTIPLY, MULTIPLY, VK_MULTIPLY, '*'),
+ KEY_TRANS_ENTRY1(LALT, LALT, LMENU, VK_LMENU, 0),
+ KEY_TRANS_ENTRY1(SPACE, SPACE, SPACE, VK_SPACE, ' '),
+ KEY_TRANS_ENTRY1(CAPSLOCK, CAPSLOCK, CAPITAL, VK_CAPITAL, 0),
+ KEY_TRANS_ENTRY1(F1, F1, F1, VK_F1, 0),
+ KEY_TRANS_ENTRY1(F2, F2, F2, VK_F2, 0),
+ KEY_TRANS_ENTRY1(F3, F3, F3, VK_F3, 0),
+ KEY_TRANS_ENTRY1(F4, F4, F4, VK_F4, 0),
+ KEY_TRANS_ENTRY1(F5, F5, F5, VK_F5, 0),
+ KEY_TRANS_ENTRY1(F6, F6, F6, VK_F6, 0),
+ KEY_TRANS_ENTRY1(F7, F7, F7, VK_F7, 0),
+ KEY_TRANS_ENTRY1(F8, F8, F8, VK_F8, 0),
+ KEY_TRANS_ENTRY1(F9, F9, F9, VK_F9, 0),
+ KEY_TRANS_ENTRY1(F10, F10, F10, VK_F10, 0),
+ KEY_TRANS_ENTRY1(NUMLOCK, NUMLOCKCLEAR, NUMLOCK, VK_NUMLOCK, 0),
+ KEY_TRANS_ENTRY1(SCRLOCK, SCROLLLOCK, SCROLL, VK_SCROLL, 0),
+ KEY_TRANS_ENTRY1(7_PAD, KP_7, NUMPAD7, VK_NUMPAD7, 0),
+ KEY_TRANS_ENTRY1(8_PAD, KP_8, NUMPAD8, VK_NUMPAD8, 0),
+ KEY_TRANS_ENTRY1(9_PAD, KP_9, NUMPAD9, VK_NUMPAD9, 0),
+ KEY_TRANS_ENTRY1(MINUS_PAD, KP_MINUS, SUBTRACT, VK_SUBTRACT, 0),
+ KEY_TRANS_ENTRY1(4_PAD, KP_4, NUMPAD4, VK_NUMPAD4, 0),
+ KEY_TRANS_ENTRY1(5_PAD, KP_5, NUMPAD5, VK_NUMPAD5, 0),
+ KEY_TRANS_ENTRY1(6_PAD, KP_6, NUMPAD6, VK_NUMPAD6, 0),
+ KEY_TRANS_ENTRY1(PLUS_PAD, KP_PLUS, ADD, VK_ADD, 0),
+ KEY_TRANS_ENTRY1(1_PAD, KP_1, NUMPAD1, VK_NUMPAD1, 0),
+ KEY_TRANS_ENTRY1(2_PAD, KP_2, NUMPAD2, VK_NUMPAD2, 0),
+ KEY_TRANS_ENTRY1(3_PAD, KP_3, NUMPAD3, VK_NUMPAD3, 0),
+ KEY_TRANS_ENTRY1(0_PAD, KP_0, NUMPAD0, VK_NUMPAD0, 0),
+ KEY_TRANS_ENTRY1(DEL_PAD, KP_PERIOD, DECIMAL, VK_DECIMAL, 0),
+ KEY_TRANS_ENTRY1(F11, F11, F11, VK_F11, 0),
+ KEY_TRANS_ENTRY1(F12, F12, F12, VK_F12, 0),
+ KEY_TRANS_ENTRY1(F13, F13, F13, VK_F13, 0),
+ KEY_TRANS_ENTRY1(F14, F14, F14, VK_F14, 0),
+ KEY_TRANS_ENTRY1(F15, F15, F15, VK_F15, 0),
+ KEY_TRANS_ENTRY1(ENTER_PAD, KP_ENTER, NUMPADENTER, VK_RETURN, 0),
+ KEY_TRANS_ENTRY1(RCONTROL, RCTRL, RCONTROL, VK_RCONTROL, 0),
+ KEY_TRANS_ENTRY1(SLASH_PAD, KP_DIVIDE, DIVIDE, VK_DIVIDE, 0),
+ KEY_TRANS_ENTRY1(PRTSCR, PRINTSCREEN, SYSRQ, 0, 0),
+ KEY_TRANS_ENTRY1(RALT, RALT, RMENU, VK_RMENU, 0),
+ KEY_TRANS_ENTRY1(HOME, HOME, HOME, VK_HOME, 0),
+ KEY_TRANS_ENTRY1(UP, UP, UP, VK_UP, 0),
+ KEY_TRANS_ENTRY1(PGUP, PAGEUP, PRIOR, VK_PRIOR, 0),
+ KEY_TRANS_ENTRY1(LEFT, LEFT, LEFT, VK_LEFT, 0),
+ KEY_TRANS_ENTRY1(RIGHT, RIGHT, RIGHT, VK_RIGHT, 0),
+ KEY_TRANS_ENTRY1(END, END, END, VK_END, 0),
+ KEY_TRANS_ENTRY1(DOWN, DOWN, DOWN, VK_DOWN, 0),
+ KEY_TRANS_ENTRY1(PGDN, PAGEDOWN, NEXT, VK_NEXT, 0),
+ KEY_TRANS_ENTRY1(INSERT, INSERT, INSERT, VK_INSERT, 0),
+ KEY_TRANS_ENTRY0(DEL, DELETE, DELETE, VK_DELETE, 0, "DELETE"),
+ KEY_TRANS_ENTRY1(LWIN, LGUI, LWIN, VK_LWIN, 0),
+ KEY_TRANS_ENTRY1(RWIN, RGUI, RWIN, VK_RWIN, 0),
+ KEY_TRANS_ENTRY1(MENU, MENU, APPS, VK_APPS, 0),
+ KEY_TRANS_ENTRY1(PAUSE, PAUSE, PAUSE, VK_PAUSE, 0),
+ KEY_TRANS_ENTRY1(CANCEL, CANCEL, UNKNOWN, 0, 0),
+ KEY_TRANS_ENTRY1(BS_PAD, KP_BACKSPACE, UNKNOWN, 0, 0),
+ KEY_TRANS_ENTRY1(TAB_PAD, KP_TAB, UNKNOWN, 0, 0),
+ KEY_TRANS_ENTRY1(00_PAD, KP_00, UNKNOWN, 0, 0),
+ KEY_TRANS_ENTRY1(000_PAD, KP_000, UNKNOWN, 0, 0),
+ KEY_TRANS_ENTRY1(COMMA_PAD, KP_COMMA, NUMPADCOMMA, VK_SEPARATOR, 0),
+ KEY_TRANS_ENTRY1(EQUALS_PAD, KP_EQUALS, NUMPADEQUALS, VK_OEM_NEC_EQUAL, 0),
+
+ // keys that have no specific MAME input item IDs
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIONEXT, NEXTTRACK, VK_MEDIA_NEXT_TRACK, 0, "AUDIONEXT"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOMUTE, MUTE, VK_VOLUME_MUTE, 0, "VOLUMEMUTE"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOPLAY, PLAYPAUSE, VK_MEDIA_PLAY_PAUSE, 0, "AUDIOPLAY"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOSTOP, MEDIASTOP, VK_MEDIA_STOP, 0, "AUDIOSTOP"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, VOLUMEDOWN, VOLUMEDOWN, VK_VOLUME_DOWN, 0, "VOLUMEDOWN"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, VOLUMEUP, VOLUMEUP, VK_VOLUME_UP, 0, "VOLUMEUP"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_HOME, WEBHOME, VK_BROWSER_HOME, 0, "NAVHOME"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_SEARCH, WEBSEARCH, VK_BROWSER_SEARCH, 0, "NAVSEARCH"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_BOOKMARKS, WEBFAVORITES, VK_BROWSER_FAVORITES, 0, "NAVFAVORITES"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_REFRESH, WEBREFRESH, VK_BROWSER_REFRESH, 0, "NAVREFRESH"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_STOP, WEBSTOP, VK_BROWSER_STOP, 0, "NAVSTOP"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_FORWARD, WEBFORWARD, VK_BROWSER_FORWARD, 0, "NAVFORWARD"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_BACK, WEBBACK, VK_BROWSER_BACK, 0, "NAVBACK"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, MAIL, MAIL, VK_LAUNCH_MAIL, 0, "MAIL"),
+ KEY_TRANS_ENTRY0(OTHER_SWITCH, MEDIASELECT, MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0, "MEDIASEL"),
+
+ // sentinel
+ KEY_TRANS_ENTRY0(INVALID, UNKNOWN, UNKNOWN, 0, 0, "INVALID")
};
// The private constructor to create the default instance
keyboard_trans_table::keyboard_trans_table()
{
m_table = s_default_table;
- m_table_size = ARRAY_LENGTH(s_default_table);
+ m_table_size = std::size(s_default_table);
}
-#else
+
+#else // defined(OSD_WINDOWS) || defined(OSD_SDL)
+
keyboard_trans_table::keyboard_trans_table()
{
m_table = nullptr;
m_table_size = 0;
}
-#endif
+#endif // defined(OSD_WINDOWS) || defined(OSD_SDL)
+
+
// public constructor to allow creation of non-default instances
keyboard_trans_table::keyboard_trans_table(std::unique_ptr<key_trans_entry[]> entries, unsigned int size)
{
@@ -231,14 +232,12 @@ input_item_id keyboard_trans_table::lookup_mame_code(const char *scode) const
}
// Windows specific lookup methods
-#if defined(OSD_WINDOWS) || defined(OSD_UWP)
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
input_item_id keyboard_trans_table::map_di_scancode_to_itemid(int scancode) const
{
- int tablenum;
-
// scan the table for a match
- for (tablenum = 0; tablenum < m_table_size; tablenum++)
+ for (int tablenum = 0; tablenum < m_table_size; tablenum++)
if (m_table[tablenum].scan_code == scancode)
return m_table[tablenum].mame_key;
@@ -246,10 +245,6 @@ input_item_id keyboard_trans_table::map_di_scancode_to_itemid(int scancode) cons
return ITEM_ID_OTHER_SWITCH;
}
-#endif
-
-#if defined(OSD_WINDOWS)
-
//============================================================
// wininput_vkey_for_mame_code
//============================================================
@@ -259,48 +254,53 @@ int keyboard_trans_table::vkey_for_mame_code(input_code code) const
// only works for keyboard switches
if (code.device_class() == DEVICE_CLASS_KEYBOARD && code.item_class() == ITEM_CLASS_SWITCH)
{
- input_item_id id = code.item_id();
- int tablenum;
+ const input_item_id id = code.item_id();
// scan the table for a match
- for (tablenum = 0; tablenum < m_table_size; tablenum++)
+ for (int tablenum = 0; tablenum < m_table_size; tablenum++)
if (m_table[tablenum].mame_key == id)
return m_table[tablenum].virtual_key;
}
return 0;
}
-#endif
-
-#if defined(OSD_UWP)
+#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
-const char* keyboard_trans_table::ui_label_for_mame_key(input_item_id itemid) const
+input_module_base::input_module_base(const char *type, const char* name) :
+ osd_module(type, name),
+ m_clock(),
+ m_last_poll(timepoint_type::min()),
+ m_background_input(false),
+ m_options(nullptr),
+ m_manager(nullptr)
{
- // scan the table for a match
- for (int tablenum = 0; tablenum < m_table_size; tablenum++)
- if (m_table[tablenum].mame_key == itemid)
- return m_table[tablenum].ui_name;
-
- // We didn't find one
- return nullptr;
}
-#endif
-
-
-int input_module_base::init(const osd_options &options)
+int input_module_base::init(osd_interface &osd, const osd_options &options)
{
m_options = &options;
- m_mouse_enabled = options.mouse();
- m_lightgun_enabled = options.lightgun();
+ // don't enable background input when debugging
+ m_background_input = !options.debug() && options.background_input();
+
+ return 0;
+}
+
+void input_module_base::input_init(running_machine &machine)
+{
+ m_manager = &machine.input();
+}
- int result = init_internal();
- if (result != 0)
- return result;
+void input_module_base::poll_if_necessary(bool relative_reset)
+{
+ timepoint_type const now = m_clock.now();
+ if (relative_reset || (now >= (m_last_poll + std::chrono::milliseconds(MIN_POLLING_INTERVAL))))
+ {
+ // grab the current time
+ m_last_poll = now;
- m_input_paused = false;
- m_input_enabled = true;
+ before_poll();
- return 0;
+ poll(relative_reset);
+ }
}
diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h
index b4005a6b76a..73ddd2bf936 100644
--- a/src/osd/modules/input/input_common.h
+++ b/src/osd/modules/input/input_common.h
@@ -7,20 +7,26 @@
// SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================
+#ifndef MAME_OSD_INPUT_INPUT_COMMON_H
+#define MAME_OSD_INPUT_INPUT_COMMON_H
-#ifndef INPUT_COMMON_H_
-#define INPUT_COMMON_H_
+#pragma once
#include "input_module.h"
-#include "inputdev.h"
+#include "interface/inputdev.h"
+#include "interface/inputman.h"
+#include "modules/osdmodule.h"
+#include "util/strformat.h"
+
+#include <algorithm>
+#include <cassert>
#include <chrono>
+#include <functional>
#include <memory>
#include <mutex>
#include <queue>
-#include <algorithm>
-#include <functional>
//============================================================
@@ -41,225 +47,66 @@ enum
#define MAX_HATS 8
#define MAX_POV 4
-/****************************************************************************
-* DirectInput compatible keyboard scan codes
-****************************************************************************/
-#define KEY_UNKNOWN 0x00
-#define KEY_ESCAPE 0x01
-#define KEY_1 0x02
-#define KEY_2 0x03
-#define KEY_3 0x04
-#define KEY_4 0x05
-#define KEY_5 0x06
-#define KEY_6 0x07
-#define KEY_7 0x08
-#define KEY_8 0x09
-#define KEY_9 0x0A
-#define KEY_0 0x0B
-#define KEY_MINUS 0x0C /* - on main keyboard */
-#define KEY_EQUALS 0x0D
-#define KEY_BACK 0x0E /* backspace */
-#define KEY_TAB 0x0F
-#define KEY_Q 0x10
-#define KEY_W 0x11
-#define KEY_E 0x12
-#define KEY_R 0x13
-#define KEY_T 0x14
-#define KEY_Y 0x15
-#define KEY_U 0x16
-#define KEY_I 0x17
-#define KEY_O 0x18
-#define KEY_P 0x19
-#define KEY_LBRACKET 0x1A
-#define KEY_RBRACKET 0x1B
-#define KEY_RETURN 0x1C /* Enter on main keyboard */
-#define KEY_LCONTROL 0x1D
-#define KEY_A 0x1E
-#define KEY_S 0x1F
-#define KEY_D 0x20
-#define KEY_F 0x21
-#define KEY_G 0x22
-#define KEY_H 0x23
-#define KEY_J 0x24
-#define KEY_K 0x25
-#define KEY_L 0x26
-#define KEY_SEMICOLON 0x27
-#define KEY_APOSTROPHE 0x28
-#define KEY_GRAVE 0x29 /* accent grave */
-#define KEY_LSHIFT 0x2A
-#define KEY_BACKSLASH 0x2B
-#define KEY_Z 0x2C
-#define KEY_X 0x2D
-#define KEY_C 0x2E
-#define KEY_V 0x2F
-#define KEY_B 0x30
-#define KEY_N 0x31
-#define KEY_M 0x32
-#define KEY_COMMA 0x33
-#define KEY_PERIOD 0x34 /* . on main keyboard */
-#define KEY_SLASH 0x35 /* / on main keyboard */
-#define KEY_RSHIFT 0x36
-#define KEY_MULTIPLY 0x37 /* * on numeric keypad */
-#define KEY_LMENU 0x38 /* left Alt */
-#define KEY_SPACE 0x39
-#define KEY_CAPITAL 0x3A
-#define KEY_F1 0x3B
-#define KEY_F2 0x3C
-#define KEY_F3 0x3D
-#define KEY_F4 0x3E
-#define KEY_F5 0x3F
-#define KEY_F6 0x40
-#define KEY_F7 0x41
-#define KEY_F8 0x42
-#define KEY_F9 0x43
-#define KEY_F10 0x44
-#define KEY_NUMLOCK 0x45
-#define KEY_SCROLL 0x46 /* Scroll Lock */
-#define KEY_NUMPAD7 0x47
-#define KEY_NUMPAD8 0x48
-#define KEY_NUMPAD9 0x49
-#define KEY_SUBTRACT 0x4A /* - on numeric keypad */
-#define KEY_NUMPAD4 0x4B
-#define KEY_NUMPAD5 0x4C
-#define KEY_NUMPAD6 0x4D
-#define KEY_ADD 0x4E /* + on numeric keypad */
-#define KEY_NUMPAD1 0x4F
-#define KEY_NUMPAD2 0x50
-#define KEY_NUMPAD3 0x51
-#define KEY_NUMPAD0 0x52
-#define KEY_DECIMAL 0x53 /* . on numeric keypad */
-#define KEY_OEM_102 0x56 /* <> or \| on RT 102-key keyboard (Non-U.S.) */
-#define KEY_F11 0x57
-#define KEY_F12 0x58
-#define KEY_F13 0x64 /* (NEC PC98) */
-#define KEY_F14 0x65 /* (NEC PC98) */
-#define KEY_F15 0x66 /* (NEC PC98) */
-#define KEY_KANA 0x70 /* (Japanese keyboard) */
-#define KEY_ABNT_C1 0x73 /* /? on Brazilian keyboard */
-#define KEY_CONVERT 0x79 /* (Japanese keyboard) */
-#define KEY_NOCONVERT 0x7B /* (Japanese keyboard) */
-#define KEY_YEN 0x7D /* (Japanese keyboard) */
-#define KEY_ABNT_C2 0x7E /* Numpad . on Brazilian keyboard */
-#define KEY_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */
-#define KEY_PREVTRACK 0x90 /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
-#define KEY_AT 0x91 /* (NEC PC98) */
-#define KEY_COLON 0x92 /* (NEC PC98) */
-#define KEY_UNDERLINE 0x93 /* (NEC PC98) */
-#define KEY_KANJI 0x94 /* (Japanese keyboard) */
-#define KEY_STOP 0x95 /* (NEC PC98) */
-#define KEY_AX 0x96 /* (Japan AX) */
-#define KEY_UNLABELED 0x97 /* (J3100) */
-#define KEY_NEXTTRACK 0x99 /* Next Track */
-#define KEY_NUMPADENTER 0x9C /* Enter on numeric keypad */
-#define KEY_RCONTROL 0x9D
-#define KEY_MUTE 0xA0 /* Mute */
-#define KEY_CALCULATOR 0xA1 /* Calculator */
-#define KEY_PLAYPAUSE 0xA2 /* Play / Pause */
-#define KEY_MEDIASTOP 0xA4 /* Media Stop */
-#define KEY_VOLUMEDOWN 0xAE /* Volume - */
-#define KEY_VOLUMEUP 0xB0 /* Volume + */
-#define KEY_WEBHOME 0xB2 /* Web home */
-#define KEY_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */
-#define KEY_DIVIDE 0xB5 /* / on numeric keypad */
-#define KEY_SYSRQ 0xB7
-#define KEY_RMENU 0xB8 /* right Alt */
-#define KEY_PAUSE 0xC5 /* Pause */
-#define KEY_HOME 0xC7 /* Home on arrow keypad */
-#define KEY_UP 0xC8 /* UpArrow on arrow keypad */
-#define KEY_PRIOR 0xC9 /* PgUp on arrow keypad */
-#define KEY_LEFT 0xCB /* LeftArrow on arrow keypad */
-#define KEY_RIGHT 0xCD /* RightArrow on arrow keypad */
-#define KEY_END 0xCF /* End on arrow keypad */
-#define KEY_DOWN 0xD0 /* DownArrow on arrow keypad */
-#define KEY_NEXT 0xD1 /* PgDn on arrow keypad */
-#define KEY_INSERT 0xD2 /* Insert on arrow keypad */
-#define KEY_DELETE 0xD3 /* Delete on arrow keypad */
-#define KEY_LWIN 0xDB /* Left Windows key */
-#define KEY_RWIN 0xDC /* Right Windows key */
-#define KEY_APPS 0xDD /* AppMenu key */
-#define KEY_POWER 0xDE /* System Power */
-#define KEY_SLEEP 0xDF /* System Sleep */
-#define KEY_WAKE 0xE3 /* System Wake */
-#define KEY_WEBSEARCH 0xE5 /* Web Search */
-#define KEY_WEBFAVORITES 0xE6 /* Web Favorites */
-#define KEY_WEBREFRESH 0xE7 /* Web Refresh */
-#define KEY_WEBSTOP 0xE8 /* Web Stop */
-#define KEY_WEBFORWARD 0xE9 /* Web Forward */
-#define KEY_WEBBACK 0xEA /* Web Back */
-#define KEY_MYCOMPUTER 0xEB /* My Computer */
-#define KEY_MAIL 0xEC /* Mail */
-#define KEY_MEDIASELECT 0xED /* Media Select */
//============================================================
// device_info
//============================================================
-class input_device_list;
-
class device_info
{
- friend input_device_list;
-
private:
- std::string m_name;
- std::string m_id;
- input_device * m_device;
- running_machine & m_machine;
+ const std::string m_name;
+ const std::string m_id;
input_module & m_module;
- input_device_class m_deviceclass;
public:
// Constructor
- device_info(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
- : m_name(name),
- m_id(id),
- m_device(nullptr),
- m_machine(machine),
- m_module(module),
- m_deviceclass(deviceclass)
+ device_info(std::string &&name, std::string &&id, input_module &module) :
+ m_name(std::move(name)),
+ m_id(std::move(id)),
+ m_module(module)
{
}
// Destructor
- virtual ~device_info() {}
+ virtual ~device_info() = default;
// Getters
- running_machine & machine() const { return m_machine; }
- const char * name() const { return m_name.c_str(); }
- const char * id() const { return m_id.c_str(); }
- input_device * device() const { return m_device; }
- input_module & module() const { return m_module; }
- input_device_class deviceclass() const { return m_deviceclass; }
+ const std::string &name() const { return m_name; }
+ const std::string &id() const { return m_id; }
+ input_module &module() const { return m_module; }
// Poll and reset methods
- virtual void poll() {};
+ virtual void poll(bool relative_reset) = 0;
virtual void reset() = 0;
+ virtual void configure(osd::input_device &device) = 0;
};
+
//============================================================
// event_based_device
//============================================================
-#define DEFAULT_EVENT_QUEUE_SIZE 20
-
template <class TEvent>
class event_based_device : public device_info
{
private:
- std::queue<TEvent> m_event_queue;
+ static inline constexpr unsigned DEFAULT_EVENT_QUEUE_SIZE = 64;
+
+ std::queue<TEvent> m_event_queue;
protected:
std::mutex m_device_lock;
- virtual void process_event(TEvent &ev) = 0;
+ virtual void process_event(TEvent const &ev) = 0;
public:
- event_based_device(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
- : device_info(machine, name, id, deviceclass, module)
+ event_based_device(std::string &&name, std::string &&id, input_module &module) :
+ device_info(std::move(name), std::move(id), module)
{
}
- void queue_events(const TEvent *events, int count)
+ void queue_events(TEvent const *events, int count)
{
std::lock_guard<std::mutex> scope_lock(m_device_lock);
for (int i = 0; i < count; i++)
@@ -270,38 +117,46 @@ public:
m_event_queue.pop();
}
- void virtual poll() override
+ virtual void poll(bool relative_reset) override
{
std::lock_guard<std::mutex> scope_lock(m_device_lock);
// Process each event until the queue is empty
while (!m_event_queue.empty())
{
- TEvent &next_event = m_event_queue.front();
- process_event(next_event);
+ process_event(m_event_queue.front());
m_event_queue.pop();
}
}
+
+ virtual void reset() override
+ {
+ std::lock_guard<std::mutex> scope_lock(m_device_lock);
+ std::queue<TEvent>().swap(m_event_queue);
+ }
};
+
//============================================================
// input_device_list class
//============================================================
+template <typename Info>
class input_device_list
{
private:
- std::vector<std::unique_ptr<device_info>> m_list;
+ std::vector<std::unique_ptr<Info> > m_list;
public:
- size_t size() const { return m_list.size(); }
- auto begin() { return m_list.begin(); }
- auto end() { return m_list.end(); }
+ auto size() const { return m_list.size(); }
+ auto empty() const { return m_list.empty(); }
+ auto begin() const { return m_list.begin(); }
+ auto end() const { return m_list.end(); }
- void poll_devices()
+ void poll_devices(bool relative_reset)
{
for (auto &device: m_list)
- device->poll();
+ device->poll(relative_reset);
}
void reset_devices()
@@ -310,17 +165,11 @@ public:
device->reset();
}
- void free_device(device_info* devinfo)
- {
- // find the device to remove
- auto device_matches = [devinfo](std::unique_ptr<device_info> &device) { return devinfo == device.get(); };
- m_list.erase(std::remove_if(std::begin(m_list), std::end(m_list), device_matches), m_list.end());
- }
-
- void for_each_device(std::function<void (device_info*)> action)
+ template <typename T>
+ void for_each_device(T &&action)
{
for (auto &device: m_list)
- action(device.get());
+ action(*device);
}
void free_all_devices()
@@ -329,46 +178,34 @@ public:
m_list.pop_back();
}
- template <typename TActual, typename... TArgs>
- TActual* create_device(running_machine &machine, const char *name, const char *id, input_module &module, TArgs&&... args)
+ template <typename Actual>
+ Actual &add_device(std::unique_ptr<Actual> &&devinfo)
{
- // allocate the device object
- auto devinfo = std::make_unique<TActual>(machine, name, id, module, std::forward<TArgs>(args)...);
-
- return add_device(machine, std::move(devinfo));
- }
-
- template <typename TActual>
- TActual* add_device(running_machine &machine, std::unique_ptr<TActual> devinfo)
- {
- // Add the device to the machine
- devinfo->m_device = machine.input().device_class(devinfo->deviceclass()).add_device(devinfo->name(), devinfo->id(), devinfo.get());
-
- // append us to the list
- m_list.push_back(std::move(devinfo));
-
- return static_cast<TActual*>(m_list.back().get());
+ // append us to the list and return reference
+ Actual &result = *devinfo;
+ m_list.emplace_back(std::move(devinfo));
+ return result;
}
};
+
// keyboard translation table
-struct key_trans_entry {
+struct key_trans_entry
+{
input_item_id mame_key;
-#if defined(OSD_SDL)
+#if defined(OSD_SDL) || defined(SDLMAME_WIN32)
int sdl_scancode;
-#elif defined(OSD_WINDOWS)
- int scan_code;
+#endif
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+ uint16_t scan_code;
unsigned char virtual_key;
-#elif defined(OSD_UWP)
- int scan_code;
- Windows::System::VirtualKey virtual_key;
#endif
char ascii_key;
- char const * mame_key_name;
- char * ui_name;
+ char const * mame_key_name;
+ char const * ui_name;
};
class keyboard_trans_table
@@ -381,7 +218,7 @@ private:
std::unique_ptr<key_trans_entry[]> m_custom_table;
key_trans_entry * m_table;
- uint32_t m_table_size;
+ uint32_t m_table_size;
public:
// constructor
@@ -394,12 +231,9 @@ public:
input_item_id lookup_mame_code(const char * scode) const;
int lookup_mame_index(const char * scode) const;
-#if defined(OSD_WINDOWS)
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
input_item_id map_di_scancode_to_itemid(int di_scancode) const;
int vkey_for_mame_code(input_code code) const;
-#elif defined(OSD_UWP)
- input_item_id map_di_scancode_to_itemid(int di_scancode) const;
- const char* ui_label_for_mame_key(input_item_id code) const;
#endif
static keyboard_trans_table& instance()
@@ -411,155 +245,153 @@ public:
key_trans_entry & operator [](int i) const { return m_table[i]; }
};
+
//============================================================
// input_module_base - base class for input modules
//============================================================
-class osd_options;
-
-typedef std::chrono::high_resolution_clock clock_type;
-typedef std::chrono::time_point<std::chrono::high_resolution_clock> timepoint_type;
-
-// 10 milliseconds polling interval
-#define MIN_POLLING_INTERVAL 10
-
-class input_module_base : public input_module
+class input_module_base : public osd_module, public input_module
{
-public:
- input_module_base(const char *type, const char* name)
- : input_module(type, name),
- m_input_enabled(false),
- m_mouse_enabled(false),
- m_lightgun_enabled(false),
- m_input_paused(false),
- m_options(nullptr)
- {
- }
-
private:
- bool m_input_enabled;
- bool m_mouse_enabled;
- bool m_lightgun_enabled;
- bool m_input_paused;
- const osd_options * m_options;
- input_device_list m_devicelist;
+ // 10 milliseconds polling interval
+ static constexpr inline unsigned MIN_POLLING_INTERVAL = 2;
+
+ using clock_type = std::chrono::high_resolution_clock;
+ using timepoint_type = std::chrono::time_point<std::chrono::high_resolution_clock>;
+
clock_type m_clock;
timepoint_type m_last_poll;
+ bool m_background_input;
+ const osd_options * m_options;
+ osd::input_manager * m_manager;
+
+ virtual void poll(bool relative_reset) = 0;
protected:
- void set_mouse_enabled(bool value) { m_mouse_enabled = value; }
+ input_module_base(char const *type, char const *name);
+
+ osd::input_manager & manager() { assert(m_manager); return *m_manager; }
+ const osd_options * options() const { return m_options; }
+ bool background_input() const { return m_background_input; }
+
+ virtual void before_poll() { }
public:
+ virtual int init(osd_interface &osd, const osd_options &options) override;
- const osd_options * options() const { return m_options; }
- input_device_list * devicelist() { return &m_devicelist; }
- bool input_enabled() const { return m_input_enabled; }
- bool input_paused() const { return m_input_paused; }
- bool mouse_enabled() const { return m_mouse_enabled; }
- bool lightgun_enabled() const { return m_lightgun_enabled; }
+ virtual void input_init(running_machine &machine) override;
+ virtual void poll_if_necessary(bool relative_reset) override;
+
+ virtual void reset_devices() = 0; // SDL OSD uses this to forcibly release keys
+};
- int init(const osd_options &options) override;
- void poll_if_necessary(running_machine &machine) override
+//============================================================
+// input_module_impl - base class for input modules
+//============================================================
+
+template <typename Info, typename OsdImpl>
+class input_module_impl : public input_module_base
+{
+public:
+ virtual void exit() override
{
- auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(m_clock.now() - m_last_poll);
- if (elapsed.count() >= MIN_POLLING_INTERVAL)
- {
- poll(machine);
- }
+ devicelist().free_all_devices();
}
- virtual void poll(running_machine &machine)
+ virtual int init(osd_interface &osd, const osd_options &options) override
{
- // ignore if not enabled
- if (m_input_enabled)
- {
- // grab the current time
- m_last_poll = m_clock.now();
+ m_osd = dynamic_cast<OsdImpl *>(&osd);
+ if (!m_osd)
+ return -1;
- before_poll(machine);
+ return input_module_base::init(osd, options);
+ }
- // track if mouse/lightgun is enabled, for mouse hiding purposes
- m_mouse_enabled = machine.input().device_class(DEVICE_CLASS_MOUSE).enabled();
- m_lightgun_enabled = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).enabled();
- }
+ virtual void reset_devices() override { devicelist().reset_devices(); }
- // poll all of the devices
- if (should_poll_devices(machine))
- {
- m_devicelist.poll_devices();
- }
- else
- {
- m_devicelist.reset_devices();
- }
+protected:
+ using input_module_base::input_module_base;
+
+ input_device_list<Info> &devicelist() { return m_devicelist; }
+ OsdImpl &osd() { assert(m_osd); return *m_osd; }
+
+ virtual void before_poll() override
+ {
+ // periodically process events, in case they're not coming through
+ // this also will make sure the mouse state is up-to-date
+ osd().process_events();
}
- virtual void pause() override
+ virtual bool should_poll_devices()
{
- // keep track of the paused state
- m_input_paused = true;
+ return background_input() || osd().has_focus();
}
- virtual void resume() override
+ template <typename Actual, typename... Params>
+ Actual &create_device(input_device_class deviceclass, std::string &&name, std::string &&id, Params &&... args)
{
- // keep track of the paused state
- m_input_paused = false;
+ // allocate the device object and add it to the input manager
+ return add_device(
+ deviceclass,
+ std::make_unique<Actual>(std::move(name), std::move(id), *this, std::forward<Params>(args)...));
}
- virtual void exit() override
+ template <typename Actual>
+ Actual &add_device(input_device_class deviceclass, std::unique_ptr<Actual> &&devinfo)
{
- devicelist()->free_all_devices();
+ // add it to the input manager and append it to the list
+ osd::input_device &osddev = manager().add_device(deviceclass, devinfo->name(), devinfo->id(), devinfo.get());
+ devinfo->configure(osddev);
+ return devicelist().add_device(std::move(devinfo));
}
-protected:
- virtual int init_internal() { return 0; }
- virtual bool should_poll_devices(running_machine &machine) = 0;
- virtual void before_poll(running_machine &machine) {}
+private:
+ virtual void poll(bool relative_reset) override final
+ {
+ // poll all of the devices
+ if (should_poll_devices())
+ m_devicelist.poll_devices(relative_reset);
+ else
+ m_devicelist.reset_devices();
+ }
+
+ input_device_list<Info> m_devicelist;
+ OsdImpl *m_osd = nullptr;
};
+
template <class TItem>
int generic_button_get_state(void *device_internal, void *item_internal)
{
- device_info *devinfo = static_cast<device_info *>(device_internal);
- TItem *itemdata = static_cast<TItem*>(item_internal);
-
// return the current state
- devinfo->module().poll_if_necessary(devinfo->machine());
- return *itemdata >> 7;
+ return *reinterpret_cast<TItem const *>(item_internal) >> 7;
}
+
template <class TItem>
int generic_axis_get_state(void *device_internal, void *item_internal)
{
- device_info *devinfo = static_cast<device_info *>(device_internal);
- TItem *axisdata = static_cast<TItem*>(item_internal);
-
- // return the current state
- devinfo->module().poll_if_necessary(devinfo->machine());
- return *axisdata;
+ return *reinterpret_cast<TItem const *>(item_internal);
}
+
//============================================================
// default_button_name
//============================================================
-inline static const char *default_button_name(int which)
+inline std::string default_button_name(int which)
{
- static char buffer[20];
- snprintf(buffer, ARRAY_LENGTH(buffer), "B%d", which);
- return buffer;
+ return util::string_format("Button %d", which + 1);
}
//============================================================
// default_pov_name
//============================================================
-inline static const char *default_pov_name(int which)
+inline std::string default_pov_name(int which)
{
- static char buffer[20];
- snprintf(buffer, ARRAY_LENGTH(buffer), "POV%d", which);
- return buffer;
+ return util::string_format("Hat %d", which + 1);
}
// default axis names
@@ -569,27 +401,25 @@ const char *const default_axis_name[] =
"RY", "RZ", "SL1", "SL2"
};
-inline static int32_t normalize_absolute_axis(double raw, double rawmin, double rawmax)
+inline int32_t normalize_absolute_axis(double raw, double rawmin, double rawmax)
{
- double center = (rawmax + rawmin) / 2.0;
-
- // make sure we have valid data
+ // make sure we have valid arguments
if (rawmin >= rawmax)
return int32_t(raw);
- // above center
+ double const center = (rawmax + rawmin) / 2.0;
if (raw >= center)
{
- double result = (raw - center) * INPUT_ABSOLUTE_MAX / (rawmax - center);
- return std::min(result, (double)INPUT_ABSOLUTE_MAX);
+ // above center
+ double const result = (raw - center) * double(osd::input_device::ABSOLUTE_MAX) / (rawmax - center);
+ return int32_t(std::min(result, double(osd::input_device::ABSOLUTE_MAX)));
}
-
- // below center
else
{
- double result = -((center - raw) * (double)-INPUT_ABSOLUTE_MIN / (center - rawmin));
- return std::max(result, (double)INPUT_ABSOLUTE_MIN);
+ // below center
+ double result = -((center - raw) * double(-osd::input_device::ABSOLUTE_MIN) / (center - rawmin));
+ return int32_t(std::max(result, double(osd::input_device::ABSOLUTE_MIN)));
}
}
-#endif // INPUT_COMMON_H_
+#endif // MAME_OSD_INPUT_INPUT_COMMON_H
diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp
index 0b5eb348dc0..6f539c2f7fc 100644
--- a/src/osd/modules/input/input_dinput.cpp
+++ b/src/osd/modules/input/input_dinput.cpp
@@ -1,281 +1,354 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles, Brad Hughes
+// copyright-holders:Aaron Giles, Brad Hughes, Vas Crabb
//============================================================
//
// input_dinput.cpp - Windows DirectInput support
//
//============================================================
+/*
+
+DirectInput joystick input is a bit of a mess. It gives eight axes
+called X, Y, Z, Rx, Ry, Rz, slider 0 and slider 1. The driver can
+assign arbitrary physical axes to these axes. Up to four hat switches
+are supported, giving a direction in hundredths of degrees. In theory,
+this supports dial-like controls with an arbitrary number of stops. In
+practice, it just makes dealing with 8-way hat switches more complicated
+and prevents contradictory inputs from being reported altogether.
+
+You may get a vague indication of the type of controller, and you can
+obtain usage information for HID controllers.
+
+The Windows HID driver supposedly uses the following mappings:
+
+0x01 Generic Desktop 0x30 X X
+0x01 Generic Desktop 0x31 Y Y
+0x01 Generic Desktop 0x32 Z Z
+0x01 Generic Desktop 0x33 Rx Rx
+0x01 Generic Desktop 0x34 Ry Ry
+0x01 Generic Desktop 0x35 Rz Rz
+0x01 Generic Desktop 0x36 Slider Slider
+0x01 Generic Desktop 0x37 Dial Slider
+0x01 Generic Desktop 0x39 Hat Switch POV Hat
+0x02 Simulation 0xBA Rudder Rz
+0x02 Simulation 0xBB Throttle Slider
+0x02 Simulation 0xC4 Accelerator Y
+0x02 Simulation 0xC5 Brake Rz
+0x02 Simulation 0xC8 Steering X
+
+Anything without an explicit mapping is treated as a button.
+
+The WinMM driver supposedly uses the following axis mappings:
+
+X X
+Y Y
+Z Slider
+R Rz
+U Slider
+V Slider
+
+The actual mapping used by various controllers doesn't match what you
+might expect from the HID mapping.
+
+Gamepads:
+
+Axis Logitech DS4 Xinput Switch
+X Left X Left X Left X Left X
+Y Left Y Left Y Left Y Left Y
+Z Right X Right X Triggers
+Rx Left trigger Right X Right X
+Ry Right trigger Right Y Right Y
+Rz Right Y Right Y
+
+Thrustmaster controllers:
+
+Axis HOTAS Side stick Throttle/Pedals Dual Throttles Triple Throttles Driving
+X Aileron Aileron Mini Stick X Left Throttle Right Brake Steering
+Y Elevator Elevator Mini stick Y Right Throttle Left Brake Brake
+Z Throttle Throttle Flaps Rudder
+Rx Right Brake Right Brake Right Brake Left Throttle
+Ry Left Brake Left Brake Left Brake Centre Throttle
+Rz Twist Rudder Rocker Air Brake Right Throttle Accelerator
+Slider 0 Rocker Throttle Antenna Rudder Clutch
+Slider 1 Rudder Rudder
+
+Logitech controllers:
+
+Axis Pro Wheels
+X Steering
+Y
+Z
+Rx Accelerator
+Ry Brake
+Rz Clutch
+Slider 0
+Slider 1
+
+MFG Crosswind pedals:
+
+X Left Brake
+Y Right Brake
+Rz Rudder
+
+*/
-#include "input_module.h"
#include "modules/osdmodule.h"
-#if defined(OSD_WINDOWS)
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
-// standard windows headers
-#include <windows.h>
-#include <initguid.h>
-#include <tchar.h>
-#include <wrl/client.h>
+#include "input_dinput.h"
-// undef WINNT for dinput.h to prevent duplicate definition
-#undef WINNT
-#include <dinput.h>
-#undef interface
+#include "interface/inputseq.h"
+#include "windows/winutil.h"
-#include <mutex>
+// emu
+#include "inpttype.h"
-// MAME headers
-#include "emu.h"
-#include "strconv.h"
+// lib/util
+#include "util/corestr.h"
-// MAMEOS headers
-#include "window.h"
-#include "winutil.h"
+#ifdef SDLMAME_WIN32
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_syswm.h>
+#endif
-#include "input_common.h"
-#include "input_windows.h"
-#include "input_dinput.h"
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <memory>
-using namespace Microsoft::WRL;
+// standard windows headers
+#include <initguid.h>
+#include <tchar.h>
-static int32_t dinput_joystick_pov_get_state(void *device_internal, void *item_internal);
-//============================================================
-// dinput_set_dword_property
-//============================================================
+namespace osd {
-#if DIRECTINPUT_VERSION >= 0x0800
-static HRESULT dinput_set_dword_property(ComPtr<IDirectInputDevice8> device, REFGUID property_guid, DWORD object, DWORD how, DWORD value)
-#else
-static HRESULT dinput_set_dword_property(ComPtr<IDirectInputDevice> device, REFGUID property_guid, DWORD object, DWORD how, DWORD value)
-#endif
+namespace {
+
+std::string guid_to_string(GUID const &guid)
{
- DIPROPDWORD dipdw;
+ // size of a GUID string with dashes plus NUL terminator
+ char guid_string[37];
+ snprintf(
+ guid_string, std::size(guid_string),
+ "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid.Data1, guid.Data2, guid.Data3,
+ guid.Data4[0], guid.Data4[1], guid.Data4[2],
+ guid.Data4[3], guid.Data4[4], guid.Data4[5],
+ guid.Data4[6], guid.Data4[7]);
+
+ return guid_string;
+}
- dipdw.diph.dwSize = sizeof(dipdw);
- dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
- dipdw.diph.dwObj = object;
- dipdw.diph.dwHow = how;
- dipdw.dwData = value;
- return device->SetProperty(property_guid, &dipdw.diph);
-}
//============================================================
-// dinput_device - base directinput device
+// dinput_keyboard_device - DirectInput keyboard device
//============================================================
-dinput_device::dinput_device(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
- : device_info(machine, name, id, deviceclass, module),
- dinput({nullptr})
+class dinput_keyboard_device : public dinput_device
{
-}
+public:
+ dinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ std::mutex m_device_lock;
+ keyboard_state m_keyboard;
+};
-dinput_device::~dinput_device()
+dinput_keyboard_device::dinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
+ m_keyboard({ { 0 } })
{
- if (dinput.device != nullptr)
- dinput.device.Reset();
}
-HRESULT dinput_device::poll_dinput(LPVOID pState) const
+void dinput_keyboard_device::poll(bool relative_reset)
{
- HRESULT result;
-
- // first poll the device, then get the state
-#if DIRECTINPUT_VERSION >= 0x0800
- dinput.device->Poll();
-#else
- if (dinput.device2 != nullptr)
- dinput.device2->Poll();
-#endif
+ // poll the DirectInput immediate state
+ std::lock_guard<std::mutex> scope_lock(m_device_lock);
+ poll_dinput(&m_keyboard.state);
+}
- // GetDeviceState returns the immediate state
- result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState);
+void dinput_keyboard_device::reset()
+{
+ memset(&m_keyboard.state, 0, sizeof(m_keyboard.state));
+}
- // handle lost inputs here
- if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
+void dinput_keyboard_device::configure(input_device &device)
+{
+ // populate it
+ char defname[20];
+ for (unsigned keynum = 0; keynum < MAX_KEYS; keynum++)
{
- result = dinput.device->Acquire();
- if (result == DI_OK)
- result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState);
+ input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum);
+
+ // generate/fetch the name
+ snprintf(defname, std::size(defname), "Scan%03d", keynum);
+
+ // add the item to the device
+ device.add_item(
+ item_name(keynum, defname, nullptr),
+ strmakeupper(defname),
+ itemid,
+ generic_button_get_state<uint8_t>,
+ &m_keyboard.state[keynum]);
}
-
- return result;
}
+
//============================================================
-// dinput_keyboard_device - directinput keyboard device
+// dinput_mouse_device - DirectInput mouse device
//============================================================
-dinput_keyboard_device::dinput_keyboard_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : dinput_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
- keyboard({{0}})
+class dinput_mouse_device : public dinput_device
{
-}
+public:
+ dinput_mouse_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
+
+ void poll(bool relative_reset) override;
+ void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ DIMOUSESTATE2 m_mouse;
+};
-// Polls the direct input immediate state
-void dinput_keyboard_device::poll()
+dinput_mouse_device::dinput_mouse_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
+ m_mouse({0})
{
- std::lock_guard<std::mutex> scope_lock(m_device_lock);
-
- // Poll the state
- dinput_device::poll_dinput(&keyboard.state);
+ // cap the number of axes and buttons based on the format
+ m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(3));
+ m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD((m_format == &c_dfDIMouse) ? 4 : 8));
}
-void dinput_keyboard_device::reset()
-{
- memset(&keyboard.state, 0, sizeof(keyboard.state));
-}
-
-//============================================================
-// dinput_api_helper - DirectInput API helper
-//============================================================
-
-dinput_api_helper::dinput_api_helper(int version)
- : m_dinput(nullptr),
- m_dinput_version(version),
- m_dinput_create_prt(nullptr)
+void dinput_mouse_device::poll(bool relative_reset)
{
+ // poll
+ if (relative_reset && (poll_dinput(&m_mouse) == DI_OK))
+ {
+ // scale the axis data
+ m_mouse.lX *= input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lY *= input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lZ *= input_device::RELATIVE_PER_PIXEL;
+ }
}
-dinput_api_helper::~dinput_api_helper()
+void dinput_mouse_device::reset()
{
- m_dinput.Reset();
+ memset(&m_mouse, 0, sizeof(m_mouse));
}
-int dinput_api_helper::initialize()
+void dinput_mouse_device::configure(input_device &device)
{
- HRESULT result;
-
-#if DIRECTINPUT_VERSION >= 0x0800
- if (m_dinput_version >= 0x0800)
+ // populate the axes
+ for (int axisnum = 0; axisnum < m_caps.dwAxes; axisnum++)
{
- result = DirectInput8Create(GetModuleHandleUni(), m_dinput_version, IID_IDirectInput8, reinterpret_cast<void **>(m_dinput.GetAddressOf()), nullptr);
- if (result != DI_OK)
- {
- m_dinput_version = 0;
- return result;
- }
+ // add to the mouse device and optionally to the gun device as well
+ device.add_item(
+ item_name(offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr),
+ std::string_view(),
+ input_item_id(ITEM_ID_XAXIS + axisnum),
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX + axisnum);
}
- else
-#endif
- {
- m_dinput_dll = osd::dynamic_module::open({ "dinput.dll" });
- m_dinput_create_prt = m_dinput_dll->bind<dinput_create_fn>("DirectInputCreateW");
- if (m_dinput_create_prt == nullptr)
- {
- osd_printf_verbose("Legacy DirectInput library dinput.dll is not available\n");
- return ERROR_DLL_NOT_FOUND;
- }
+ // populate the buttons
+ for (int butnum = 0; butnum < m_caps.dwButtons; butnum++)
+ {
+ auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]);
- // first attempt to initialize DirectInput at v7
- m_dinput_version = 0x0700;
- result = (*m_dinput_create_prt)(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr);
- if (result != DI_OK)
- {
- // if that fails, try version 5
- m_dinput_version = 0x0500;
- result = (*m_dinput_create_prt)(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr);
- if (result != DI_OK)
- {
- m_dinput_version = 0;
- return result;
- }
- }
+ // add to the mouse device
+ device.add_item(
+ item_name(offset, default_button_name(butnum), nullptr),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_mouse.rgbButtons[butnum]);
}
-
- osd_printf_verbose("DirectInput: Using DirectInput %d\n", m_dinput_version >> 8);
- return 0;
-}
-
-
-HRESULT dinput_api_helper::enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const
-{
- device_enum_interface::dinput_callback_context ctx;
- ctx.self = enumerate_interface;
- ctx.state = state;
-
- return m_dinput->EnumDevices(devclass, device_enum_interface::enum_callback, &ctx, DIEDFL_ATTACHEDONLY);
}
//============================================================
-// dinput_module - base directinput module
+// dinput_module - base DirectInput module
//============================================================
-class dinput_module : public wininput_module, public device_enum_interface
+class dinput_module : public input_module_impl<dinput_device, osd_common_t>
{
-protected:
- std::unique_ptr<dinput_api_helper> m_dinput_helper;
-
public:
- dinput_module(const char* type, const char* name)
- : wininput_module(type, name),
- m_dinput_helper(nullptr)
+ dinput_module(const char* type, const char* name) :
+ input_module_impl<dinput_device, osd_common_t>(type, name),
+ m_dinput_helper(nullptr)
{
}
- int init_internal() override
+ virtual int init(osd_interface &osd, osd_options const &options) override
{
- m_dinput_helper = std::make_unique<dinput_api_helper>(DIRECTINPUT_VERSION);
- int result = m_dinput_helper->initialize();
- if (result != 0)
+ m_dinput_helper = std::make_unique<dinput_api_helper>();
+ int const result = m_dinput_helper->initialize();
+ if (result)
+ {
+ m_dinput_helper.reset();
return result;
+ }
- return 0;
+ return input_module_impl<dinput_device, osd_common_t>::init(osd, options);
}
- void exit() override
+ virtual void exit() override
{
- wininput_module::exit();
- m_dinput_helper.reset();
- }
+ input_module_impl<dinput_device, osd_common_t>::exit();
- void input_init(running_machine &machine) override
- {
- HRESULT result = m_dinput_helper->enum_attached_devices(dinput_devclass(), this, &machine);
- if (result != DI_OK)
- fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", static_cast<uint32_t>(result));
+ m_dinput_helper.reset();
}
- static std::string device_item_name(dinput_device * devinfo, int offset, const char * defstring, const TCHAR * suffix)
+ virtual void input_init(running_machine &machine) override
{
- DIDEVICEOBJECTINSTANCE instance = { 0 };
- HRESULT result;
-
- // query the key name
- instance.dwSize = sizeof(instance);
- result = devinfo->dinput.device->GetObjectInfo(&instance, offset, DIPH_BYOFFSET);
+ input_module_impl<dinput_device, osd_common_t>::input_init(machine);
- // if we got an error and have no default string, just return nullptr
+ HRESULT const result = m_dinput_helper->enum_attached_devices(
+ dinput_devclass(),
+ [this] (LPCDIDEVICEINSTANCE instance) { return device_enum_callback(instance); });
if (result != DI_OK)
- {
- if (defstring == nullptr)
- return nullptr;
-
- // Return the default value
- return std::string(defstring);
- }
-
- // convert the name to utf8
- std::string namestring = osd::text::from_tstring(instance.tszName);
-
- // if no suffix, return as-is
- if (suffix == nullptr)
- return namestring;
-
- // convert the suffix to utf8
- std::string suffix_utf8 = osd::text::from_tstring(suffix);
-
- // Concat the name and suffix
- return namestring + " " + suffix_utf8;
+ fatalerror("DirectInput: Unable to enumerate devices (result=%08X)\n", uint32_t(result));
}
protected:
virtual int dinput_devclass() = 0;
+ virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) = 0;
+
+ std::unique_ptr<dinput_api_helper> m_dinput_helper;
};
+
class keyboard_input_dinput : public dinput_module
{
public:
@@ -284,69 +357,30 @@ public:
{
}
- int dinput_devclass() override
+protected:
+ virtual int dinput_devclass() override
{
-#if DIRECTINPUT_VERSION >= 0x0800
return DI8DEVCLASS_KEYBOARD;
-#else
- return DIDEVTYPE_KEYBOARD;
-#endif
}
- BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override
+ virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override
{
- running_machine &machine = *static_cast<running_machine *>(ref);
- dinput_keyboard_device *devinfo;
- int keynum;
-
// allocate and link in a new device
- devinfo = m_dinput_helper->create_device<dinput_keyboard_device>(machine, *this, instance, &c_dfDIKeyboard, nullptr, dinput_cooperative_level::FOREGROUND);
- if (devinfo == nullptr)
- goto exit;
-
- // populate it
- for (keynum = 0; keynum < MAX_KEYS; keynum++)
- {
- input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum);
- char defname[20];
- std::string name;
-
- // generate/fetch the name
- snprintf(defname, ARRAY_LENGTH(defname), "Scan%03d", keynum);
- name = device_item_name(devinfo, keynum, defname, nullptr);
+ auto devinfo = m_dinput_helper->create_device<dinput_keyboard_device>(
+ *this,
+ instance,
+ &c_dfDIKeyboard,
+ nullptr,
+ background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND,
+ [] (auto...) { return true; });
+ if (devinfo)
+ add_device(DEVICE_CLASS_KEYBOARD, std::move(devinfo));
- // add the item to the device
- devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]);
- }
-
- exit:
return DIENUM_CONTINUE;
}
};
-dinput_mouse_device::dinput_mouse_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : dinput_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
- mouse({0})
-{
-}
-
-void dinput_mouse_device::poll()
-{
- // poll
- dinput_device::poll_dinput(&mouse);
-
- // scale the axis data
- mouse.lX *= INPUT_RELATIVE_PER_PIXEL;
- mouse.lY *= INPUT_RELATIVE_PER_PIXEL;
- mouse.lZ *= INPUT_RELATIVE_PER_PIXEL;
-}
-
-void dinput_mouse_device::reset()
-{
- memset(&mouse, 0, sizeof(mouse));
-}
-
class mouse_input_dinput : public dinput_module
{
public:
@@ -355,284 +389,954 @@ public:
{
}
- int dinput_devclass() override
+protected:
+ virtual int dinput_devclass() override
{
-#if DIRECTINPUT_VERSION >= 0x0800
return DI8DEVCLASS_POINTER;
-#else
- return DIDEVTYPE_MOUSE;
-#endif
}
- BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override
+ virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override
{
- dinput_mouse_device *devinfo = nullptr;
- running_machine &machine = *static_cast<running_machine *>(ref);
- int axisnum, butnum;
- HRESULT result;
-
// allocate and link in a new device
- devinfo = m_dinput_helper->create_device<dinput_mouse_device>(machine, *this, instance, &c_dfDIMouse2, &c_dfDIMouse, dinput_cooperative_level::FOREGROUND);
- if (devinfo == nullptr)
- goto exit;
+ auto devinfo = m_dinput_helper->create_device<dinput_mouse_device>(
+ *this,
+ instance,
+ &c_dfDIMouse2,
+ &c_dfDIMouse,
+ background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND,
+ [] (auto const &device, auto const &format) -> bool
+ {
+ // set relative mode
+ HRESULT const result = dinput_api_helper::set_dword_property(
+ device,
+ DIPROP_AXISMODE,
+ 0,
+ DIPH_DEVICE,
+ DIPROPAXISMODE_REL);
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
+ {
+ osd_printf_error("DirectInput: Unable to set relative mode for mouse.\n");
+ return false;
+ }
+ return true;
+ });
+ if (devinfo)
+ add_device(DEVICE_CLASS_MOUSE, std::move(devinfo));
- // set relative mode on the mouse device
- result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_REL);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
- {
- osd_printf_error("DirectInput: Unable to set relative mode for mouse %u (%s)\n", static_cast<unsigned int>(devicelist()->size()), devinfo->name());
- goto error;
- }
+ return DIENUM_CONTINUE;
+ }
+};
- // cap the number of axes and buttons based on the format
- devinfo->dinput.caps.dwAxes = std::min(devinfo->dinput.caps.dwAxes, DWORD(3));
- devinfo->dinput.caps.dwButtons = std::min(devinfo->dinput.caps.dwButtons, DWORD((devinfo->dinput.format == &c_dfDIMouse) ? 4 : 8));
- // populate the axes
- for (axisnum = 0; axisnum < devinfo->dinput.caps.dwAxes; axisnum++)
- {
- // add to the mouse device and optionally to the gun device as well
- std::string name = device_item_name(devinfo, offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr);
- devinfo->device()->add_item(
- name.c_str(),
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &devinfo->mouse.lX + axisnum);
- }
+class joystick_input_dinput : public dinput_module
+{
+public:
+ joystick_input_dinput() :
+ dinput_module(OSD_JOYSTICKINPUT_PROVIDER, "dinput")
+ {
+ }
- // populate the buttons
- for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++)
- {
- uintptr_t offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]);
+protected:
+ virtual int dinput_devclass() override
+ {
+ return DI8DEVCLASS_GAMECTRL;
+ }
- // add to the mouse device
- std::string name = device_item_name(devinfo, offset, default_button_name(butnum), nullptr);
- devinfo->device()->add_item(
- name.c_str(),
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &devinfo->mouse.rgbButtons[butnum]);
- }
+ virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override
+ {
+ // allocate and link in a new device
+ auto devinfo = m_dinput_helper->create_device<dinput_joystick_device>(
+ *this,
+ instance,
+ &c_dfDIJoystick,
+ nullptr,
+ background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND,
+ [] (auto const &device, auto const &format) -> bool
+ {
+ // set absolute mode
+ HRESULT const result = dinput_api_helper::set_dword_property(
+ device,
+ DIPROP_AXISMODE,
+ 0,
+ DIPH_DEVICE,
+ DIPROPAXISMODE_ABS);
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
+ {
+ osd_printf_error("DirectInput: Unable to set absolute mode for joystick.\n");
+ return false;
+ }
+ return true;
+ });
+ if (devinfo)
+ add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo));
- exit:
return DIENUM_CONTINUE;
-
- error:
- if (devinfo != nullptr)
- devicelist()->free_device(devinfo);
- goto exit;
}
};
-dinput_joystick_device::dinput_joystick_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : dinput_device(machine, name, id, DEVICE_CLASS_JOYSTICK, module),
- joystick({{0}})
+} // anonymous namespace
+
+
+//============================================================
+// dinput_device - base DirectInput device
+//============================================================
+
+dinput_device::dinput_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ device_info(std::move(name), std::move(id), module),
+ m_device(std::move(device)),
+ m_caps(caps),
+ m_format(format)
{
}
-void dinput_joystick_device::reset()
+HRESULT dinput_device::poll_dinput(LPVOID pState) const
+{
+ HRESULT result;
+
+ // first poll the device, then get the state
+ result = m_device->Poll();
+
+ // handle lost inputs here
+ if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
+ {
+ result = m_device->Acquire();
+ if ((result == DI_OK) || (result == S_FALSE))
+ result = m_device->Poll();
+ }
+
+ // GetDeviceState returns the immediate state
+ if ((result == DI_OK) || (result == DI_NOEFFECT))
+ result = m_device->GetDeviceState(m_format->dwDataSize, pState);
+
+ return result;
+}
+
+std::string dinput_device::item_name(int offset, std::string_view defstring, const char *suffix) const
{
- memset(&joystick.state, 0, sizeof(joystick.state));
+ // query the key name
+ DIDEVICEOBJECTINSTANCE instance = { 0 };
+ instance.dwSize = sizeof(instance);
+ HRESULT const result = m_device->GetObjectInfo(&instance, offset, DIPH_BYOFFSET);
+
+ // use the default value if it failed
+ std::string name;
+ if (result != DI_OK)
+ name = defstring;
+ else
+ name = text::from_tstring(instance.tszName);
+
+ // if no suffix, return as-is
+ if (suffix)
+ name.append(" ").append(suffix);
+
+ return name;
}
-void dinput_joystick_device::poll()
+
+//============================================================
+// dinput_joystick_device - directinput joystick device
+//============================================================
+
+dinput_joystick_device::dinput_joystick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
+ m_joystick({ { 0 } })
{
- int axisnum;
+ // cap the number of axes, POVs, and buttons based on the format
+ m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(8));
+ m_caps.dwPOVs = std::min(m_caps.dwPOVs, DWORD(4));
+ m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD(128));
+}
- // poll the device first
- if (dinput_device::poll_dinput(&joystick.state) != ERROR_SUCCESS)
- return;
+void dinput_joystick_device::reset()
+{
+ memset(&m_joystick.state, 0, sizeof(m_joystick.state));
+ std::fill(std::begin(m_joystick.state.rgdwPOV), std::end(m_joystick.state.rgdwPOV), 0xffff);
+}
- // normalize axis values
- for (axisnum = 0; axisnum < 8; axisnum++)
+void dinput_joystick_device::poll(bool relative_reset)
+{
+ // poll the device first
+ if (dinput_device::poll_dinput(&m_joystick.state) == DI_OK)
{
- LONG *axis = (&joystick.state.lX) + axisnum;
- *axis = normalize_absolute_axis(*axis, joystick.rangemin[axisnum], joystick.rangemax[axisnum]);
+ // normalize axis values
+ for (int axisnum = 0; axisnum < 8; axisnum++)
+ {
+ auto const range = m_joystick.rangemax[axisnum] - m_joystick.rangemin[axisnum];
+ if (range)
+ {
+ // assumes output range is symmetrical
+ LONG *const axis = &m_joystick.state.lX + axisnum;
+ double const offset = *axis - m_joystick.rangemin[axisnum];
+ double const scaled = offset * double(input_device::ABSOLUTE_MAX - input_device::ABSOLUTE_MIN) / double(range);
+ *axis = lround(std::clamp<double>(scaled + input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MAX));
+ }
+ }
}
}
-int dinput_joystick_device::configure()
+void dinput_joystick_device::configure(input_device &device)
{
+ input_device::assignment_vector assignments;
HRESULT result;
- uint32_t axisnum, axiscount;
-
- auto devicelist = static_cast<input_module_base&>(module()).devicelist();
- // temporary approximation of index
- int devindex = devicelist->size();
-
- // set absolute mode
- result = dinput_set_dword_property(dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
- osd_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", devindex, name());
+ // get device information - it gives clues about axis usage
+ DIDEVICEINSTANCE info;
+ info.dwSize = sizeof(info);
+ result = m_device->GetDeviceInfo(&info);
+ bool hid = false;
+ uint8_t type = DI8DEVTYPE_DEVICE;
+ uint8_t subtype = 0;
+ if (result == DI_OK)
+ {
+ hid = (info.dwDevType & DIDEVTYPE_HID) != 0;
+ type = info.dwDevType & 0x00ff;
+ subtype = (info.dwDevType >> 8) & 0x00ff;
+ osd_printf_verbose(
+ "DirectInput: Device type=0x%02X subtype=0x%02X HID=%u\n",
+ type,
+ subtype,
+ hid ? "yes" : "no");
+ }
// turn off deadzone; we do our own calculations
- result = dinput_set_dword_property(dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
- osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", devindex, name());
+ result = dinput_api_helper::set_dword_property(m_device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0);
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
+ osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %s.\n", name());
// turn off saturation; we do our own calculations
- result = dinput_set_dword_property(dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
- osd_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", devindex, name());
-
- // cap the number of axes, POVs, and buttons based on the format
- dinput.caps.dwAxes = std::min(dinput.caps.dwAxes, DWORD(8));
- dinput.caps.dwPOVs = std::min(dinput.caps.dwPOVs, DWORD(4));
- dinput.caps.dwButtons = std::min(dinput.caps.dwButtons, DWORD(128));
+ result = dinput_api_helper::set_dword_property(m_device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000);
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
+ osd_printf_warning("DirectInput: Unable to reset saturation for joystick %s.\n", name());
// populate the axes
- for (axisnum = axiscount = 0; axiscount < dinput.caps.dwAxes && axisnum < 8; axisnum++)
+ input_item_id axisitems[8];
+ std::fill(std::begin(axisitems), std::end(axisitems), ITEM_ID_INVALID);
+ for (uint32_t axisnum = 0, axiscount = 0; (axiscount < m_caps.dwAxes) && (axisnum < 8); axisnum++)
{
- DIPROPRANGE dipr;
- std::string name;
-
// fetch the range of this axis
+ DIPROPRANGE dipr;
dipr.diph.dwSize = sizeof(dipr);
dipr.diph.dwHeaderSize = sizeof(dipr.diph);
dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG);
dipr.diph.dwHow = DIPH_BYOFFSET;
- result = dinput.device->GetProperty(DIPROP_RANGE, &dipr.diph);
+ result = m_device->GetProperty(DIPROP_RANGE, &dipr.diph);
if (result != DI_OK)
+ {
+ // this is normal when axes are skipped, e.g. X/Y/Z present, rX/rY absent, rZ present
+ osd_printf_verbose("DirectInput: Unable to get properties for joystick %s axis %u.\n", name(), axisnum);
continue;
+ }
- joystick.rangemin[axisnum] = dipr.lMin;
- joystick.rangemax[axisnum] = dipr.lMax;
+ m_joystick.rangemin[axisnum] = dipr.lMin;
+ m_joystick.rangemax[axisnum] = dipr.lMax;
// populate the item description as well
- name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr);
- device()->add_item(
- name.c_str(),
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &joystick.state.lX + axisnum);
+ axisitems[axisnum] = device.add_item(
+ item_name(offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr),
+ std::string_view(),
+ input_item_id(ITEM_ID_XAXIS + axisnum),
+ generic_axis_get_state<LONG>,
+ &m_joystick.state.lX + axisnum);
axiscount++;
}
- // populate the POVs
- for (uint32_t povnum = 0; povnum < dinput.caps.dwPOVs; povnum++)
+ // take a guess at which axes might be pedals depending on type and remap onto negative half of range
+ input_item_id pedalitems[3] = { ITEM_ID_INVALID, ITEM_ID_INVALID, ITEM_ID_INVALID };
+ if (DI8DEVTYPE_FLIGHT == type)
{
- std::string name;
+ // Rx/Ry are often used for brakes
+ bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]);
+ bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]);
+ if (rxpedal && rypedal)
+ {
+ pedalitems[0] = axisitems[3];
+ m_joystick.rangemin[3] = m_joystick.rangemax[3];
+ m_joystick.rangemax[3] = -m_joystick.rangemax[3];
+ pedalitems[1] = axisitems[4];
+ m_joystick.rangemin[4] = m_joystick.rangemax[4];
+ m_joystick.rangemax[4] = -m_joystick.rangemax[4];
+ }
+ }
+ else if (DI8DEVTYPE_DRIVING == type)
+ {
+ bool const ypedal = (ITEM_ID_INVALID != axisitems[1]) && !m_joystick.rangemin[1] && (0 < m_joystick.rangemax[1]);
+ bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]);
+ bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]);
+ bool const rzpedal = (ITEM_ID_INVALID != axisitems[5]) && !m_joystick.rangemin[5] && (0 < m_joystick.rangemax[5]);
+ bool const s0pedal = (ITEM_ID_INVALID != axisitems[6]) && !m_joystick.rangemin[6] && (0 < m_joystick.rangemax[6]);
+ if (DI8DEVTYPEDRIVING_DUALPEDALS == subtype)
+ {
+ // dual pedals are usually Y and Rz
+ if (ypedal && rzpedal)
+ {
+ pedalitems[0] = axisitems[1];
+ m_joystick.rangemin[1] = m_joystick.rangemax[1];
+ m_joystick.rangemax[1] = -m_joystick.rangemax[1];
+
+ pedalitems[1] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+ }
+ }
+ else if (DI8DEVTYPEDRIVING_THREEPEDALS == subtype)
+ {
+ // triple pedals may be Y, Rz and slider 0, or Rx, Ry and Rz
+ if (ypedal && rzpedal && s0pedal)
+ {
+ pedalitems[0] = axisitems[1];
+ m_joystick.rangemin[1] = m_joystick.rangemax[1];
+ m_joystick.rangemax[1] = -m_joystick.rangemax[1];
+
+ pedalitems[1] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+
+ pedalitems[2] = axisitems[6];
+ m_joystick.rangemin[6] = m_joystick.rangemax[6];
+ m_joystick.rangemax[6] = -m_joystick.rangemax[6];
+ }
+ else if (rxpedal && rypedal && rzpedal)
+ {
+ pedalitems[0] = axisitems[3];
+ m_joystick.rangemin[3] = m_joystick.rangemax[3];
+ m_joystick.rangemax[3] = -m_joystick.rangemax[3];
+
+ pedalitems[1] = axisitems[4];
+ m_joystick.rangemin[4] = m_joystick.rangemax[4];
+ m_joystick.rangemax[4] = -m_joystick.rangemax[4];
+
+ pedalitems[2] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+ }
+ }
+ }
+
+ // populate the POV hats
+ input_item_id povitems[4][4];
+ for (auto &pov : povitems)
+ std::fill(std::begin(pov), std::end(pov), ITEM_ID_INVALID);
+ for (uint32_t povnum = 0; povnum < m_caps.dwPOVs; povnum++)
+ {
// left
- name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L"));
- device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_LEFT)));
+ povitems[povnum][0] = device.add_item(
+ item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Left"),
+ std::string_view(),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1LEFT),
+ &dinput_joystick_device::pov_get_state,
+ reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_LEFT)));
// right
- name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R"));
- device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_RIGHT)));
+ povitems[povnum][1] = device.add_item(
+ item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Right"),
+ std::string_view(),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1RIGHT),
+ &dinput_joystick_device::pov_get_state,
+ reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_RIGHT)));
// up
- name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U"));
- device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_UP)));
+ povitems[povnum][2] = device.add_item(
+ item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Up"),
+ std::string_view(),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1UP),
+ &dinput_joystick_device::pov_get_state,
+ reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_UP)));
// down
- name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D"));
- device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_DOWN)));
+ povitems[povnum][3] = device.add_item(
+ item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Down"),
+ std::string_view(),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1DOWN),
+ &dinput_joystick_device::pov_get_state,
+ reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_DOWN)));
}
// populate the buttons
- for (uint32_t butnum = 0; butnum < dinput.caps.dwButtons; butnum++)
+ for (uint32_t butnum = 0; butnum < m_caps.dwButtons; butnum++)
{
- uintptr_t offset = reinterpret_cast<uintptr_t>(&static_cast<DIJOYSTATE2 *>(nullptr)->rgbButtons[butnum]);
- std::string name = dinput_module::device_item_name(this, offset, default_button_name(butnum), nullptr);
+ auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIJOYSTATE2 *>(nullptr)->rgbButtons[butnum]);
input_item_id itemid;
-
if (butnum < INPUT_MAX_BUTTONS)
- itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum);
+ itemid = input_item_id(ITEM_ID_BUTTON1 + butnum);
else if (butnum < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH)
- itemid = static_cast<input_item_id>(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum);
+ itemid = input_item_id(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum);
else
itemid = ITEM_ID_OTHER_SWITCH;
- device()->add_item(name.c_str(), itemid, generic_button_get_state<BYTE>, &joystick.state.rgbButtons[butnum]);
- }
+ input_item_id const actual = device.add_item(
+ item_name(offset, default_button_name(butnum), nullptr),
+ std::string_view(),
+ itemid,
+ generic_button_get_state<BYTE>,
+ &m_joystick.state.rgbButtons[butnum]);
- return 0;
-}
+ // there are sixteen action button types
+ if (butnum < 16)
+ {
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + butnum), SEQ_TYPE_STANDARD, seq);
-class joystick_input_dinput : public dinput_module
-{
-public:
- joystick_input_dinput() :
- dinput_module(OSD_JOYSTICKINPUT_PROVIDER, "dinput")
- {
+ // assign the first few buttons to UI actions and pedals
+ // TODO: don't map pedals for driving controls that have them present
+ switch (butnum)
+ {
+ case 0:
+ assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 1:
+ assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back((3 > m_caps.dwButtons) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 2:
+ assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 3:
+ assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq);
+ break;
+ }
+ }
}
- int dinput_devclass() override
+ // add default assignments depending on type
+ if (DI8DEVTYPE_FLIGHT == type)
{
-#if DIRECTINPUT_VERSION >= 0x0800
- return DI8DEVCLASS_GAMECTRL;
-#else
- return DIDEVTYPE_JOYSTICK;
-#endif
- }
+ if (((ITEM_ID_INVALID == axisitems[0]) || (ITEM_ID_INVALID == axisitems[1])) && (1 <= m_caps.dwPOVs))
+ {
+ // X or Y missing, fall back to using POV hat for navigation
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ axisitems[1],
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // try using throttle for zoom/focus
+ if (ITEM_ID_INVALID != axisitems[2])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2]));
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
- BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override
- {
- dinput_cooperative_level cooperative_level = dinput_cooperative_level::FOREGROUND;
- running_machine &machine = *static_cast<running_machine *>(ref);
- dinput_joystick_device *devinfo;
- int result = 0;
+ // try using twist/rudder for next/previous group
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5]));
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ else
+ {
+ // only use stick for primary navigation/movement
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ axisitems[1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+
+ // try using hat for secondary navigation functions
+ if (1 <= m_caps.dwPOVs)
+ {
+ input_seq const leftseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][0]));
+ input_seq const rightseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][1]));
+ input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][2]));
+ input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][3]));
+ if ((ITEM_ID_INVALID != axisitems[2]) || (ITEM_ID_INVALID != axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_PAGE_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_PAGE_DOWN, SEQ_TYPE_STANDARD, downseq);
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, downseq);
+ }
+ }
- if (!osd_common_t::s_window_list.empty() && osd_common_t::s_window_list.front()->win_has_menu())
- cooperative_level = dinput_cooperative_level::BACKGROUND;
+ // try using throttle for zoom
+ if (ITEM_ID_INVALID != axisitems[2])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2]));
+ if ((1 <= m_caps.dwPOVs) || (ITEM_ID_INVALID != axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ if ((1 > m_caps.dwPOVs) || (ITEM_ID_INVALID == axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
- // allocate and link in a new device
- devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level);
- if (devinfo == nullptr)
- goto exit;
+ // try using twist/rudder for focus next/previous
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5]));
+ if (1 <= m_caps.dwPOVs)
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ if (ITEM_ID_INVALID == axisitems[2])
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ }
- result = devinfo->configure();
- if (result != 0)
+ // Z or slider 0 is usually the throttle - use one of them for joystick Z
+ add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { axisitems[2], axisitems[6] });
+
+ // use Z for the first two pedals if present
+ if (ITEM_ID_INVALID != axisitems[2])
{
- osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result));
+ // TODO: use Rx/Ry as well if they appear to be brakes
+ input_seq const pedal1seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[2]));
+ input_seq const pedal2seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[2]));
+ assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_STANDARD, pedal1seq);
+ assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_STANDARD, pedal2seq);
+ }
+ }
+ else if (DI8DEVTYPE_DRIVING == type)
+ {
+ // use the wheel and D-pad for navigation and directional controls
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ ITEM_ID_INVALID,
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // check subtype to determine how pedals should be assigned
+ if (DI8DEVTYPEDRIVING_COMBINEDPEDALS == subtype)
+ {
+ if (ITEM_ID_INVALID != axisitems[1])
+ {
+ // put first two pedals on opposite sides of Y axis
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[1])));
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[1])));
+
+ // use for previous/next group as well
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[1])));
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[1])));
+ }
+ }
+ else
+ {
+ // see if we have individual pedals
+ if (ITEM_ID_INVALID != pedalitems[0])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[0])));
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[0])));
+ }
+ if (ITEM_ID_INVALID != pedalitems[1])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[1])));
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[1])));
+ }
+ if (ITEM_ID_INVALID != pedalitems[2])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL3,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[2])));
+ assignments.emplace_back(
+ IPT_UI_FOCUS_NEXT,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[2])));
+ }
+ }
+ }
+ else
+ {
+ // assume this is a gamepad - see if it looks like it has dual analog sticks
+ input_item_id stickaxes[2][2] = {
+ { axisitems[0], axisitems[1] },
+ { ITEM_ID_INVALID, ITEM_ID_INVALID } };
+ input_item_id pedalaxis = ITEM_ID_INVALID;
+ if ((ITEM_ID_INVALID != axisitems[2]) && (ITEM_ID_INVALID != axisitems[5]))
+ {
+ // assume Z/Rz are right stick
+ stickaxes[1][0] = axisitems[2];
+ stickaxes[1][1] = axisitems[5];
+ add_twin_stick_assignments(
+ assignments,
+ stickaxes[0][0],
+ stickaxes[0][1],
+ stickaxes[1][0],
+ stickaxes[1][1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+ }
+ else if ((ITEM_ID_INVALID != axisitems[3]) && (ITEM_ID_INVALID != axisitems[4]))
+ {
+ // assume Rx/Ry are right stick and Z is triggers if present
+ stickaxes[1][0] = axisitems[3];
+ stickaxes[1][1] = axisitems[4];
+ pedalaxis = axisitems[2];
+ add_twin_stick_assignments(
+ assignments,
+ stickaxes[0][0],
+ stickaxes[0][1],
+ stickaxes[1][0],
+ stickaxes[1][1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+ }
+ else
+ {
+ // if Z is present, use it as secondary Y
+ stickaxes[1][1] = axisitems[2];
}
- exit:
- return DIENUM_CONTINUE;
+ // try to find a "complete" stick for primary movement controls
+ input_item_id diraxis[2][2];
+ choose_primary_stick(diraxis, stickaxes[0][0], stickaxes[0][1], stickaxes[1][0], stickaxes[1][1]);
+ add_directional_assignments(
+ assignments,
+ diraxis[0][0],
+ diraxis[0][1],
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // assign a secondary stick axis to joystick Z
+ add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { diraxis[1][1], diraxis[1][0] });
+
+ // try to find a suitable axis to use for the first two pedals
+ add_assignment(
+ assignments,
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NEG,
+ { pedalaxis, diraxis[1][1], diraxis[0][1] });
+ add_assignment(
+ assignments,
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_POS,
+ { pedalaxis, diraxis[1][1], diraxis[0][1] });
+
+ // try to choose an axis for previous/next group
+ if (ITEM_ID_INVALID != pedalaxis)
+ {
+ // this is reversed because right trigger is negative direction
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, pedalaxis)));
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalaxis)));
+ pedalaxis = ITEM_ID_INVALID;
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1]))
+ {
+ // took secondary Y
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0]))
+ {
+ // took secondary X
+ }
+
+ // use secondary Y for page up/down if available
+ consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1]);
+
+ // put focus previous/next and zoom on secondary X if available
+ add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0]);
+ add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0]);
}
-};
-//============================================================
-// dinput_joystick_pov_get_state
-//============================================================
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
+}
-static int32_t dinput_joystick_pov_get_state(void *device_internal, void *item_internal)
+int32_t dinput_joystick_device::pov_get_state(void *device_internal, void *item_internal)
{
- dinput_joystick_device *devinfo = static_cast<dinput_joystick_device *>(device_internal);
- int povnum = reinterpret_cast<uintptr_t>(item_internal) / 4;
- int povdir = reinterpret_cast<uintptr_t>(item_internal) % 4;
- int32_t result = 0;
- DWORD pov;
+ auto *const devinfo = static_cast<dinput_joystick_device *>(device_internal);
+ int const povnum = uintptr_t(item_internal) / 4;
+ int const povdir = uintptr_t(item_internal) % 4;
// get the current state
- devinfo->module().poll_if_necessary(devinfo->machine());
- pov = devinfo->joystick.state.rgdwPOV[povnum];
+ DWORD const pov = devinfo->m_joystick.state.rgdwPOV[povnum];
// if invalid, return 0
if ((pov & 0xffff) == 0xffff)
- return result;
+ return 0;
// return the current state
switch (povdir)
{
- case POVDIR_LEFT: result = (pov >= 22500 && pov <= 31500); break;
- case POVDIR_RIGHT: result = (pov >= 4500 && pov <= 13500); break;
- case POVDIR_UP: result = (pov >= 31500 || pov <= 4500); break;
- case POVDIR_DOWN: result = (pov >= 13500 && pov <= 22500); break;
+ case POVDIR_LEFT: return (pov >= 22500) && (pov <= 31500);
+ case POVDIR_RIGHT: return (pov >= 4500) && (pov <= 13500);
+ case POVDIR_UP: return (pov >= 31500) || (pov <= 4500);
+ case POVDIR_DOWN: return (pov >= 13500) && (pov <= 22500);
}
- return result;
+
+ return 0;
+}
+
+
+//============================================================
+// dinput_api_helper - DirectInput API helper
+//============================================================
+
+dinput_api_helper::dinput_api_helper()
+{
+}
+
+dinput_api_helper::~dinput_api_helper()
+{
+}
+
+int dinput_api_helper::initialize()
+{
+ HRESULT result = DirectInput8Create(GetModuleHandleUni(), DIRECTINPUT_VERSION, IID_IDirectInput8, &m_dinput, nullptr);
+ if (result != DI_OK)
+ {
+ return result;
+ }
+
+ osd_printf_verbose("DirectInput: Using DirectInput %d\n", DIRECTINPUT_VERSION >> 8);
+ return 0;
+}
+
+
+std::pair<Microsoft::WRL::ComPtr<IDirectInputDevice8>, LPCDIDATAFORMAT> dinput_api_helper::open_device(
+ LPCDIDEVICEINSTANCE instance,
+ LPCDIDATAFORMAT format1,
+ LPCDIDATAFORMAT format2,
+ dinput_cooperative_level cooperative_level)
+{
+ HRESULT result;
+
+ // attempt to create a device
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> device;
+ result = m_dinput->CreateDevice(instance->guidInstance, &device, nullptr);
+ if (result != DI_OK)
+ {
+ osd_printf_error("DirectInput: Unable to create device.\n");
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ // attempt to set the data format
+ LPCDIDATAFORMAT format = format1;
+ result = device->SetDataFormat(format);
+ if ((result != DI_OK) && format2)
+ {
+ // use the secondary format if available
+ osd_printf_verbose("DirectInput: Error setting primary data format, trying secondary format.\n");
+ format = format2;
+ result = device->SetDataFormat(format);
+ }
+ if (result != DI_OK)
+ {
+ osd_printf_error("DirectInput: Unable to set data format.\n");
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ // default window to the first window in the list
+ // For now, we always use the desktop window due to multiple issues:
+ // * MAME recreates windows on toggling fullscreen. DirectInput really doesn't like this.
+ // * DirectInput doesn't like the window used for D3D fullscreen exclusive mode.
+ // * With multiple windows, the first window needs to have focus when using foreground mode.
+ // This makes it impossible to use force feedback as that requires foreground exclusive mode.
+ // The only way to get around this would be to reopen devices on focus changes.
+ [[maybe_unused]] HWND window_handle;
+ DWORD di_cooperative_level;
+#if defined(OSD_WINDOWS)
+ auto const &window = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front());
+ bool const standalone_window = !window.attached_mode();
+#elif defined(SDLMAME_WIN32)
+ auto const &window = dynamic_cast<sdl_window_info &>(*osd_common_t::window_list().front());
+ bool const standalone_window = true;
+#endif
+ if (!standalone_window)
+ {
+ // in attached mode we have to ignore the caller and hook up to the desktop window
+ window_handle = GetDesktopWindow();
+ di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
+ }
+ else
+ {
+#if defined(OSD_WINDOWS)
+ window_handle = window.platform_window();
+#elif defined(SDLMAME_WIN32)
+ auto const sdlwindow = window.platform_window();
+ SDL_SysWMinfo info;
+ SDL_VERSION(&info.version);
+ if (!SDL_GetWindowWMInfo(sdlwindow, &info))
+ return std::make_pair(nullptr, nullptr);
+ window_handle = info.info.win.window;
+#endif
+ switch (cooperative_level)
+ {
+ case dinput_cooperative_level::BACKGROUND:
+ di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
+ break;
+ case dinput_cooperative_level::FOREGROUND:
+ //di_cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
+ di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
+ break;
+ default:
+ throw false;
+ }
+ }
+
+ // set the cooperative level
+ result = device->SetCooperativeLevel(GetDesktopWindow(), di_cooperative_level);
+ if (result != DI_OK)
+ {
+ osd_printf_error("DirectInput: Unable to set cooperative level.\n");
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ // return new device
+ return std::make_pair(std::move(device), format);
+}
+
+
+std::string dinput_api_helper::make_id(LPCDIDEVICEINSTANCE instance)
+{
+ // use name, product GUID and instance GUID as identifier
+ return
+ text::from_tstring(instance->tszInstanceName) +
+ " product_" +
+ guid_to_string(instance->guidProduct) +
+ " instance_" +
+ guid_to_string(instance->guidInstance);
}
-#else
+} // namespace osd
+
+
+#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
+#include "input_module.h"
+
+namespace osd {
+
+namespace {
+
MODULE_NOT_SUPPORTED(keyboard_input_dinput, OSD_KEYBOARDINPUT_PROVIDER, "dinput")
MODULE_NOT_SUPPORTED(mouse_input_dinput, OSD_MOUSEINPUT_PROVIDER, "dinput")
MODULE_NOT_SUPPORTED(joystick_input_dinput, OSD_JOYSTICKINPUT_PROVIDER, "dinput")
-#endif
-MODULE_DEFINITION(KEYBOARDINPUT_DINPUT, keyboard_input_dinput)
-MODULE_DEFINITION(MOUSEINPUT_DINPUT, mouse_input_dinput)
-MODULE_DEFINITION(JOYSTICKINPUT_DINPUT, joystick_input_dinput)
+} // anonymous namespace
+
+} // namespace osd
+
+#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
+MODULE_DEFINITION(KEYBOARDINPUT_DINPUT, osd::keyboard_input_dinput)
+MODULE_DEFINITION(MOUSEINPUT_DINPUT, osd::mouse_input_dinput)
+MODULE_DEFINITION(JOYSTICKINPUT_DINPUT, osd::joystick_input_dinput)
diff --git a/src/osd/modules/input/input_dinput.h b/src/osd/modules/input/input_dinput.h
index a2b6c2edcbb..98fdfb011bb 100644
--- a/src/osd/modules/input/input_dinput.h
+++ b/src/osd/modules/input/input_dinput.h
@@ -1,54 +1,42 @@
-#ifndef INPUT_DINPUT_H_
-#define INPUT_DINPUT_H_
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Brad Hughes, Vas Crabb
+//============================================================
+//
+// input_dinput.h - Windows DirectInput support
+//
+//============================================================
+#ifndef MAME_OSD_INPUT_INPUT_DINPUT_H
+#define MAME_OSD_INPUT_INPUT_DINPUT_H
+
+#pragma once
+
+#include "assignmenthelper.h"
+#include "input_wincommon.h"
-#include "input_common.h"
#include "modules/lib/osdlib.h"
+#include "modules/lib/osdobj_common.h"
-//============================================================
-// dinput_device - base directinput device
-//============================================================
+#include "window.h"
-// DirectInput-specific information about a device
-struct dinput_api_state
-{
-#if DIRECTINPUT_VERSION >= 0x0800
- Microsoft::WRL::ComPtr<IDirectInputDevice8> device;
-#else
- Microsoft::WRL::ComPtr<IDirectInputDevice> device;
- Microsoft::WRL::ComPtr<IDirectInputDevice2> device2;
-#endif
- DIDEVCAPS caps;
- LPCDIDATAFORMAT format;
-};
+#include "strconv.h"
-class device_enum_interface
-{
-public:
- struct dinput_callback_context
- {
- device_enum_interface * self;
- void * state;
- };
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
- virtual ~device_enum_interface()
- {
- }
+#include <windows.h>
+#include <dinput.h>
+#include <wrl/client.h>
- static BOOL CALLBACK enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref)
- {
- auto context = static_cast<dinput_callback_context*>(ref);
- return context->self->device_enum_callback(instance, context->state);
- }
- virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) = 0;
-};
+namespace osd {
-// Typedef for dynamically loaded function
-#if DIRECTINPUT_VERSION >= 0x0800
-typedef HRESULT (WINAPI *dinput_create_fn)(HINSTANCE, DWORD, LPDIRECTINPUT8 *, LPUNKNOWN);
-#else
-typedef HRESULT (WINAPI *dinput_create_fn)(HINSTANCE, DWORD, LPDIRECTINPUT *, LPUNKNOWN);
-#endif
+//============================================================
+// dinput_device - base directinput device
+//============================================================
enum class dinput_cooperative_level
{
@@ -58,187 +46,144 @@ enum class dinput_cooperative_level
class dinput_api_helper
{
-private:
-#if DIRECTINPUT_VERSION >= 0x0800
- Microsoft::WRL::ComPtr<IDirectInput8> m_dinput;
-#else
- Microsoft::WRL::ComPtr<IDirectInput> m_dinput;
-#endif
- int m_dinput_version;
- osd::dynamic_module::ptr m_dinput_dll;
- dinput_create_fn m_dinput_create_prt;
-
public:
- dinput_api_helper(int version);
- virtual ~dinput_api_helper();
+ dinput_api_helper();
+ ~dinput_api_helper();
+
int initialize();
- template<class TDevice>
- TDevice* create_device(
- running_machine &machine,
- input_module_base &module,
- LPCDIDEVICEINSTANCE instance,
- LPCDIDATAFORMAT format1,
- LPCDIDATAFORMAT format2,
- dinput_cooperative_level cooperative_level)
+ template <typename TDevice, typename TCallback>
+ std::unique_ptr<TDevice> create_device(
+ input_module_base &module,
+ LPCDIDEVICEINSTANCE instance,
+ LPCDIDATAFORMAT format1,
+ LPCDIDATAFORMAT format2,
+ dinput_cooperative_level cooperative_level,
+ TCallback &&callback)
{
- HRESULT result;
- std::shared_ptr<win_window_info> window;
- HWND hwnd;
-
- // convert instance name to utf8
- std::string utf8_instance_name = osd::text::from_tstring(instance->tszInstanceName);
-
- // set device id to name + product unique identifier + instance unique identifier
- std::string utf8_instance_id = utf8_instance_name + " product_" + guid_to_string(instance->guidProduct) + " instance_" + guid_to_string(instance->guidInstance);
-
- // allocate memory for the device object
- TDevice* devinfo = module.devicelist()->create_device<TDevice>(machine, utf8_instance_name.c_str(), utf8_instance_id.c_str(), module);
-
- // attempt to create a device
- result = m_dinput->CreateDevice(instance->guidInstance, devinfo->dinput.device.GetAddressOf(), nullptr);
- if (result != DI_OK)
- goto error;
-
-#if DIRECTINPUT_VERSION < 0x0800
- // try to get a version 2 device for it so we can use the poll method
- result = devinfo->dinput.device.CopyTo(IID_IDirectInputDevice2, reinterpret_cast<void**>(devinfo->dinput.device2.GetAddressOf()));
+ auto [device, format] = open_device(instance, format1, format2, cooperative_level);
+ if (!device)
+ return nullptr;
+
+ // get the capabilities
+ DIDEVCAPS caps;
+ caps.dwSize = sizeof(caps);
+ HRESULT const result = device->GetCapabilities(&caps);
if (result != DI_OK)
- devinfo->dinput.device2 = nullptr;
-#endif
+ return nullptr;
- // get the caps
- devinfo->dinput.caps.dwSize = sizeof(devinfo->dinput.caps);
- result = devinfo->dinput.device->GetCapabilities(&devinfo->dinput.caps);
- if (result != DI_OK)
- goto error;
+ if (!callback(device, format))
+ return nullptr;
- // attempt to set the data format
- devinfo->dinput.format = format1;
- result = devinfo->dinput.device->SetDataFormat(devinfo->dinput.format);
- if (result != DI_OK)
- {
- // use the secondary format if available
- if (format2 != nullptr)
- {
- devinfo->dinput.format = format2;
- result = devinfo->dinput.device->SetDataFormat(devinfo->dinput.format);
- }
- if (result != DI_OK)
- goto error;
- }
-
- // default window to the first window in the list
- window = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front());
- DWORD di_cooperative_level;
- if (window->attached_mode())
- {
- // in attached mode we have to ignore the caller and hook up to the desktop window
- hwnd = GetDesktopWindow();
- di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
- }
- else
- {
- hwnd = window->platform_window();
- switch (cooperative_level)
- {
- case dinput_cooperative_level::BACKGROUND:
- di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
- break;
- case dinput_cooperative_level::FOREGROUND:
- di_cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
- break;
- default:
- throw false;
- }
- }
-
- // set the cooperative level
- result = devinfo->dinput.device->SetCooperativeLevel(hwnd, di_cooperative_level);
- if (result != DI_OK)
- goto error;
+ // allocate memory for the device object
+ return std::make_unique<TDevice>(
+ text::from_tstring(instance->tszInstanceName),
+ make_id(instance),
+ module,
+ std::move(device),
+ caps,
+ format);
+ }
- return devinfo;
+ template <typename T>
+ HRESULT enum_attached_devices(int devclass, T &&callback) const
+ {
+ return m_dinput->EnumDevices(
+ devclass,
+ &di_enum_devices_cb<std::remove_reference_t<T> >,
+ LPVOID(&callback),
+ DIEDFL_ATTACHEDONLY);
+ }
- error:
- module.devicelist()->free_device(devinfo);
- return nullptr;
+ template <typename T>
+ static HRESULT set_dword_property(
+ T &&device,
+ REFGUID property_guid,
+ DWORD object,
+ DWORD how,
+ DWORD value)
+ {
+ DIPROPDWORD dipdw;
+ dipdw.diph.dwSize = sizeof(dipdw);
+ dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
+ dipdw.diph.dwObj = object;
+ dipdw.diph.dwHow = how;
+ dipdw.dwData = value;
+
+ return device->SetProperty(property_guid, &dipdw.diph);
}
- HRESULT enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const;
+private:
+ std::pair<Microsoft::WRL::ComPtr<IDirectInputDevice8>, LPCDIDATAFORMAT> open_device(
+ LPCDIDEVICEINSTANCE instance,
+ LPCDIDATAFORMAT format1,
+ LPCDIDATAFORMAT format2,
+ dinput_cooperative_level cooperative_level);
+
+ static std::string make_id(LPCDIDEVICEINSTANCE instance);
- static std::string guid_to_string(const GUID& guid)
+ template <typename T>
+ static BOOL CALLBACK di_enum_devices_cb(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
- // Size of a GUID string with dashes plus null terminator
- char guid_string[37];
-
- snprintf(
- guid_string, ARRAY_LENGTH(guid_string),
- "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- guid.Data1, guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1], guid.Data4[2],
- guid.Data4[3], guid.Data4[4], guid.Data4[5],
- guid.Data4[6], guid.Data4[7]);
-
- return guid_string;
+ return (*reinterpret_cast<T *>(ref))(instance);
}
+
+ Microsoft::WRL::ComPtr<IDirectInput8> m_dinput;
+ dynamic_module::ptr m_dinput_dll;
};
+
class dinput_device : public device_info
{
-public:
- dinput_api_state dinput;
-
- dinput_device(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module);
- virtual ~dinput_device();
-
protected:
- HRESULT poll_dinput(LPVOID pState) const;
-};
-
-class dinput_keyboard_device : public dinput_device
-{
-private:
- std::mutex m_device_lock;
+ dinput_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
-public:
- keyboard_state keyboard;
+ HRESULT poll_dinput(LPVOID pState) const;
- dinput_keyboard_device(running_machine &machine, const char *name, const char *id, input_module &module);
+ std::string item_name(int offset, std::string_view defstr, char const *suffix) const;
- void poll() override;
- void reset() override;
+ // DirectInput-specific information about a device
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> const m_device;
+ DIDEVCAPS m_caps;
+ LPCDIDATAFORMAT const m_format;
};
-class dinput_mouse_device : public dinput_device
+
+class dinput_joystick_device : public dinput_device, protected joystick_assignment_helper
{
public:
- mouse_state mouse;
+ dinput_joystick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
-public:
- dinput_mouse_device(running_machine &machine, const char *name, const char *id, input_module &module);
- void poll() override;
void reset() override;
-};
+ void poll(bool relative_reset) override;
+ void configure(input_device &device) override;
-// state information for a joystick; DirectInput state must be first element
-struct dinput_joystick_state
-{
- DIJOYSTATE state;
- LONG rangemin[8];
- LONG rangemax[8];
-};
+private:
+ // state information for a joystick; DirectInput state must be first element
+ struct dinput_joystick_state
+ {
+ DIJOYSTATE state;
+ LONG rangemin[8];
+ LONG rangemax[8];
+ };
-class dinput_joystick_device : public dinput_device
-{
-public:
- dinput_joystick_state joystick;
-public:
- dinput_joystick_device(running_machine &machine, const char *name, const char *id, input_module &module);
- void reset() override;
- void poll() override;
- int configure();
+ static int32_t pov_get_state(void *device_internal, void *item_internal);
+
+ dinput_joystick_state m_joystick;
};
+} // namespace osd
-#endif
+#endif // MAME_OSD_INPUT_INPUT_DINPUT_H
diff --git a/src/osd/modules/input/input_mac.cpp b/src/osd/modules/input/input_mac.cpp
index 4dd030b331a..90360adaa3f 100644
--- a/src/osd/modules/input/input_mac.cpp
+++ b/src/osd/modules/input/input_mac.cpp
@@ -14,8 +14,8 @@
#if defined(OSD_MAC)
// System headers
-#include <ctype.h>
-#include <stddef.h>
+#include <cctype>
+#include <cstddef>
#include <mutex>
#include <memory>
#include <algorithm>
@@ -31,15 +31,8 @@
#include "../../mac/osdmac.h"
#include "input_common.h"
-extern void MacPollInputs();
-
-void mac_osd_interface::customize_input_type_list(simple_list<input_type_entry> &typelist)
-{
-}
-
-void mac_osd_interface::poll_inputs(running_machine &machine)
+void mac_osd_interface::customize_input_type_list(std::vector<input_type_entry> &typelist)
{
- MacPollInputs();
}
void mac_osd_interface::release_keys()
diff --git a/src/osd/modules/input/input_module.h b/src/osd/modules/input/input_module.h
index d521d0d1860..64e5ebb2332 100644
--- a/src/osd/modules/input/input_module.h
+++ b/src/osd/modules/input/input_module.h
@@ -5,29 +5,23 @@
// input_module.h - OSD input module contracts
//
//============================================================
+#ifndef MAME_OSD_INPUT_INPUT_MODULE_H
+#define MAME_OSD_INPUT_INPUT_MODULE_H
-#ifndef INPUT_MODULE_H_
-#define INPUT_MODULE_H_
+#pragma once
#include "osdepend.h"
#include "modules/osdmodule.h"
-class input_module : public osd_module
+class input_module
{
public:
- input_module(const char *type, const char *name)
- : osd_module(type, name)
- {
- }
-
- virtual ~input_module() { }
+ virtual ~input_module() = default;
virtual void input_init(running_machine &machine) = 0;
- virtual void poll_if_necessary(running_machine &machine) = 0;
- virtual void pause() = 0;
- virtual void resume() = 0;
+ virtual void poll_if_necessary(bool relative_reset) = 0;
};
//============================================================
@@ -39,4 +33,4 @@ public:
#define OSD_LIGHTGUNINPUT_PROVIDER "lightgunprovider"
#define OSD_JOYSTICKINPUT_PROVIDER "joystickprovider"
-#endif /* INPUT_MODULE_H_ */
+#endif // MAME_OSD_INPUT_INPUT_MODULE_H
diff --git a/src/osd/modules/input/input_none.cpp b/src/osd/modules/input/input_none.cpp
index 0bad0fb1e7b..96c3f116868 100644
--- a/src/osd/modules/input/input_none.cpp
+++ b/src/osd/modules/input/input_none.cpp
@@ -7,60 +7,58 @@
//============================================================
#include "input_module.h"
-#include "modules/osdmodule.h"
-class keyboard_input_none : public input_module
+
+namespace osd {
+
+namespace {
+
+class keyboard_input_none : public osd_module, public input_module
{
public:
- keyboard_input_none()
- : input_module(OSD_KEYBOARDINPUT_PROVIDER, "none") {}
- int init(const osd_options &options) override { return 0; }
- void poll_if_necessary(running_machine &machine) override {};
- void input_init(running_machine &machine) override {};
- void pause() override {};
- void resume() override {};
+ keyboard_input_none() : osd_module(OSD_KEYBOARDINPUT_PROVIDER, "none") { }
+ int init(osd_interface &osd, const osd_options &options) override { return 0; }
+ void input_init(running_machine &machine) override { }
+ void poll_if_necessary(bool relative_reset) override { }
};
-MODULE_DEFINITION(KEYBOARD_NONE, keyboard_input_none)
-class mouse_input_none : public input_module
+class mouse_input_none : public osd_module, public input_module
{
public:
- mouse_input_none()
- : input_module(OSD_MOUSEINPUT_PROVIDER, "none") {}
- int init(const osd_options &options) override { return 0; }
- void input_init(running_machine &machine) override {};
- void poll_if_necessary(running_machine &machine) override {};
- void pause() override {};
- void resume() override {};
+ mouse_input_none() : osd_module(OSD_MOUSEINPUT_PROVIDER, "none") { }
+ int init(osd_interface &osd, const osd_options &options) override { return 0; }
+ void input_init(running_machine &machine) override { }
+ void poll_if_necessary(bool relative_reset) override { }
};
-MODULE_DEFINITION(MOUSE_NONE, mouse_input_none)
-class lightgun_input_none : public input_module
+class lightgun_input_none : public osd_module, public input_module
{
public:
- lightgun_input_none()
- : input_module(OSD_LIGHTGUNINPUT_PROVIDER, "none") {}
- int init(const osd_options &options) override { return 0; }
- void input_init(running_machine &machine) override {};
- void poll_if_necessary(running_machine &machine) override {};
- void pause() override {};
- void resume() override {};
+ lightgun_input_none() : osd_module(OSD_LIGHTGUNINPUT_PROVIDER, "none") { }
+ int init(osd_interface &osd, const osd_options &options) override { return 0; }
+ void input_init(running_machine &machine) override { }
+ void poll_if_necessary(bool relative_reset) override { }
};
-MODULE_DEFINITION(LIGHTGUN_NONE, lightgun_input_none)
-class joystick_input_none : public input_module
+class joystick_input_none : public osd_module, public input_module
{
public:
- joystick_input_none()
- : input_module(OSD_JOYSTICKINPUT_PROVIDER, "none") {}
- int init(const osd_options &options) override { return 0; }
- void input_init(running_machine &machine) override {};
- void poll_if_necessary(running_machine &machine) override {};
- void pause() override {};
- void resume() override {};
+ joystick_input_none() : osd_module(OSD_JOYSTICKINPUT_PROVIDER, "none") { }
+ int init(osd_interface &osd, const osd_options &options) override { return 0; }
+ void input_init(running_machine &machine) override { }
+ void poll_if_necessary(bool relative_reset) override { }
};
-MODULE_DEFINITION(JOYSTICK_NONE, joystick_input_none)
+} // anonymous namesapce
+
+} // namespace osd
+
+
+
+MODULE_DEFINITION(KEYBOARD_NONE, osd::keyboard_input_none)
+MODULE_DEFINITION(MOUSE_NONE, osd::mouse_input_none)
+MODULE_DEFINITION(LIGHTGUN_NONE, osd::lightgun_input_none)
+MODULE_DEFINITION(JOYSTICK_NONE, osd::joystick_input_none)
diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp
index 9243ec4a0f4..5b6813dcd59 100644
--- a/src/osd/modules/input/input_rawinput.cpp
+++ b/src/osd/modules/input/input_rawinput.cpp
@@ -6,41 +6,39 @@
//
//============================================================
-#include "input_module.h"
#include "modules/osdmodule.h"
#if defined(OSD_WINDOWS)
-// standard windows headers
-#include <windows.h>
-#include <tchar.h>
-#undef interface
+#include "input_windows.h"
-#include <mutex>
-#include <functional>
-#include <algorithm>
+#include "input_wincommon.h"
-// MAME headers
-#include "emu.h"
-#include "strconv.h"
-
-// MAMEOS headers
-#include "modules/lib/osdlib.h"
#include "winmain.h"
#include "window.h"
-#include "input_common.h"
-#include "input_windows.h"
+#include "modules/lib/osdlib.h"
+#include "strconv.h"
-//============================================================
-// MACROS
-//============================================================
+// MAME headers
+#include "inpttype.h"
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <new>
+#include <utility>
+
+// standard windows headers
+#include <windows.h>
+#include <tchar.h>
-// Typedefs for dynamically loaded functions
-typedef UINT (WINAPI *get_rawinput_device_list_ptr)(PRAWINPUTDEVICELIST, PUINT, UINT);
-typedef UINT (WINAPI *get_rawinput_data_ptr)( HRAWINPUT, UINT, LPVOID, PUINT, UINT);
-typedef UINT (WINAPI *get_rawinput_device_info_ptr)(HANDLE, UINT, LPVOID, PUINT);
-typedef BOOL (WINAPI *register_rawinput_devices_ptr)(PCRAWINPUTDEVICE, UINT, UINT);
+
+namespace osd {
+
+namespace {
class safe_regkey
{
@@ -48,17 +46,24 @@ private:
HKEY m_key;
public:
- safe_regkey()
- : m_key(nullptr)
- {
- }
+ safe_regkey() : m_key(nullptr) { }
+ safe_regkey(safe_regkey const &) = delete;
+ safe_regkey(safe_regkey &&key) : m_key(key.m_key) { key.m_key = nullptr; }
+ explicit safe_regkey(HKEY key) : m_key(key) { }
- explicit safe_regkey(HKEY key)
- : m_key(key)
+ ~safe_regkey() { close(); }
+
+ safe_regkey &operator=(safe_regkey const &) = delete;
+
+ safe_regkey &operator=(safe_regkey &&key)
{
+ close();
+ m_key = key.m_key;
+ key.m_key = nullptr;
+ return *this;
}
- bool valid() const { return m_key != nullptr; }
+ explicit operator bool() const { return m_key != nullptr; }
void close()
{
@@ -69,70 +74,68 @@ public:
}
}
- ~safe_regkey()
- {
- close();
- }
-
operator HKEY() const { return m_key; }
-};
-//============================================================
-// reg_open_key
-//============================================================
-
-static safe_regkey reg_open_key(HKEY basekey, const std::wstring &subkey)
-{
- HKEY key;
- if (RegOpenKeyEx(basekey, subkey.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
- return safe_regkey(key);
+ safe_regkey open(std::wstring const &subkey) const { return open(m_key, subkey); }
- return safe_regkey();
+ std::wstring enum_key(int index) const
+ {
+ WCHAR keyname[256];
+ DWORD namelen = std::size(keyname);
+ if (RegEnumKeyEx(m_key, index, keyname, &namelen, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
+ return std::wstring(keyname, namelen);
+ else
+ return std::wstring();
+ }
-}
+ std::wstring query_string(WCHAR const *path) const
+ {
+ // first query to get the length
+ DWORD datalen;
+ if (RegQueryValueExW(m_key, path, nullptr, nullptr, nullptr, &datalen) != ERROR_SUCCESS)
+ return std::wstring();
-//============================================================
-// reg_enum_key
-//============================================================
+ // allocate a buffer
+ auto buffer = std::make_unique<WCHAR []>((datalen + (sizeof(WCHAR) * 2) - 1) / sizeof(WCHAR));
-static std::wstring reg_enum_key(HKEY key, int index)
-{
- WCHAR keyname[MAX_PATH];
- DWORD namelen;
- if (RegEnumKeyEx(key, index, keyname, &namelen, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
- return std::wstring(keyname, namelen);
+ // now get the actual data
+ if (RegQueryValueExW(m_key, path, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer.get()), &datalen) != ERROR_SUCCESS)
+ return std::wstring();
- return std::wstring();
-}
-
-//============================================================
-// reg_query_string
-//============================================================
-
-static std::wstring reg_query_string(HKEY key, const TCHAR *path)
-{
- DWORD datalen;
- LONG result;
+ buffer[datalen / sizeof(WCHAR)] = 0;
+ return std::wstring(buffer.get());
+ }
- // first query to get the length
- result = RegQueryValueEx(key, path, nullptr, nullptr, nullptr, &datalen);
- if (result != ERROR_SUCCESS)
- return std::wstring();
+ template <typename T> void foreach_subkey(T &&action) const
+ {
+ std::wstring name;
+ for (int i = 0; ; i++)
+ {
+ name = enum_key(i);
+ if (name.empty())
+ break;
- // allocate a buffer
- auto buffer = std::make_unique<TCHAR[]>(datalen + sizeof(TCHAR));
- buffer[datalen / sizeof(TCHAR)] = 0;
+ safe_regkey const subkey = open(name);
+ if (!subkey)
+ break;
- // now get the actual data
- result = RegQueryValueEx(key, path, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer.get()), &datalen);
- if (result == ERROR_SUCCESS)
- return std::wstring(buffer.get());
+ bool const shouldcontinue = action(subkey);
+ if (!shouldcontinue)
+ break;
+ }
+ }
- // otherwise return an empty string
- return std::wstring();
-}
+ static safe_regkey open(HKEY basekey, std::wstring const &subkey)
+ {
+ HKEY key(nullptr);
+ if (RegOpenKeyEx(basekey, subkey.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
+ return safe_regkey(key);
+ else
+ return safe_regkey();
+ }
+};
-static std::wstring trim_prefix(const std::wstring &devicename)
+std::wstring trim_prefix(const std::wstring &devicename)
{
// remove anything prior to the final semicolon
auto semicolon_index = devicename.find_last_of(';');
@@ -142,22 +145,22 @@ static std::wstring trim_prefix(const std::wstring &devicename)
return devicename;
}
-static std::wstring compute_device_regpath(const std::wstring &name)
+std::wstring compute_device_regpath(const std::wstring &name)
{
static const std::wstring basepath(L"SYSTEM\\CurrentControlSet\\Enum\\");
// allocate a temporary string and concatenate the base path plus the name
- auto regpath_buffer = std::make_unique<TCHAR[]>(basepath.length() + 1 + name.length());
+ auto regpath_buffer = std::make_unique<WCHAR []>(basepath.length() + 1 + name.length());
wcscpy(regpath_buffer.get(), basepath.c_str());
- WCHAR * chdst = regpath_buffer.get() + basepath.length();
+ WCHAR *chdst = regpath_buffer.get() + basepath.length();
// convert all # to \ in the name
for (int i = 4; i < name.length(); i++)
- *chdst++ = (name[i] == '#') ? '\\' : name[i];
+ *chdst++ = (name[i] == '#') ? L'\\' : name[i];
*chdst = 0;
// remove the final chunk
- chdst = wcsrchr(regpath_buffer.get(), '\\');
+ chdst = wcsrchr(regpath_buffer.get(), L'\\');
if (chdst == nullptr)
return std::wstring();
@@ -166,15 +169,15 @@ static std::wstring compute_device_regpath(const std::wstring &name)
return std::wstring(regpath_buffer.get());
}
-static std::wstring improve_name_from_base_path(const std::wstring &regpath, bool *hid)
+std::wstring improve_name_from_base_path(const std::wstring &regpath, bool *hid)
{
// now try to open the registry key
- auto device_key = reg_open_key(HKEY_LOCAL_MACHINE, regpath);
- if (!device_key.valid())
+ auto device_key = safe_regkey::open(HKEY_LOCAL_MACHINE, regpath);
+ if (!device_key)
return std::wstring();
// fetch the device description; if it exists, we are finished
- auto regstring = reg_query_string(device_key, L"DeviceDesc");
+ auto regstring = device_key.query_string(L"DeviceDesc");
if (!regstring.empty())
return trim_prefix(regstring);
@@ -183,25 +186,7 @@ static std::wstring improve_name_from_base_path(const std::wstring &regpath, boo
return std::wstring();
}
-static void foreach_subkey(HKEY key, std::function<bool(HKEY)> action)
-{
- for (int i = 0; ; i++)
- {
- std::wstring name = reg_enum_key(key, i);
- if (name.empty())
- break;
-
- safe_regkey subkey = reg_open_key(key, name);
- if (!subkey.valid())
- break;
-
- bool shouldcontinue = action(subkey);
- if (!shouldcontinue)
- break;
- }
-}
-
-static std::wstring improve_name_from_usb_path(const std::wstring &regpath)
+std::wstring improve_name_from_usb_path(const std::wstring &regpath)
{
static const std::wstring usbbasepath(L"SYSTEM\\CurrentControlSet\\Enum\\USB");
@@ -213,40 +198,43 @@ static std::wstring improve_name_from_usb_path(const std::wstring &regpath)
std::wstring parentid = regpath.substr(last_slash_index + 1);
// open the USB key
- auto usb_key = reg_open_key(HKEY_LOCAL_MACHINE, usbbasepath);
- if (!usb_key.valid())
+ auto usb_key = safe_regkey::open(HKEY_LOCAL_MACHINE, usbbasepath);
+ if (!usb_key)
return std::wstring();
std::wstring regstring;
- foreach_subkey(usb_key, [&regstring, &parentid](HKEY subkey)
- {
- foreach_subkey(subkey, [&regstring, &parentid](HKEY endkey)
- {
- std::wstring endparentid = reg_query_string(endkey, L"ParentIdPrefix");
+ usb_key.foreach_subkey(
+ [&regstring, &parentid] (safe_regkey const &subkey)
+ {
+ subkey.foreach_subkey(
+ [&regstring, &parentid] (safe_regkey const &endkey)
+ {
+ std::wstring endparentid = endkey.query_string(L"ParentIdPrefix");
- // This key doesn't have a ParentIdPrefix
- if (endparentid.empty())
- return true;
+ // This key doesn't have a ParentIdPrefix
+ if (endparentid.empty())
+ return true;
- // do we have a match?
- if (parentid.find(endparentid) == 0)
- regstring = reg_query_string(endkey, L"DeviceDesc");
+ // do we have a match?
+ if (parentid.find(endparentid) == 0)
+ regstring = endkey.query_string(L"DeviceDesc");
- return regstring.empty();
- });
+ return regstring.empty();
+ });
- return regstring.empty();
- });
+ return regstring.empty();
+ });
return trim_prefix(regstring);
}
+
//============================================================
// rawinput_device_improve_name
//============================================================
-static std::wstring rawinput_device_improve_name(const std::wstring &name)
+std::wstring rawinput_device_improve_name(const std::wstring &name)
{
// The RAW name received is formatted as:
// \??\type-id#hardware-id#instance-id#{DeviceClasses-id}
@@ -282,20 +270,36 @@ static std::wstring rawinput_device_improve_name(const std::wstring &name)
class rawinput_device : public event_based_device<RAWINPUT>
{
-private:
- HANDLE m_handle;
-
public:
- rawinput_device(running_machine& machine, const char *name, const char *id, input_device_class deviceclass, input_module& module)
- : event_based_device(machine, name, id, deviceclass, module),
- m_handle(nullptr)
+ rawinput_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_handle(handle)
{
}
HANDLE device_handle() const { return m_handle; }
- void set_handle(HANDLE handle) { m_handle = handle; }
+
+ bool reconnect_candidate(std::string_view i) const { return !m_handle && (id() == i); }
+
+ void detach_device()
+ {
+ assert(m_handle);
+ m_handle = nullptr;
+ osd_printf_verbose("RawInput: %s [ID %s] disconnected\n", name(), id());
+ }
+
+ void attach_device(HANDLE handle)
+ {
+ assert(!m_handle);
+ m_handle = handle;
+ osd_printf_verbose("RawInput: %s [ID %s] reconnected\n", name(), id());
+ }
+
+private:
+ HANDLE m_handle;
};
+
//============================================================
// rawinput_keyboard_device
//============================================================
@@ -303,490 +307,672 @@ public:
class rawinput_keyboard_device : public rawinput_device
{
public:
- keyboard_state keyboard;
+ rawinput_keyboard_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
+ rawinput_device(std::move(name), std::move(id), module, handle),
+ m_pause_pressed(std::chrono::steady_clock::time_point::min()),
+ m_e1(0xffff),
+ m_keyboard({ { 0 } })
+ {
+ }
- rawinput_keyboard_device(running_machine& machine, const char *name, const char *id, input_module& module)
- : rawinput_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
- keyboard({{0}})
+ virtual void reset() override
{
+ rawinput_device::reset();
+ m_pause_pressed = std::chrono::steady_clock::time_point::min();
+ memset(&m_keyboard, 0, sizeof(m_keyboard));
+ m_e1 = 0xffff;
}
- void reset() override
+ virtual void poll(bool relative_reset) override
{
- memset(&keyboard, 0, sizeof(keyboard));
+ rawinput_device::poll(relative_reset);
+ if (m_keyboard.state[0x80 | 0x45] && (std::chrono::steady_clock::now() > (m_pause_pressed + std::chrono::milliseconds(30))))
+ m_keyboard.state[0x80 | 0x45] = 0x00;
}
- void process_event(RAWINPUT &rawinput) override
+ virtual void process_event(RAWINPUT const &rawinput) override
{
// determine the full DIK-compatible scancode
- uint8_t scancode = (rawinput.data.keyboard.MakeCode & 0x7f) | ((rawinput.data.keyboard.Flags & RI_KEY_E0) ? 0x80 : 0x00);
+ uint8_t scancode;
- // scancode 0xaa is a special shift code we need to ignore
- if (scancode == 0xaa)
+ // the only thing that uses this is Pause
+ if (rawinput.data.keyboard.Flags & RI_KEY_E1)
+ {
+ m_e1 = rawinput.data.keyboard.MakeCode;
return;
+ }
+ else if (0xffff != m_e1)
+ {
+ auto const e1 = std::exchange(m_e1, 0xffff);
+ if (!(rawinput.data.keyboard.Flags & RI_KEY_E0))
+ {
+ if (((e1 & ~USHORT(0x80)) == 0x1d) && ((rawinput.data.keyboard.MakeCode & ~USHORT(0x80)) == 0x45))
+ {
+ if (rawinput.data.keyboard.Flags & RI_KEY_BREAK)
+ return; // RawInput generates a fake break immediately after the make - ignore it
+
+ m_pause_pressed = std::chrono::steady_clock::now();
+ scancode = 0x80 | 0x45;
+ }
+ else
+ {
+ return; // no idea
+ }
+ }
+ else
+ {
+ return; // shouldn't happen, ignore it
+ }
+ }
+ else
+ {
+ // strip bit 7 of the make code to work around dodgy drivers that set it for key up events
+ if (rawinput.data.keyboard.MakeCode & ~USHORT(0xff))
+ {
+ // won't fit in a byte along with the E0 flag
+ return;
+ }
+ scancode = (rawinput.data.keyboard.MakeCode & 0x7f) | ((rawinput.data.keyboard.Flags & RI_KEY_E0) ? 0x80 : 0x00);
+
+ // fake shift generated with cursor control and Ins/Del for compatibility with very old DOS software
+ if (scancode == 0xaa)
+ return;
+ }
// set or clear the key
- keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80;
+ m_keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80;
}
+
+ virtual void configure(input_device &device) override
+ {
+ keyboard_trans_table const &table = keyboard_trans_table::instance();
+
+ // FIXME: GetKeyNameTextW is for scan codes from WM_KEYDOWN, which aren't quite the same as DIK_* keycodes
+ // in particular, NumLock and Pause are reversed for US-style keyboard systems
+ for (unsigned keynum = 0; keynum < MAX_KEYS; keynum++)
+ {
+ input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
+ WCHAR keyname[100];
+
+ // generate the name
+ // FIXME: GetKeyNameText gives bogus names for media keys and various other things
+ // in many cases it ignores the "extended" bit and returns the key name corresponding to the scan code alone
+ LONG lparam = ((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17);
+ if ((keynum & 0x7f) == 0x45)
+ lparam ^= 0x0100'0000; // horrid hack
+ if (GetKeyNameTextW(lparam, keyname, std::size(keyname)) == 0)
+ _snwprintf(keyname, std::size(keyname), L"Scan%03d", keynum);
+ std::string name = text::from_wstring(keyname);
+
+ // add the item to the device
+ device.add_item(
+ name,
+ util::string_format("SCAN%03d", keynum),
+ itemid,
+ generic_button_get_state<std::uint8_t>,
+ &m_keyboard.state[keynum]);
+ }
+ }
+
+private:
+ std::chrono::steady_clock::time_point m_pause_pressed;
+ uint16_t m_e1;
+ keyboard_state m_keyboard;
};
+
//============================================================
// rawinput_mouse_device
//============================================================
class rawinput_mouse_device : public rawinput_device
{
-private:
- std::mutex m_device_lock;
public:
- mouse_state mouse;
-
- rawinput_mouse_device(running_machine& machine, const char *name, const char *id, input_module& module)
- : rawinput_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
- mouse({0})
+ rawinput_mouse_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
+ rawinput_device(std::move(name), std::move(id), module, handle),
+ m_mouse({0}),
+ m_x(0),
+ m_y(0),
+ m_v(0),
+ m_h(0)
{
}
- void poll() override
+ virtual void poll(bool relative_reset) override
{
- mouse.lX = 0;
- mouse.lY = 0;
- mouse.lZ = 0;
-
- rawinput_device::poll();
+ rawinput_device::poll(relative_reset);
+ if (relative_reset)
+ {
+ m_mouse.lX = std::exchange(m_x, 0);
+ m_mouse.lY = std::exchange(m_y, 0);
+ m_mouse.lV = std::exchange(m_v, 0);
+ m_mouse.lH = std::exchange(m_h, 0);
+ }
}
- void reset() override
+ virtual void reset() override
{
- memset(&mouse, 0, sizeof(mouse));
+ rawinput_device::reset();
+ memset(&m_mouse, 0, sizeof(m_mouse));
+ m_x = m_y = m_v = m_h = 0;
}
- void process_event(RAWINPUT &rawinput) override
+ virtual void configure(input_device &device) override
{
+ // populate the axes
+ device.add_item(
+ "X",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX);
+ device.add_item(
+ "Y",
+ std::string_view(),
+ ITEM_ID_YAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lY);
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_RZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
+
+ // populate the buttons
+ for (int butnum = 0; butnum < 5; butnum++)
+ {
+ device.add_item(
+ default_button_name(butnum),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_mouse.rgbButtons[butnum]);
+ }
+ }
+ virtual void process_event(RAWINPUT const &rawinput) override
+ {
// If this data was intended for a rawinput mouse
if (rawinput.data.mouse.usFlags == MOUSE_MOVE_RELATIVE)
{
+ m_x += rawinput.data.mouse.lLastX * input_device::RELATIVE_PER_PIXEL;
+ m_y += rawinput.data.mouse.lLastY * input_device::RELATIVE_PER_PIXEL;
- mouse.lX += rawinput.data.mouse.lLastX * INPUT_RELATIVE_PER_PIXEL;
- mouse.lY += rawinput.data.mouse.lLastY * INPUT_RELATIVE_PER_PIXEL;
-
- // update zaxis
+ // update Z/Rz axes (vertical/horizontal scroll)
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
- mouse.lZ += static_cast<int16_t>(rawinput.data.mouse.usButtonData) * INPUT_RELATIVE_PER_PIXEL;
+ m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL)
+ m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
// update the button states; always update the corresponding mouse buttons
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) mouse.rgbButtons[0] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) mouse.rgbButtons[0] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) mouse.rgbButtons[1] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) mouse.rgbButtons[1] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) mouse.rgbButtons[2] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) mouse.rgbButtons[2] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouse.rgbButtons[3] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouse.rgbButtons[3] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouse.rgbButtons[4] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouse.rgbButtons[4] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_mouse.rgbButtons[0] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) m_mouse.rgbButtons[0] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) m_mouse.rgbButtons[1] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) m_mouse.rgbButtons[1] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) m_mouse.rgbButtons[2] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) m_mouse.rgbButtons[2] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) m_mouse.rgbButtons[3] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) m_mouse.rgbButtons[3] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) m_mouse.rgbButtons[4] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) m_mouse.rgbButtons[4] = 0x00;
}
}
+
+private:
+ mouse_state m_mouse;
+ LONG m_x, m_y, m_v, m_h;
};
+
//============================================================
// rawinput_lightgun_device
//============================================================
class rawinput_lightgun_device : public rawinput_device
{
-private:
- std::mutex m_device_lock;
public:
- mouse_state lightgun;
-
- rawinput_lightgun_device(running_machine& machine, const char *name, const char *id, input_module& module)
- : rawinput_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
- lightgun({0})
+ rawinput_lightgun_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
+ rawinput_device(std::move(name), std::move(id), module, handle),
+ m_lightgun({0}),
+ m_v(0),
+ m_h(0)
{
}
- void poll() override
+ virtual void poll(bool relative_reset) override
{
- lightgun.lZ = 0;
+ rawinput_device::poll(relative_reset);
+ if (relative_reset)
+ {
+ m_lightgun.lV = std::exchange(m_v, 0);
+ m_lightgun.lH = std::exchange(m_h, 0);
+ }
+ }
- rawinput_device::poll();
+ virtual void reset() override
+ {
+ rawinput_device::reset();
+ memset(&m_lightgun, 0, sizeof(m_lightgun));
+ m_v = 0;
+ m_h = 0;
}
- void reset() override
+ virtual void configure(input_device &device) override
{
- memset(&lightgun, 0, sizeof(lightgun));
+ // populate the axes
+ for (int axisnum = 0; axisnum < 2; axisnum++)
+ {
+ device.add_item(
+ default_axis_name[axisnum],
+ std::string_view(),
+ input_item_id(ITEM_ID_XAXIS + axisnum),
+ generic_axis_get_state<LONG>,
+ &m_lightgun.lX + axisnum);
+ }
+
+ // scroll wheels are always relative if present
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE1,
+ generic_axis_get_state<LONG>,
+ &m_lightgun.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<LONG>,
+ &m_lightgun.lH);
+
+ // populate the buttons
+ for (int butnum = 0; butnum < 5; butnum++)
+ {
+ device.add_item(
+ default_button_name(butnum),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_lightgun.rgbButtons[butnum]);
+ }
}
- void process_event(RAWINPUT &rawinput) override
+ virtual void process_event(RAWINPUT const &rawinput) override
{
// If this data was intended for a rawinput lightgun
if (rawinput.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
{
-
// update the X/Y positions
- lightgun.lX = normalize_absolute_axis(rawinput.data.mouse.lLastX, 0, INPUT_ABSOLUTE_MAX);
- lightgun.lY = normalize_absolute_axis(rawinput.data.mouse.lLastY, 0, INPUT_ABSOLUTE_MAX);
+ m_lightgun.lX = normalize_absolute_axis(rawinput.data.mouse.lLastX, 0, input_device::ABSOLUTE_MAX);
+ m_lightgun.lY = normalize_absolute_axis(rawinput.data.mouse.lLastY, 0, input_device::ABSOLUTE_MAX);
- // update zaxis
+ // update Z/Rz axes
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
- lightgun.lZ += static_cast<int16_t>(rawinput.data.mouse.usButtonData) * INPUT_RELATIVE_PER_PIXEL;
+ m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL)
+ m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
// update the button states; always update the corresponding mouse buttons
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) lightgun.rgbButtons[0] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) lightgun.rgbButtons[0] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) lightgun.rgbButtons[1] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) lightgun.rgbButtons[1] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) lightgun.rgbButtons[2] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) lightgun.rgbButtons[2] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) lightgun.rgbButtons[3] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) lightgun.rgbButtons[3] = 0x00;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) lightgun.rgbButtons[4] = 0x80;
- if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) lightgun.rgbButtons[4] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_lightgun.rgbButtons[0] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) m_lightgun.rgbButtons[0] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) m_lightgun.rgbButtons[1] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) m_lightgun.rgbButtons[1] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) m_lightgun.rgbButtons[2] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) m_lightgun.rgbButtons[2] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) m_lightgun.rgbButtons[3] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) m_lightgun.rgbButtons[3] = 0x00;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) m_lightgun.rgbButtons[4] = 0x80;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) m_lightgun.rgbButtons[4] = 0x00;
}
}
+
+private:
+ mouse_state m_lightgun;
+ LONG m_v, m_h;
};
+
//============================================================
-// rawinput_module - base class for rawinput modules
+// rawinput_module - base class for RawInput modules
//============================================================
-class rawinput_module : public wininput_module
+class rawinput_module : public wininput_module<rawinput_device>
{
private:
- osd::dynamic_module::ptr m_user32_dll;
- get_rawinput_device_list_ptr get_rawinput_device_list;
- get_rawinput_data_ptr get_rawinput_data;
- get_rawinput_device_info_ptr get_rawinput_device_info;
- register_rawinput_devices_ptr register_rawinput_devices;
- std::mutex m_module_lock;
+ std::mutex m_module_lock;
public:
- rawinput_module(const char *type, const char* name)
- : wininput_module(type, name),
- get_rawinput_device_list(nullptr),
- get_rawinput_data(nullptr),
- get_rawinput_device_info(nullptr),
- register_rawinput_devices(nullptr)
+ rawinput_module(const char *type, const char *name) : wininput_module<rawinput_device>(type, name)
{
}
- bool probe() override
+ virtual bool probe() override
{
- m_user32_dll = osd::dynamic_module::open({ "user32.dll" });
-
- get_rawinput_device_list = m_user32_dll->bind<get_rawinput_device_list_ptr>("GetRawInputDeviceList");
- get_rawinput_data = m_user32_dll->bind<get_rawinput_data_ptr>("GetRawInputData");
- get_rawinput_device_info = m_user32_dll->bind<get_rawinput_device_info_ptr>("GetRawInputDeviceInfoW");
- register_rawinput_devices = m_user32_dll->bind<register_rawinput_devices_ptr>("RegisterRawInputDevices");
-
- if (!get_rawinput_device_list || !get_rawinput_data ||
- !get_rawinput_device_info || !register_rawinput_devices )
- {
- return false;
- }
-
return true;
}
- void input_init(running_machine &machine) override
+ virtual void input_init(running_machine &machine) override
{
- // get the number of devices, allocate a device list, and fetch it
- UINT device_count = 0;
- if ((*get_rawinput_device_list)(nullptr, &device_count, sizeof(RAWINPUTDEVICELIST)) != 0)
- return;
+ wininput_module<rawinput_device>::input_init(machine);
- if (device_count == 0)
+ // get initial number of devices
+ UINT device_count = 0;
+ if (GetRawInputDeviceList(nullptr, &device_count, sizeof(RAWINPUTDEVICELIST)) != 0)
+ {
+ osd_printf_error("Error getting initial number of RawInput devices.\n");
return;
-
- auto rawinput_devices = std::make_unique<RAWINPUTDEVICELIST[]>(device_count);
- if ((*get_rawinput_device_list)(rawinput_devices.get(), &device_count, sizeof(RAWINPUTDEVICELIST)) == -1)
+ }
+ if (!device_count)
return;
- // iterate backwards through devices; new devices are added at the head
- for (int devnum = device_count - 1; devnum >= 0; devnum--)
+ std::unique_ptr<RAWINPUTDEVICELIST []> rawinput_devices;
+ UINT retrieved;
+ do
{
- RAWINPUTDEVICELIST *device = &rawinput_devices[devnum];
- add_rawinput_device(machine, device);
+ rawinput_devices.reset(new (std::nothrow) RAWINPUTDEVICELIST [device_count]);
+ if (!rawinput_devices)
+ {
+ osd_printf_error("Error allocating buffer for RawInput device list.\n");
+ return;
+ }
+ retrieved = GetRawInputDeviceList(rawinput_devices.get(), &device_count, sizeof(RAWINPUTDEVICELIST));
}
-
- // don't enable global inputs when debugging
- if (!machine.options().debug())
+ while ((UINT(-1) == retrieved) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER));
+ if (UINT(-1) == retrieved)
{
- m_global_inputs_enabled = downcast<windows_options &>(machine.options()).global_inputs();
+ osd_printf_error("Error listing RawInput devices.\n");
+ return;
}
+ // iterate backwards through devices; new devices are added at the head
+ for (int devnum = retrieved - 1; devnum >= 0; devnum--)
+ add_rawinput_device(rawinput_devices[devnum]);
+
// If we added no devices, no need to register for notifications
- if (devicelist()->size() == 0)
+ if (devicelist().empty())
return;
// finally, register to receive raw input WM_INPUT messages if we found devices
RAWINPUTDEVICE registration;
registration.usUsagePage = usagepage();
registration.usUsage = usage();
- registration.dwFlags = m_global_inputs_enabled ? 0x00000100 : 0;
- registration.hwndTarget = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window();
+ registration.dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
+ registration.hwndTarget = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
// register the device
- (*register_rawinput_devices)(&registration, 1, sizeof(registration));
+ RegisterRawInputDevices(&registration, 1, sizeof(registration));
}
protected:
- virtual void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) = 0;
+ virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) = 0;
virtual USHORT usagepage() = 0;
virtual USHORT usage() = 0;
- int init_internal() override
- {
- if (!get_rawinput_device_list || !get_rawinput_data ||
- !get_rawinput_device_info || !register_rawinput_devices )
- {
- return 1;
- }
-
- osd_printf_verbose("RawInput: APIs detected\n");
- return 0;
- }
-
template<class TDevice>
- TDevice* create_rawinput_device(running_machine &machine, PRAWINPUTDEVICELIST rawinputdevice)
+ TDevice *create_rawinput_device(input_device_class deviceclass, RAWINPUTDEVICELIST const &rawinputdevice)
{
- TDevice* devinfo;
- UINT name_length = 0;
// determine the length of the device name, allocate it, and fetch it if not nameless
- if ((*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, nullptr, &name_length) != 0)
+ UINT name_length = 0;
+ if (GetRawInputDeviceInfoW(rawinputdevice.hDevice, RIDI_DEVICENAME, nullptr, &name_length) != 0)
return nullptr;
- std::unique_ptr<TCHAR[]> tname = std::make_unique<TCHAR[]>(name_length + 1);
- if (name_length > 1 && (*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == -1)
+ std::unique_ptr<WCHAR []> tname = std::make_unique<WCHAR []>(name_length + 1);
+ if (name_length > 1 && GetRawInputDeviceInfoW(rawinputdevice.hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == UINT(-1))
return nullptr;
// if this is an RDP name, skip it
- if (_tcsstr(tname.get(), TEXT("Root#RDP_")) != nullptr)
+ if (wcsstr(tname.get(), L"Root#RDP_") != nullptr)
return nullptr;
- // improve the name and then allocate a device
- std::wstring name = rawinput_device_improve_name(tname.get());
-
- // convert name to utf8
- std::string utf8_name = osd::text::from_wstring(name.c_str());
-
- // set device id to raw input name
- std::string utf8_id = osd::text::from_wstring(tname.get());
+ // improve the name
+ std::string utf8_name = text::from_wstring(rawinput_device_improve_name(tname.get()));
- devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.c_str(), utf8_id.c_str(), *this);
+ // set device ID to raw input name
+ std::string utf8_id = text::from_wstring(tname.get());
- // Add the handle
- devinfo->set_handle(rawinputdevice->hDevice);
+ tname.reset();
- return devinfo;
+ // allocate a device
+ return &create_device<TDevice>(
+ deviceclass,
+ std::move(utf8_name),
+ std::move(utf8_id),
+ rawinputdevice.hDevice);
}
- bool handle_input_event(input_event eventid, void* eventdata) override
+ virtual bool handle_input_event(input_event eventid, void const *eventdata) override
{
- // Only handle raw input data
- if (!input_enabled() || eventid != INPUT_EVENT_RAWINPUT)
- return false;
-
- HRAWINPUT rawinputdevice = *static_cast<HRAWINPUT*>(eventdata);
-
- BYTE small_buffer[4096];
- std::unique_ptr<BYTE[]> larger_buffer;
- LPBYTE data = small_buffer;
- UINT size;
-
- // ignore if not enabled
- if (!input_enabled())
- return false;
+ switch (eventid)
+ {
+ // handle raw input data
+ case INPUT_EVENT_RAWINPUT:
+ {
+ HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata);
+
+ union { RAWINPUT r; BYTE b[4096]; } small_buffer;
+ std::unique_ptr<BYTE []> larger_buffer;
+ LPVOID data = &small_buffer;
+ UINT size;
+
+ // determine the size of data buffer we need
+ if (GetRawInputData(rawinputdevice, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER)) != 0)
+ return false;
+
+ // if necessary, allocate a temporary buffer and fetch the data
+ if (size > sizeof(small_buffer))
+ {
+ larger_buffer.reset(new (std::align_val_t(alignof(RAWINPUT)), std::nothrow) BYTE [size]);
+ data = larger_buffer.get();
+ if (!data)
+ return false;
+ }
+
+ // fetch the data and process the appropriate message types
+ UINT result = GetRawInputData(rawinputdevice, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
+ if (UINT(-1) == result)
+ {
+ return false;
+ }
+ else if (result)
+ {
+ std::lock_guard<std::mutex> scope_lock(m_module_lock);
+
+ auto *const input = reinterpret_cast<RAWINPUT const *>(data);
+ if (!input->header.hDevice)
+ return false;
+
+ // find the device in the list and update
+ auto target_device = std::find_if(
+ devicelist().begin(),
+ devicelist().end(),
+ [input] (auto const &device)
+ {
+ return input->header.hDevice == device->device_handle();
+ });
+ if (devicelist().end() == target_device)
+ return false;
+
+ (*target_device)->queue_events(input, 1);
+ return true;
+ }
+ }
+ break;
- // determine the size of databuffer we need
- if ((*get_rawinput_data)(rawinputdevice, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER)) != 0)
- return false;
+ case INPUT_EVENT_ARRIVAL:
+ {
+ HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata);
+
+ // determine the length of the device name, allocate it, and fetch it if not nameless
+ UINT name_length = 0;
+ if (GetRawInputDeviceInfoW(rawinputdevice, RIDI_DEVICENAME, nullptr, &name_length) != 0)
+ return false;
+
+ std::unique_ptr<WCHAR []> tname = std::make_unique<WCHAR []>(name_length + 1);
+ if (name_length > 1 && GetRawInputDeviceInfoW(rawinputdevice, RIDI_DEVICENAME, tname.get(), &name_length) == UINT(-1))
+ return false;
+ std::string utf8_id = text::from_wstring(tname.get());
+ tname.reset();
+
+ std::lock_guard<std::mutex> scope_lock(m_module_lock);
+
+ // find the device in the list and update
+ auto target_device = std::find_if(
+ devicelist().begin(),
+ devicelist().end(),
+ [&utf8_id] (auto const &device)
+ {
+ return device->reconnect_candidate(utf8_id);
+ });
+ if (devicelist().end() == target_device)
+ return false;
+
+ (*target_device)->attach_device(rawinputdevice);
+ return true;
+ }
+ break;
- // if necessary, allocate a temporary buffer and fetch the data
- if (size > sizeof(small_buffer))
- {
- larger_buffer = std::make_unique<BYTE[]>(size);
- data = larger_buffer.get();
- if (data == nullptr)
- return false;
- }
+ case INPUT_EVENT_REMOVAL:
+ {
+ HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata);
- // fetch the data and process the appropriate message types
- bool result = (*get_rawinput_data)(static_cast<HRAWINPUT>(rawinputdevice), RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
- if (result)
- {
- std::lock_guard<std::mutex> scope_lock(m_module_lock);
+ std::lock_guard<std::mutex> scope_lock(m_module_lock);
- RAWINPUT *input = reinterpret_cast<RAWINPUT*>(data);
+ // find the device in the list and update
+ auto target_device = std::find_if(
+ devicelist().begin(),
+ devicelist().end(),
+ [rawinputdevice] (auto const &device)
+ {
+ return rawinputdevice == device->device_handle();
+ });
- // find the device in the list and update
- auto target_device = std::find_if(devicelist()->begin(), devicelist()->end(), [input](auto &device)
- {
- auto devinfo = dynamic_cast<rawinput_device*>(device.get());
- return devinfo != nullptr && input->header.hDevice == devinfo->device_handle();
- });
+ if (devicelist().end() == target_device)
+ return false;
- if (target_device != devicelist()->end())
- {
- static_cast<rawinput_device*>((*target_device).get())->queue_events(input, 1);
+ (*target_device)->reset();
+ (*target_device)->detach_device();
return true;
}
+ break;
+
+ default:
+ break;
}
+ // must have been unhandled
return false;
}
};
+
//============================================================
-// keyboard_input_rawinput - rawinput keyboard module
+// keyboard_input_rawinput - RawInput keyboard module
//============================================================
class keyboard_input_rawinput : public rawinput_module
{
public:
- keyboard_input_rawinput()
- : rawinput_module(OSD_KEYBOARDINPUT_PROVIDER, "rawinput")
+ keyboard_input_rawinput() : rawinput_module(OSD_KEYBOARDINPUT_PROVIDER, "rawinput")
{
}
protected:
- USHORT usagepage() override { return 1; }
- USHORT usage() override { return 6; }
- void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
+ virtual USHORT usagepage() override { return 1; }
+ virtual USHORT usage() override { return 6; }
+
+ virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
{
// make sure this is a keyboard
- if (device->dwType != RIM_TYPEKEYBOARD)
+ if (device.dwType != RIM_TYPEKEYBOARD)
return;
// allocate and link in a new device
- rawinput_keyboard_device *devinfo = create_rawinput_device<rawinput_keyboard_device>(machine, device);
- if (devinfo == nullptr)
- return;
-
- keyboard_trans_table &table = keyboard_trans_table::instance();
-
- // populate it
- for (int keynum = 0; keynum < MAX_KEYS; keynum++)
- {
- input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
- TCHAR keyname[100];
-
- // generate the name
- if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
- _sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
- std::string name = osd::text::from_tstring(keyname);
-
- // add the item to the device
- devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]);
- }
+ create_rawinput_device<rawinput_keyboard_device>(DEVICE_CLASS_KEYBOARD, device);
}
};
+
//============================================================
-// mouse_input_rawinput - rawinput mouse module
+// mouse_input_rawinput - RawInput mouse module
//============================================================
class mouse_input_rawinput : public rawinput_module
{
public:
- mouse_input_rawinput()
- : rawinput_module(OSD_MOUSEINPUT_PROVIDER, "rawinput")
+ mouse_input_rawinput() : rawinput_module(OSD_MOUSEINPUT_PROVIDER, "rawinput")
{
}
protected:
- USHORT usagepage() override { return 1; }
- USHORT usage() override { return 2; }
- void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
+ virtual USHORT usagepage() override { return 1; }
+ virtual USHORT usage() override { return 2; }
+
+ virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
{
// make sure this is a mouse
- if (device->dwType != RIM_TYPEMOUSE)
+ if (device.dwType != RIM_TYPEMOUSE)
return;
// allocate and link in a new device
- rawinput_mouse_device *devinfo = create_rawinput_device<rawinput_mouse_device>(machine, device);
- if (devinfo == nullptr)
- return;
-
- // populate the axes
- for (int axisnum = 0; axisnum < 3; axisnum++)
- {
- devinfo->device()->add_item(
- default_axis_name[axisnum],
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &devinfo->mouse.lX + axisnum);
- }
-
- // populate the buttons
- for (int butnum = 0; butnum < 5; butnum++)
- {
- devinfo->device()->add_item(
- default_button_name(butnum),
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &devinfo->mouse.rgbButtons[butnum]);
- }
+ create_rawinput_device<rawinput_mouse_device>(DEVICE_CLASS_MOUSE, device);
}
};
+
//============================================================
-// lightgun_input_rawinput - rawinput lightgun module
+// lightgun_input_rawinput - RawInput lightgun module
//============================================================
class lightgun_input_rawinput : public rawinput_module
{
public:
- lightgun_input_rawinput()
- : rawinput_module(OSD_LIGHTGUNINPUT_PROVIDER, "rawinput")
+ lightgun_input_rawinput() : rawinput_module(OSD_LIGHTGUNINPUT_PROVIDER, "rawinput")
{
}
protected:
- USHORT usagepage() override { return 1; }
- USHORT usage() override { return 2; }
- void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
- {
+ virtual USHORT usagepage() override { return 1; }
+ virtual USHORT usage() override { return 2; }
+ virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
+ {
// make sure this is a mouse
- if (device->dwType != RIM_TYPEMOUSE)
+ if (device.dwType != RIM_TYPEMOUSE)
return;
// allocate and link in a new device
- rawinput_lightgun_device *devinfo = create_rawinput_device<rawinput_lightgun_device>(machine, device);
- if (devinfo == nullptr)
- return;
-
- // populate the axes
- for (int axisnum = 0; axisnum < 3; axisnum++)
- {
- devinfo->device()->add_item(
- default_axis_name[axisnum],
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &devinfo->lightgun.lX + axisnum);
- }
-
- // populate the buttons
- for (int butnum = 0; butnum < 5; butnum++)
- {
- devinfo->device()->add_item(
- default_button_name(butnum),
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &devinfo->lightgun.rgbButtons[butnum]);
- }
+ create_rawinput_device<rawinput_lightgun_device>(DEVICE_CLASS_LIGHTGUN, device);
}
};
-#else
+} // anonymous namespace
+
+} // namespace osd
+
+#else // defined(OSD_WINDOWS)
+
+#include "input_module.h"
+
+namespace osd {
+
+namespace {
+
MODULE_NOT_SUPPORTED(keyboard_input_rawinput, OSD_KEYBOARDINPUT_PROVIDER, "rawinput")
MODULE_NOT_SUPPORTED(mouse_input_rawinput, OSD_MOUSEINPUT_PROVIDER, "rawinput")
MODULE_NOT_SUPPORTED(lightgun_input_rawinput, OSD_LIGHTGUNINPUT_PROVIDER, "rawinput")
-#endif
-MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, keyboard_input_rawinput)
-MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, mouse_input_rawinput)
-MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, lightgun_input_rawinput)
+} // anonymous namespace
+
+} // namespace osd
+
+#endif // defined(OSD_WINDOWS)
+
+MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, osd::keyboard_input_rawinput)
+MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, osd::mouse_input_rawinput)
+MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, osd::lightgun_input_rawinput)
diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp
index 40fe037f8c7..f1fb0241b40 100644
--- a/src/osd/modules/input/input_sdl.cpp
+++ b/src/osd/modules/input/input_sdl.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes
+// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes, Vas Crabb
//============================================================
//
// input_sdl.cpp - SDL 2.0 implementation of MAME input routines
@@ -12,32 +12,39 @@
//============================================================
#include "input_module.h"
+
#include "modules/osdmodule.h"
-#if defined(SDLMAME_SDL2)
+#if defined(OSD_SDL)
-// standard sdl header
-#include <SDL2/SDL.h>
-#include <ctype.h>
-// ReSharper disable once CppUnusedIncludeDirective
-#include <stddef.h>
-#include <mutex>
-#include <memory>
-#include <queue>
-#include <iterator>
-#include <algorithm>
+#include "assignmenthelper.h"
+#include "input_common.h"
-// MAME headers
-#include "emu.h"
-#include "uiinput.h"
-#include "strconv.h"
+#include "interface/inputseq.h"
+#include "modules/lib/osdobj_common.h"
+#include "sdl/osdsdl.h"
-// MAMEOS headers
-#include "input_common.h"
-#include "../lib/osdobj_common.h"
-#include "input_sdlcommon.h"
-#include "../../sdl/osdsdl.h"
-#include "../../sdl/window.h"
+// emu
+#include "inpttype.h"
+
+// standard SDL header
+#include <SDL2/SDL.h>
+
+#include <algorithm>
+#include <cctype>
+#include <chrono>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <iterator>
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <utility>
// winnt.h defines this
#ifdef DELETE
@@ -45,9 +52,217 @@
#endif
-// FIXME: sdl does not properly report the window for certain OS.
-#define GET_FOCUS_WINDOW(ev) focus_window()
-//#define GET_FOCUS_WINDOW(ev) window_from_id((ev)->windowID)
+namespace osd {
+
+namespace {
+
+char const *const CONTROLLER_AXIS_XBOX[]{
+ "LSX",
+ "LSY",
+ "RSX",
+ "RSY",
+ "LT",
+ "RT" };
+
+char const *const CONTROLLER_AXIS_PS[]{
+ "LSX",
+ "LSY",
+ "RSX",
+ "RSY",
+ "L2",
+ "R2" };
+
+char const *const CONTROLLER_AXIS_SWITCH[]{
+ "LSX",
+ "LSY",
+ "RSX",
+ "RSY",
+ "ZL",
+ "ZR" };
+
+char const *const CONTROLLER_BUTTON_XBOX360[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "Back",
+ "Guide",
+ "Start",
+ "LSB",
+ "RSB",
+ "LB",
+ "RB",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Share",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+char const *const CONTROLLER_BUTTON_XBOXONE[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "View",
+ "Logo",
+ "Menu",
+ "LSB",
+ "RSB",
+ "LB",
+ "RB",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Share",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+char const *const CONTROLLER_BUTTON_PS3[]{
+ "Cross",
+ "Circle",
+ "Square",
+ "Triangle",
+ "Select",
+ "PS",
+ "Start",
+ "L3",
+ "R3",
+ "L1",
+ "R1",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Mute",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+char const *const CONTROLLER_BUTTON_PS4[]{
+ "Cross",
+ "Circle",
+ "Square",
+ "Triangle",
+ "Share",
+ "PS",
+ "Options",
+ "L3",
+ "R3",
+ "L1",
+ "R1",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Mute",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+char const *const CONTROLLER_BUTTON_PS5[]{
+ "Cross",
+ "Circle",
+ "Square",
+ "Triangle",
+ "Create",
+ "PS",
+ "Options",
+ "L3",
+ "R3",
+ "L1",
+ "R1",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Mute",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+char const *const CONTROLLER_BUTTON_SWITCH[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "-",
+ "Home",
+ "+",
+ "LSB",
+ "RSB",
+ "L",
+ "R",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Capture",
+ "RSR",
+ "LSL",
+ "RSL",
+ "LSR",
+ "Touchpad" };
+
+[[maybe_unused]] char const *const CONTROLLER_BUTTON_STADIA[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "Options",
+ "Logo",
+ "Menu",
+ "L3",
+ "R3",
+ "L1",
+ "R1",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Capture",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
+
+[[maybe_unused]] char const *const CONTROLLER_BUTTON_SHIELD[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "Back",
+ "Logo",
+ "Start",
+ "LSB",
+ "RSB",
+ "LB",
+ "RB",
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "Share",
+ "P1",
+ "P2",
+ "P3",
+ "P4",
+ "Touchpad" };
struct key_lookup_table
{
@@ -56,12 +271,8 @@ struct key_lookup_table
};
#define KE(x) { SDL_SCANCODE_ ## x, "SDL_SCANCODE_" #x },
-#define KE8(A, B, C, D, E, F, G, H) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) KE(H)
-#define KE7(A, B, C, D, E, F, G) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G)
-#define KE5(A, B, C, D, E) KE(A) KE(B) KE(C) KE(D) KE(E)
-#define KE3(A, B, C) KE(A) KE(B) KE(C)
-static key_lookup_table sdl_lookup_table[] =
+key_lookup_table const sdl_lookup_table[] =
{
KE(UNKNOWN)
@@ -320,520 +531,1711 @@ static key_lookup_table sdl_lookup_table[] =
KE(APP1)
KE(APP2)
+};
+
+//============================================================
+// lookup_sdl_code
+//============================================================
+int lookup_sdl_code(std::string_view scode)
{
- -1, ""
+ auto const found = std::find_if(
+ std::begin(sdl_lookup_table),
+ std::end(sdl_lookup_table),
+ [&scode] (auto const &key) { return scode == key.name; });
+ return (std::end(sdl_lookup_table) != found) ? found->code : -1;
}
-};
+
//============================================================
-// lookup_sdl_code
+// sdl_device
//============================================================
-static int lookup_sdl_code(const char *scode)
+using sdl_device = event_based_device<SDL_Event>;
+
+
+//============================================================
+// sdl_keyboard_device
+//============================================================
+
+class sdl_keyboard_device : public sdl_device
{
- int i = 0;
+public:
+ sdl_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ keyboard_trans_table const &trans_table) :
+ sdl_device(std::move(name), std::move(id), module),
+ m_trans_table(trans_table),
+ m_keyboard({{0}}),
+ m_capslock_pressed(std::chrono::steady_clock::time_point::min())
+ {
+ }
- while (sdl_lookup_table[i].code >= 0)
+ virtual void poll(bool relative_reset) override
{
- if (!strcmp(scode, sdl_lookup_table[i].name))
- return sdl_lookup_table[i].code;
- i++;
+ sdl_device::poll(relative_reset);
+
+#ifdef __APPLE__
+ if (m_keyboard.state[SDL_SCANCODE_CAPSLOCK] && (std::chrono::steady_clock::now() > (m_capslock_pressed + std::chrono::milliseconds(30))))
+ m_keyboard.state[SDL_SCANCODE_CAPSLOCK] = 0x00;
+#endif
}
- return -1;
-}
+
+ virtual void process_event(SDL_Event const &event) override
+ {
+ switch (event.type)
+ {
+ case SDL_KEYDOWN:
+ if (event.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK)
+ m_capslock_pressed = std::chrono::steady_clock::now();
+
+ m_keyboard.state[event.key.keysym.scancode] = 0x80;
+ break;
+
+ case SDL_KEYUP:
+#ifdef __APPLE__
+ if (event.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK)
+ break;
+#endif
+
+ m_keyboard.state[event.key.keysym.scancode] = 0x00;
+ break;
+ }
+ }
+
+ virtual void reset() override
+ {
+ sdl_device::reset();
+ memset(&m_keyboard.state, 0, sizeof(m_keyboard.state));
+ m_capslock_pressed = std::chrono::steady_clock::time_point::min();
+ }
+
+ virtual void configure(input_device &device) override
+ {
+ // populate it
+ for (int keynum = 0; m_trans_table[keynum].mame_key != ITEM_ID_INVALID; keynum++)
+ {
+ input_item_id itemid = m_trans_table[keynum].mame_key;
+ device.add_item(
+ m_trans_table[keynum].ui_name,
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_keyboard.state[m_trans_table[keynum].sdl_scancode]);
+ }
+ }
+
+private:
+ // state information for a keyboard
+ struct keyboard_state
+ {
+ s32 state[0x3ff]; // must be s32!
+ s8 oldkey[MAX_KEYS];
+ s8 currkey[MAX_KEYS];
+ };
+
+ keyboard_trans_table const &m_trans_table;
+ keyboard_state m_keyboard;
+ std::chrono::steady_clock::time_point m_capslock_pressed;
+};
+
//============================================================
-// sdl_device
+// sdl_mouse_device_base
//============================================================
-class sdl_device : public event_based_device<SDL_Event>
+class sdl_mouse_device_base : public sdl_device
{
public:
- sdl_device(running_machine &machine, const char *name, const char *id, input_device_class devclass, input_module &module)
- : event_based_device(machine, name, id, devclass, module)
+ virtual void poll(bool relative_reset) override
{
+ sdl_device::poll(relative_reset);
+
+ if (relative_reset)
+ {
+ m_mouse.lV = std::exchange(m_v, 0);
+ m_mouse.lH = std::exchange(m_h, 0);
+ }
+ }
+
+ virtual void reset() override
+ {
+ sdl_device::reset();
+ memset(&m_mouse, 0, sizeof(m_mouse));
+ m_v = m_h = 0;
}
protected:
- std::shared_ptr<sdl_window_info> focus_window()
+ // state information for a mouse
+ struct mouse_state
+ {
+ s32 lX, lY, lV, lH;
+ s32 buttons[MAX_BUTTONS];
+ };
+
+ sdl_mouse_device_base(std::string &&name, std::string &&id, input_module &module) :
+ sdl_device(std::move(name), std::move(id), module),
+ m_mouse({0}),
+ m_v(0),
+ m_h(0)
{
- return sdl_event_manager::instance().focus_window();
}
+
+ void add_common_items(input_device &device, unsigned buttons)
+ {
+ // add horizontal and vertical axes - relative for a mouse or absolute for a gun
+ device.add_item(
+ "X",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<s32>,
+ &m_mouse.lX);
+ device.add_item(
+ "Y",
+ std::string_view(),
+ ITEM_ID_YAXIS,
+ generic_axis_get_state<s32>,
+ &m_mouse.lY);
+
+ // add buttons
+ for (int button = 0; button < buttons; button++)
+ {
+ input_item_id itemid = (input_item_id)(ITEM_ID_BUTTON1 + button);
+ int const offset = button ^ (((1 == button) || (2 == button)) ? 3 : 0);
+ device.add_item(
+ default_button_name(button),
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_mouse.buttons[offset]);
+ }
+ }
+
+ mouse_state m_mouse;
+ s32 m_v, m_h;
};
+
//============================================================
-// sdl_keyboard_device
+// sdl_mouse_device
//============================================================
-class sdl_keyboard_device : public sdl_device
+class sdl_mouse_device : public sdl_mouse_device_base
{
public:
- keyboard_state keyboard;
+ sdl_mouse_device(std::string &&name, std::string &&id, input_module &module) :
+ sdl_mouse_device_base(std::move(name), std::move(id), module),
+ m_x(0),
+ m_y(0)
+ {
+ }
- sdl_keyboard_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : sdl_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
- keyboard({{0}})
+ virtual void poll(bool relative_reset) override
{
+ sdl_mouse_device_base::poll(relative_reset);
+
+ if (relative_reset)
+ {
+ m_mouse.lX = std::exchange(m_x, 0);
+ m_mouse.lY = std::exchange(m_y, 0);
+ }
}
- void process_event(SDL_Event &sdlevent) override
+ virtual void reset() override
{
- switch (sdlevent.type)
+ sdl_mouse_device_base::reset();
+ m_x = m_y = 0;
+ }
+
+ virtual void configure(input_device &device) override
+ {
+ add_common_items(device, 5);
+
+ // add scroll axes
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ZAXIS,
+ generic_axis_get_state<s32>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_RZAXIS,
+ generic_axis_get_state<s32>,
+ &m_mouse.lH);
+ }
+
+ virtual void process_event(SDL_Event const &event) override
+ {
+ switch (event.type)
{
- case SDL_KEYDOWN:
- keyboard.state[sdlevent.key.keysym.scancode] = 0x80;
- if (sdlevent.key.keysym.sym < 0x20)
- machine().ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), sdlevent.key.keysym.sym);
+ case SDL_MOUSEMOTION:
+ m_x += event.motion.xrel * input_device::RELATIVE_PER_PIXEL;
+ m_y += event.motion.yrel * input_device::RELATIVE_PER_PIXEL;
break;
- case SDL_KEYUP:
- keyboard.state[sdlevent.key.keysym.scancode] = 0x00;
+ case SDL_MOUSEBUTTONDOWN:
+ m_mouse.buttons[event.button.button - 1] = 0x80;
break;
- case SDL_TEXTINPUT:
- if (*sdlevent.text.text)
- {
- auto window = GET_FOCUS_WINDOW(&event.text);
- //printf("Focus window is %p - wl %p\n", window, osd_common_t::s_window_list);
- if (window != nullptr)
- {
- auto ptr = sdlevent.text.text;
- auto len = std::strlen(sdlevent.text.text);
- while (len)
- {
- char32_t ch;
- auto chlen = uchar_from_utf8(&ch, ptr, len);
- if (0 > chlen)
- {
- ch = 0x0fffd;
- chlen = 1;
- }
- ptr += chlen;
- len -= chlen;
- machine().ui_input().push_char_event(window->target(), ch);
- }
- }
- }
+ case SDL_MOUSEBUTTONUP:
+ m_mouse.buttons[event.button.button - 1] = 0;
+ break;
+
+ case SDL_MOUSEWHEEL:
+ // adjust SDL 1-per-click to match Win32 120-per-click
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+ m_v += std::lround(event.wheel.preciseY * 120 * input_device::RELATIVE_PER_PIXEL);
+ m_h += std::lround(event.wheel.preciseX * 120 * input_device::RELATIVE_PER_PIXEL);
+#else
+ m_v += event.wheel.y * 120 * input_device::RELATIVE_PER_PIXEL;
+ m_h += event.wheel.x * 120 * input_device::RELATIVE_PER_PIXEL;
+#endif
break;
}
}
- void reset() override
- {
- memset(&keyboard.state, 0, sizeof(keyboard.state));
- }
+private:
+ s32 m_x, m_y;
};
+
//============================================================
-// sdl_mouse_device
+// sdl_lightgun_device
//============================================================
-class sdl_mouse_device : public sdl_device
+class sdl_lightgun_device : public sdl_mouse_device_base
{
-private:
- const std::chrono::milliseconds double_click_speed = std::chrono::milliseconds(250);
- std::chrono::system_clock::time_point last_click;
- int last_x;
- int last_y;
-
public:
- mouse_state mouse;
+ sdl_lightgun_device(std::string &&name, std::string &&id, input_module &module) :
+ sdl_mouse_device_base(std::move(name), std::move(id), module),
+ m_x(0),
+ m_y(0),
+ m_window(0)
+ {
+ }
- sdl_mouse_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : sdl_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
- last_x(0),
- last_y(0),
- mouse({0})
+ virtual void poll(bool relative_reset) override
{
+ sdl_mouse_device_base::poll(relative_reset);
+
+ SDL_Window *const win(m_window ? SDL_GetWindowFromID(m_window) : nullptr);
+ if (win)
+ {
+ int w, h;
+ SDL_GetWindowSize(win, &w, &h);
+ m_mouse.lX = normalize_absolute_axis(m_x, 0, w - 1);
+ m_mouse.lY = normalize_absolute_axis(m_y, 0, h - 1);
+ }
+ else
+ {
+ m_mouse.lX = 0;
+ m_mouse.lY = 0;
+ }
}
- void reset() override
+ virtual void reset() override
{
- memset(&mouse, 0, sizeof(mouse));
+ sdl_mouse_device_base::reset();
+ m_x = m_y = 0;
+ m_window = 0;
}
- void poll() override
+ virtual void configure(input_device &device) override
{
- mouse.lX = 0;
- mouse.lY = 0;
- sdl_device::poll();
+ add_common_items(device, 5);
+
+ // add scroll axes
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE1,
+ generic_axis_get_state<s32>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<s32>,
+ &m_mouse.lH);
}
- virtual void process_event(SDL_Event &sdlevent) override
+ virtual void process_event(SDL_Event const &event) override
{
- switch (sdlevent.type)
+ switch (event.type)
{
case SDL_MOUSEMOTION:
- mouse.lX += sdlevent.motion.xrel * INPUT_RELATIVE_PER_PIXEL;
- mouse.lY += sdlevent.motion.yrel * INPUT_RELATIVE_PER_PIXEL;
-
- {
- int cx = -1, cy = -1;
- auto window = GET_FOCUS_WINDOW(&sdlevent.motion);
-
- if (window != nullptr && window->xy_to_render_target(sdlevent.motion.x, sdlevent.motion.y, &cx, &cy))
- machine().ui_input().push_mouse_move_event(window->target(), cx, cy);
- }
+ m_x = event.motion.x;
+ m_y = event.motion.y;
+ m_window = event.motion.windowID;
break;
case SDL_MOUSEBUTTONDOWN:
- mouse.buttons[sdlevent.button.button - 1] = 0x80;
- //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str());
- if (sdlevent.button.button == 1)
- {
- int cx, cy;
- auto click = std::chrono::system_clock::now();
- auto window = GET_FOCUS_WINDOW(&sdlevent.button);
- if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy))
- {
- machine().ui_input().push_mouse_down_event(window->target(), cx, cy);
+ m_mouse.buttons[event.button.button - 1] = 0x80;
+ m_x = event.button.x;
+ m_y = event.button.y;
+ m_window = event.button.windowID;
+ break;
- // avoid overflow with std::chrono::time_point::min() by adding rather than subtracting
- if (click < last_click + double_click_speed
- && (cx >= last_x - 4 && cx <= last_x + 4)
- && (cy >= last_y - 4 && cy <= last_y + 4))
- {
- last_click = std::chrono::time_point<std::chrono::system_clock>::min();
- machine().ui_input().push_mouse_double_click_event(window->target(), cx, cy);
- }
- else
- {
- last_click = click;
- last_x = cx;
- last_y = cy;
- }
- }
- }
+ case SDL_MOUSEBUTTONUP:
+ m_mouse.buttons[event.button.button - 1] = 0;
+ m_x = event.button.x;
+ m_y = event.button.y;
+ m_window = event.button.windowID;
+ break;
- else if (sdlevent.button.button == 3)
- {
- int cx, cy;
- auto window = GET_FOCUS_WINDOW(&sdlevent.button);
+ case SDL_MOUSEWHEEL:
+ // adjust SDL 1-per-click to match Win32 120-per-click
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+ m_v += std::lround(event.wheel.preciseY * 120 * input_device::RELATIVE_PER_PIXEL);
+ m_h += std::lround(event.wheel.preciseX * 120 * input_device::RELATIVE_PER_PIXEL);
+#else
+ m_v += event.wheel.y * 120 * input_device::RELATIVE_PER_PIXEL;
+ m_h += event.wheel.x * 120 * input_device::RELATIVE_PER_PIXEL;
+#endif
+ break;
- if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy))
- {
- machine().ui_input().push_mouse_rdown_event(window->target(), cx, cy);
- }
- }
+ case SDL_WINDOWEVENT:
+ if ((event.window.windowID == m_window) && (SDL_WINDOWEVENT_LEAVE == event.window.event))
+ m_window = 0;
break;
+ }
+ }
- case SDL_MOUSEBUTTONUP:
- mouse.buttons[sdlevent.button.button - 1] = 0;
- //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y);
+private:
+ s32 m_x, m_y;
+ u32 m_window;
+};
- if (sdlevent.button.button == 1)
- {
- int cx, cy;
- auto window = GET_FOCUS_WINDOW(&sdlevent.button);
- if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy))
- {
- machine().ui_input().push_mouse_up_event(window->target(), cx, cy);
- }
- }
- else if (sdlevent.button.button == 3)
- {
- int cx, cy;
- auto window = GET_FOCUS_WINDOW(&sdlevent.button);
+//============================================================
+// sdl_dual_lightgun_device
+//============================================================
+
+class sdl_dual_lightgun_device : public sdl_mouse_device_base
+{
+public:
+ sdl_dual_lightgun_device(std::string &&name, std::string &&id, input_module &module, u8 index) :
+ sdl_mouse_device_base(std::move(name), std::move(id), module),
+ m_index(index)
+ {
+ }
+
+ virtual void configure(input_device &device) override
+ {
+ add_common_items(device, 2);
+ }
- if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy))
+ virtual void process_event(SDL_Event const &event) override
+ {
+ switch (event.type)
+ {
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ SDL_Window *const win(SDL_GetWindowFromID(event.button.windowID));
+ u8 const button = translate_button(event);
+ if (win && ((button / 2) == m_index))
{
- machine().ui_input().push_mouse_rup_event(window->target(), cx, cy);
+ int w, h;
+ SDL_GetWindowSize(win, &w, &h);
+ m_mouse.buttons[(button & 1) << 1] = 0x80;
+ m_mouse.lX = normalize_absolute_axis(event.button.x, 0, w - 1);
+ m_mouse.lY = normalize_absolute_axis(event.button.y, 0, h - 1);
}
}
break;
- case SDL_MOUSEWHEEL:
- auto window = GET_FOCUS_WINDOW(&sdlevent.wheel);
- if (window != nullptr)
- machine().ui_input().push_mouse_wheel_event(window->target(), 0, 0, sdlevent.wheel.y, 3);
+ case SDL_MOUSEBUTTONUP:
+ {
+ u8 const button = translate_button(event);
+ if ((button / 2) == m_index)
+ m_mouse.buttons[(button & 1) << 1] = 0;
+ }
break;
}
}
+
+private:
+ static u8 translate_button(SDL_Event const &event)
+ {
+ u8 const index(event.button.button - 1);
+ return index ^ (((1 == index) || (2 == index)) ? 3 : 0);
+ }
+
+ u8 const m_index;
};
+
//============================================================
-// sdl_joystick_device
+// sdl_joystick_device_base
//============================================================
-// state information for a joystick
-struct sdl_joystick_state
+class sdl_joystick_device_base : public sdl_device, protected joystick_assignment_helper
{
- int32_t axes[MAX_AXES];
- int32_t buttons[MAX_BUTTONS];
- int32_t hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS];
- int32_t balls[MAX_AXES];
-};
+public:
+ std::optional<std::string> const &serial() const { return m_serial; }
+ SDL_JoystickID instance() const { return m_instance; }
-struct sdl_api_state
-{
- SDL_Joystick *device;
- SDL_Haptic *hapdevice;
- SDL_JoystickID joystick_id;
-};
+ bool is_instance(SDL_JoystickID instance) const { return m_instance == instance; }
-class sdl_joystick_device : public sdl_device
-{
-public:
- sdl_joystick_state joystick;
- sdl_api_state sdl_state;
+ bool reconnect_match(std::string_view g, char const *s) const
+ {
+ return
+ (0 > m_instance) &&
+ (id() == g) &&
+ ((s && serial() && (*serial() == s)) || (!s && !serial()));
+ }
- sdl_joystick_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : sdl_device(machine, name, id, DEVICE_CLASS_JOYSTICK, module),
- joystick({{0}}),
- sdl_state({ nullptr })
+protected:
+ sdl_joystick_device_base(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ char const *serial) :
+ sdl_device(std::move(name), std::move(id), module),
+ m_instance(-1)
{
+ if (serial)
+ m_serial = serial;
}
- ~sdl_joystick_device()
+ void set_instance(SDL_JoystickID instance)
{
- if (sdl_state.device != nullptr)
- {
- if (sdl_state.hapdevice != nullptr)
- {
- SDL_HapticClose(sdl_state.hapdevice);
- sdl_state.hapdevice = nullptr;
- }
- SDL_JoystickClose(sdl_state.device);
- sdl_state.device = nullptr;
- }
+ assert(0 > m_instance);
+ assert(0 <= instance);
+
+ m_instance = instance;
}
- void reset() override
+ void clear_instance()
+ {
+ m_instance = -1;
+ }
+
+private:
+ std::optional<std::string> m_serial;
+ SDL_JoystickID m_instance;
+};
+
+
+//============================================================
+// sdl_joystick_device
+//============================================================
+
+class sdl_joystick_device : public sdl_joystick_device_base
+{
+public:
+ sdl_joystick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ SDL_Joystick *joy,
+ char const *serial) :
+ sdl_joystick_device_base(
+ std::move(name),
+ std::move(id),
+ module,
+ serial),
+ m_joystick({{0}}),
+ m_joydevice(joy),
+ m_hapdevice(SDL_HapticOpenFromJoystick(joy))
{
- memset(&joystick, 0, sizeof(joystick));
+ set_instance(SDL_JoystickInstanceID(joy));
}
- void process_event(SDL_Event &sdlevent) override
+ virtual void configure(input_device &device) override
{
- switch (sdlevent.type)
+ input_device::assignment_vector assignments;
+ char tempname[32];
+
+ int const axiscount = SDL_JoystickNumAxes(m_joydevice);
+ int const buttoncount = SDL_JoystickNumButtons(m_joydevice);
+ int const hatcount = SDL_JoystickNumHats(m_joydevice);
+ int const ballcount = SDL_JoystickNumBalls(m_joydevice);
+
+ // loop over all axes
+ input_item_id axisactual[MAX_AXES];
+ for (int axis = 0; (axis < MAX_AXES) && (axis < axiscount); axis++)
{
- case SDL_JOYAXISMOTION:
- joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2);
- break;
+ input_item_id itemid;
- case SDL_JOYBALLMOTION:
- //printf("Ball %d %d\n", sdlevent.jball.xrel, sdlevent.jball.yrel);
- joystick.balls[sdlevent.jball.ball * 2] = sdlevent.jball.xrel * INPUT_RELATIVE_PER_PIXEL;
- joystick.balls[sdlevent.jball.ball * 2 + 1] = sdlevent.jball.yrel * INPUT_RELATIVE_PER_PIXEL;
- break;
+ if (axis < INPUT_MAX_AXIS)
+ itemid = input_item_id(ITEM_ID_XAXIS + axis);
+ else if (axis < (INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE))
+ itemid = input_item_id(ITEM_ID_ADD_ABSOLUTE1 + axis - INPUT_MAX_AXIS);
+ else
+ itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE;
+
+ snprintf(tempname, sizeof(tempname), "A%d", axis + 1);
+ axisactual[axis] = device.add_item(
+ tempname,
+ std::string_view(),
+ itemid,
+ generic_axis_get_state<s32>,
+ &m_joystick.axes[axis]);
+ }
- case SDL_JOYHATMOTION:
- if (sdlevent.jhat.value & SDL_HAT_UP)
- {
- joystick.hatsU[sdlevent.jhat.hat] = 0x80;
- }
+ // loop over all buttons
+ for (int button = 0; (button < MAX_BUTTONS) && (button < buttoncount); button++)
+ {
+ input_item_id itemid;
+
+ m_joystick.buttons[button] = 0;
+
+ if (button < INPUT_MAX_BUTTONS)
+ itemid = input_item_id(ITEM_ID_BUTTON1 + button);
+ else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH)
+ itemid = input_item_id(ITEM_ID_ADD_SWITCH1 + button - INPUT_MAX_BUTTONS);
else
+ itemid = ITEM_ID_OTHER_SWITCH;
+
+ input_item_id const actual = device.add_item(
+ default_button_name(button),
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_joystick.buttons[button]);
+
+ // there are sixteen action button types
+ if (button < 16)
{
- joystick.hatsU[sdlevent.jhat.hat] = 0;
- }
- if (sdlevent.jhat.value & SDL_HAT_DOWN)
- {
- joystick.hatsD[sdlevent.jhat.hat] = 0x80;
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button), SEQ_TYPE_STANDARD, seq);
+
+ // assign the first few buttons to UI actions and pedals
+ switch (button)
+ {
+ case 0:
+ assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 1:
+ assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back((3 > buttoncount) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 2:
+ assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 3:
+ assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq);
+ break;
+ }
}
+ }
+
+ // loop over all hats
+ input_item_id hatactual[MAX_HATS][4];
+ for (int hat = 0; (hat < MAX_HATS) && (hat < hatcount); hat++)
+ {
+ input_item_id itemid;
+
+ snprintf(tempname, sizeof(tempname), "Hat %d Up", hat + 1);
+ itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + (4 * hat) : ITEM_ID_OTHER_SWITCH);
+ hatactual[hat][0] = device.add_item(
+ tempname,
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_joystick.hatsU[hat]);
+
+ snprintf(tempname, sizeof(tempname), "Hat %d Down", hat + 1);
+ itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + (4 * hat) : ITEM_ID_OTHER_SWITCH);
+ hatactual[hat][1] = device.add_item(
+ tempname,
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_joystick.hatsD[hat]);
+
+ snprintf(tempname, sizeof(tempname), "Hat %d Left", hat + 1);
+ itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + (4 * hat) : ITEM_ID_OTHER_SWITCH);
+ hatactual[hat][2] = device.add_item(
+ tempname,
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_joystick.hatsL[hat]);
+
+ snprintf(tempname, sizeof(tempname), "Hat %d Right", hat + 1);
+ itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + (4 * hat) : ITEM_ID_OTHER_SWITCH);
+ hatactual[hat][3] = device.add_item(
+ tempname,
+ std::string_view(),
+ itemid,
+ generic_button_get_state<s32>,
+ &m_joystick.hatsR[hat]);
+ }
+
+ // loop over all (track)balls
+ for (int ball = 0; (ball < (MAX_AXES / 2)) && (ball < ballcount); ball++)
+ {
+ int itemid;
+
+ if (ball * 2 < INPUT_MAX_ADD_RELATIVE)
+ itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2;
else
+ itemid = ITEM_ID_OTHER_AXIS_RELATIVE;
+
+ snprintf(tempname, sizeof(tempname), "R%d X", ball + 1);
+ input_item_id const xactual = device.add_item(
+ tempname,
+ std::string_view(),
+ input_item_id(itemid),
+ generic_axis_get_state<s32>,
+ &m_joystick.balls[ball * 2]);
+
+ snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1);
+ input_item_id const yactual = device.add_item(
+ tempname,
+ std::string_view(),
+ input_item_id(itemid + 1),
+ generic_axis_get_state<s32>,
+ &m_joystick.balls[ball * 2 + 1]);
+
+ if (0 == ball)
{
- joystick.hatsD[sdlevent.jhat.hat] = 0;
+ // assign the first trackball to dial, trackball, mouse and lightgun inputs
+ input_seq const xseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, xactual));
+ input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual));
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, yseq);
+ assignments.emplace_back(IPT_MOUSE_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_MOUSE_Y, SEQ_TYPE_STANDARD, yseq);
+ if (2 > axiscount)
+ {
+ // use it for joystick inputs if axes are limited
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, xseq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, yseq);
+ }
+ else
+ {
+ // use for non-centring throttle control
+ assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq);
+ }
}
- if (sdlevent.jhat.value & SDL_HAT_LEFT)
+ else if ((1 == ball) && (2 > axiscount))
{
- joystick.hatsL[sdlevent.jhat.hat] = 0x80;
+ // provide a non-centring throttle control
+ input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual));
+ assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq);
}
- else
+ }
+
+ // set up default assignments for axes and hats
+ add_directional_assignments(
+ assignments,
+ (1 <= axiscount) ? axisactual[0] : ITEM_ID_INVALID, // assume first axis is X
+ (2 <= axiscount) ? axisactual[1] : ITEM_ID_INVALID, // assume second axis is Y
+ (1 <= hatcount) ? hatactual[0][2] : ITEM_ID_INVALID,
+ (1 <= hatcount) ? hatactual[0][3] : ITEM_ID_INVALID,
+ (1 <= hatcount) ? hatactual[0][0] : ITEM_ID_INVALID,
+ (1 <= hatcount) ? hatactual[0][1] : ITEM_ID_INVALID);
+ if (2 <= axiscount)
+ {
+ // put pedals on the last of the second, third or fourth axis
+ input_item_id const pedalitem = axisactual[(std::min)(axiscount, 4) - 1];
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitem)));
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, pedalitem)));
+ }
+ if (3 <= axiscount)
+ {
+ // assign X/Y to one of the twin sticks
+ assignments.emplace_back(
+ (4 <= axiscount) ? IPT_JOYSTICKLEFT_LEFT : IPT_JOYSTICKRIGHT_LEFT,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_LEFT, axisactual[0])));
+ assignments.emplace_back(
+ (4 <= axiscount) ? IPT_JOYSTICKLEFT_RIGHT : IPT_JOYSTICKRIGHT_RIGHT,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_RIGHT, axisactual[0])));
+ assignments.emplace_back(
+ (4 <= axiscount) ? IPT_JOYSTICKLEFT_UP : IPT_JOYSTICKRIGHT_UP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_UP, axisactual[1])));
+ assignments.emplace_back(
+ (4 <= axiscount) ? IPT_JOYSTICKLEFT_DOWN : IPT_JOYSTICKRIGHT_DOWN,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_DOWN, axisactual[1])));
+
+ // use third or fourth axis for Z
+ input_seq const seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisactual[(std::min)(axiscount, 4) - 1]));
+ assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, seq);
+
+ // use this for focus next/previous to make system selection menu practical to navigate
+ input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[2]));
+ input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[2]));
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, downseq);
+ if (4 <= axiscount)
{
- joystick.hatsL[sdlevent.jhat.hat] = 0;
+ // use for zoom as well if there's another axis to use for previous/next group
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, downseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, upseq);
}
- if (sdlevent.jhat.value & SDL_HAT_RIGHT)
+
+ // use this for twin sticks, too
+ assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_LEFT : IPT_JOYSTICKLEFT_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_RIGHT : IPT_JOYSTICKLEFT_DOWN, SEQ_TYPE_STANDARD, downseq);
+
+ // put previous/next group on the last of the third or fourth axis
+ input_item_id const groupitem = axisactual[(std::min)(axiscount, 4) - 1];
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, groupitem)));
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, groupitem)));
+ }
+ if (4 <= axiscount)
+ {
+ // use this for twin sticks
+ input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[3]));
+ input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[3]));
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_JOYSTICKRIGHT_DOWN, SEQ_TYPE_STANDARD, downseq);
+ }
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
+ }
+
+ ~sdl_joystick_device()
+ {
+ close_device();
+ }
+
+ virtual void reset() override
+ {
+ sdl_joystick_device_base::reset();
+ clear_buffer();
+ }
+
+ virtual void process_event(SDL_Event const &event) override
+ {
+ if (!m_joydevice)
+ return;
+
+ switch (event.type)
+ {
+ case SDL_JOYAXISMOTION:
+ if (event.jaxis.axis < MAX_AXES)
+ m_joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2);
+ break;
+
+ case SDL_JOYBALLMOTION:
+ //printf("Ball %d %d\n", event.jball.xrel, event.jball.yrel);
+ if (event.jball.ball < (MAX_AXES / 2))
{
- joystick.hatsR[sdlevent.jhat.hat] = 0x80;
+ m_joystick.balls[event.jball.ball * 2] = event.jball.xrel * input_device::RELATIVE_PER_PIXEL;
+ m_joystick.balls[event.jball.ball * 2 + 1] = event.jball.yrel * input_device::RELATIVE_PER_PIXEL;
}
- else
+ break;
+
+ case SDL_JOYHATMOTION:
+ if (event.jhat.hat < MAX_HATS)
{
- joystick.hatsR[sdlevent.jhat.hat] = 0;
+ m_joystick.hatsU[event.jhat.hat] = (event.jhat.value & SDL_HAT_UP) ? 0x80 : 0;
+ m_joystick.hatsD[event.jhat.hat] = (event.jhat.value & SDL_HAT_DOWN) ? 0x80 : 0;
+ m_joystick.hatsL[event.jhat.hat] = (event.jhat.value & SDL_HAT_LEFT) ? 0x80 : 0;
+ m_joystick.hatsR[event.jhat.hat] = (event.jhat.value & SDL_HAT_RIGHT) ? 0x80 : 0;
}
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
- joystick.buttons[sdlevent.jbutton.button] = (sdlevent.jbutton.state == SDL_PRESSED) ? 0x80 : 0;
+ if (event.jbutton.button < MAX_BUTTONS)
+ m_joystick.buttons[event.jbutton.button] = (event.jbutton.state == SDL_PRESSED) ? 0x80 : 0;
break;
+
+ case SDL_JOYDEVICEREMOVED:
+ osd_printf_verbose("Joystick: %s [ID %s] disconnected\n", name(), id());
+ clear_instance();
+ clear_buffer();
+ close_device();
+ break;
+ }
+ }
+
+ bool has_haptic() const
+ {
+ return m_hapdevice != nullptr;
+ }
+
+ void attach_device(SDL_Joystick *joy)
+ {
+ assert(joy);
+ assert(!m_joydevice);
+
+ set_instance(SDL_JoystickInstanceID(joy));
+ m_joydevice = joy;
+ m_hapdevice = SDL_HapticOpenFromJoystick(joy);
+
+ osd_printf_verbose("Joystick: %s [ID %s] reconnected\n", name(), id());
+ }
+
+protected:
+ // state information for a joystick
+ struct sdl_joystick_state
+ {
+ s32 axes[MAX_AXES];
+ s32 buttons[MAX_BUTTONS];
+ s32 hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS];
+ s32 balls[MAX_AXES];
+ };
+
+ sdl_joystick_state m_joystick;
+
+private:
+ SDL_Joystick *m_joydevice;
+ SDL_Haptic *m_hapdevice;
+
+ void clear_buffer()
+ {
+ memset(&m_joystick, 0, sizeof(m_joystick));
+ }
+
+ void close_device()
+ {
+ if (m_joydevice)
+ {
+ if (m_hapdevice)
+ {
+ SDL_HapticClose(m_hapdevice);
+ m_hapdevice = nullptr;
+ }
+ SDL_JoystickClose(m_joydevice);
+ m_joydevice = nullptr;
}
}
};
+
+//============================================================
+// sdl_sixaxis_joystick_device
+//============================================================
+
class sdl_sixaxis_joystick_device : public sdl_joystick_device
{
public:
- sdl_sixaxis_joystick_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : sdl_joystick_device(machine, name, id, module)
- {
- }
+ using sdl_joystick_device::sdl_joystick_device;
- void process_event(SDL_Event &sdlevent) override
+ virtual void process_event(SDL_Event const &event) override
{
- switch (sdlevent.type)
+ switch (event.type)
{
case SDL_JOYAXISMOTION:
{
- int axis = sdlevent.jaxis.axis;
-
+ int const axis = event.jaxis.axis;
if (axis <= 3)
{
- joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2);
+ m_joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2);
}
else
{
- int magic = (sdlevent.jaxis.value / 2) + 16384;
- joystick.axes[sdlevent.jaxis.axis] = magic;
+ int const magic = (event.jaxis.value / 2) + 16384;
+ m_joystick.axes[event.jaxis.axis] = magic;
}
}
break;
default:
// Call the base for other events
- sdl_joystick_device::process_event(sdlevent);
+ sdl_joystick_device::process_event(event);
break;
}
}
};
+
//============================================================
-// sdl_input_module
+// sdl_game_controller_device
//============================================================
-class sdl_input_module : public input_module_base, public sdl_event_subscriber
+class sdl_game_controller_device : public sdl_joystick_device_base
{
public:
- sdl_input_module(const char *type)
- : input_module_base(type, "sdl")
+ sdl_game_controller_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ SDL_GameController *ctrl,
+ char const *serial) :
+ sdl_joystick_device_base(
+ std::move(name),
+ std::move(id),
+ module,
+ serial),
+ m_controller({{0}}),
+ m_ctrldevice(ctrl)
+ {
+ set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl)));
+ }
+
+ ~sdl_game_controller_device()
{
+ close_device();
}
- void input_init(running_machine &machine) override
+ virtual void configure(input_device &device) override
{
- if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ input_device::assignment_vector assignments;
+ char const *const *axisnames = CONTROLLER_AXIS_XBOX;
+ char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360;
+ bool digitaltriggers = false;
+ bool avoidpaddles = false;
+ auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice);
+ switch (ctrltype)
+ {
+ case SDL_CONTROLLER_TYPE_UNKNOWN:
+ osd_printf_verbose("Game Controller: ... unknown type\n", int(ctrltype));
+ break;
+ case SDL_CONTROLLER_TYPE_XBOX360:
+ osd_printf_verbose("Game Controller: ... Xbox 360 type\n");
+ axisnames = CONTROLLER_AXIS_XBOX;
+ buttonnames = CONTROLLER_BUTTON_XBOX360;
+ break;
+ case SDL_CONTROLLER_TYPE_XBOXONE:
+ osd_printf_verbose("Game Controller: ... Xbox One type\n");
+ axisnames = CONTROLLER_AXIS_XBOX;
+ buttonnames = CONTROLLER_BUTTON_XBOXONE;
+ break;
+ case SDL_CONTROLLER_TYPE_PS3:
+ osd_printf_verbose("Game Controller: ... PlayStation 3 type\n");
+ axisnames = CONTROLLER_AXIS_PS;
+ buttonnames = CONTROLLER_BUTTON_PS3;
+ break;
+ case SDL_CONTROLLER_TYPE_PS4:
+ osd_printf_verbose("Game Controller: ... PlayStation 4 type\n");
+ axisnames = CONTROLLER_AXIS_PS;
+ buttonnames = CONTROLLER_BUTTON_PS4;
+ break;
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
+ osd_printf_verbose("Game Controller: ... Switch Pro Controller type\n");
+ axisnames = CONTROLLER_AXIS_SWITCH;
+ buttonnames = CONTROLLER_BUTTON_SWITCH;
+ digitaltriggers = true;
+ break;
+ //case SDL_CONTROLLER_TYPE_VIRTUAL:
+ case SDL_CONTROLLER_TYPE_PS5:
+ osd_printf_verbose("Game Controller: ... PlayStation 5 type\n");
+ axisnames = CONTROLLER_AXIS_PS;
+ buttonnames = CONTROLLER_BUTTON_PS5;
+ break;
+#if SDL_VERSION_ATLEAST(2, 0, 16)
+ //case SDL_CONTROLLER_TYPE_AMAZON_LUNA:
+ case SDL_CONTROLLER_TYPE_GOOGLE_STADIA:
+ osd_printf_verbose("Game Controller: ... Google Stadia type\n");
+ axisnames = CONTROLLER_AXIS_PS;
+ buttonnames = CONTROLLER_BUTTON_STADIA;
+ break;
+#endif
+#if SDL_VERSION_ATLEAST(2, 24, 0)
+ case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD:
+ osd_printf_verbose("Game Controller: ... NVIDIA Shield type\n");
+ axisnames = CONTROLLER_AXIS_XBOX;
+ buttonnames = CONTROLLER_BUTTON_SHIELD;
+ break;
+ //case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
+ //case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
+ osd_printf_verbose("Game Controller: ... Joy-Con pair type\n");
+ axisnames = CONTROLLER_AXIS_SWITCH;
+ buttonnames = CONTROLLER_BUTTON_SWITCH;
+ digitaltriggers = true;
+ avoidpaddles = true;
+ break;
+#endif
+ default: // default to Xbox 360 names
+ osd_printf_verbose("Game Controller: ... unrecognized type (%d)\n", int(ctrltype));
+ break;
+ }
+
+ // keep track of item numbers as we add controls
+ std::pair<input_item_id, input_item_id> axisitems[SDL_CONTROLLER_AXIS_MAX];
+ input_item_id buttonitems[SDL_CONTROLLER_BUTTON_MAX];
+ std::tuple<input_item_id, SDL_GameControllerButton, SDL_GameControllerAxis> numberedbuttons[16];
+ std::fill(
+ std::begin(axisitems),
+ std::end(axisitems),
+ std::make_pair(ITEM_ID_INVALID, ITEM_ID_INVALID));
+ std::fill(
+ std::begin(buttonitems),
+ std::end(buttonitems),
+ ITEM_ID_INVALID);
+ std::fill(
+ std::begin(numberedbuttons),
+ std::end(numberedbuttons),
+ std::make_tuple(ITEM_ID_INVALID, SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_INVALID));
+
+ // add axes
+ std::tuple<SDL_GameControllerAxis, input_item_id, bool> const axes[]{
+ { SDL_CONTROLLER_AXIS_LEFTX, ITEM_ID_XAXIS, false },
+ { SDL_CONTROLLER_AXIS_LEFTY, ITEM_ID_YAXIS, false },
+ { SDL_CONTROLLER_AXIS_RIGHTX, ITEM_ID_ZAXIS, false },
+ { SDL_CONTROLLER_AXIS_RIGHTY, ITEM_ID_RZAXIS, false },
+ { SDL_CONTROLLER_AXIS_TRIGGERLEFT, ITEM_ID_SLIDER1, true },
+ { SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ITEM_ID_SLIDER2, true } };
+ for (auto [axis, item, buttontest] : axes)
+ {
+ bool avail = !buttontest || !digitaltriggers;
+ avail = avail && SDL_GameControllerHasAxis(m_ctrldevice, axis);
+ if (avail)
+ {
+ auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis);
+ switch (binding.bindType)
+ {
+ case SDL_CONTROLLER_BINDTYPE_NONE:
+ avail = false;
+ break;
+ case SDL_CONTROLLER_BINDTYPE_BUTTON:
+ if (buttontest)
+ avail = false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (avail)
+ {
+ axisitems[axis].first = device.add_item(
+ axisnames[axis],
+ std::string_view(),
+ item,
+ generic_axis_get_state<s32>,
+ &m_controller.axes[axis]);
+ }
+ }
+
+ // add automatically numbered buttons
+ std::tuple<SDL_GameControllerButton, SDL_GameControllerAxis, bool> const generalbuttons[]{
+ { SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERLEFT, true },
+ { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, true },
+ { SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_PADDLE1, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_PADDLE2, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_PADDLE3, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_PADDLE4, SDL_CONTROLLER_AXIS_INVALID, true },
+ { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID, false },
+ { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID, false },
+ { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID, false },
+ };
+ input_item_id button_item = ITEM_ID_BUTTON1;
+ unsigned buttoncount = 0;
+ for (auto [button, axis, field] : generalbuttons)
+ {
+ bool avail = true;
+ input_item_id actual = ITEM_ID_INVALID;
+ if (SDL_CONTROLLER_BUTTON_INVALID != button)
+ {
+ avail = SDL_GameControllerHasButton(m_ctrldevice, button);
+ if (avail)
+ {
+ auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button);
+ switch (binding.bindType)
+ {
+ case SDL_CONTROLLER_BINDTYPE_NONE:
+ avail = false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (avail)
+ {
+ actual = buttonitems[button] = device.add_item(
+ buttonnames[button],
+ std::string_view(),
+ button_item++,
+ generic_button_get_state<s32>,
+ &m_controller.buttons[button]);
+ if (field && (std::size(numberedbuttons) > buttoncount))
+ std::get<1>(numberedbuttons[buttoncount]) = button;
+ }
+ }
+ else
+ {
+ avail = SDL_GameControllerHasAxis(m_ctrldevice, axis);
+ if (avail)
+ {
+ auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis);
+ switch (binding.bindType)
+ {
+ case SDL_CONTROLLER_BINDTYPE_NONE:
+ avail = false;
+ break;
+ case SDL_CONTROLLER_BINDTYPE_BUTTON:
+ break;
+ default:
+ avail = digitaltriggers;
+ }
+ }
+ if (avail)
+ {
+ actual = axisitems[axis].second = device.add_item(
+ axisnames[axis],
+ std::string_view(),
+ button_item++,
+ [] (void *device_internal, void *item_internal) -> int
+ {
+ return (*reinterpret_cast<s32 const *>(item_internal) <= -16'384) ? 1 : 0;
+ },
+ &m_controller.axes[axis]);
+ if (field && (std::size(numberedbuttons) > buttoncount))
+ std::get<2>(numberedbuttons[buttoncount]) = axis;
+ }
+ }
+
+ // add default button assignments
+ if (field && avail && (std::size(numberedbuttons) > buttoncount))
+ {
+ std::get<0>(numberedbuttons[buttoncount]) = actual;
+ add_button_assignment(assignments, ioport_type(IPT_BUTTON1 + buttoncount++), { actual });
+ }
+ }
+
+ // add buttons with fixed item IDs
+ std::pair<SDL_GameControllerButton, input_item_id> const fixedbuttons[]{
+ { SDL_CONTROLLER_BUTTON_BACK, ITEM_ID_SELECT },
+ { SDL_CONTROLLER_BUTTON_START, ITEM_ID_START },
+ { SDL_CONTROLLER_BUTTON_DPAD_UP, ITEM_ID_HAT1UP },
+ { SDL_CONTROLLER_BUTTON_DPAD_DOWN, ITEM_ID_HAT1DOWN },
+ { SDL_CONTROLLER_BUTTON_DPAD_LEFT, ITEM_ID_HAT1LEFT },
+ { SDL_CONTROLLER_BUTTON_DPAD_RIGHT, ITEM_ID_HAT1RIGHT } };
+ for (auto [button, item] : fixedbuttons)
{
- osd_printf_warning("Debug Build: Disabling input grab for -debug\n");
- set_mouse_enabled(false);
+ bool avail = true;
+ avail = SDL_GameControllerHasButton(m_ctrldevice, button);
+ if (avail)
+ {
+ auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button);
+ switch (binding.bindType)
+ {
+ case SDL_CONTROLLER_BINDTYPE_NONE:
+ avail = false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (avail)
+ {
+ buttonitems[button] = device.add_item(
+ buttonnames[button],
+ std::string_view(),
+ item,
+ generic_button_get_state<s32>,
+ &m_controller.buttons[button]);
+ }
}
+
+ // try to get a "complete" joystick for primary movement controls
+ input_item_id diraxis[2][2];
+ choose_primary_stick(
+ diraxis,
+ axisitems[SDL_CONTROLLER_AXIS_LEFTX].first,
+ axisitems[SDL_CONTROLLER_AXIS_LEFTY].first,
+ axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first,
+ axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first);
+
+ // now set up controls using the primary joystick
+ add_directional_assignments(
+ assignments,
+ diraxis[0][0],
+ diraxis[0][1],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN]);
+
+ // assign a secondary stick axis to joystick Z if available
+ bool const zaxis = add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { diraxis[1][1], diraxis[1][0] });
+ if (!zaxis)
+ {
+ // if both triggers are present, combine them, or failing that, fall back to a pair of buttons
+ if ((ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first) && (ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first))
+ {
+ assignments.emplace_back(
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ input_seq(
+ make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first),
+ make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_REVERSE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)));
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER]))
+ {
+ // took shoulder buttons
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second))
+ {
+ // took trigger buttons
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2]))
+ {
+ // took P1/P2
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4]))
+ {
+ // took P3/P4
+ }
+ }
+
+ // prefer trigger axes for pedals, otherwise take half axes and buttons
+ unsigned pedalbutton = 0;
+ if (!add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first }))
+ {
+ add_assignment(
+ assignments,
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NEG,
+ { diraxis[1][1], diraxis[0][1] });
+ bool const incbutton = add_assignment(
+ assignments,
+ IPT_PEDAL,
+ SEQ_TYPE_INCREMENT,
+ ITEM_CLASS_SWITCH,
+ ITEM_MODIFIER_NONE,
+ { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second, buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] });
+ if (!incbutton)
+ {
+ if (add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) }))
+ ++pedalbutton;
+ }
+ }
+ if (!add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first }))
+ {
+ add_assignment(
+ assignments,
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_POS,
+ { diraxis[1][1], diraxis[0][1] });
+ bool const incbutton = add_assignment(
+ assignments,
+ IPT_PEDAL2,
+ SEQ_TYPE_INCREMENT,
+ ITEM_CLASS_SWITCH,
+ ITEM_MODIFIER_NONE,
+ { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] });
+ if (!incbutton)
+ {
+ if (add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) }))
+ ++pedalbutton;
+ }
+ }
+ add_assignment(assignments, IPT_PEDAL3, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) });
+
+ // potentially use thumb sticks and/or D-pad and A/B/X/Y diamond for twin sticks
+ add_twin_stick_assignments(
+ assignments,
+ axisitems[SDL_CONTROLLER_AXIS_LEFTX].first,
+ axisitems[SDL_CONTROLLER_AXIS_LEFTY].first,
+ axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first,
+ axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first,
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP],
+ buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN],
+ buttonitems[SDL_CONTROLLER_BUTTON_X],
+ buttonitems[SDL_CONTROLLER_BUTTON_B],
+ buttonitems[SDL_CONTROLLER_BUTTON_Y],
+ buttonitems[SDL_CONTROLLER_BUTTON_A]);
+
+ // add assignments for buttons with fixed functions
+ add_button_assignment(assignments, IPT_SELECT, { buttonitems[SDL_CONTROLLER_BUTTON_BACK] });
+ add_button_assignment(assignments, IPT_START, { buttonitems[SDL_CONTROLLER_BUTTON_START] });
+ add_button_assignment(assignments, IPT_UI_MENU, { buttonitems[SDL_CONTROLLER_BUTTON_GUIDE] });
+
+ // the first button is always UI select
+ if (add_button_assignment(assignments, IPT_UI_SELECT, { std::get<0>(numberedbuttons[0]) }))
+ {
+ if (SDL_CONTROLLER_BUTTON_INVALID != std::get<1>(numberedbuttons[0]))
+ buttonitems[std::get<1>(numberedbuttons[0])] = ITEM_ID_INVALID;
+ if (SDL_CONTROLLER_AXIS_INVALID != std::get<2>(numberedbuttons[0]))
+ axisitems[std::get<2>(numberedbuttons[0])].second = ITEM_ID_INVALID;
+ }
+
+ // try to get a matching pair of buttons for previous/next group
+ if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second))
+ {
+ // took digital triggers
+ }
+ else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2]))
+ {
+ // took upper paddles
+ }
+ else if (consume_trigger_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first))
+ {
+ // took analog triggers
+ }
+ else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4]))
+ {
+ // took lower paddles
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1]))
+ {
+ // took secondary Y
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0]))
+ {
+ // took secondary X
+ }
+
+ // try to get a matching pair of buttons for page up/down
+ if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2]))
+ {
+ // took upper paddles
+ }
+ else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4]))
+ {
+ // took lower paddles
+ }
+ else
+ if (consume_trigger_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first))
+ {
+ // took analog triggers
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1]))
+ {
+ // took secondary Y
+ }
+
+ // try to assign X button to UI clear
+ if (add_button_assignment(assignments, IPT_UI_CLEAR, { buttonitems[SDL_CONTROLLER_BUTTON_X] }))
+ {
+ buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID;
+ }
+ else
+ {
+ // otherwise try to find an unassigned button
+ for (auto [item, button, axis] : numberedbuttons)
+ {
+ if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button]))
+ {
+ add_button_assignment(assignments, IPT_UI_CLEAR, { item });
+ buttonitems[button] = ITEM_ID_INVALID;
+ break;
+ }
+ else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second))
+ {
+ add_button_assignment(assignments, IPT_UI_CLEAR, { item });
+ axisitems[axis].second = ITEM_ID_INVALID;
+ break;
+ }
+ }
+ }
+
+ // try to assign B button to UI back
+ if (add_button_assignment(assignments, IPT_UI_BACK, { buttonitems[SDL_CONTROLLER_BUTTON_B] }))
+ {
+ buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID;
+ }
+ else
+ {
+ // otherwise try to find an unassigned button
+ for (auto [item, button, axis] : numberedbuttons)
+ {
+ if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button]))
+ {
+ add_button_assignment(assignments, IPT_UI_CLEAR, { item });
+ buttonitems[button] = ITEM_ID_INVALID;
+ break;
+ }
+ else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second))
+ {
+ add_button_assignment(assignments, IPT_UI_CLEAR, { item });
+ axisitems[axis].second = ITEM_ID_INVALID;
+ break;
+ }
+ }
+ }
+
+ // try to assign Y button to UI help
+ if (add_button_assignment(assignments, IPT_UI_HELP, { buttonitems[SDL_CONTROLLER_BUTTON_Y] }))
+ {
+ buttonitems[SDL_CONTROLLER_BUTTON_Y] = ITEM_ID_INVALID;
+ }
+ else
+ {
+ // otherwise try to find an unassigned button
+ for (auto [item, button, axis] : numberedbuttons)
+ {
+ if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button]))
+ {
+ add_button_assignment(assignments, IPT_UI_HELP, { item });
+ buttonitems[button] = ITEM_ID_INVALID;
+ break;
+ }
+ else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second))
+ {
+ add_button_assignment(assignments, IPT_UI_HELP, { item });
+ axisitems[axis].second = ITEM_ID_INVALID;
+ break;
+ }
+ }
+ }
+
+ // put focus previous/next on the shoulder buttons if available - this can be overloaded with zoom
+ if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER]))
+ {
+ // took shoulder buttons
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0]))
+ {
+ // took secondary X
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][1]))
+ {
+ // took secondary Y
+ }
+
+ // put zoom on the secondary stick if available, or fall back to shoulder buttons
+ if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0]))
+ {
+ // took secondary X
+ if (axisitems[SDL_CONTROLLER_AXIS_LEFTX].first == diraxis[1][0])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] });
+ else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first == diraxis[1][0])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] });
+ diraxis[1][0] = ITEM_ID_INVALID;
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_IN, IPT_UI_ZOOM_OUT, diraxis[1][1]))
+ {
+ // took secondary Y
+ if (axisitems[SDL_CONTROLLER_AXIS_LEFTY].first == diraxis[1][1])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] });
+ else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first == diraxis[1][1])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] });
+ diraxis[1][1] = ITEM_ID_INVALID;
+ }
+ else if (consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER]))
+ {
+ // took shoulder buttons
+ }
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
}
- void exit() override
+ virtual void reset() override
{
- // unsubscribe for events
- sdl_event_manager::instance().unsubscribe(this);
+ sdl_joystick_device_base::reset();
+ clear_buffer();
+ }
+
+ virtual void process_event(SDL_Event const &event) override
+ {
+ if (!m_ctrldevice)
+ return;
+
+ switch (event.type)
+ {
+ case SDL_CONTROLLERAXISMOTION:
+ if (event.caxis.axis < SDL_CONTROLLER_AXIS_MAX)
+ {
+ switch (event.caxis.axis)
+ {
+ case SDL_CONTROLLER_AXIS_TRIGGERLEFT: // MAME wants negative values for triggers
+ case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+ m_controller.axes[event.caxis.axis] = -normalize_absolute_axis(event.caxis.value, -32'767, 32'767);
+ break;
+ default:
+ m_controller.axes[event.caxis.axis] = normalize_absolute_axis(event.caxis.value, -32'767, 32'767);
+ }
+ }
+ break;
- input_module_base::exit();
+ case SDL_CONTROLLERBUTTONDOWN:
+ case SDL_CONTROLLERBUTTONUP:
+ if (event.cbutton.button < SDL_CONTROLLER_BUTTON_MAX)
+ m_controller.buttons[event.cbutton.button] = (event.cbutton.state == SDL_PRESSED) ? 0x80 : 0x00;
+ break;
+
+ case SDL_CONTROLLERDEVICEREMOVED:
+ osd_printf_verbose("Game Controller: %s [ID %s] disconnected\n", name(), id());
+ clear_instance();
+ clear_buffer();
+ close_device();
+ break;
+ }
}
- void before_poll(running_machine& machine) override
+ void attach_device(SDL_GameController *ctrl)
{
- // Tell the event manager to process events and push them to the devices
- sdl_event_manager::instance().process_events(machine);
+ assert(ctrl);
+ assert(!m_ctrldevice);
+
+ set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl)));
+ m_ctrldevice = ctrl;
+
+ osd_printf_verbose("Game Controller: %s [ID %s] reconnected\n", name(), id());
}
- bool should_poll_devices(running_machine& machine) override
+private:
+ // state information for a game controller
+ struct sdl_controller_state
{
- return sdl_event_manager::instance().has_focus() && input_enabled();
+ s32 axes[SDL_CONTROLLER_AXIS_MAX];
+ s32 buttons[SDL_CONTROLLER_BUTTON_MAX];
+ };
+
+ sdl_controller_state m_controller;
+ SDL_GameController *m_ctrldevice;
+
+ void clear_buffer()
+ {
+ memset(&m_controller, 0, sizeof(m_controller));
}
- void handle_event(SDL_Event &sdlevent) override
+ void close_device()
{
- // By default dispatch event to every device
- devicelist()->for_each_device([&sdlevent](auto device) {
- downcast<sdl_device*>(device)->queue_events(&sdlevent, 1);
- });
+ if (m_ctrldevice)
+ {
+ SDL_GameControllerClose(m_ctrldevice);
+ m_ctrldevice = nullptr;
+ }
}
};
+
//============================================================
-// sdl_keyboard_module
+// sdl_input_module
//============================================================
-class sdl_keyboard_module : public sdl_input_module
+template <typename Info>
+class sdl_input_module :
+ public input_module_impl<Info, sdl_osd_interface>,
+ protected sdl_event_manager::subscriber
{
- keyboard_trans_table * m_key_trans_table;
public:
- sdl_keyboard_module()
- : sdl_input_module(OSD_KEYBOARDINPUT_PROVIDER), m_key_trans_table(nullptr)
+ sdl_input_module(char const *type, char const *name) :
+ input_module_impl<Info, sdl_osd_interface>(type, name)
{
}
- void input_init(running_machine &machine) override
+ virtual void exit() override
{
- sdl_input_module::input_init(machine);
+ // unsubscribe for events
+ unsubscribe();
- static int event_types[] = {
- static_cast<int>(SDL_KEYDOWN),
- static_cast<int>(SDL_KEYUP),
- static_cast<int>(SDL_TEXTINPUT)
- };
+ input_module_impl<Info, sdl_osd_interface>::exit();
+ }
- sdl_event_manager::instance().subscribe(event_types, ARRAY_LENGTH(event_types), this);
+protected:
+ virtual void handle_event(SDL_Event const &event) override
+ {
+ // dispatch event to every device by default
+ this->devicelist().for_each_device(
+ [&event] (auto &device) { device.queue_events(&event, 1); });
+ }
+};
- sdl_keyboard_device *devinfo;
- // Read our keymap and store a pointer to our table
- m_key_trans_table = sdlinput_read_keymap(machine);
+//============================================================
+// sdl_keyboard_module
+//============================================================
- keyboard_trans_table& local_table = *m_key_trans_table;
+class sdl_keyboard_module : public sdl_input_module<sdl_keyboard_device>
+{
+public:
+ sdl_keyboard_module() :
+ sdl_input_module<sdl_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "sdl")
+ {
+ }
- osd_printf_verbose("Keyboard: Start initialization\n");
+ virtual void input_init(running_machine &machine) override
+ {
+ sdl_input_module<sdl_keyboard_device>::input_init(machine);
- // SDL only has 1 keyboard add it now
- devinfo = devicelist()->create_device<sdl_keyboard_device>(machine, "System keyboard", "System keyboard", *this);
+ constexpr int event_types[] = {
+ int(SDL_KEYDOWN),
+ int(SDL_KEYUP) };
- // populate it
- for (int keynum = 0; local_table[keynum].mame_key != ITEM_ID_INVALID; keynum++)
- {
- input_item_id itemid = local_table[keynum].mame_key;
+ subscribe(osd(), event_types);
+
+ // Read our keymap and store a pointer to our table
+ sdlinput_read_keymap();
- // generate the default / modified name
- char defname[20];
- snprintf(defname, sizeof(defname) - 1, "%s", local_table[keynum].ui_name);
+ osd_printf_verbose("Keyboard: Start initialization\n");
- devinfo->device()->add_item(defname, itemid, generic_button_get_state<std::int32_t>, &devinfo->keyboard.state[local_table[keynum].sdl_scancode]);
- }
+ // SDL only has 1 keyboard add it now
+ auto &devinfo = create_device<sdl_keyboard_device>(
+ DEVICE_CLASS_KEYBOARD,
+ "System keyboard",
+ "System keyboard",
+ *m_key_trans_table);
- osd_printf_verbose("Keyboard: Registered %s\n", devinfo->name());
+ osd_printf_verbose("Keyboard: Registered %s\n", devinfo.name());
osd_printf_verbose("Keyboard: End initialization\n");
}
private:
- static keyboard_trans_table* sdlinput_read_keymap(running_machine &machine)
+ void sdlinput_read_keymap()
{
- char *keymap_filename;
- FILE *keymap_file;
- int line = 1;
- int sdl2section = 0;
-
keyboard_trans_table &default_table = keyboard_trans_table::instance();
- if (!machine.options().bool_value(SDLOPTION_KEYMAP))
- return &default_table;
-
- keymap_filename = (char *)downcast<sdl_options &>(machine.options()).keymap_file();
- osd_printf_verbose("Keymap: Start reading keymap_file %s\n", keymap_filename);
-
- keymap_file = fopen(keymap_filename, "r");
- if (keymap_file == nullptr)
- {
- osd_printf_warning("Keymap: Unable to open keymap %s, using default\n", keymap_filename);
- return &default_table;
- }
-
// Allocate a block of translation entries big enough to hold what's in the default table
- auto key_trans_entries = std::make_unique<key_trans_entry[]>(default_table.size());
+ auto key_trans_entries = std::make_unique<key_trans_entry []>(default_table.size());
- // copy the elements from the default table
+ // copy the elements from the default table and ask SDL for key names
for (int i = 0; i < default_table.size(); i++)
+ {
key_trans_entries[i] = default_table[i];
+ char const *const name = SDL_GetScancodeName(SDL_Scancode(default_table[i].sdl_scancode));
+ if (name && *name)
+ key_trans_entries[i].ui_name = name;
+ }
// Allocate the trans table to be associated with the machine so we don't have to free it
- keyboard_trans_table *custom_table = auto_alloc(machine, keyboard_trans_table(std::move(key_trans_entries), default_table.size()));
+ m_key_trans_table = std::make_unique<keyboard_trans_table>(std::move(key_trans_entries), default_table.size());
+
+ if (!options()->bool_value(SDLOPTION_KEYMAP))
+ return;
+
+ const char *const keymap_filename = dynamic_cast<sdl_options const &>(*options()).keymap_file();
+ osd_printf_verbose("Keymap: Start reading keymap_file %s\n", keymap_filename);
+
+ FILE *const keymap_file = fopen(keymap_filename, "r");
+ if (!keymap_file)
+ {
+ osd_printf_warning("Keymap: Unable to open keymap %s, using default\n", keymap_filename);
+ return;
+ }
+ int line = 1;
+ int sdl2section = 0;
while (!feof(keymap_file))
{
char buf[256];
@@ -864,34 +2266,36 @@ private:
if (sk >= 0 && index >= 0)
{
- key_trans_entry &entry = (*custom_table)[index];
+ key_trans_entry &entry = (*m_key_trans_table)[index];
entry.sdl_scancode = sk;
- entry.ui_name = auto_alloc_array(machine, char, strlen(kns) + 1);
- strcpy(entry.ui_name, kns);
+ entry.ui_name = const_cast<char *>(m_ui_names.emplace_back(kns).c_str());
osd_printf_verbose("Keymap: Mapped <%s> to <%s> with ui-text <%s>\n", sks, mks, kns);
}
else
+ {
osd_printf_error("Keymap: Error on line %d - %s key not found: %s\n", line, (sk<0) ? "sdl" : "mame", buf);
+ }
}
}
line++;
}
fclose(keymap_file);
osd_printf_verbose("Keymap: Processed %d lines\n", line);
-
- return custom_table;
}
+
+ std::unique_ptr<keyboard_trans_table> m_key_trans_table;
+ std::list<std::string> m_ui_names;
};
+
//============================================================
// sdl_mouse_module
//============================================================
-class sdl_mouse_module : public sdl_input_module
+class sdl_mouse_module : public sdl_input_module<sdl_mouse_device>
{
public:
- sdl_mouse_module()
- : sdl_input_module(OSD_MOUSEINPUT_PROVIDER)
+ sdl_mouse_module() : sdl_input_module<sdl_mouse_device>(OSD_MOUSEINPUT_PROVIDER, "sdl")
{
}
@@ -899,285 +2303,578 @@ public:
{
sdl_input_module::input_init(machine);
- static int event_types[] = {
- static_cast<int>(SDL_MOUSEMOTION),
- static_cast<int>(SDL_MOUSEBUTTONDOWN),
- static_cast<int>(SDL_MOUSEBUTTONUP),
- static_cast<int>(SDL_MOUSEWHEEL)
- };
-
- sdl_event_manager::instance().subscribe(event_types, ARRAY_LENGTH(event_types), this);
+ constexpr int event_types[] = {
+ int(SDL_MOUSEMOTION),
+ int(SDL_MOUSEBUTTONDOWN),
+ int(SDL_MOUSEBUTTONUP),
+ int(SDL_MOUSEWHEEL) };
- sdl_mouse_device *devinfo;
- char defname[20];
- int button;
+ subscribe(osd(), event_types);
osd_printf_verbose("Mouse: Start initialization\n");
// SDL currently only supports one mouse
- devinfo = devicelist()->create_device<sdl_mouse_device>(machine, "System mouse", "System mouse", *this);
-
- // add the axes
- devinfo->device()->add_item("X", ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &devinfo->mouse.lX);
- devinfo->device()->add_item("Y", ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &devinfo->mouse.lY);
+ auto &devinfo = create_device<sdl_mouse_device>(
+ DEVICE_CLASS_MOUSE,
+ "System mouse",
+ "System mouse");
- for (button = 0; button < 4; button++)
- {
- input_item_id itemid = (input_item_id)(ITEM_ID_BUTTON1 + button);
- snprintf(defname, sizeof(defname), "B%d", button + 1);
-
- devinfo->device()->add_item(defname, itemid, generic_button_get_state<std::int32_t>, &devinfo->mouse.buttons[button]);
- }
-
- osd_printf_verbose("Mouse: Registered %s\n", devinfo->name());
+ osd_printf_verbose("Mouse: Registered %s\n", devinfo.name());
osd_printf_verbose("Mouse: End initialization\n");
}
};
-static void devmap_register(device_map_t &devmap, int physical_idx, const std::string &name)
-{
- // Attempt to find the entry by name
- auto entry = std::find_if(std::begin(devmap.map), std::end(devmap.map), [&name](auto &item)
- {
- return item.name == name && item.physical < 0;
- });
+//============================================================
+// sdl_lightgun_module
+//============================================================
- // If we didn't find it by name, find the first free slot
- if (entry == std::end(devmap.map))
+class sdl_lightgun_module : public sdl_input_module<sdl_mouse_device_base>
+{
+public:
+ sdl_lightgun_module() : sdl_input_module<sdl_mouse_device_base>(OSD_LIGHTGUNINPUT_PROVIDER, "sdl")
{
- entry = std::find_if(std::begin(devmap.map), std::end(devmap.map), [](auto &item) { return item.name.empty(); });
}
- if (entry != std::end(devmap.map))
+ virtual void input_init(running_machine &machine) override
{
- entry->physical = physical_idx;
- entry->name = name;
- int logical_idx = std::distance(std::begin(devmap.map), entry);
- devmap.logical[physical_idx] = logical_idx;
+ auto &sdlopts = dynamic_cast<sdl_options const &>(*options());
+ sdl_input_module::input_init(machine);
+ bool const dual(sdlopts.dual_lightgun());
+
+ if (!dual)
+ {
+ constexpr int event_types[] = {
+ int(SDL_MOUSEMOTION),
+ int(SDL_MOUSEBUTTONDOWN),
+ int(SDL_MOUSEBUTTONUP),
+ int(SDL_MOUSEWHEEL),
+ int(SDL_WINDOWEVENT) };
+ subscribe(osd(), event_types);
+ }
+ else
+ {
+ constexpr int event_types[] = {
+ int(SDL_MOUSEBUTTONDOWN),
+ int(SDL_MOUSEBUTTONUP) };
+ subscribe(osd(), event_types);
+ }
+
+ osd_printf_verbose("Lightgun: Start initialization\n");
+
+ if (!dual)
+ {
+ auto &devinfo = create_device<sdl_lightgun_device>(
+ DEVICE_CLASS_LIGHTGUN,
+ "System pointer gun 1",
+ "System pointer gun 1");
+ osd_printf_verbose("Lightgun: Registered %s\n", devinfo.name());
+ }
+ else
+ {
+ auto &dev1info = create_device<sdl_dual_lightgun_device>(
+ DEVICE_CLASS_LIGHTGUN,
+ "System pointer gun 1",
+ "System pointer gun 1",
+ 0);
+ osd_printf_verbose("Lightgun: Registered %s\n", dev1info.name());
+
+ auto &dev2info = create_device<sdl_dual_lightgun_device>(
+ DEVICE_CLASS_LIGHTGUN,
+ "System pointer gun 2",
+ "System pointer gun 2",
+ 1);
+ osd_printf_verbose("Lightgun: Registered %s\n", dev2info.name());
+ }
+
+ osd_printf_verbose("Lightgun: End initialization\n");
}
-}
+};
+
//============================================================
-// sdl_joystick_module
+// sdl_joystick_module_base
//============================================================
-class sdl_joystick_module : public sdl_input_module
+class sdl_joystick_module_base : public sdl_input_module<sdl_joystick_device_base>
{
-private:
- device_map_t m_joy_map;
- bool m_initialized_joystick;
- bool m_initialized_haptic;
- bool m_sixaxis_mode;
-public:
- sdl_joystick_module()
- : sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER), m_initialized_joystick(false), m_initialized_haptic(false), m_sixaxis_mode(false)
+protected:
+ sdl_joystick_module_base(char const *name) :
+ sdl_input_module<sdl_joystick_device_base>(OSD_JOYSTICKINPUT_PROVIDER, name),
+ m_initialized_joystick(false),
+ m_initialized_haptic(false)
{
}
- virtual void exit() override
+ virtual ~sdl_joystick_module_base()
+ {
+ assert(!m_initialized_joystick);
+ assert(!m_initialized_haptic);
+ }
+
+ bool have_joystick() const { return m_initialized_joystick; }
+ bool have_haptic() const { return m_initialized_haptic; }
+
+ void init_joystick()
{
- sdl_input_module::exit();
+ assert(!m_initialized_joystick);
+ assert(!m_initialized_haptic);
+
+ m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK);
+ if (!m_initialized_joystick)
+ {
+ osd_printf_error("Could not initialize SDL Joystick subsystem: %s.\n", SDL_GetError());
+ return;
+ }
+ m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC);
+ if (!m_initialized_haptic)
+ osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError());
+ }
+
+ void quit_joystick()
+ {
if (m_initialized_joystick)
{
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+ m_initialized_joystick = false;
}
if (m_initialized_haptic)
{
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
+ m_initialized_haptic = false;
}
}
- virtual void input_init(running_machine &machine) override
+ sdl_joystick_device *create_joystick_device(int index, bool sixaxis)
{
- SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
-
- m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK);
- if (!m_initialized_joystick)
+ // open the joystick device
+ SDL_Joystick *const joy = SDL_JoystickOpen(index);
+ if (!joy)
{
- osd_printf_error("Could not initialize SDL Joystick: %s.\n", SDL_GetError());
- return;
+ osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", index, SDL_GetError());
+ return nullptr;
}
- m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC);
- if (!m_initialized_haptic)
+ // get basic info
+ char const *const name = SDL_JoystickName(joy);
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
+ char guid_str[256];
+ guid_str[0] = '\0';
+ SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
+ char const *const serial = SDL_JoystickGetSerial(joy);
+ std::string id(guid_str);
+ if (serial)
+ id.append(1, '-').append(serial);
+
+ // print some diagnostic info
+ osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n",
+ name ? name : "<nullptr>",
+ guid_str,
+ SDL_JoystickGetVendor(joy),
+ SDL_JoystickGetProduct(joy),
+ SDL_JoystickGetProductVersion(joy),
+ serial ? serial : "<nullptr>");
+ osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n",
+ SDL_JoystickNumAxes(joy),
+ SDL_JoystickNumButtons(joy),
+ SDL_JoystickNumHats(joy),
+ SDL_JoystickNumBalls(joy));
+ if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS)
+ osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS);
+
+ // instantiate device
+ sdl_joystick_device &devinfo = sixaxis
+ ? create_device<sdl_sixaxis_joystick_device>(DEVICE_CLASS_JOYSTICK, name ? name : guid_str, guid_str, joy, serial)
+ : create_device<sdl_joystick_device>(DEVICE_CLASS_JOYSTICK, name ? name : guid_str, guid_str, joy, serial);
+
+ if (devinfo.has_haptic())
+ osd_printf_verbose("Joystick: ... Has haptic capability\n");
+ else
+ osd_printf_verbose("Joystick: ... Does not have haptic capability\n");
+
+ return &devinfo;
+ }
+
+ void dispatch_joystick_event(SDL_Event const &event)
+ {
+ // figure out which joystick this event is destined for
+ sdl_joystick_device_base *const target_device = find_joystick(event.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32
+
+ // if we find a matching joystick, dispatch the event to the joystick
+ if (target_device)
+ target_device->queue_events(&event, 1);
+ }
+
+ device_info *find_reconnect_match(SDL_JoystickGUID const &guid, char const *serial)
+ {
+ char guid_str[256];
+ guid_str[0] = '\0';
+ SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
+ auto target_device = std::find_if(
+ devicelist().begin(),
+ devicelist().end(),
+ [&guid_str, &serial] (auto const &device)
+ {
+ return device->reconnect_match(guid_str, serial);
+ });
+ return (devicelist().end() != target_device) ? target_device->get() : nullptr;
+ }
+
+ sdl_joystick_device_base *find_joystick(SDL_JoystickID instance)
+ {
+ for (auto &device : devicelist())
{
- osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError());
+ if (device->is_instance(instance))
+ return device.get();
}
+ return nullptr;
+ }
- sdl_input_module::input_init(machine);
+private:
+ bool m_initialized_joystick;
+ bool m_initialized_haptic;
+};
+
+
+//============================================================
+// sdl_joystick_module
+//============================================================
+
+class sdl_joystick_module : public sdl_joystick_module_base
+{
+public:
+ sdl_joystick_module() : sdl_joystick_module_base("sdljoy")
+ {
+ }
- char tempname[512];
+ virtual void exit() override
+ {
+ sdl_joystick_module_base::exit();
- m_sixaxis_mode = downcast<const sdl_options*>(options())->sixaxis();
+ quit_joystick();
+ }
- devmap_init(machine, &m_joy_map, SDLOPTION_JOYINDEX, 8, "Joystick mapping");
+ virtual void input_init(running_machine &machine) override
+ {
+ auto &sdlopts = dynamic_cast<sdl_options const &>(*options());
+ bool const sixaxis_mode = sdlopts.sixaxis();
- osd_printf_verbose("Joystick: Start initialization\n");
- int physical_stick;
- for (physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++)
- {
- std::string joy_name = remove_spaces(SDL_JoystickNameForIndex(physical_stick));
- devmap_register(m_joy_map, physical_stick, joy_name);
- }
+ if (!sdlopts.debug() && sdlopts.background_input())
+ SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
+ SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
- for (int stick = 0; stick < MAX_DEVMAP_ENTRIES; stick++)
- {
- sdl_joystick_device *devinfo = create_joystick_device(machine, &m_joy_map, stick, DEVICE_CLASS_JOYSTICK);
+ init_joystick();
+ if (!have_joystick())
+ return;
- if (devinfo == nullptr)
- continue;
+ sdl_joystick_module_base::input_init(machine);
- physical_stick = m_joy_map.map[stick].physical;
- SDL_Joystick *joy = SDL_JoystickOpen(physical_stick);
- devinfo->sdl_state.device = joy;
- devinfo->sdl_state.joystick_id = SDL_JoystickInstanceID(joy);
- devinfo->sdl_state.hapdevice = SDL_HapticOpenFromJoystick(joy);
+ osd_printf_verbose("Joystick: Start initialization\n");
+ for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++)
+ create_joystick_device(physical_stick, sixaxis_mode);
+
+ constexpr int event_types[] = {
+ int(SDL_JOYAXISMOTION),
+ int(SDL_JOYBALLMOTION),
+ int(SDL_JOYHATMOTION),
+ int(SDL_JOYBUTTONDOWN),
+ int(SDL_JOYBUTTONUP),
+ int(SDL_JOYDEVICEADDED),
+ int(SDL_JOYDEVICEREMOVED) };
+ subscribe(osd(), event_types);
- osd_printf_verbose("Joystick: %s\n", SDL_JoystickNameForIndex(physical_stick));
- osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy), SDL_JoystickNumBalls(joy));
- osd_printf_verbose("Joystick: ... Physical id %d mapped to logical id %d\n", physical_stick, stick + 1);
- if (devinfo->sdl_state.hapdevice != nullptr)
+ osd_printf_verbose("Joystick: End initialization\n");
+ }
+
+ virtual void handle_event(SDL_Event const &event) override
+ {
+ if (SDL_JOYDEVICEADDED == event.type)
+ {
+ SDL_Joystick *const joy = SDL_JoystickOpen(event.jdevice.which);
+ if (!joy)
{
- osd_printf_verbose("Joystick: ... Has haptic capability\n");
+ osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", event.jdevice.which, SDL_GetError());
}
else
{
- osd_printf_verbose("Joystick: ... Does not have haptic capability\n");
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
+ char const *const serial = SDL_JoystickGetSerial(joy);
+ auto *const target_device = find_reconnect_match(guid, serial);
+ if (target_device)
+ {
+ auto &devinfo = dynamic_cast<sdl_joystick_device &>(*target_device);
+ devinfo.attach_device(joy);
+ }
+ else
+ {
+ SDL_JoystickClose(joy);
+ }
}
+ }
+ else
+ {
+ dispatch_joystick_event(event);
+ }
+ }
+};
- // loop over all axes
- for (int axis = 0; axis < SDL_JoystickNumAxes(joy); axis++)
- {
- input_item_id itemid;
- if (axis < INPUT_MAX_AXIS)
- itemid = (input_item_id)(ITEM_ID_XAXIS + axis);
- else if (axis < INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE)
- itemid = (input_item_id)(ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis);
- else
- itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE;
+//============================================================
+// sdl_game_controller_module
+//============================================================
- snprintf(tempname, sizeof(tempname), "A%d %s", axis, devinfo->name());
- devinfo->device()->add_item(tempname, itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.axes[axis]);
- }
+class sdl_game_controller_module : public sdl_joystick_module_base
+{
+public:
+ sdl_game_controller_module() :
+ sdl_joystick_module_base("sdlgame"),
+ m_initialized_game_controller(false)
+ {
+ }
- // loop over all buttons
- for (int button = 0; button < SDL_JoystickNumButtons(joy); button++)
- {
- input_item_id itemid;
+ virtual void exit() override
+ {
+ sdl_joystick_module_base::exit();
- devinfo->joystick.buttons[button] = 0;
+ if (m_initialized_game_controller)
+ SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
- if (button < INPUT_MAX_BUTTONS)
- itemid = (input_item_id)(ITEM_ID_BUTTON1 + button);
- else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH)
- itemid = (input_item_id)(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + button);
- else
- itemid = ITEM_ID_OTHER_SWITCH;
+ quit_joystick();
+ }
- snprintf(tempname, sizeof(tempname), "button %d", button);
- devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.buttons[button]);
- }
+ virtual void input_init(running_machine &machine) override
+ {
+ auto &sdlopts = dynamic_cast<sdl_options const &>(*options());
+ bool const sixaxis_mode = sdlopts.sixaxis();
- // loop over all hats
- for (int hat = 0; hat < SDL_JoystickNumHats(joy); hat++)
- {
- input_item_id itemid;
-
- snprintf(tempname, sizeof(tempname), "hat %d Up", hat);
- itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + 4 * hat : ITEM_ID_OTHER_SWITCH);
- devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsU[hat]);
- snprintf(tempname, sizeof(tempname), "hat %d Down", hat);
- itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + 4 * hat : ITEM_ID_OTHER_SWITCH);
- devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsD[hat]);
- snprintf(tempname, sizeof(tempname), "hat %d Left", hat);
- itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + 4 * hat : ITEM_ID_OTHER_SWITCH);
- devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsL[hat]);
- snprintf(tempname, sizeof(tempname), "hat %d Right", hat);
- itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + 4 * hat : ITEM_ID_OTHER_SWITCH);
- devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsR[hat]);
- }
+ if (!sdlopts.debug() && sdlopts.background_input())
+ SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
+ SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
- // loop over all (track)balls
- for (int ball = 0; ball < SDL_JoystickNumBalls(joy); ball++)
- {
- int itemid;
+ init_joystick();
+ if (!have_joystick())
+ return;
- if (ball * 2 < INPUT_MAX_ADD_RELATIVE)
- itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2;
+ m_initialized_game_controller = !SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
+ if (m_initialized_game_controller)
+ {
+ char const *const mapfile = sdlopts.controller_mapping_file();
+ if (mapfile && *mapfile && std::strcmp(mapfile, OSDOPTVAL_NONE))
+ {
+ auto const count = SDL_GameControllerAddMappingsFromFile(mapfile);
+ if (0 <= count)
+ osd_printf_verbose("Game Controller: %d controller mapping(s) added from file [%s].\n", count, mapfile);
else
- itemid = ITEM_ID_OTHER_AXIS_RELATIVE;
-
- snprintf(tempname, sizeof(tempname), "R%d %s", ball * 2, devinfo->name());
- devinfo->device()->add_item(tempname, (input_item_id)itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2]);
- snprintf(tempname, sizeof(tempname), "R%d %s", ball * 2 + 1, devinfo->name());
- devinfo->device()->add_item(tempname, (input_item_id)(itemid + 1), generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2 + 1]);
+ osd_printf_error("Game Controller: Error adding mappings from file [%s]: %s.\n", mapfile, SDL_GetError());
}
}
+ else
+ {
+ osd_printf_warning("Could not initialize SDL Game Controller: %s.\n", SDL_GetError());
+ }
- static int event_types[] = {
- static_cast<int>(SDL_JOYAXISMOTION),
- static_cast<int>(SDL_JOYBALLMOTION),
- static_cast<int>(SDL_JOYHATMOTION),
- static_cast<int>(SDL_JOYBUTTONDOWN),
- static_cast<int>(SDL_JOYBUTTONUP)
- };
+ sdl_joystick_module_base::input_init(machine);
- sdl_event_manager::instance().subscribe(event_types, ARRAY_LENGTH(event_types), this);
+ osd_printf_verbose("Game Controller: Start initialization\n");
+ for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++)
+ {
+ // try to open as a game controller
+ SDL_GameController *ctrl = nullptr;
+ if (m_initialized_game_controller && SDL_IsGameController(physical_stick))
+ {
+ ctrl = SDL_GameControllerOpen(physical_stick);
+ if (!ctrl)
+ osd_printf_warning("Game Controller: Could not open SDL game controller %d: %s.\n", physical_stick, SDL_GetError());
+ }
- osd_printf_verbose("Joystick: End initialization\n");
+ // fall back to joystick API if necessary
+ if (!ctrl)
+ create_joystick_device(physical_stick, sixaxis_mode);
+ else
+ create_game_controller_device(physical_stick, ctrl);
+ }
+
+ constexpr int joy_event_types[] = {
+ int(SDL_JOYAXISMOTION),
+ int(SDL_JOYBALLMOTION),
+ int(SDL_JOYHATMOTION),
+ int(SDL_JOYBUTTONDOWN),
+ int(SDL_JOYBUTTONUP),
+ int(SDL_JOYDEVICEADDED),
+ int(SDL_JOYDEVICEREMOVED) };
+ constexpr int event_types[] = {
+ int(SDL_JOYAXISMOTION),
+ int(SDL_JOYBALLMOTION),
+ int(SDL_JOYHATMOTION),
+ int(SDL_JOYBUTTONDOWN),
+ int(SDL_JOYBUTTONUP),
+ int(SDL_JOYDEVICEADDED),
+ int(SDL_JOYDEVICEREMOVED),
+ int(SDL_CONTROLLERAXISMOTION),
+ int(SDL_CONTROLLERBUTTONDOWN),
+ int(SDL_CONTROLLERBUTTONUP),
+ int(SDL_CONTROLLERDEVICEADDED),
+ int(SDL_CONTROLLERDEVICEREMOVED) };
+ if (m_initialized_game_controller)
+ subscribe(osd(), event_types);
+ else
+ subscribe(osd(), joy_event_types);
+
+ osd_printf_verbose("Game Controller: End initialization\n");
}
- virtual void handle_event(SDL_Event &sdlevent) override
+ virtual void handle_event(SDL_Event const &event) override
{
- // Figure out which joystick this event id destined for
- auto target_device = std::find_if(devicelist()->begin(), devicelist()->end(), [&sdlevent](auto &device)
+ switch (event.type)
{
- std::unique_ptr<device_info> &ptr = device;
- return downcast<sdl_joystick_device*>(ptr.get())->sdl_state.joystick_id == sdlevent.jdevice.which;
- });
+ case SDL_JOYDEVICEADDED:
+ {
+ // make sure this isn't an event for a reconnected game controller
+ auto const controller = find_joystick(SDL_JoystickGetDeviceInstanceID(event.jdevice.which));
+ if (find_joystick(SDL_JoystickGetDeviceInstanceID(event.jdevice.which)))
+ {
+ osd_printf_verbose(
+ "Game Controller: Got SDL joystick added event for reconnected game controller %s [ID %s]\n",
+ controller->name(),
+ controller->id());
+ break;
+ }
- // If we find a matching joystick, dispatch the event to the joystick
- if (target_device != devicelist()->end())
- {
- downcast<sdl_joystick_device*>((*target_device).get())->queue_events(&sdlevent, 1);
+ SDL_Joystick *const joy = SDL_JoystickOpen(event.jdevice.which);
+ if (!joy)
+ {
+ osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", event.jdevice.which, SDL_GetError());
+ break;
+ }
+
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
+ char const *const serial = SDL_JoystickGetSerial(joy);
+ auto *const target_device = find_reconnect_match(guid, serial);
+ if (target_device)
+ {
+ // if this downcast fails, opening as a game controller worked initially but failed on reconnection
+ auto *const devinfo = dynamic_cast<sdl_joystick_device *>(target_device);
+ if (devinfo)
+ devinfo->attach_device(joy);
+ else
+ SDL_JoystickClose(joy);
+ }
+ else
+ {
+ SDL_JoystickClose(joy);
+ }
+ }
+ break;
+
+ // for devices supported by the game controller API, this is received before the corresponding SDL_JOYDEVICEADDED
+ case SDL_CONTROLLERDEVICEADDED:
+ if (m_initialized_game_controller)
+ {
+ SDL_GameController *const ctrl = SDL_GameControllerOpen(event.cdevice.which);
+ if (!ctrl)
+ {
+ osd_printf_error("Game Controller: Could not open SDL game controller %d: %s.\n", event.cdevice.which, SDL_GetError());
+ break;
+ }
+
+ SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(event.cdevice.which);
+ char const *const serial = SDL_GameControllerGetSerial(ctrl);
+ auto *const target_device = find_reconnect_match(guid, serial);
+ if (target_device)
+ {
+ // downcast can fail if there was an error opening the device as a game controller the first time
+ auto *const devinfo = dynamic_cast<sdl_game_controller_device *>(target_device);
+ if (devinfo)
+ devinfo->attach_device(ctrl);
+ else
+ SDL_GameControllerClose(ctrl);
+ }
+ else
+ {
+ SDL_GameControllerClose(ctrl);
+ }
+ }
+ break;
+
+ default:
+ dispatch_joystick_event(event);
}
}
private:
- sdl_joystick_device* create_joystick_device(running_machine &machine, device_map_t *devmap, int index, input_device_class devclass)
+ sdl_game_controller_device *create_game_controller_device(int index, SDL_GameController *ctrl)
{
- char tempname[20];
-
- if (devmap->map[index].name.empty())
+ // get basic info
+ char const *const name = SDL_GameControllerName(ctrl);
+ SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(index);
+ char guid_str[256];
+ guid_str[0] = '\0';
+ SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
+ char const *const serial = SDL_GameControllerGetSerial(ctrl);
+ std::string id(guid_str);
+ if (serial)
+ id.append(1, '-').append(serial);
+
+ // print some diagnostic info
+ osd_printf_verbose("Game Controller: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n",
+ name ? name : "<nullptr>",
+ guid_str,
+ SDL_GameControllerGetVendor(ctrl),
+ SDL_GameControllerGetProduct(ctrl),
+ SDL_GameControllerGetProductVersion(ctrl),
+ serial ? serial : "<nullptr>");
+ char *const mapping = SDL_GameControllerMapping(ctrl);
+ if (mapping)
{
- // only map place holders if there were mappings specified
- if (devmap->initialized)
- {
- snprintf(tempname, ARRAY_LENGTH(tempname), "NC%d", index);
- m_sixaxis_mode
- ? devicelist()->create_device<sdl_sixaxis_joystick_device>(machine, tempname, tempname, *this)
- : devicelist()->create_device<sdl_joystick_device>(machine, tempname, tempname, *this);
- }
-
- return nullptr;
+ osd_printf_verbose("Game Controller: ... mapping [%s]\n", mapping);
+ SDL_free(mapping);
+ }
+ else
+ {
+ osd_printf_verbose("Game Controller: ... no mapping\n");
}
- return m_sixaxis_mode
- ? devicelist()->create_device<sdl_sixaxis_joystick_device>(machine, devmap->map[index].name.c_str(), devmap->map[index].name.c_str(), *this)
- : devicelist()->create_device<sdl_joystick_device>(machine, devmap->map[index].name.c_str(), devmap->map[index].name.c_str(), *this);
+ // instantiate device
+ sdl_game_controller_device &devinfo = create_device<sdl_game_controller_device>(
+ DEVICE_CLASS_JOYSTICK,
+ name ? name : guid_str,
+ guid_str,
+ ctrl,
+ serial);
+ return &devinfo;
}
+
+ bool m_initialized_game_controller;
};
-#else
+} // anonymous namespace
+
+} // namespace osd
+
+
+#else // defined(SDLMAME_SDL2)
+
+namespace osd {
+
+namespace {
+
MODULE_NOT_SUPPORTED(sdl_keyboard_module, OSD_KEYBOARDINPUT_PROVIDER, "sdl")
MODULE_NOT_SUPPORTED(sdl_mouse_module, OSD_MOUSEINPUT_PROVIDER, "sdl")
-MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdl")
-#endif
+MODULE_NOT_SUPPORTED(sdl_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "sdl")
+MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdljoy")
+MODULE_NOT_SUPPORTED(sdl_game_controller_module, OSD_JOYSTICKINPUT_PROVIDER, "sdlgame")
+
+} // anonymous namespace
+
+} // namespace osd
+
+#endif // defined(SDLMAME_SDL2)
+
-MODULE_DEFINITION(KEYBOARDINPUT_SDL, sdl_keyboard_module)
-MODULE_DEFINITION(MOUSEINPUT_SDL, sdl_mouse_module)
-MODULE_DEFINITION(JOYSTICKINPUT_SDL, sdl_joystick_module)
+MODULE_DEFINITION(KEYBOARDINPUT_SDL, osd::sdl_keyboard_module)
+MODULE_DEFINITION(MOUSEINPUT_SDL, osd::sdl_mouse_module)
+MODULE_DEFINITION(LIGHTGUNINPUT_SDL, osd::sdl_lightgun_module)
+MODULE_DEFINITION(JOYSTICKINPUT_SDLJOY, osd::sdl_joystick_module)
+MODULE_DEFINITION(JOYSTICKINPUT_SDLGAME, osd::sdl_game_controller_module)
diff --git a/src/osd/modules/input/input_sdlcommon.cpp b/src/osd/modules/input/input_sdlcommon.cpp
deleted file mode 100644
index a5982ea891d..00000000000
--- a/src/osd/modules/input/input_sdlcommon.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes
-//============================================================
-//
-// input_sdlcommon.cpp - SDL Common code shared by SDL modules
-//
-// Note: this code is also used by the X11 input modules
-//
-//============================================================
-
-#include "input_module.h"
-#include "modules/osdmodule.h"
-
-#if defined(OSD_SDL)
-
-// standard sdl header
-#include <SDL2/SDL.h>
-#include <ctype.h>
-#include <stddef.h>
-#include <mutex>
-#include <memory>
-#include <algorithm>
-
-// MAME headers
-#include "emu.h"
-#include "osdepend.h"
-#include "ui/uimain.h"
-#include "uiinput.h"
-#include "window.h"
-#include "strconv.h"
-
-#include "../../sdl/osdsdl.h"
-#include "input_common.h"
-#include "input_sdlcommon.h"
-
-#define GET_WINDOW(ev) window_from_id((ev)->windowID)
-//#define GET_WINDOW(ev) ((ev)->windowID)
-
-static std::shared_ptr<sdl_window_info> window_from_id(Uint32 windowID)
-{
- SDL_Window *sdl_window = SDL_GetWindowFromID(windowID);
-
- auto& windows = osd_common_t::s_window_list;
- auto window = std::find_if(windows.begin(), windows.end(), [sdl_window](std::shared_ptr<osd_window> w)
- {
- return std::static_pointer_cast<sdl_window_info>(w)->platform_window() == sdl_window;
- });
-
- if (window == windows.end())
- return nullptr;
-
- return std::static_pointer_cast<sdl_window_info>(*window);
-}
-
-void sdl_event_manager::process_events(running_machine &machine)
-{
- std::lock_guard<std::mutex> scope_lock(m_lock);
- SDL_Event sdlevent;
- while (SDL_PollEvent(&sdlevent))
- {
- // process window events if they come in
- if (sdlevent.type == SDL_WINDOWEVENT)
- process_window_event(machine, sdlevent);
-
- // Find all subscribers for the event type
- auto subscribers = m_subscription_index.equal_range(sdlevent.type);
-
- // Dispatch the events
- std::for_each(subscribers.first, subscribers.second, [&sdlevent](auto sub)
- {
- sub.second->handle_event(sdlevent);
- });
- }
-}
-
-void sdl_event_manager::process_window_event(running_machine &machine, SDL_Event &sdlevent)
-{
- std::shared_ptr<sdl_window_info> window = GET_WINDOW(&sdlevent.window);
-
- if (window == nullptr)
- {
- // This condition may occur when the fullscreen toggle is used
- osd_printf_verbose("Skipped window event due to missing window param from SDL\n");
- return;
- }
-
- switch (sdlevent.window.event)
- {
- case SDL_WINDOWEVENT_SHOWN:
- m_has_focus = true;
- break;
-
- case SDL_WINDOWEVENT_CLOSE:
- machine.schedule_exit();
- break;
-
- case SDL_WINDOWEVENT_LEAVE:
- machine.ui_input().push_mouse_leave_event(window->target());
- m_mouse_over_window = 0;
- break;
-
- case SDL_WINDOWEVENT_MOVED:
- window->notify_changed();
- m_focus_window = window;
- m_has_focus = true;
- break;
-
- case SDL_WINDOWEVENT_RESIZED:
-#ifdef SDLMAME_LINUX
- /* FIXME: SDL2 sends some spurious resize events on Ubuntu
- * while in fullscreen mode. Ignore them for now.
- */
- if (!window->fullscreen())
-#endif
- {
- //printf("event data1,data2 %d x %d %ld\n", event.window.data1, event.window.data2, sizeof(SDL_Event));
- window->resize(sdlevent.window.data1, sdlevent.window.data2);
- }
- m_focus_window = window;
- m_has_focus = true;
- break;
-
- case SDL_WINDOWEVENT_ENTER:
- m_mouse_over_window = 1;
- /* fall through */
- case SDL_WINDOWEVENT_FOCUS_GAINED:
- case SDL_WINDOWEVENT_EXPOSED:
- case SDL_WINDOWEVENT_MAXIMIZED:
- case SDL_WINDOWEVENT_RESTORED:
- m_focus_window = window;
- m_has_focus = true;
- break;
-
- case SDL_WINDOWEVENT_MINIMIZED:
- case SDL_WINDOWEVENT_FOCUS_LOST:
- m_has_focus = false;
- break;
- }
-}
-
-//============================================================
-// customize_input_type_list
-//============================================================
-
-void sdl_osd_interface::customize_input_type_list(simple_list<input_type_entry> &typelist)
-{
- input_item_id mameid_code;
- input_code ui_code;
- const char* uimode;
- char fullmode[64];
-
- // loop over the defaults
- for (input_type_entry &entry : typelist)
- {
- switch (entry.type())
- {
- // configurable UI mode switch
- case IPT_UI_TOGGLE_UI:
- uimode = options().ui_mode_key();
- if (!strcmp(uimode, "auto"))
- {
-#if defined(__APPLE__) && defined(__MACH__)
- mameid_code = keyboard_trans_table::instance().lookup_mame_code("ITEM_ID_INSERT");
-#else
- mameid_code = keyboard_trans_table::instance().lookup_mame_code("ITEM_ID_SCRLOCK");
-#endif
- }
- else
- {
- snprintf(fullmode, 63, "ITEM_ID_%s", uimode);
- mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode);
- }
- ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code));
- entry.defseq(SEQ_TYPE_STANDARD).set(ui_code);
- break;
- // alt-enter for fullscreen
- case IPT_OSD_1:
- entry.configure_osd("TOGGLE_FULLSCREEN", "Toggle Fullscreen");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_ENTER, KEYCODE_LALT);
- break;
-
- // disable UI_SELECT when LALT is down, this stops selecting
- // things in the menu when toggling fullscreen with LALT+ENTER
- /* case IPT_UI_SELECT:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_ENTER, input_seq::not_code, KEYCODE_LALT);
- break;*/
-
- // page down for fastforward (must be OSD_3 as per src/emu/ui.c)
- case IPT_UI_FAST_FORWARD:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_PGDN);
- break;
-
- // OSD hotkeys use LCTRL and start at F3, they start at
- // F3 because F1-F2 are hardcoded into many drivers to
- // various dipswitches, and pressing them together with
- // LCTRL will still press/toggle these dipswitches.
-
- // LCTRL-F3 to toggle fullstretch
- case IPT_OSD_2:
- entry.configure_osd("TOGGLE_FULLSTRETCH", "Toggle Uneven stretch");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F3, KEYCODE_LCONTROL);
- break;
- // add a Not lcrtl condition to the reset key
- case IPT_UI_SOFT_RESET:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F3, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT);
- break;
-
- // LCTRL-F4 to toggle keep aspect
- case IPT_OSD_4:
- entry.configure_osd("TOGGLE_KEEP_ASPECT", "Toggle Keepaspect");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F4, KEYCODE_LCONTROL);
- break;
- // add a Not lcrtl condition to the show gfx key
- case IPT_UI_SHOW_GFX:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F4, input_seq::not_code, KEYCODE_LCONTROL);
- break;
-
- // LCTRL-F5 to toggle OpenGL filtering
- case IPT_OSD_5:
- entry.configure_osd("TOGGLE_FILTER", "Toggle Filter");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, KEYCODE_LCONTROL);
- break;
- // add a Not lcrtl condition to the toggle debug key
- case IPT_UI_TOGGLE_DEBUG:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, input_seq::not_code, KEYCODE_LCONTROL);
- break;
-
- // LCTRL-F6 to decrease OpenGL prescaling
- case IPT_OSD_6:
- entry.configure_osd("DECREASE_PRESCALE", "Decrease Prescaling");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, KEYCODE_LCONTROL);
- break;
- // add a Not lcrtl condition to the toggle cheat key
- case IPT_UI_TOGGLE_CHEAT:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, input_seq::not_code, KEYCODE_LCONTROL);
- break;
-
- // LCTRL-F7 to increase OpenGL prescaling
- case IPT_OSD_7:
- entry.configure_osd("INCREASE_PRESCALE", "Increase Prescaling");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, KEYCODE_LCONTROL);
- break;
-
- // lshift-lalt-F12 for fullscreen video (BGFX)
- case IPT_OSD_8:
- entry.configure_osd("RENDER_AVI", "Record Rendered Video");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LALT);
- break;
-
- // add a Not lcrtl condition to the load state key
- case IPT_UI_LOAD_STATE:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT);
- break;
-
- // add a Not lcrtl condition to the throttle key
- case IPT_UI_THROTTLE:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LCONTROL);
- break;
-
- // disable the config menu if the ALT key is down
- // (allows ALT-TAB to switch between apps)
- case IPT_UI_CONFIGURE:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT);
- break;
-
-#if defined(__APPLE__) && defined(__MACH__)
- // 78-key Apple MacBook & Bluetooth keyboards have no right control key
- case IPT_MAHJONG_SCORE:
- if (entry.player() == 0)
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_SLASH);
- break;
-#endif
-
- // leave everything else alone
- default:
- break;
- }
- }
-}
-
-void sdl_osd_interface::poll_inputs(running_machine &machine)
-{
- m_keyboard_input->poll_if_necessary(machine);
- m_mouse_input->poll_if_necessary(machine);
- m_lightgun_input->poll_if_necessary(machine);
- m_joystick_input->poll_if_necessary(machine);
-}
-
-void sdl_osd_interface::release_keys()
-{
- auto keybd = dynamic_cast<input_module_base*>(m_keyboard_input);
- if (keybd != nullptr)
- keybd->devicelist()->reset_devices();
-}
-
-bool sdl_osd_interface::should_hide_mouse()
-{
- // if we are paused, no
- if (machine().paused())
- return false;
-
- // if neither mice nor lightguns enabled in the core, then no
- if (!options().mouse() && !options().lightgun())
- return false;
-
- if (!sdl_event_manager::instance().mouse_over_window())
- return false;
-
- // otherwise, yes
- return true;
-}
-
-void sdl_osd_interface::process_events_buf()
-{
- SDL_PumpEvents();
-}
-
-#endif
diff --git a/src/osd/modules/input/input_sdlcommon.h b/src/osd/modules/input/input_sdlcommon.h
deleted file mode 100644
index 377b04d71c9..00000000000
--- a/src/osd/modules/input/input_sdlcommon.h
+++ /dev/null
@@ -1,204 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes
-//============================================================
-//
-// input_sdlcommon.h - SDL Common code shared by SDL modules
-//
-// Note: this code is also used by the X11 input modules
-//
-//============================================================
-
-#ifndef INPUT_SDLCOMMON_H_
-#define INPUT_SDLCOMMON_H_
-
-#include <unordered_map>
-#include <algorithm>
-
-#define MAX_DEVMAP_ENTRIES 16
-#define SDL_MODULE_EVENT_BUFFER_SIZE 5
-
-// state information for a keyboard
-struct keyboard_state
-{
- int32_t state[0x3ff]; // must be int32_t!
- int8_t oldkey[MAX_KEYS];
- int8_t currkey[MAX_KEYS];
-};
-
-// state information for a mouse
-struct mouse_state
-{
- int32_t lX, lY;
- int32_t buttons[MAX_BUTTONS];
-};
-
-
-// state information for a joystick; DirectInput state must be first element
-struct joystick_state
-{
- SDL_Joystick *device;
- int32_t axes[MAX_AXES];
- int32_t buttons[MAX_BUTTONS];
- int32_t hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS];
- int32_t balls[MAX_AXES];
-};
-
-struct device_map_t
-{
- struct {
- std::string name;
- int physical;
- } map[MAX_DEVMAP_ENTRIES];
- int logical[MAX_DEVMAP_ENTRIES];
- int initialized;
-};
-
-//============================================================
-// event_manager_t
-//============================================================
-
-class sdl_event_subscriber
-{
-public:
- virtual ~sdl_event_subscriber() {}
- virtual void handle_event(SDL_Event &sdlevent) = 0;
-};
-
-template <class TSubscriber>
-class event_manager_t
-{
-protected:
- std::mutex m_lock;
- std::unordered_multimap<int, TSubscriber*> m_subscription_index;
- event_manager_t()
- {
- }
-
-public:
- virtual ~event_manager_t()
- {
- }
-
- void subscribe(int* event_types, int num_event_types, TSubscriber *subscriber)
- {
- std::lock_guard<std::mutex> scope_lock(m_lock);
-
- // Add the subscription
- for (int i = 0; i < num_event_types; i++)
- {
- m_subscription_index.emplace(event_types[i], subscriber);
- }
- }
-
- void unsubscribe(TSubscriber *subscriber)
- {
- std::lock_guard<std::mutex> scope_lock(m_lock);
-
- // Remove the events that match the subscriber
- for (auto it = begin(m_subscription_index); it != end(m_subscription_index);)
- {
- if (it->second == subscriber)
- {
- it = m_subscription_index.erase(it);
- }
- else
- {
- ++it;
- }
- }
- }
-
- virtual void process_events(running_machine &machine) = 0;
-};
-
-class sdl_window_info;
-
-class sdl_event_manager : public event_manager_t<sdl_event_subscriber>
-{
-private:
- bool m_mouse_over_window;
- bool m_has_focus;
- std::shared_ptr<sdl_window_info> m_focus_window;
-
- sdl_event_manager()
- : m_mouse_over_window(true),
- m_has_focus(true),
- m_focus_window(nullptr)
- {
- }
-
-public:
- bool mouse_over_window() const { return m_mouse_over_window; }
- bool has_focus() const { return m_focus_window != nullptr; }
- std::shared_ptr<sdl_window_info> focus_window() const { return m_focus_window; }
-
- static sdl_event_manager& instance()
- {
- static sdl_event_manager s_instance;
- return s_instance;
- }
-
- void process_events(running_machine &machine) override;
-
-private:
- void process_window_event(running_machine &machine, SDL_Event &sdlevent);
-};
-
-//============================================================
-// INLINE FUNCTIONS
-//============================================================
-
-static inline std::string remove_spaces(const char *s)
-{
- // Remove the spaces
- auto output = std::string(s);
- output.erase(std::remove_if(output.begin(), output.end(), isspace), output.end());
-
- return output;
-}
-
-//============================================================
-// devmap_init - initializes a device_map based on
-// an input option prefix and max number of devices
-//============================================================
-
-static inline void devmap_init(running_machine &machine, device_map_t *devmap, const char *opt, int max_devices, const char *label)
-{
- int dev;
- char defname[20];
-
- // The max devices the user specified, better not be bigger than the max the arrays can old
- assert(max_devices <= MAX_DEVMAP_ENTRIES);
-
- // Initialize the map to default uninitialized values
- for (dev = 0; dev < MAX_DEVMAP_ENTRIES; dev++)
- {
- devmap->map[dev].name.clear();
- devmap->map[dev].physical = -1;
- devmap->logical[dev] = -1;
- }
- devmap->initialized = 0;
-
- // populate the device map up to the max number of devices
- for (dev = 0; dev < max_devices; dev++)
- {
- const char *dev_name;
-
- // derive the parameter name from the option name and index. For instance: lightgun_index1 to lightgun_index8
- sprintf(defname, "%s%d", opt, dev + 1);
-
- // Get the user-specified name that matches the parameter
- dev_name = machine.options().value(defname);
-
- // If they've specified a name and it's not "auto", treat it as a custom mapping
- if (dev_name && *dev_name && strcmp(dev_name, OSDOPTVAL_AUTO))
- {
- // remove the spaces from the name store it in the index
- devmap->map[dev].name = remove_spaces(dev_name);
- osd_printf_verbose("%s: Logical id %d: %s\n", label, dev + 1, devmap->map[dev].name.c_str());
- devmap->initialized = 1;
- }
- }
-}
-
-#endif
diff --git a/src/osd/modules/input/input_uwp.cpp b/src/osd/modules/input/input_uwp.cpp
deleted file mode 100644
index 6b3b8675cff..00000000000
--- a/src/osd/modules/input/input_uwp.cpp
+++ /dev/null
@@ -1,649 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Brad Hughes
-//============================================================
-//
-// input_uwp.cpp - UWP input implementation
-//
-//============================================================
-
-#include "input_module.h"
-#include "modules/osdmodule.h"
-
-#if defined(OSD_UWP)
-
-#include <agile.h>
-#include <ppltasks.h>
-#include <collection.h>
-#undef interface
-
-// MAME headers
-#include "emu.h"
-#include "uiinput.h"
-#include "strconv.h"
-
-// MAMEOS headers
-#include "winmain.h"
-#include "input_common.h"
-#include "input_windows.h"
-
-#define UWP_BUTTON_COUNT 32
-
-using namespace concurrency;
-using namespace Windows::UI::Core;
-using namespace Windows::Foundation;
-using namespace Windows::Foundation::Collections;
-using namespace Windows::Gaming::Input;
-
-//============================================================
-// UWP Base device/module implementation
-//============================================================
-
-//============================================================
-// UwpInputDevice - base class for implementing an input
-// device in C++/CX. To be used with uwp_input_device
-//============================================================
-
-private ref class UwpInputDevice
-{
-private:
- running_machine & m_machine;
- std::string m_name;
- std::string m_id;
- input_device_class m_devclass;
- input_module & m_module;
- input_device *m_inputdevice;
-
-internal:
- UwpInputDevice(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
- : m_machine(machine),
- m_name(name),
- m_id(id),
- m_devclass(deviceclass),
- m_module(module),
- m_inputdevice(nullptr)
- {
- }
-
- property running_machine & Machine
- {
- running_machine & get() { return m_machine; }
- }
-
- property const std::string & Name
- {
- const std::string & get() { return m_name; }
- }
-
- property const std::string & Id
- {
- const std::string & get() { return m_id; }
- }
-
- property input_device_class DeviceClass
- {
- input_device_class get() { return m_devclass; }
- }
-
- property input_module & Module
- {
- input_module & get() { return m_module; }
- }
-
- property input_device* InputDevice
- {
- input_device* get() { return m_inputdevice; }
- void set(input_device* value) { m_inputdevice = value; }
- }
-
- virtual void Poll()
- {
- }
-
- virtual void Reset()
- {
- }
-};
-
-//============================================================
-// uwp_input_device - a device that can be used to wrap a
-// C++/CX ref class for an input device implementation
-//============================================================
-
-class uwp_input_device : public device_info
-{
-private:
- UwpInputDevice ^m_wrapped_device;
-
-public:
- uwp_input_device(UwpInputDevice ^device)
- : device_info(device->Machine, device->Name.c_str(), device->Id.c_str(), device->DeviceClass, device->Module),
- m_wrapped_device(device)
- {
- }
-
- void poll() override
- {
- m_wrapped_device->Poll();
- }
-
- void reset() override
- {
- m_wrapped_device->Reset();
- }
-};
-
-//============================================================
-// UwpInputModule - a base class that can be used to
-// implement an input module with a C++/CX class.
-// normally used with uwp_wininput_module
-//============================================================
-
-class uwp_input_module;
-
-private ref class UwpInputModule
-{
-private:
- const std::string m_type;
- const std::string m_name;
- uwp_input_module *m_module;
-
-internal:
- UwpInputModule(const char *type, const char *name)
- : m_type(type),
- m_name(name),
- m_module(nullptr)
- {
- }
-
- property const std::string & Type
- {
- const std::string & get() { return m_type; }
- }
-
- property const std::string & Name
- {
- const std::string & get() { return m_name; }
- }
-
- property uwp_input_module * NativeModule
- {
- uwp_input_module * get() { return m_module; }
- void set(uwp_input_module * value) { m_module = value; }
- }
-
- virtual void input_init(running_machine &machine)
- {
- }
-};
-
-//============================================================
-// uwp_input_module - an input module that can be
-// used to create an input module with a C++/CX ref class
-//============================================================
-
-class uwp_input_module : public wininput_module
-{
-private:
- UwpInputModule^ m_refmodule;
-
-public:
- uwp_input_module(UwpInputModule^ refmodule)
- : wininput_module(refmodule->Type.c_str(), refmodule->Name.c_str()),
- m_refmodule(refmodule)
- {
- refmodule->NativeModule = this;
- }
-
- void input_init(running_machine &machine) override
- {
- m_refmodule->input_init(machine);
- }
-};
-
-//============================================================
-// UWP Keyboard Implementation
-//============================================================
-
-//============================================================
-// UwpKeyboardDevice
-//============================================================
-
-private ref class UwpKeyboardDevice : public UwpInputDevice
-{
-private:
- keyboard_state keyboard;
- Platform::Agile<CoreWindow> m_coreWindow;
- std::mutex m_state_lock;
-
-internal:
- UwpKeyboardDevice(Platform::Agile<CoreWindow> coreWindow, running_machine& machine, char *name, const char *id, input_module &module)
- : UwpInputDevice(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
- keyboard({{0}}),
- m_coreWindow(coreWindow)
- {
- coreWindow->Dispatcher->AcceleratorKeyActivated += ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(this, &UwpKeyboardDevice::OnAcceleratorKeyActivated);
- coreWindow->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &UwpKeyboardDevice::OnKeyDown);
- coreWindow->KeyUp += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &UwpKeyboardDevice::OnKeyUp);
- coreWindow->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &UwpKeyboardDevice::OnCharacterReceived);
- }
-
- void Reset() override
- {
- std::lock_guard<std::mutex> scope_lock(m_state_lock);
- memset(&keyboard, 0, sizeof(keyboard));
- }
-
- void Configure()
- {
- keyboard_trans_table &table = keyboard_trans_table::instance();
-
- // populate it indexed by the scancode
- for (int keynum = KEY_UNKNOWN + 1; keynum < MAX_KEYS; keynum++)
- {
- input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
- const char *keyname = table.ui_label_for_mame_key(itemid);
-
- char temp[256];
- if (keyname == nullptr)
- {
- snprintf(temp, ARRAY_LENGTH(temp), "Scan%03d", keynum);
- keyname = temp;
- }
-
- // add the item to the device
- this->InputDevice->add_item(keyname, itemid, generic_button_get_state<std::uint8_t>, &keyboard.state[keynum]);
- }
- }
-
- void OnKeyDown(CoreWindow^ win, KeyEventArgs^ args)
- {
- std::lock_guard<std::mutex> scope_lock(m_state_lock);
- CorePhysicalKeyStatus status = args->KeyStatus;
- int discancode = (status.ScanCode & 0x7f) | (status.IsExtendedKey ? 0x80 : 0x00);
- keyboard.state[discancode] = 0x80;
- }
-
- void OnKeyUp(CoreWindow^ win, KeyEventArgs^ args)
- {
- std::lock_guard<std::mutex> scope_lock(m_state_lock);
- CorePhysicalKeyStatus status = args->KeyStatus;
- int discancode = (status.ScanCode & 0x7f) | (status.IsExtendedKey ? 0x80 : 0x00);
- keyboard.state[discancode] = 0;
- }
-
- void OnCharacterReceived(CoreWindow ^sender, CharacterReceivedEventArgs ^args)
- {
- this->Machine.ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), args->KeyCode);
- }
-
- void OnAcceleratorKeyActivated(CoreDispatcher ^sender, AcceleratorKeyEventArgs ^args)
- {
- std::lock_guard<std::mutex> scope_lock(m_state_lock);
- auto eventType = args->EventType;
- if (eventType == CoreAcceleratorKeyEventType::SystemKeyDown ||
- eventType == CoreAcceleratorKeyEventType::SystemKeyUp)
- {
- CorePhysicalKeyStatus status = args->KeyStatus;
- int discancode = (status.ScanCode & 0x7f) | (status.IsExtendedKey ? 0x80 : 0x00);
- keyboard.state[discancode] =
- eventType == CoreAcceleratorKeyEventType::SystemKeyDown ? 0x80 : 0;
- }
- }
-};
-
-//============================================================
-// UwpKeyboardModule
-//============================================================
-
-private ref class UwpKeyboardModule : public UwpInputModule
-{
-private:
- running_machine *m_machine;
-
-internal:
- UwpKeyboardModule()
- : UwpInputModule(OSD_KEYBOARDINPUT_PROVIDER, "uwp")
- {
- }
-
- void input_init(running_machine &machine) override
- {
- auto first_window = std::static_pointer_cast<uwp_window_info>(osd_common_t::s_window_list.front());
- auto coreWindow = first_window->platform_window();
-
- // allocate the UWP implementation of the device object
- UwpKeyboardDevice ^refdevice = ref new UwpKeyboardDevice(coreWindow, machine, "UWP Keyboard 1", "UWP Keyboard 1", *this->NativeModule);
-
- // Allocate the wrapper and add it to the list
- auto created_devinfo = std::make_unique<uwp_input_device>(refdevice);
- uwp_input_device *devinfo = NativeModule->devicelist()->add_device<uwp_input_device>(machine, std::move(created_devinfo));
-
- // Give the UWP implementation a handle to the input_device
- refdevice->InputDevice = devinfo->device();
-
- // Configure the device
- refdevice->Configure();
- }
-};
-
-//============================================================
-// uwp_keyboard_module
-//============================================================
-
-class uwp_keyboard_module : public uwp_input_module
-{
-public:
- uwp_keyboard_module()
- : uwp_input_module(ref new UwpKeyboardModule())
- {
- }
-};
-
-// default axis names
-static const char *const uwp_axis_name[] =
-{
- "LSX",
- "LSY",
- "RSX",
- "RSY"
-};
-
-static const input_item_id uwp_axis_ids[] =
-{
- ITEM_ID_XAXIS,
- ITEM_ID_YAXIS,
- ITEM_ID_RXAXIS,
- ITEM_ID_RYAXIS
-};
-
-struct gamepad_state
-{
- BYTE buttons[UWP_BUTTON_COUNT];
- LONG left_trigger;
- LONG right_trigger;
- LONG left_thumb_x;
- LONG left_thumb_y;
- LONG right_thumb_x;
- LONG right_thumb_y;
-};
-
-// Maps different UWP GameControllerButtonLabels to a halfway-sane input_item_id in many cases
-static input_item_id buttonlabel_to_itemid[] =
-{
- input_item_id::ITEM_ID_INVALID, // GameControllerButtonLabel::None
- input_item_id::ITEM_ID_SELECT, // GameControllerButtonLabel::XboxBack
- input_item_id::ITEM_ID_START, // GameControllerButtonLabel::XboxStart
- input_item_id::ITEM_ID_START, // GameControllerButtonLabel::XboxMenu
- input_item_id::ITEM_ID_SELECT, // GameControllerButtonLabel::XboxView
- input_item_id::ITEM_ID_HAT1UP, // GameControllerButtonLabel::XboxUp
- input_item_id::ITEM_ID_HAT1DOWN, // GameControllerButtonLabel::XboxDown
- input_item_id::ITEM_ID_HAT1LEFT, // GameControllerButtonLabel::XboxLeft
- input_item_id::ITEM_ID_HAT1RIGHT, // GameControllerButtonLabel::XboxRight
- input_item_id::ITEM_ID_BUTTON1, // GameControllerButtonLabel::XboxA
- input_item_id::ITEM_ID_BUTTON2, // GameControllerButtonLabel::XboxB
- input_item_id::ITEM_ID_BUTTON3, // GameControllerButtonLabel::XboxX
- input_item_id::ITEM_ID_BUTTON4, // GameControllerButtonLabel::XboxY
- input_item_id::ITEM_ID_BUTTON5, // GameControllerButtonLabel::XboxLeftBumper
- input_item_id::ITEM_ID_ZAXIS, // GameControllerButtonLabel::XboxLeftTrigger
- input_item_id::ITEM_ID_BUTTON7, // GameControllerButtonLabel::XboxLeftStickButton
- input_item_id::ITEM_ID_BUTTON6, // GameControllerButtonLabel::XboxRightBumper
- input_item_id::ITEM_ID_RZAXIS, // GameControllerButtonLabel::XboxRightTrigger
- input_item_id::ITEM_ID_BUTTON8, // GameControllerButtonLabel::XboxRightStickButton
- input_item_id::ITEM_ID_BUTTON9, // GameControllerButtonLabel::XboxPaddle1
- input_item_id::ITEM_ID_BUTTON10, // GameControllerButtonLabel::XboxPaddle2
- input_item_id::ITEM_ID_BUTTON11, // GameControllerButtonLabel::XboxPaddle3
- input_item_id::ITEM_ID_BUTTON12, // GameControllerButtonLabel::XboxPaddle4
- input_item_id::ITEM_ID_INVALID, // GameControllerButtonLabel_Mode
- input_item_id::ITEM_ID_SELECT, // GameControllerButtonLabel_Select
- input_item_id::ITEM_ID_START, // GameControllerButtonLabel_Menu
- input_item_id::ITEM_ID_SELECT, // GameControllerButtonLabel_View
- input_item_id::ITEM_ID_SELECT, // GameControllerButtonLabel_Back
- input_item_id::ITEM_ID_START, // GameControllerButtonLabel_Start
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Options
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Share
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Up
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Down
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Left
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Right
- input_item_id::ITEM_ID_BUTTON1, // GameControllerButtonLabel_LetterA
- input_item_id::ITEM_ID_BUTTON2, // GameControllerButtonLabel_LetterB
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_LetterC
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_LetterL
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_LetterR
- input_item_id::ITEM_ID_BUTTON3, // GameControllerButtonLabel_LetterX
- input_item_id::ITEM_ID_BUTTON4, // GameControllerButtonLabel_LetterY
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_LetterZ
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Cross
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Circle
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Square
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Triangle
- input_item_id::ITEM_ID_BUTTON5, // GameControllerButtonLabel_LeftBumper
- input_item_id::ITEM_ID_ZAXIS, // GameControllerButtonLabel_LeftTrigger
- input_item_id::ITEM_ID_BUTTON7, // GameControllerButtonLabel_LeftStickButton
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Left1
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Left2
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Left3
- input_item_id::ITEM_ID_BUTTON6, // GameControllerButtonLabel_RightBumper
- input_item_id::ITEM_ID_RZAXIS, // GameControllerButtonLabel_RightTrigger
- input_item_id::ITEM_ID_BUTTON8, // GameControllerButtonLabel_RightStickButton
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Right1
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Right2
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Right3
- input_item_id::ITEM_ID_BUTTON9, // GameControllerButtonLabel_Paddle1
- input_item_id::ITEM_ID_BUTTON10, // GameControllerButtonLabel_Paddle2
- input_item_id::ITEM_ID_BUTTON11, // GameControllerButtonLabel_Paddle3
- input_item_id::ITEM_ID_BUTTON12, // GameControllerButtonLabel_Paddle4
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Plus
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Minus
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_DownLeftArrow
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_DialLeft
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_DialRight
- input_item_id::ITEM_ID_OTHER_SWITCH, // GameControllerButtonLabel_Suspension
-};
-
-//============================================================
-// UwpJoystickDevice
-//============================================================
-
-private ref class UwpJoystickDevice : public UwpInputDevice
-{
-private:
- Gamepad ^m_pad;
- bool m_configured;
- gamepad_state state;
-
-internal:
- UwpJoystickDevice(Gamepad^ pad, running_machine &machine, const char *name, const char *id, input_module &module)
- : UwpInputDevice(machine, name, id, DEVICE_CLASS_JOYSTICK, module),
- m_pad(pad),
- m_configured(false),
- state({0})
- {}
-
- void Poll() override
- {
- // If the device hasn't been configured, don't poll
- if (!m_configured)
- return;
-
- GamepadReading reading = m_pad->GetCurrentReading();
-
- for (int butnum = 0; butnum < UWP_BUTTON_COUNT; butnum++)
- {
- GamepadButtons currentButton = GamepadButtons(1 << butnum);
- state.buttons[butnum] = (reading.Buttons & currentButton) != GamepadButtons::None ? 0xFF : 0;
- }
-
- // Now grab the axis values
- // Each of the thumbstick axis members is a signed value between -32768 and 32767 describing the position of the thumbstick
- // However, the Y axis values are inverted from what MAME expects, so negate the value
- state.left_thumb_x = normalize_absolute_axis(reading.LeftThumbstickX, -1, 1);
- state.left_thumb_y = -normalize_absolute_axis(reading.LeftThumbstickY, -1, 1);
- state.right_thumb_x = normalize_absolute_axis(reading.RightThumbstickX, -1, 1);
- state.right_thumb_y = -normalize_absolute_axis(reading.RightThumbstickY, -1, 1);
-
- // Get the trigger values
- state.left_trigger = normalize_absolute_axis(reading.LeftTrigger, 0.0, 1.0);
- state.right_trigger = normalize_absolute_axis(reading.RightTrigger, 0.0, 1.0);
-
- // For the UI, triggering UI_CONFIGURE is odd. It requires a EVENT_CHAR first
- static constexpr int menuhotkey = (int)(GamepadButtons::View | GamepadButtons::X);
- if (((int)reading.Buttons & menuhotkey) == menuhotkey)
- {
- ui_event uiev;
- memset(&uiev, 0, sizeof(uiev));
- uiev.event_type = ui_event::IME_CHAR;
- this->Machine.ui_input().push_event(uiev);
- }
- }
-
- void Reset() override
- {
- memset(&state, 0, sizeof(state));
- }
-
- void Configure()
- {
- // If the device has already been configured, don't do it again
- if (m_configured)
- return;
-
- GamepadReading r = m_pad->GetCurrentReading();
-
- // Add the axes
- for (int axisnum = 0; axisnum < 4; axisnum++)
- {
- this->InputDevice->add_item(
- uwp_axis_name[axisnum],
- uwp_axis_ids[axisnum],
- generic_axis_get_state<LONG>,
- &state.left_thumb_x + axisnum);
- }
-
- // populate the buttons
- for (int butnum = 0; butnum < UWP_BUTTON_COUNT; butnum++)
- {
- GamepadButtons button = GamepadButtons(1 << butnum);
- auto label = m_pad->GetButtonLabel(button);
- if (label != GameControllerButtonLabel::None)
- {
- std::string desc = osd::text::from_wstring(label.ToString()->Data());
- this->InputDevice->add_item(
- desc.c_str(),
- buttonlabel_to_itemid[static_cast<int>(label)],
- generic_button_get_state<BYTE>,
- &state.buttons[butnum]);
- }
- }
-
- this->InputDevice->add_item(
- "Left Trigger",
- ITEM_ID_ZAXIS,
- generic_axis_get_state<LONG>,
- &state.left_trigger);
-
- this->InputDevice->add_item(
- "Right Trigger",
- ITEM_ID_RZAXIS,
- generic_axis_get_state<LONG>,
- &state.right_trigger);
-
- m_configured = true;
- }
-};
-
-//============================================================
-// UwpJoystickModule
-//============================================================
-
-private ref class UwpJoystickModule : public UwpInputModule
-{
-private:
- boolean m_joysticks_discovered;
-
-internal:
- UwpJoystickModule()
- : UwpInputModule(OSD_JOYSTICKINPUT_PROVIDER, "uwp"),
- m_joysticks_discovered(false)
- {
- }
-
- void input_init(running_machine &machine) override
- {
- PerformGamepadDiscovery();
-
- auto pads = Gamepad::Gamepads;
-
- int padindex = 0;
- std::for_each(begin(pads), end(pads), [&](Gamepad^ pad)
- {
- uwp_input_device *devinfo;
-
- std::ostringstream namestream;
- namestream << "UWP Gamepad " << (padindex + 1);
-
- auto name = namestream.str();
-
- // allocate the UWP implementation of the device object
- UwpJoystickDevice ^refdevice = ref new UwpJoystickDevice(pad, machine, name.c_str(), name.c_str(), *this->NativeModule);
-
- // Allocate the wrapper and add it to the list
- auto created_devinfo = std::make_unique<uwp_input_device>(refdevice);
- devinfo = NativeModule->devicelist()->add_device<uwp_input_device>(machine, std::move(created_devinfo));
-
- // Give the UWP implementation a handle to the input_device
- refdevice->InputDevice = devinfo->device();
-
- // Configure the device
- refdevice->Configure();
-
- padindex++;
- });
- }
-
-private:
- void PerformGamepadDiscovery()
- {
- Gamepad::GamepadAdded += ref new EventHandler<Gamepad ^>(this, &UwpJoystickModule::OnGamepadAdded);
- auto start = std::chrono::system_clock::now();
-
- // We need to pause a bit and pump events so gamepads get discovered
- while (std::chrono::system_clock::now() - start < std::chrono::milliseconds(1000))
- CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
-
- m_joysticks_discovered = true;
- }
-
- void OnGamepadAdded(Platform::Object ^sender, Gamepad ^pad)
- {
- if (m_joysticks_discovered)
- {
- osd_printf_error("Input: UWP Compatible %s gamepad plugged in AFTER discovery complete!\n", pad->IsWireless ? "Wireless" : "Wired");
- }
- else
- {
- osd_printf_verbose("Input: UWP Compatible %s gamepad discovered.\n", pad->IsWireless ? "Wireless" : "Wired");
- }
- }
-};
-
-//============================================================
-// uwp_joystick_module
-//============================================================
-
-class uwp_joystick_module : public uwp_input_module
-{
-public:
- uwp_joystick_module()
- : uwp_input_module(ref new UwpJoystickModule())
- {
- }
-};
-
-#else
-MODULE_NOT_SUPPORTED(uwp_keyboard_module, OSD_KEYBOARDINPUT_PROVIDER, "uwp")
-MODULE_NOT_SUPPORTED(uwp_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "uwp")
-#endif
-
-MODULE_DEFINITION(KEYBOARDINPUT_UWP, uwp_keyboard_module)
-MODULE_DEFINITION(JOYSTICKINPUT_UWP, uwp_joystick_module)
diff --git a/src/osd/modules/input/input_win32.cpp b/src/osd/modules/input/input_win32.cpp
index c07a1c681d8..921c902ad31 100644
--- a/src/osd/modules/input/input_win32.cpp
+++ b/src/osd/modules/input/input_win32.cpp
@@ -7,25 +7,28 @@
//============================================================
#include "input_module.h"
-#include "modules/osdmodule.h"
#if defined(OSD_WINDOWS)
+#include "input_windows.h"
+
+#include "input_wincommon.h"
+
+// osd/windows
+#include "window.h"
+
+// emu
+#include "inpttype.h"
+
+#include "strconv.h"
+
// standard windows headers
-#include <windows.h>
#include <tchar.h>
-#undef interface
-// MAME headers
-#include "emu.h"
-#include "strconv.h"
-// MAMEOS headers
-#include "winmain.h"
-#include "window.h"
+namespace osd {
-#include "input_common.h"
-#include "input_windows.h"
+namespace {
//============================================================
// win32_keyboard_device
@@ -35,117 +38,118 @@
class win32_keyboard_device : public event_based_device<KeyPressEventArgs>
{
public:
- keyboard_state keyboard;
+ win32_keyboard_device(std::string &&name, std::string &&id, input_module &module) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_keyboard({{0}})
+ {
+ }
- win32_keyboard_device(running_machine& machine, const char *name, const char *id, input_module &module)
- : event_based_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
- keyboard({{0}})
+ virtual void reset() override
{
+ event_based_device::reset();
+ memset(&m_keyboard, 0, sizeof(m_keyboard));
}
- void reset() override
+ virtual void configure(input_device &device) override
{
- memset(&keyboard, 0, sizeof(keyboard));
+ keyboard_trans_table const &table = keyboard_trans_table::instance();
+
+ for (int keynum = 0; keynum < MAX_KEYS; keynum++)
+ {
+ input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
+ TCHAR keyname[100];
+
+ // generate the name
+ // FIXME: GetKeyNameText gives bogus names for media keys and various other things
+ // in many cases it ignores the "extended" bit and returns the key name corresponding to the scan code alone
+ if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, std::size(keyname)) == 0)
+ _sntprintf(keyname, std::size(keyname), TEXT("Scan%03d"), keynum);
+ std::string name = text::from_tstring(keyname);
+
+ // add the item to the device
+ device.add_item(
+ name,
+ util::string_format("SCAN%03d", keynum),
+ itemid,
+ generic_button_get_state<std::uint8_t>,
+ &m_keyboard.state[keynum]);
+ }
}
protected:
- void process_event(KeyPressEventArgs &args) override
+ virtual void process_event(KeyPressEventArgs const &args) override
{
- keyboard.state[args.scancode] = args.event_id == INPUT_EVENT_KEYDOWN ? 0x80 : 0x00;
+ m_keyboard.state[args.scancode] = args.event_id == INPUT_EVENT_KEYDOWN ? 0x80 : 0x00;
}
+
+private:
+ keyboard_state m_keyboard;
};
+
//============================================================
// keyboard_input_win32 - win32 keyboard input module
//============================================================
-class keyboard_input_win32 : public wininput_module
+class keyboard_input_win32 : public wininput_module<win32_keyboard_device>
{
-private:
-
public:
- keyboard_input_win32()
- : wininput_module(OSD_KEYBOARDINPUT_PROVIDER, "win32")
+ keyboard_input_win32() : wininput_module<win32_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "win32")
{
}
virtual void input_init(running_machine &machine) override
{
- // Add a single win32 keyboard device that we'll monitor using Win32
- win32_keyboard_device *devinfo = devicelist()->create_device<win32_keyboard_device>(machine, "Win32 Keyboard 1", "Win32 Keyboard 1", *this);
-
- keyboard_trans_table &table = keyboard_trans_table::instance();
-
- // populate it
- for (int keynum = 0; keynum < MAX_KEYS; keynum++)
- {
- input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
- TCHAR keyname[100];
-
- // generate the name
- if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
- _sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
- std::string name = osd::text::from_tstring(keyname);
+ wininput_module<win32_keyboard_device>::input_init(machine);
- // add the item to the device
- devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]);
- }
+ // Add a single win32 keyboard device that we'll monitor using Win32
+ create_device<win32_keyboard_device>(DEVICE_CLASS_KEYBOARD, "Win32 Keyboard 1", "Win32 Keyboard 1");
}
- bool handle_input_event(input_event eventid, void *eventdata) override
+ virtual bool handle_input_event(input_event eventid, void const *eventdata) override
{
- if (!input_enabled())
- return false;
-
- KeyPressEventArgs *args;
-
switch (eventid)
{
- case INPUT_EVENT_KEYDOWN:
- case INPUT_EVENT_KEYUP:
- args = static_cast<KeyPressEventArgs*>(eventdata);
- devicelist()->for_each_device([args](auto device)
- {
- auto keyboard = dynamic_cast<win32_keyboard_device*>(device);
- if (keyboard != nullptr)
- keyboard->queue_events(args, 1);
- });
-
- return true;
-
- default:
- return false;
+ case INPUT_EVENT_KEYDOWN:
+ case INPUT_EVENT_KEYUP:
+ devicelist().for_each_device(
+ [args = reinterpret_cast<KeyPressEventArgs const *>(eventdata)] (auto &device)
+ {
+ device.queue_events(args, 1);
+ });
+ return false; // we still want text input events to be generated
+
+ default:
+ return false;
}
}
};
+
//============================================================
// win32_mouse_device
//============================================================
-struct win32_mouse_state
-{
- POINT last_point;
-};
-
-class win32_mouse_device : public event_based_device<MouseButtonEventArgs>
+class win32_mouse_device : public event_based_device<MouseUpdateEventArgs>
{
public:
- mouse_state mouse;
- win32_mouse_state win32_mouse;
-
- win32_mouse_device(running_machine& machine, const char *name, const char *id, input_module &module)
- : event_based_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
- mouse({0}),
- win32_mouse({{0}})
+ win32_mouse_device(std::string &&name, std::string &&id, input_module &module) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_mouse({0}),
+ m_win32_mouse({{0}}),
+ m_vscroll(0),
+ m_hscroll(0)
{
}
- void poll() override
+ virtual void poll(bool relative_reset) override
{
- event_based_device::poll();
+ event_based_device::poll(relative_reset);
- CURSORINFO cursor_info = {0};
+ if (!relative_reset)
+ return;
+
+ CURSORINFO cursor_info = { 0 };
cursor_info.cbSize = sizeof(CURSORINFO);
GetCursorInfo(&cursor_info);
@@ -154,145 +158,228 @@ public:
if (!(cursor_info.flags & CURSOR_SHOWING))
{
// We measure the position change from the previously set center position
- mouse.lX = (cursor_info.ptScreenPos.x - win32_mouse.last_point.x) * INPUT_RELATIVE_PER_PIXEL;
- mouse.lY = (cursor_info.ptScreenPos.y - win32_mouse.last_point.y) * INPUT_RELATIVE_PER_PIXEL;
+ m_mouse.lX = (cursor_info.ptScreenPos.x - m_win32_mouse.last_point.x) * input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lY = (cursor_info.ptScreenPos.y - m_win32_mouse.last_point.y) * input_device::RELATIVE_PER_PIXEL;
RECT window_pos = {0};
- GetWindowRect(std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(), &window_pos);
+ GetWindowRect(
+ dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(),
+ &window_pos);
// We reset the cursor position to the middle of the window each frame
- win32_mouse.last_point.x = window_pos.left + (window_pos.right - window_pos.left) / 2;
- win32_mouse.last_point.y = window_pos.top + (window_pos.bottom - window_pos.top) / 2;
+ m_win32_mouse.last_point.x = window_pos.left + (window_pos.right - window_pos.left) / 2;
+ m_win32_mouse.last_point.y = window_pos.top + (window_pos.bottom - window_pos.top) / 2;
- SetCursorPos(win32_mouse.last_point.x, win32_mouse.last_point.y);
+ SetCursorPos(m_win32_mouse.last_point.x, m_win32_mouse.last_point.y);
}
+
+ // update scroll axes
+ m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL;
}
- void reset() override
+ virtual void configure(input_device &device) override
{
- memset(&mouse, 0, sizeof(mouse));
- memset(&win32_mouse, 0, sizeof(win32_mouse));
+ // populate the axes
+ device.add_item(
+ "X",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX);
+ device.add_item(
+ "Y",
+ std::string_view(),
+ ITEM_ID_YAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lY);
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_RZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
+
+ // populate the buttons
+ for (int butnum = 0; butnum < 5; butnum++)
+ {
+ device.add_item(
+ default_button_name(butnum),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_mouse.rgbButtons[butnum]);
+ }
+ }
+
+ virtual void reset() override
+ {
+ event_based_device::reset();
+ memset(&m_mouse, 0, sizeof(m_mouse));
+ memset(&m_win32_mouse, 0, sizeof(m_win32_mouse));
+ m_vscroll = m_hscroll = 0;
}
protected:
- void process_event(MouseButtonEventArgs &args) override
+ virtual void process_event(MouseUpdateEventArgs const &args) override
{
// set the button state
- mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 5 > i; ++i)
+ {
+ if (BIT(args.pressed, i))
+ m_mouse.rgbButtons[i] = 0x80;
+ else if (BIT(args.released, i))
+ m_mouse.rgbButtons[i] = 0x00;
+ }
- // Make sure we have a fresh mouse position on button down
- if (args.keydown)
- module().poll_if_necessary(machine());
+ // accumulate scroll delta
+ m_vscroll += args.vdelta;
+ m_hscroll += args.hdelta;
}
+
+private:
+ struct win32_mouse_state
+ {
+ POINT last_point;
+ };
+
+ mouse_state m_mouse;
+ win32_mouse_state m_win32_mouse;
+ long m_vscroll, m_hscroll;
};
+
//============================================================
// mouse_input_win32 - win32 mouse input module
//============================================================
-class mouse_input_win32 : public wininput_module
+class mouse_input_win32 : public wininput_module<win32_mouse_device>
{
public:
- mouse_input_win32()
- : wininput_module(OSD_MOUSEINPUT_PROVIDER, "win32")
+ mouse_input_win32() : wininput_module<win32_mouse_device>(OSD_MOUSEINPUT_PROVIDER, "win32")
{
}
virtual void input_init(running_machine &machine) override
{
- win32_mouse_device *devinfo;
- int axisnum, butnum;
+ wininput_module<win32_mouse_device>::input_init(machine);
- if (!input_enabled() || !mouse_enabled())
+ if (!options()->mouse())
return;
// allocate a device
- devinfo = devicelist()->create_device<win32_mouse_device>(machine, "Win32 Mouse 1", "Win32 Mouse 1", *this);
- if (devinfo == nullptr)
- return;
+ create_device<win32_mouse_device>(DEVICE_CLASS_MOUSE, "Win32 Mouse 1", "Win32 Mouse 1");
+ }
- // populate the axes
- for (axisnum = 0; axisnum < 2; axisnum++)
+ virtual bool handle_input_event(input_event eventid, void const *eventdata) override
+ {
+ if (manager().class_enabled(DEVICE_CLASS_MOUSE))
{
- devinfo->device()->add_item(
- default_axis_name[axisnum],
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &devinfo->mouse.lX + axisnum);
+ if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL))
+ {
+ auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata);
+ devicelist().for_each_device(
+ [args] (auto &device) { device.queue_events(args, 1); });
+ return true;
+ }
}
- // populate the buttons
- for (butnum = 0; butnum < 2; butnum++)
- {
- devinfo->device()->add_item(
- default_button_name(butnum),
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &devinfo->mouse.rgbButtons[butnum]);
- }
+ return false;
}
+};
+
+
+//============================================================
+// win32_lightgun_device_base
+//============================================================
- bool handle_input_event(input_event eventid, void *eventdata) override
+class win32_lightgun_device_base : public event_based_device<MouseUpdateEventArgs>
+{
+public:
+ virtual void reset() override
{
- if (!input_enabled() || !mouse_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON)
- return false;
+ event_based_device::reset();
+ memset(&m_mouse, 0, sizeof(m_mouse));
+ }
- auto args = static_cast<MouseButtonEventArgs*>(eventdata);
- devicelist()->for_each_device([args](auto device)
+protected:
+ win32_lightgun_device_base(
+ std::string &&name,
+ std::string &&id,
+ input_module &module) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_mouse({ 0 })
+ {
+ }
+
+ void do_configure(input_device &device, unsigned buttons)
+ {
+ // populate the axes
+ for (int axisnum = 0; axisnum < 2; axisnum++)
{
- auto mouse = dynamic_cast<win32_mouse_device*>(device);
- if (mouse != nullptr)
- mouse->queue_events(args, 1);
- });
+ device.add_item(
+ default_axis_name[axisnum],
+ std::string_view(),
+ input_item_id(ITEM_ID_XAXIS + axisnum),
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX + axisnum);
+ }
- return true;
+ // populate the buttons
+ for (int butnum = 0; butnum < buttons; butnum++)
+ {
+ device.add_item(
+ default_button_name(butnum),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_mouse.rgbButtons[butnum]);
+ }
}
+
+ mouse_state m_mouse;
};
+
+
//============================================================
// win32_lightgun_device
//============================================================
-class win32_lightgun_device : public event_based_device<MouseButtonEventArgs>
+class win32_lightgun_device : public win32_lightgun_device_base
{
-private:
- BOOL m_lightgun_shared_axis_mode;
- int m_gun_index;
-
public:
- mouse_state mouse;
-
- win32_lightgun_device(running_machine& machine, const char *name, const char *id, input_module &module)
- : event_based_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
- m_lightgun_shared_axis_mode(FALSE),
- m_gun_index(0),
- mouse({0})
+ win32_lightgun_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module) :
+ win32_lightgun_device_base(std::move(name), std::move(id), module),
+ m_vscroll(0),
+ m_hscroll(0)
{
- m_lightgun_shared_axis_mode = downcast<windows_options &>(machine.options()).dual_lightgun();
-
- // Since we are about to be added to the list, the current size is the zero-based index of where we will be
- m_gun_index = downcast<wininput_module&>(module).devicelist()->size();
}
- void poll() override
+ virtual void poll(bool relative_reset) override
{
- event_based_device::poll();
+ event_based_device::poll(relative_reset);
int32_t xpos = 0, ypos = 0;
- POINT mousepos;
-
- // if we are using the shared axis hack, the data is updated via Windows messages only
- if (m_lightgun_shared_axis_mode)
- return;
// get the cursor position and transform into final results
+ POINT mousepos;
GetCursorPos(&mousepos);
- if (!osd_common_t::s_window_list.empty())
+ if (!osd_common_t::window_list().empty())
{
- RECT client_rect;
-
// get the position relative to the window
- HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window();
+ HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
+ RECT client_rect;
GetClientRect(hwnd, &client_rect);
ScreenToClient(hwnd, &mousepos);
@@ -302,149 +389,194 @@ public:
}
// update the X/Y positions
- mouse.lX = xpos;
- mouse.lY = ypos;
+ m_mouse.lX = xpos;
+ m_mouse.lY = ypos;
+
+ // update the scroll axes if appropriate
+ if (relative_reset)
+ {
+ m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ }
+ }
+
+ virtual void configure(input_device &device) override
+ {
+ do_configure(device, 5);
+
+ // add scroll axes
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE1,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
}
- void reset() override
+ virtual void reset() override
{
- memset(&mouse, 0, sizeof(mouse));
+ win32_lightgun_device_base::reset();
+ m_vscroll = m_hscroll = 0;
}
protected:
- void process_event(MouseButtonEventArgs &args) override
+ virtual void process_event(MouseUpdateEventArgs const &args) override
{
- // Are we in shared axis mode?
- if (m_lightgun_shared_axis_mode)
- {
- handle_shared_axis_mode(args);
- }
- else
+ // In non-shared axis mode, just update the button state
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 5 > i; ++i)
{
- // In non-shared axis mode, just update the button state
- mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
+ if (BIT(args.pressed, i))
+ m_mouse.rgbButtons[i] = 0x80;
+ else if (BIT(args.released, i))
+ m_mouse.rgbButtons[i] = 0x00;
}
+
+ // accumulate scroll delta
+ m_vscroll += args.vdelta;
+ m_hscroll += args.hdelta;
}
private:
- void handle_shared_axis_mode(MouseButtonEventArgs &args)
- {
- int button = args.button;
+ long m_vscroll, m_hscroll;
+};
- // We only handle the first four buttons in shared axis mode
- if (button > 3)
- return;
- // First gun doesn't handle buttons 2 & 3
- if (button >= 2 && m_gun_index == 0)
- return;
+//============================================================
+// win32_dual_lightgun_device
+//============================================================
- // Second gun doesn't handle buttons 0 & 1
- if (button < 2 && m_gun_index == 1)
- return;
+class win32_dual_lightgun_device : public win32_lightgun_device_base
+{
+public:
+ win32_dual_lightgun_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ int index) :
+ win32_lightgun_device_base(std::move(name), std::move(id), module),
+ m_gun_index(index)
+ {
+ }
- // Adjust the button if we're the second lightgun
- int logical_button = m_gun_index == 1 ? button - 2 : button;
+ virtual void configure(input_device &device) override
+ {
+ do_configure(device, 2);
+ }
- // set the button state
- mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00;
- if (args.keydown)
+protected:
+ virtual void process_event(MouseUpdateEventArgs const &args) override
+ {
+ // We only handle the first four buttons in shared axis mode
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 2 > i; ++i)
{
- RECT client_rect;
- POINT mousepos;
+ // Adjust the button if we're the second lightgun
+ unsigned const bit = i + ((1 == m_gun_index) ? 2 : 0);
+ if (BIT(args.pressed, bit))
+ {
+ m_mouse.rgbButtons[i] = 0x80;
- // get the position relative to the window
- HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window();
- GetClientRect(hwnd, &client_rect);
- mousepos.x = args.xpos;
- mousepos.y = args.ypos;
- ScreenToClient(hwnd, &mousepos);
+ // get the position relative to the window
+ HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
+ RECT client_rect;
+ GetClientRect(hwnd, &client_rect);
- // convert to absolute coordinates
- mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
- mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ POINT mousepos;
+ mousepos.x = args.xpos;
+ mousepos.y = args.ypos;
+ ScreenToClient(hwnd, &mousepos);
+
+ // convert to absolute coordinates
+ m_mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
+ m_mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ }
+ else if (BIT(args.released, bit))
+ {
+ m_mouse.rgbButtons[i] = 0x00;
+ }
}
}
+
+private:
+ int const m_gun_index;
};
+
//============================================================
// lightgun_input_win32 - win32 lightgun input module
//============================================================
-class lightgun_input_win32 : public wininput_module
+class lightgun_input_win32 : public wininput_module<win32_lightgun_device_base>
{
public:
- lightgun_input_win32()
- : wininput_module(OSD_LIGHTGUNINPUT_PROVIDER, "win32")
- {
- }
-
- int init_internal() override
+ lightgun_input_win32() : wininput_module<win32_lightgun_device_base>(OSD_LIGHTGUNINPUT_PROVIDER, "win32")
{
- return 0;
}
virtual void input_init(running_machine &machine) override
{
- int max_guns = downcast<windows_options&>(machine.options()).dual_lightgun() ? 2 : 1;
+ wininput_module<win32_lightgun_device_base>::input_init(machine);
+
+ bool const shared_axis_mode = dynamic_cast<windows_options const &>(*options()).dual_lightgun();
+ int const max_guns = shared_axis_mode ? 2 : 1;
// allocate the lightgun devices
for (int gunnum = 0; gunnum < max_guns; gunnum++)
{
static const char *const gun_names[] = { "Win32 Gun 1", "Win32 Gun 2" };
- win32_lightgun_device *devinfo;
- int axisnum, butnum;
// allocate a device
- devinfo = devicelist()->create_device<win32_lightgun_device>(machine, gun_names[gunnum], gun_names[gunnum], *this);
- if (devinfo == nullptr)
- break;
-
- // populate the axes
- for (axisnum = 0; axisnum < 2; axisnum++)
- {
- devinfo->device()->add_item(
- default_axis_name[axisnum],
- static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &devinfo->mouse.lX + axisnum);
- }
-
- // populate the buttons
- for (butnum = 0; butnum < 2; butnum++)
- {
- devinfo->device()->add_item(
- default_button_name(butnum),
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &devinfo->mouse.rgbButtons[butnum]);
- }
+ if (shared_axis_mode)
+ create_device<win32_dual_lightgun_device>(DEVICE_CLASS_LIGHTGUN, gun_names[gunnum], gun_names[gunnum], gunnum);
+ else
+ create_device<win32_lightgun_device>(DEVICE_CLASS_LIGHTGUN, gun_names[gunnum], gun_names[gunnum]);
}
}
- bool handle_input_event(input_event eventid, void* eventdata) override
+ virtual bool handle_input_event(input_event eventid, void const *eventdata) override
{
- if (!input_enabled() || !lightgun_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON)
- return false;
-
- auto args = static_cast<MouseButtonEventArgs*>(eventdata);
- devicelist()->for_each_device([args](auto device)
+ if (manager().class_enabled(DEVICE_CLASS_LIGHTGUN))
{
- auto lightgun = dynamic_cast<win32_lightgun_device*>(device);
- if (lightgun != nullptr)
- lightgun->queue_events(args, 1);
- });
+ if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL))
+ {
+ auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata);
+ devicelist().for_each_device(
+ [args] (auto &device) { device.queue_events(args, 1); });
+ return true;
+ }
+ }
- return true;
+ return false;
}
};
-#else
+} // anonymous namespace
+
+} // namespace osd
+
+#else // defined(OSD_WINDOWS)
+
+namespace osd {
+
+namespace {
+
MODULE_NOT_SUPPORTED(keyboard_input_win32, OSD_KEYBOARDINPUT_PROVIDER, "win32")
MODULE_NOT_SUPPORTED(mouse_input_win32, OSD_MOUSEINPUT_PROVIDER, "win32")
MODULE_NOT_SUPPORTED(lightgun_input_win32, OSD_LIGHTGUNINPUT_PROVIDER, "win32")
-#endif
-MODULE_DEFINITION(KEYBOARDINPUT_WIN32, keyboard_input_win32)
-MODULE_DEFINITION(MOUSEINPUT_WIN32, mouse_input_win32)
-MODULE_DEFINITION(LIGHTGUNINPUT_WIN32, lightgun_input_win32)
+} // anonymous namespace
+
+} // namespace osd
+
+#endif // defined(OSD_WINDOWS)
+
+MODULE_DEFINITION(KEYBOARDINPUT_WIN32, osd::keyboard_input_win32)
+MODULE_DEFINITION(MOUSEINPUT_WIN32, osd::mouse_input_win32)
+MODULE_DEFINITION(LIGHTGUNINPUT_WIN32, osd::lightgun_input_win32)
diff --git a/src/osd/modules/input/input_wincommon.h b/src/osd/modules/input/input_wincommon.h
new file mode 100644
index 00000000000..6b34d60abca
--- /dev/null
+++ b/src/osd/modules/input/input_wincommon.h
@@ -0,0 +1,40 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+// input_wincommon.h - Common code used by Windows input modules
+//
+//============================================================
+#ifndef MAME_OSD_INPUT_INPUT_WINCOMMON_H
+#define MAME_OSD_INPUT_INPUT_WINCOMMON_H
+
+#pragma once
+
+#include "input_common.h"
+
+#include <windows.h>
+
+
+namespace osd {
+
+// state information for a keyboard
+struct keyboard_state
+{
+ uint8_t state[MAX_KEYS];
+ int8_t oldkey[MAX_KEYS];
+ int8_t currkey[MAX_KEYS];
+};
+
+// state information for a mouse
+struct mouse_state
+{
+ LONG lX;
+ LONG lY;
+ LONG lV;
+ LONG lH;
+ BYTE rgbButtons[8];
+};
+
+} // namespace osd
+
+#endif // MAME_OSD_INPUT_INPUT_WINCOMMON_H
diff --git a/src/osd/modules/input/input_windows.cpp b/src/osd/modules/input/input_windows.cpp
index 164ee0e3c31..12002ccf893 100644
--- a/src/osd/modules/input/input_windows.cpp
+++ b/src/osd/modules/input/input_windows.cpp
@@ -8,123 +8,109 @@
#include "input_module.h"
-#if defined(OSD_WINDOWS) || defined(OSD_UWP)
+#if defined(OSD_WINDOWS)
// MAME headers
#include "emu.h"
-#include "osdepend.h"
-// MAMEOS headers
+#include "input_windows.h"
+
+#include "window.h"
#include "winmain.h"
-#include "input_common.h"
-#include "input_windows.h"
+#include "util/language.h"
-bool windows_osd_interface::should_hide_mouse() const
-{
- bool hidemouse = false;
- wininput_module* mod;
+#include "osdepend.h"
- mod = dynamic_cast<wininput_module*>(m_keyboard_input);
- if (mod) hidemouse |= mod->should_hide_mouse();
- mod = dynamic_cast<wininput_module*>(m_mouse_input);
- if (mod) hidemouse |= mod->should_hide_mouse();
+bool windows_osd_interface::should_hide_mouse() const
+{
+ if (!winwindow_has_focus())
+ return false;
- mod = dynamic_cast<wininput_module*>(m_lightgun_input);
- if (mod) hidemouse |= mod->should_hide_mouse();
+ if (machine().paused())
+ return false;
- mod = dynamic_cast<wininput_module*>(m_joystick_input);
- if (mod) hidemouse |= mod->should_hide_mouse();
+ // track if mouse/lightgun is enabled, for mouse hiding purposes
+ bool const mouse_enabled = machine().input().class_enabled(DEVICE_CLASS_MOUSE);
+ bool const lightgun_enabled = machine().input().class_enabled(DEVICE_CLASS_LIGHTGUN);
+ if (!mouse_enabled && !lightgun_enabled)
+ return false;
- return hidemouse;
+ return true;
}
-bool windows_osd_interface::handle_input_event(input_event eventid, void* eventdata) const
+bool windows_osd_interface::handle_input_event(input_event eventid, void const *eventdata) const
{
bool handled = false;
- wininput_module* mod;
+ wininput_event_handler *mod;
- mod = dynamic_cast<wininput_module*>(m_keyboard_input);
- if (mod) handled |= mod->handle_input_event(eventid, eventdata);
+ mod = dynamic_cast<wininput_event_handler *>(m_keyboard_input);
+ if (mod)
+ handled |= mod->handle_input_event(eventid, eventdata);
- mod = dynamic_cast<wininput_module*>(m_mouse_input);
- if (mod) handled |= mod->handle_input_event(eventid, eventdata);
+ mod = dynamic_cast<wininput_event_handler *>(m_mouse_input);
+ if (mod)
+ handled |= mod->handle_input_event(eventid, eventdata);
- mod = dynamic_cast<wininput_module*>(m_lightgun_input);
- if (mod) handled |= mod->handle_input_event(eventid, eventdata);
+ mod = dynamic_cast<wininput_event_handler *>(m_lightgun_input);
+ if (mod)
+ handled |= mod->handle_input_event(eventid, eventdata);
- mod = dynamic_cast<wininput_module*>(m_joystick_input);
- if (mod) handled |= mod->handle_input_event(eventid, eventdata);
+ mod = dynamic_cast<wininput_event_handler *>(m_joystick_input);
+ if (mod)
+ handled |= mod->handle_input_event(eventid, eventdata);
return handled;
}
-void windows_osd_interface::poll_input(running_machine &machine) const
-{
- m_keyboard_input->poll_if_necessary(machine);
- m_mouse_input->poll_if_necessary(machine);
- m_lightgun_input->poll_if_necessary(machine);
- m_joystick_input->poll_if_necessary(machine);
-}
-
//============================================================
// customize_input_type_list
//============================================================
-void windows_osd_interface::customize_input_type_list(simple_list<input_type_entry> &typelist)
+void windows_osd_interface::customize_input_type_list(std::vector<input_type_entry> &typelist)
{
- const char* uimode;
-
// loop over the defaults
for (input_type_entry &entry : typelist)
switch (entry.type())
{
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// disable the config menu if the ALT key is down
// (allows ALT-TAB to switch between windows apps)
- case IPT_UI_CONFIGURE:
+ case IPT_UI_MENU:
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT);
break;
-#else
- // UWP: Hotkey Select + X => UI_CONFIGURE (Menu)
- case IPT_UI_CONFIGURE:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::or_code, JOYCODE_SELECT, JOYCODE_BUTTON3);
- break;
- // UWP: Hotkey Select + Start => CANCEL
- case IPT_UI_CANCEL:
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_ESC, input_seq::or_code, JOYCODE_SELECT, JOYCODE_START);
- break;
-#endif
// configurable UI mode switch
case IPT_UI_TOGGLE_UI:
- uimode = options().ui_mode_key();
- if (strcmp(uimode, "auto"))
{
- std::string fullmode = "ITEM_ID_";
- fullmode += uimode;
- input_item_id const mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode.c_str());
- if (ITEM_ID_INVALID != mameid_code)
+ char const *const uimode = options().ui_mode_key();
+ if (uimode && *uimode && strcmp(uimode, "auto"))
{
- input_code const ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code));
- entry.defseq(SEQ_TYPE_STANDARD).set(ui_code);
+ std::string fullmode("ITEM_ID_");
+ fullmode.append(uimode);
+ input_item_id const mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode.c_str());
+ if (ITEM_ID_INVALID != mameid_code)
+ {
+ input_code const ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code));
+ entry.defseq(SEQ_TYPE_STANDARD).set(ui_code);
+ }
}
}
break;
// alt-enter for fullscreen
case IPT_OSD_1:
- entry.configure_osd("TOGGLE_FULLSCREEN", "Toggle Fullscreen");
+ entry.configure_osd("TOGGLE_FULLSCREEN", N_p("input-name", "Toggle Fullscreen"));
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_ENTER, KEYCODE_LALT, input_seq::or_code, KEYCODE_ENTER, KEYCODE_RALT);
break;
// lalt-F12 for fullscreen snap (HLSL)
case IPT_OSD_2:
- entry.configure_osd("RENDER_SNAP", "Take Rendered Snapshot");
+ entry.configure_osd("RENDER_SNAP", N_p("input-name", "Take Rendered Snapshot"));
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LALT, input_seq::not_code, KEYCODE_LSHIFT);
break;
+
// add a NOT-lalt to our default F12
case IPT_UI_SNAPSHOT: // emu/input.c: input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT)
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_LALT);
@@ -132,7 +118,7 @@ void windows_osd_interface::customize_input_type_list(simple_list<input_type_ent
// lshift-lalt-F12 for fullscreen video (HLSL, BGFX)
case IPT_OSD_3:
- entry.configure_osd("RENDER_AVI", "Record Rendered Video");
+ entry.configure_osd("RENDER_AVI", N_p("input-name", "Record Rendered Video"));
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LALT);
break;
@@ -146,19 +132,15 @@ void windows_osd_interface::customize_input_type_list(simple_list<input_type_ent
entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LALT);
break;
- // add a NOT-lalt to write timecode file
- case IPT_UI_TIMECODE: // emu/input.c: input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT)
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_LALT);
- break;
-
- // lctrl-lalt-F5 to toggle post-processing
+ // lalt-F10 to toggle post-processing
case IPT_OSD_4:
- entry.configure_osd("POST_PROCESS", "Toggle Post-Processing");
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, KEYCODE_LALT, KEYCODE_LCONTROL);
+ entry.configure_osd("POST_PROCESS", N_p("input-name", "Toggle Post-Processing"));
+ entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, KEYCODE_LALT);
break;
- // add a NOT-lctrl-lalt to our default F5
- case IPT_UI_TOGGLE_DEBUG: // emu/input.c: input_seq(KEYCODE_F5)
- entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LALT);
+
+ // add a Not LALT condition to the throttle key
+ case IPT_UI_THROTTLE:
+ entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LALT);
break;
// leave everything else alone
@@ -167,4 +149,4 @@ void windows_osd_interface::customize_input_type_list(simple_list<input_type_ent
}
}
-#endif
+#endif // defined(OSD_WINDOWS)
diff --git a/src/osd/modules/input/input_windows.h b/src/osd/modules/input/input_windows.h
index 7afc487fb62..882f80d053b 100644
--- a/src/osd/modules/input/input_windows.h
+++ b/src/osd/modules/input/input_windows.h
@@ -5,83 +5,44 @@
// input_windows.h - Common code used by Windows input modules
//
//============================================================
+#ifndef MAME_OSD_INPUT_INPUT_WINDOWS_H
+#define MAME_OSD_INPUT_INPUT_WINDOWS_H
-#ifndef INPUT_WIN_H_
-#define INPUT_WIN_H_
+#pragma once
-// standard windows headers
-#include <windows.h>
-#undef interface
+#include "input_common.h"
#include "window.h"
#include "winmain.h"
+// standard windows headers
+#include <windows.h>
+
+
//============================================================
// TYPEDEFS
//============================================================
-// state information for a keyboard
-struct keyboard_state
-{
- uint8_t state[MAX_KEYS];
- int8_t oldkey[MAX_KEYS];
- int8_t currkey[MAX_KEYS];
-};
-
-// state information for a mouse (matches DIMOUSESTATE exactly)
-struct mouse_state
-{
- LONG lX;
- LONG lY;
- LONG lZ;
- BYTE rgbButtons[8];
-};
-class wininput_module : public input_module_base
+class wininput_event_handler
{
protected:
- bool m_global_inputs_enabled;
+ wininput_event_handler() = default;
+ virtual ~wininput_event_handler() = default;
public:
- wininput_module(const char * type, const char * name)
- : input_module_base(type, name),
- m_global_inputs_enabled(false)
+ virtual bool handle_input_event(input_event eventid, void const *data)
{
- }
-
- virtual ~wininput_module() { }
-
- virtual bool should_hide_mouse()
- {
- if (winwindow_has_focus() // has focus
- && (!video_config.windowed || !osd_common_t::s_window_list.front()->win_has_menu()) // not windowed or doesn't have a menu
- && (input_enabled() && !input_paused()) // input enabled and not paused
- && (mouse_enabled() || lightgun_enabled())) // either mouse or lightgun enabled in the core
- {
- return true;
- }
-
return false;
}
+};
- virtual bool handle_input_event(input_event eventid, void* data)
- {
- return false;
- }
+template <typename Info>
+class wininput_module : public input_module_impl<Info, osd_common_t>, public wininput_event_handler
+{
protected:
-
- void before_poll(running_machine& machine) override
- {
- // periodically process events, in case they're not coming through
- // this also will make sure the mouse state is up-to-date
- winwindow_process_events_periodic(machine);
- }
-
- bool should_poll_devices(running_machine &machine) override
- {
- return input_enabled() && (m_global_inputs_enabled || winwindow_has_focus());
- }
+ using input_module_impl<Info, osd_common_t>::input_module_impl;
};
-#endif
+#endif // MAME_OSD_INPUT_INPUT_WINDOWS_H
diff --git a/src/osd/modules/input/input_winhybrid.cpp b/src/osd/modules/input/input_winhybrid.cpp
index 1436ae009d6..2f1023e24fb 100644
--- a/src/osd/modules/input/input_winhybrid.cpp
+++ b/src/osd/modules/input/input_winhybrid.cpp
@@ -6,48 +6,31 @@
//
//============================================================
-#include "input_module.h"
#include "modules/osdmodule.h"
-#if defined(OSD_WINDOWS)
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
+#include "input_dinput.h"
+#include "input_xinput.h"
-#include <list>
#include <vector>
-// standard windows headers
-#include <windows.h>
-#include <wrl/client.h>
+#include <oleauto.h>
#include <wbemcli.h>
-// XInput/DirectInput
-#include <xinput.h>
-#include <dinput.h>
-
-#undef interface
-// MAME headers
-#include "emu.h"
-
-// MAMEOS headers
-#include "strconv.h"
-#include "winmain.h"
-
-#include "input_common.h"
-#include "input_windows.h"
-#include "input_xinput.h"
-#include "input_dinput.h"
+namespace osd {
-using namespace Microsoft::WRL;
+namespace {
-template<class TCom>
+template <class TCom>
class ComArray
{
private:
- std::vector<TCom*> m_entries;
+ std::vector<TCom *> m_entries;
public:
- ComArray(size_t capacity)
- : m_entries(capacity, nullptr)
+ ComArray(size_t capacity) : m_entries(capacity, nullptr)
{
}
@@ -56,7 +39,7 @@ public:
Release();
}
- TCom** ReleaseAndGetAddressOf()
+ TCom **ReleaseAndGetAddressOf()
{
Release();
@@ -64,7 +47,7 @@ public:
return &m_entries[0];
}
- TCom* operator [] (int i)
+ TCom *operator[](int i)
{
return m_entries[i];
}
@@ -76,12 +59,12 @@ public:
void Release()
{
- for (int i = 0; i < m_entries.size(); i++)
+ for (auto &entry : m_entries)
{
- if (m_entries[i] != nullptr)
+ if (entry)
{
- m_entries[i]->Release();
- m_entries[i] = nullptr;
+ entry->Release();
+ entry = nullptr;
}
}
}
@@ -91,7 +74,7 @@ struct bstr_deleter
{
void operator () (BSTR bstr) const
{
- if (bstr != nullptr)
+ if (bstr)
SysFreeString(bstr);
}
};
@@ -104,8 +87,8 @@ private:
public:
variant_wrapper()
- : m_variant({0})
{
+ VariantInit(&m_variant);
}
~variant_wrapper()
@@ -133,28 +116,24 @@ public:
typedef std::unique_ptr<OLECHAR, bstr_deleter> bstr_ptr;
+
//============================================================
// winhybrid_joystick_module
//============================================================
-class winhybrid_joystick_module : public wininput_module, public device_enum_interface
+class winhybrid_joystick_module : public input_module_impl<device_info, osd_common_t>
{
private:
- std::shared_ptr<xinput_api_helper> m_xinput_helper;
+ std::unique_ptr<xinput_api_helper> m_xinput_helper;
std::unique_ptr<dinput_api_helper> m_dinput_helper;
- std::list<DWORD> m_xinput_deviceids;
- bool m_xinput_detect_failed;
public:
- winhybrid_joystick_module()
- : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "winhybrid"),
- m_xinput_helper(nullptr),
- m_dinput_helper(nullptr),
- m_xinput_detect_failed(false)
+ winhybrid_joystick_module() :
+ input_module_impl<device_info, osd_common_t>(OSD_JOYSTICKINPUT_PROVIDER, "winhybrid")
{
}
- bool probe() override
+ virtual bool probe() override
{
int status = init_helpers();
if (status != 0)
@@ -166,10 +145,12 @@ public:
return true;
}
- int init(const osd_options &options) override
+ virtual int init(osd_interface &osd, const osd_options &options) override
{
+ int status;
+
// Call the base
- int status = wininput_module::init(options);
+ status = input_module_impl<device_info, osd_common_t>::init(osd, options);
if (status != 0)
return status;
@@ -184,129 +165,116 @@ public:
return 0;
}
- BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override
+ virtual void input_init(running_machine &machine) override
{
- dinput_cooperative_level cooperative_level = dinput_cooperative_level::FOREGROUND;
- running_machine &machine = *static_cast<running_machine *>(ref);
- dinput_joystick_device *devinfo;
- int result = 0;
-
- // First check if this device is XInput Compatible. If so, don't add it here
- // as it'll be picked up by Xinput
- if (!m_xinput_detect_failed && is_xinput_device(&instance->guidProduct))
- {
- osd_printf_verbose("Skipping DirectInput for XInput compatible joystick %S.\n", instance->tszInstanceName);
- goto exit;
- }
+ input_module_impl<device_info, osd_common_t>::input_init(machine);
- if (!osd_common_t::s_window_list.empty() && osd_common_t::s_window_list.front()->win_has_menu())
- cooperative_level = dinput_cooperative_level::BACKGROUND;
-
- // allocate and link in a new device
- devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level);
- if (devinfo == nullptr)
- goto exit;
-
- result = devinfo->configure();
+ bool xinput_detect_failed = false;
+ std::vector<DWORD> xinput_deviceids;
+ HRESULT result = get_xinput_devices(xinput_deviceids);
if (result != 0)
{
- osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result));
+ xinput_detect_failed = true;
+ xinput_deviceids.clear();
+ osd_printf_warning("XInput device detection failed. XInput won't be used. Error: 0x%X\n", uint32_t(result));
}
- exit:
- return DIENUM_CONTINUE;
- }
-
- void exit() override
- {
- m_xinput_helper.reset();
- m_dinput_helper.reset();
-
- wininput_module::exit();
- }
-
-protected:
- virtual void input_init(running_machine &machine) override
- {
- HRESULT result = get_xinput_devices(m_xinput_deviceids);
- if (result != 0)
- {
- m_xinput_detect_failed = true;
- osd_printf_warning("XInput device detection failed. XInput won't be used. Error: 0x%X\n", static_cast<unsigned int>(result));
- }
+ // Enumerate all the DirectInput joysticks and add them if they aren't XInput compatible
+ result = m_dinput_helper->enum_attached_devices(
+ DI8DEVCLASS_GAMECTRL,
+ [this, &xinput_deviceids] (LPCDIDEVICEINSTANCE instance)
+ {
+ // First check if this device is XInput compatible.
+ // If so, don't add it here as it'll be picked up by Xinput.
+ auto const found = std::find(
+ xinput_deviceids.begin(),
+ xinput_deviceids.end(),
+ instance->guidProduct.Data1);
+ if (xinput_deviceids.end() != found)
+ {
+ osd_printf_verbose("Skipping DirectInput for XInput compatible joystick %S.\n", instance->tszInstanceName);
+ return DIENUM_CONTINUE;
+ }
- // Enumerate all the directinput joysticks and add them if they aren't xinput compatible
- result = m_dinput_helper->enum_attached_devices(DI8DEVCLASS_GAMECTRL, this, &machine);
+ // allocate and link in a new device
+ auto devinfo = m_dinput_helper->create_device<dinput_joystick_device>(
+ *this,
+ instance,
+ &c_dfDIJoystick,
+ nullptr,
+ background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND,
+ [] (auto const &device, auto const &format) -> bool
+ {
+ // set absolute mode
+ HRESULT const result = dinput_api_helper::set_dword_property(
+ device,
+ DIPROP_AXISMODE,
+ 0,
+ DIPH_DEVICE,
+ DIPROPAXISMODE_ABS);
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
+ {
+ osd_printf_error("DirectInput: Unable to set absolute mode for joystick.\n");
+ return false;
+ }
+ return true;
+ });
+ if (devinfo)
+ add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo));
+
+ return DIENUM_CONTINUE;
+ });
if (result != DI_OK)
- fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", static_cast<uint32_t>(result));
-
- xinput_joystick_device *devinfo;
+ fatalerror("DirectInput: Unable to enumerate game controllers (result=%08X).\n", uint32_t(result));
// now add all xinput devices
- if (!m_xinput_detect_failed)
+ if (!xinput_detect_failed)
{
// Loop through each gamepad to determine if they are connected
for (UINT i = 0; i < XUSER_MAX_COUNT; i++)
{
- XINPUT_STATE state = { 0 };
-
- if (m_xinput_helper->xinput_get_state(i, &state) == ERROR_SUCCESS)
- {
- // allocate and link in a new device
- devinfo = m_xinput_helper->create_xinput_device(machine, i, *this);
- if (devinfo == nullptr)
- continue;
-
- // Configure each gamepad to add buttons and Axes, etc.
- devinfo->configure();
- }
+ // allocate and link in a new device
+ auto devinfo = m_xinput_helper->create_xinput_device(i, *this);
+ if (devinfo)
+ add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo));
}
}
}
+ virtual void exit() override
+ {
+ input_module_impl<device_info, osd_common_t>::exit();
+
+ m_xinput_helper.reset();
+ m_dinput_helper.reset();
+ }
+
private:
int init_helpers()
{
- int status = 0;
-
- if (m_xinput_helper == nullptr)
+ if (!m_xinput_helper)
{
- m_xinput_helper = std::make_shared<xinput_api_helper>();
- status = m_xinput_helper->initialize();
+ m_xinput_helper = std::make_unique<xinput_api_helper>();
+ int const status = m_xinput_helper->initialize();
if (status != 0)
{
- osd_printf_verbose("xinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status));
+ osd_printf_verbose("Failed to initialize XInput API! Error: %u\n", static_cast<unsigned int>(status));
return -1;
}
}
- if (m_dinput_helper == nullptr)
+ if (!m_dinput_helper)
{
- m_dinput_helper = std::make_unique<dinput_api_helper>(DIRECTINPUT_VERSION);
- status = m_dinput_helper->initialize();
+ m_dinput_helper = std::make_unique<dinput_api_helper>();
+ int const status = m_dinput_helper->initialize();
if (status != DI_OK)
{
- osd_printf_verbose("dinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status));
+ osd_printf_verbose("Failed to initialize DirectInput API! Error: %u\n", static_cast<unsigned int>(status));
return -1;
}
}
- return status;
- }
-
- //-----------------------------------------------------------------------------
- // Returns true if the DirectInput device is also an XInput device.
- //-----------------------------------------------------------------------------
- bool is_xinput_device(const GUID* pGuidProductFromDirectInput)
- {
- // Check each xinput device to see if this device's vid/pid matches
- for (auto devid = m_xinput_deviceids.begin(); devid != m_xinput_deviceids.end(); ++devid)
- {
- if (*devid == pGuidProductFromDirectInput->Data1)
- return true;
- }
-
- return false;
+ return 0;
}
//-----------------------------------------------------------------------------
@@ -316,54 +284,43 @@ private:
// Checking against a VID/PID of 0x028E/0x045E won't find 3rd party or future
// XInput devices.
//-----------------------------------------------------------------------------
- HRESULT get_xinput_devices(std::list<DWORD> &xinput_id_list) const
+ HRESULT get_xinput_devices(std::vector<DWORD> &xinput_deviceids)
{
- ComPtr<IWbemServices> pIWbemServices;
- ComPtr<IEnumWbemClassObject> pEnumDevices;
- ComPtr<IWbemLocator> pIWbemLocator;
- ComArray<IWbemClassObject> pDevices(20);
- bstr_ptr bstrDeviceID;
- bstr_ptr bstrClassName;
- bstr_ptr bstrNamespace;
- DWORD uReturned = 0;
- UINT iDevice;
- variant_wrapper var;
- HRESULT hr;
+ xinput_deviceids.clear();
- // CoInit if needed
- CoInitialize(nullptr);
+ HRESULT hr;
// Create WMI
+ Microsoft::WRL::ComPtr<IWbemLocator> pIWbemLocator;
hr = CoCreateInstance(
- __uuidof(WbemLocator),
- nullptr,
- CLSCTX_INPROC_SERVER,
- __uuidof(IWbemLocator),
- reinterpret_cast<void**>(pIWbemLocator.GetAddressOf()));
-
- if (FAILED(hr) || pIWbemLocator == nullptr)
+ __uuidof(WbemLocator),
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(IWbemLocator),
+ reinterpret_cast<void **>(pIWbemLocator.GetAddressOf()));
+ if (FAILED(hr) || !pIWbemLocator)
{
osd_printf_error("Creating WbemLocator failed. Error: 0x%X\n", static_cast<unsigned int>(hr));
return hr;
}
// Create BSTRs for WMI
- bstrNamespace = bstr_ptr(SysAllocString(L"\\\\.\\root\\cimv2"));
- bstrDeviceID = bstr_ptr(SysAllocString(L"DeviceID"));
- bstrClassName = bstr_ptr(SysAllocString(L"Win32_PNPEntity"));
+ bstr_ptr bstrNamespace = bstr_ptr(SysAllocString(L"\\\\.\\root\\cimv2"));
+ bstr_ptr bstrDeviceID = bstr_ptr(SysAllocString(L"DeviceID"));
+ bstr_ptr bstrClassName = bstr_ptr(SysAllocString(L"Win32_PNPEntity"));
// Connect to WMI
+ Microsoft::WRL::ComPtr<IWbemServices> pIWbemServices;
hr = pIWbemLocator->ConnectServer(
- bstrNamespace.get(),
- nullptr,
- nullptr,
- nullptr,
- 0L,
- nullptr,
- nullptr,
- pIWbemServices.GetAddressOf());
-
- if (FAILED(hr) || pIWbemServices == nullptr)
+ bstrNamespace.get(),
+ nullptr,
+ nullptr,
+ nullptr,
+ 0L,
+ nullptr,
+ nullptr,
+ pIWbemServices.GetAddressOf());
+ if (FAILED(hr) || !pIWbemServices)
{
osd_printf_error("Connecting to WMI Server failed. Error: 0x%X\n", static_cast<unsigned int>(hr));
return hr;
@@ -371,38 +328,41 @@ private:
// Switch security level to IMPERSONATE
(void)CoSetProxyBlanket(
- pIWbemServices.Get(),
- RPC_C_AUTHN_WINNT,
- RPC_C_AUTHZ_NONE,
- nullptr,
- RPC_C_AUTHN_LEVEL_CALL,
- RPC_C_IMP_LEVEL_IMPERSONATE,
- nullptr,
- 0);
+ pIWbemServices.Get(),
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ nullptr,
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ nullptr,
+ 0);
// Get list of Win32_PNPEntity devices
+ Microsoft::WRL::ComPtr<IEnumWbemClassObject> pEnumDevices;
hr = pIWbemServices->CreateInstanceEnum(bstrClassName.get(), 0, nullptr, pEnumDevices.GetAddressOf());
- if (FAILED(hr) || pEnumDevices == nullptr)
+ if (FAILED(hr) || !pEnumDevices)
{
osd_printf_error("Getting list of Win32_PNPEntity devices failed. Error: 0x%X\n", static_cast<unsigned int>(hr));
return hr;
}
// Loop over all devices
- for (; ; )
+ ComArray<IWbemClassObject> pDevices(20);
+ variant_wrapper var;
+ for ( ; ; )
{
// Get a few at a time
+ DWORD uReturned = 0;
hr = pEnumDevices->Next(10000, pDevices.Size(), pDevices.ReleaseAndGetAddressOf(), &uReturned);
if (FAILED(hr))
{
osd_printf_error("Enumerating WMI classes failed. Error: 0x%X\n", static_cast<unsigned int>(hr));
return hr;
}
-
if (uReturned == 0)
break;
- for (iDevice = 0; iDevice < uReturned; iDevice++)
+ for (UINT iDevice = 0; iDevice < uReturned; iDevice++)
{
if (!pDevices[iDevice])
continue;
@@ -416,19 +376,18 @@ private:
if (wcsstr(var.Get().bstrVal, L"IG_"))
{
// If it does, then get the VID/PID from var.bstrVal
- DWORD dwPid = 0, dwVid = 0;
- WCHAR* strVid = wcsstr(var.Get().bstrVal, L"VID_");
+ DWORD dwVid = 0;
+ WCHAR const *const strVid = wcsstr(var.Get().bstrVal, L"VID_");
if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1)
dwVid = 0;
- WCHAR* strPid = wcsstr(var.Get().bstrVal, L"PID_");
+ DWORD dwPid = 0;
+ WCHAR const *const strPid = wcsstr(var.Get().bstrVal, L"PID_");
if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1)
dwPid = 0;
- DWORD dwVidPid = MAKELONG(dwVid, dwPid);
-
- // Add the VID/PID to a linked list
- xinput_id_list.push_back(dwVidPid);
+ // Add the VID/PID to a list
+ xinput_deviceids.push_back(MAKELONG(dwVid, dwPid));
}
}
}
@@ -441,8 +400,16 @@ private:
}
};
-#else
-MODULE_NOT_SUPPORTED(winhybrid_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "winhybrid")
-#endif
+} // anonymous namespace
+
+} // namespace osd
+
+#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
+#include "input_module.h"
+
+namespace osd { namespace { MODULE_NOT_SUPPORTED(winhybrid_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "winhybrid") } }
+
+#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
-MODULE_DEFINITION(JOYSTICKINPUT_WINHYBRID, winhybrid_joystick_module)
+MODULE_DEFINITION(JOYSTICKINPUT_WINHYBRID, osd::winhybrid_joystick_module)
diff --git a/src/osd/modules/input/input_x11.cpp b/src/osd/modules/input/input_x11.cpp
index 40df3d528b6..5230dfd7164 100644
--- a/src/osd/modules/input/input_x11.cpp
+++ b/src/osd/modules/input/input_x11.cpp
@@ -9,34 +9,41 @@
//============================================================
#include "input_module.h"
+
#include "modules/osdmodule.h"
#if defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT
+#include "input_common.h"
+
+#include "sdl/osdsdl.h"
+
+// MAME headers
+#include "inpttype.h"
+
+// standard SDL header
+#include <SDL2/SDL.h>
+
// for X11 xinput
#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>
#include <X11/Xutil.h>
-// standard sdl header
-#include <SDL2/SDL.h>
-#include <ctype.h>
-#include <stddef.h>
-#include <mutex>
+#include <algorithm>
+#include <cctype>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
#include <memory>
+#include <mutex>
#include <queue>
#include <string>
-#include <algorithm>
+#include <unordered_map>
-// MAME headers
-#include "emu.h"
-#include "osdepend.h"
-// MAMEOS headers
-#include "../lib/osdobj_common.h"
-#include "input_common.h"
-#include "../../sdl/osdsdl.h"
-#include "input_sdlcommon.h"
+namespace osd {
+
+namespace {
#define MAX_DEVMAP_ENTRIES 16
@@ -49,20 +56,6 @@ static int key_release_type = INVALID_EVENT_TYPE;
static int proximity_in_type = INVALID_EVENT_TYPE;
static int proximity_out_type = INVALID_EVENT_TYPE;
-// state information for a lightgun
-struct lightgun_state
-{
- int32_t lX, lY;
- int32_t buttons[MAX_BUTTONS];
-};
-
-struct x11_api_state
-{
- XID deviceid; // X11 device id
- int32_t maxx, maxy;
- int32_t minx, miny;
-};
-
//============================================================
// DEBUG MACROS
//============================================================
@@ -71,56 +64,64 @@ struct x11_api_state
#define XI_DBG(format, ...) osd_printf_verbose(format, __VA_ARGS__)
#define print_motion_event(motion) print_motion_event_impl(motion)
-static inline void print_motion_event_impl(XDeviceMotionEvent *motion)
+inline void print_motion_event_impl(XDeviceMotionEvent const *motion)
{
/*
* print a lot of debug informations of the motion event(s).
*/
osd_printf_verbose(
- "XDeviceMotionEvent:\n"
- " type: %d\n"
- " serial: %lu\n"
- " send_event: %d\n"
- " display: %p\n"
- " window: --\n"
- " deviceid: %lu\n"
- " root: --\n"
- " subwindow: --\n"
- " time: --\n"
- " x: %d, y: %d\n"
- " x_root: %d, y_root: %d\n"
- " state: %u\n"
- " is_hint: %2.2X\n"
- " same_screen: %d\n"
- " device_state: %u\n"
- " axes_count: %2.2X\n"
- " first_axis: %2.2X\n"
- " axis_data[6]: {%d,%d,%d,%d,%d,%d}\n",
- motion->type,
- motion->serial,
- motion->send_event,
- motion->display,
- /* motion->window, */
- motion->deviceid,
- /* motion->root */
- /* motion->subwindow */
- /* motion->time, */
- motion->x, motion->y,
- motion->x_root, motion->y_root,
- motion->state,
- motion->is_hint,
- motion->same_screen,
- motion->device_state,
- motion->axes_count,
- motion->first_axis,
- motion->axis_data[0], motion->axis_data[1], motion->axis_data[2], motion->axis_data[3], motion->axis_data[4], motion->axis_data[5]
- );
+ "XDeviceMotionEvent:\n"
+ " type: %d\n"
+ " serial: %lu\n"
+ " send_event: %d\n"
+ " display: %p\n"
+ " window: --\n"
+ " deviceid: %lu\n"
+ " root: --\n"
+ " subwindow: --\n"
+ " time: --\n"
+ " x: %d, y: %d\n"
+ " x_root: %d, y_root: %d\n"
+ " state: %u\n"
+ " is_hint: %2.2X\n"
+ " same_screen: %d\n"
+ " device_state: %u\n"
+ " axes_count: %2.2X\n"
+ " first_axis: %2.2X\n"
+ " axis_data[6]: {%d,%d,%d,%d,%d,%d}\n",
+ motion->type,
+ motion->serial,
+ motion->send_event,
+ motion->display,
+ /* motion->window, */
+ motion->deviceid,
+ /* motion->root */
+ /* motion->subwindow */
+ /* motion->time, */
+ motion->x, motion->y,
+ motion->x_root, motion->y_root,
+ motion->state,
+ motion->is_hint,
+ motion->same_screen,
+ motion->device_state,
+ motion->axes_count,
+ motion->first_axis,
+ motion->axis_data[0], motion->axis_data[1], motion->axis_data[2], motion->axis_data[3], motion->axis_data[4], motion->axis_data[5]);
}
#else
-#define XI_DBG(format, ...) while(0) {}
-#define print_motion_event(motion) while(0) {}
+#define XI_DBG(format, ...) do { } while (false)
+#define print_motion_event(motion) do { } while (false)
#endif
+
+inline std::string remove_spaces(const char *s)
+{
+ std::string output(s);
+ output.erase(std::remove_if(output.begin(), output.end(), isspace), output.end());
+ return output;
+}
+
+
//============================================================
// lightgun helpers: copy-past from xinfo
//============================================================
@@ -138,7 +139,7 @@ find_device_info(Display *display,
bool is_id = true;
XID id = static_cast<XID>(-1);
- for(loop = 0; loop < len; loop++)
+ for (loop = 0; loop < len; loop++)
{
if (!isdigit(name[loop]))
{
@@ -154,7 +155,7 @@ find_device_info(Display *display,
devices = XListInputDevices(display, &num_devices);
- for(loop = 0; loop < num_devices; loop++)
+ for (loop = 0; loop < num_devices; loop++)
{
osd_printf_verbose("Evaluating device with name: %s\n", devices[loop].name);
@@ -185,7 +186,7 @@ find_device_info(Display *display,
}
//Copypasted from xinfo
-static int
+int
register_events(
Display *dpy,
XDeviceInfo *info,
@@ -252,32 +253,90 @@ register_events(
return number;
}
-//============================================================
-// x11_event_manager
-//============================================================
-class x11_event_handler
+struct device_map
{
-public:
- virtual ~x11_event_handler() {}
+ static inline constexpr unsigned MAX_ENTRIES = 16;
+
+ struct {
+ std::string name;
+ int physical;
+ } map[MAX_ENTRIES];
+ int logical[MAX_ENTRIES];
+ int initialized;
+
+ void init(osd_options const &options, const char *opt, int max_devices, const char *label)
+ {
+ // initialize based on an input option prefix and max number of devices
+ char defname[20];
+
+ // The max devices the user specified, better not be bigger than the max the arrays can old
+ assert(max_devices <= MAX_ENTRIES);
- virtual void handle_event(XEvent &xevent) = 0;
+ // Initialize the map to default uninitialized values
+ for (int dev = 0; dev < MAX_ENTRIES; dev++)
+ {
+ map[dev].name.clear();
+ map[dev].physical = -1;
+ logical[dev] = -1;
+ }
+ initialized = 0;
+
+ // populate the device map up to the max number of devices
+ for (int dev = 0; dev < max_devices; dev++)
+ {
+ const char *dev_name;
+
+ // derive the parameter name from the option name and index. For instance: lightgun_index1 to lightgun_index8
+ sprintf(defname, "%s%d", opt, dev + 1);
+
+ // Get the user-specified name that matches the parameter
+ dev_name = options.value(defname);
+
+ // If they've specified a name and it's not "auto", treat it as a custom mapping
+ if (dev_name && *dev_name && strcmp(dev_name, OSDOPTVAL_AUTO))
+ {
+ // remove the spaces from the name store it in the index
+ map[dev].name = remove_spaces(dev_name);
+ osd_printf_verbose("%s: Logical id %d: %s\n", label, dev + 1, map[dev].name);
+ initialized = 1;
+ }
+ }
+ }
};
-class x11_event_manager : public event_manager_t<x11_event_handler>
+
+//============================================================
+// x11_event_manager
+//============================================================
+
+class x11_event_manager : public event_subscription_manager<XEvent, int>
{
private:
- Display * m_display;
-
- x11_event_manager()
- : event_manager_t(),
- m_display(nullptr)
+ struct x_cleanup
{
- }
+ void operator()(Display *ptr) const
+ {
+ if (ptr)
+ XCloseDisplay(ptr);
+ }
+ void operator()(XExtensionVersion *ptr) const
+ {
+ if (ptr)
+ XFree(ptr);
+ }
+ };
+
+ template <typename T> using x_ptr = std::unique_ptr<T, x_cleanup>;
+
+ x_ptr<Display> m_display;
+
+ x11_event_manager() = default;
+
public:
- Display * display() const { return m_display; }
+ Display *display() const { return m_display.get(); }
- static x11_event_manager& instance()
+ static x11_event_manager &instance()
{
static x11_event_manager s_instance;
return s_instance;
@@ -285,20 +344,20 @@ public:
int initialize()
{
- std::lock_guard<std::mutex> scope_lock(m_lock);
+ std::lock_guard<std::mutex> scope_lock(subscription_mutex());
- if (m_display != nullptr)
+ if (m_display)
return 0;
- m_display = XOpenDisplay(nullptr);
- if (m_display == nullptr)
+ m_display.reset(XOpenDisplay(nullptr));
+ if (!m_display)
{
osd_printf_verbose("Unable to connect to X server\n");
return -1;
}
- XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
- if (!version || (version == reinterpret_cast<XExtensionVersion*>(NoSuchExtension)))
+ x_ptr<XExtensionVersion> version(XGetExtensionVersion(m_display.get(), INAME));
+ if (!version || (version.get() == reinterpret_cast<XExtensionVersion *>(NoSuchExtension)))
{
osd_printf_verbose("xinput extension not available!\n");
return -1;
@@ -307,28 +366,24 @@ public:
return 0;
}
- void process_events(running_machine &machine) override
+ void process_events()
{
- std::lock_guard<std::mutex> scope_lock(m_lock);
- XEvent xevent;
+ std::lock_guard<std::mutex> scope_lock(subscription_mutex());
- //Get XInput events
- while (XPending(m_display) != 0)
- {
- XNextEvent(m_display, &xevent);
-
- // Find all subscribers for the event type
- auto subscribers = m_subscription_index.equal_range(xevent.type);
+ // If X11 has become invalid for some reason, XPending will crash. Assert instead.
+ assert(m_display);
- // Dispatch the events
- std::for_each(subscribers.first, subscribers.second, [&xevent](auto &pair)
- {
- pair.second->handle_event(xevent);
- });
+ // Get XInput events
+ while (XPending(m_display.get()) != 0)
+ {
+ XEvent event;
+ XNextEvent(m_display.get(), &event);
+ dispatch_event(event.type, event);
}
}
};
+
//============================================================
// x11_input_device
//============================================================
@@ -336,15 +391,23 @@ public:
class x11_input_device : public event_based_device<XEvent>
{
public:
- x11_api_state x11_state;
-
- x11_input_device(running_machine &machine, const char *name, const char *id, input_device_class devclass, input_module &module)
- : event_based_device(machine, name, id, devclass, module),
- x11_state({0})
+ x11_input_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ XDeviceInfo const *info) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_device_id(info ? info->id : 0)
{
}
+
+ XID const &device_id() const { return m_device_id; }
+
+protected:
+ XID const m_device_id; // X11 device ID
};
+
//============================================================
// x11_lightgun_device
//============================================================
@@ -352,55 +415,124 @@ public:
class x11_lightgun_device : public x11_input_device
{
public:
- lightgun_state lightgun;
+ x11_lightgun_device(
+ std::string &&name,
+ std::string &&id,
+ input_module
+ &module,
+ XDeviceInfo *info) :
+ x11_input_device(std::move(name), std::move(id), module, info),
+ m_axis_count(0),
+ m_button_count(0),
+ m_maxx(0),
+ m_maxy(0),
+ m_minx(0),
+ m_miny(0),
+ m_lightgun({ 0 })
+ {
+ if (info && (info->num_classes > 0))
+ {
+ // Grab device info and translate to stuff MAME can use
+ XAnyClassPtr any = static_cast<XAnyClassPtr>(info->inputclassinfo);
+ for (int i = 0; i < info->num_classes; i++)
+ {
+ switch (any->c_class)
+ {
+ // Set the axis min/max ranges if we got them
+ case ValuatorClass:
+ {
+ auto const valuator_info = reinterpret_cast<XValuatorInfoPtr>(any);
+ auto axis_info = reinterpret_cast<XAxisInfoPtr>(reinterpret_cast<char *>(valuator_info) + sizeof(XValuatorInfo));
+ for (int j = 0; (j < valuator_info->num_axes) && (j < 2); j++, axis_info++)
+ {
+ if (j == 0)
+ {
+ XI_DBG("Set minx=%d, maxx=%d\n", axis_info->min_value, axis_info->max_value);
+ m_axis_count = 1;
+ m_maxx = axis_info->max_value;
+ m_minx = axis_info->min_value;
+ }
+
+ if (j == 1)
+ {
+ XI_DBG("Set miny=%d, maxy=%d\n", axis_info->min_value, axis_info->max_value);
+ m_axis_count = 2;
+ m_maxy = axis_info->max_value;
+ m_miny = axis_info->min_value;
+ }
+ }
+ }
+ break;
+
+ // Count the lightgun buttons based on what we read
+ case ButtonClass:
+ {
+ XButtonInfoPtr b = reinterpret_cast<XButtonInfoPtr>(any);
+ if (b->num_buttons < 0)
+ m_button_count = 0;
+ else if (b->num_buttons <= MAX_BUTTONS)
+ m_button_count = b->num_buttons;
+ else
+ m_button_count = MAX_BUTTONS;
+ }
+ break;
+ }
- x11_lightgun_device(running_machine &machine, const char *name, const char *id, input_module &module)
- : x11_input_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
- lightgun({0})
+ any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length);
+ }
+ }
+ }
+
+ virtual void reset() override
{
+ memset(&m_lightgun, 0, sizeof(m_lightgun));
}
- void process_event(XEvent &xevent) override
+ virtual void configure(input_device &device) override
+ {
+ // Add buttons
+ for (int button = 0; button < m_button_count; button++)
+ {
+ input_item_id const itemid = input_item_id(ITEM_ID_BUTTON1 + button);
+ device.add_item(default_button_name(button), std::string_view(), itemid, generic_button_get_state<std::int32_t>, &m_lightgun.buttons[button]);
+ }
+
+ // Add X and Y axis
+ if (1 <= m_axis_count)
+ device.add_item("X", std::string_view(), ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &m_lightgun.lX);
+ if (2 <= m_axis_count)
+ device.add_item("Y", std::string_view(), ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &m_lightgun.lY);
+ }
+
+ virtual void process_event(XEvent const &xevent) override
{
if (xevent.type == motion_type)
{
- XDeviceMotionEvent *motion = reinterpret_cast<XDeviceMotionEvent *>(&xevent);
+ auto const motion = reinterpret_cast<XDeviceMotionEvent const *>(&xevent);
print_motion_event(motion);
- /*
- * We have to check with axis will start on array index 0.
- * We have also to check the number of axes that are stored in the array.
- */
+ // We have to check with axis will start on array index 0.
+ // We also have to check the number of axes that are stored in the array.
switch (motion->first_axis)
{
- /*
- * Starting with x, check number of axis, if there is also the y axis stored.
- */
+ // Starting with x, check number of axes, if there is also the y axis stored.
case 0:
if (motion->axes_count >= 1)
- {
- lightgun.lX = normalize_absolute_axis(motion->axis_data[0], x11_state.minx, x11_state.maxx);
- if (motion->axes_count >= 2)
- {
- lightgun.lY = normalize_absolute_axis(motion->axis_data[1], x11_state.miny, x11_state.maxy);
- }
- }
+ m_lightgun.lX = normalize_absolute_axis(motion->axis_data[0], m_minx, m_maxx);
+ if (motion->axes_count >= 2)
+ m_lightgun.lY = normalize_absolute_axis(motion->axis_data[1], m_miny, m_maxy);
break;
- /*
- * Starting with y, ...
- */
+ // Starting with y, ...
case 1:
if (motion->axes_count >= 1)
- {
- lightgun.lY = normalize_absolute_axis(motion->axis_data[0], x11_state.miny, x11_state.maxy);
- }
+ m_lightgun.lY = normalize_absolute_axis(motion->axis_data[0], m_miny, m_maxy);
break;
}
}
else if (xevent.type == button_press_type || xevent.type == button_release_type)
{
- XDeviceButtonEvent *button = reinterpret_cast<XDeviceButtonEvent *>(&xevent);
+ auto const button = reinterpret_cast<XDeviceButtonEvent const *>(&xevent);
/*
* SDL/X11 Number the buttons 1,2,3, while windows and other parts of MAME
@@ -408,140 +540,160 @@ public:
* -1 the button number to align the numbering schemes.
*/
int button_number = button->button;
- switch (button_number)
+ if (button_number <= MAX_BUTTONS)
{
+ switch (button_number)
+ {
case 2:
- button_number = 3;
- break;
case 3:
- button_number = 2;
+ button_number ^= 1;
break;
+ }
+ m_lightgun.buttons[button_number - 1] = (xevent.type == button_press_type) ? 0x80 : 0;
}
- lightgun.buttons[button_number - 1] = (xevent.type == button_press_type) ? 0x80 : 0;
}
}
- void reset() override
+private:
+ struct lightgun_state
{
- memset(&lightgun, 0, sizeof(lightgun));
- }
+ int32_t lX, lY;
+ int32_t buttons[MAX_BUTTONS];
+ };
+
+ int m_axis_count;
+ int m_button_count;
+ int32_t m_maxx;
+ int32_t m_maxy;
+ int32_t m_minx;
+ int32_t m_miny;
+ lightgun_state m_lightgun;
};
+
//============================================================
// x11_lightgun_module
//============================================================
-class x11_lightgun_module : public input_module_base, public x11_event_handler
+class x11_lightgun_module : public input_module_impl<x11_input_device, osd_common_t>, public x11_event_manager::subscriber
{
private:
- device_map_t m_lightgun_map;
- Display * m_display;
+ device_map m_lightgun_map;
+ Display *m_display;
+
public:
- x11_lightgun_module()
- : input_module_base(OSD_LIGHTGUNINPUT_PROVIDER, "x11"),
- m_display(nullptr)
+ x11_lightgun_module() :
+ input_module_impl<x11_input_device, osd_common_t>(OSD_LIGHTGUNINPUT_PROVIDER, "x11"),
+ m_display(nullptr)
{
}
- void input_init(running_machine &machine) override
+ virtual bool probe() override
{
- int index;
+ // If there is no X server, X11 lightguns cannot be supported
+ Display *const display = XOpenDisplay(nullptr);
+ if (!display)
+ return false;
+ XCloseDisplay(display);
- osd_printf_verbose("Lightgun: Begin initialization\n");
-
- devmap_init(machine, &m_lightgun_map, SDLOPTION_LIGHTGUNINDEX, 8, "Lightgun mapping");
+ return true;
+ }
+ virtual int init(osd_interface &osd, osd_options const &options) override
+ {
+ // If the X server has become invalid, a crash can occur
x11_event_manager::instance().initialize();
m_display = x11_event_manager::instance().display();
+ if (!m_display)
+ return -1;
+
+ return input_module_impl<x11_input_device, osd_common_t>::init(osd, options);
+ }
+
+ virtual void input_init(running_machine &machine) override
+ {
+ assert(m_display);
+
+ input_module_impl<x11_input_device, osd_common_t>::input_init(machine);
+
+ osd_printf_verbose("Lightgun: Begin initialization\n");
+
+ m_lightgun_map.init(*options(), SDLOPTION_LIGHTGUNINDEX, 8, "Lightgun mapping");
// Loop through all 8 possible devices
- for (index = 0; index < 8; index++)
+ for (int index = 0; index < 8; index++)
{
- XDeviceInfo *info;
-
// Skip if the name is empty
- if (m_lightgun_map.map[index].name.length() == 0)
+ if (m_lightgun_map.map[index].name.empty())
continue;
- x11_lightgun_device *devinfo;
+ // Find the device info associated with the name
std::string const &name = m_lightgun_map.map[index].name;
- char defname[512];
+ osd_printf_verbose("%i: %s\n", index, name);
+ XDeviceInfo *const info = find_device_info(m_display, name.c_str(), 0);
+ if (!info)
+ osd_printf_verbose("Lightgun: Can't find device %s!\n", name);
- // Register and add the device
- devinfo = create_lightgun_device(machine, index);
- osd_printf_verbose("%i: %s\n", index, name.c_str());
-
- // Find the device info associated with the name
- info = find_device_info(m_display, name.c_str(), 0);
+ // previously had code to use "NC%d" format if name was empty
+ // but that couldn't happen because device creation would be skipped
- // If we couldn't find the device, skip
- if (info == nullptr)
- {
- osd_printf_verbose("Can't find device %s!\n", name.c_str());
- continue;
- }
+ // Register and add the device
+ create_device<x11_lightgun_device>(
+ DEVICE_CLASS_LIGHTGUN,
+ std::string(name),
+ std::string(name),
+ info);
- //Grab device info and translate to stuff mame can use
- if (info->num_classes > 0)
+ // Register this device to receive event notifications
+ if (info)
{
- // Add the lightgun buttons based on what we read
- add_lightgun_buttons(static_cast<XAnyClassPtr>(info->inputclassinfo), info->num_classes, devinfo);
-
- // Also, set the axix min/max ranges if we got them
- set_lightgun_axis_props(static_cast<XAnyClassPtr>(info->inputclassinfo), info->num_classes, devinfo);
+ int const events_registered = register_events(m_display, info, name.c_str(), 0);
+ osd_printf_verbose("Device %i: Registered %i events.\n", int(info->id), events_registered);
}
+ }
- // Add X and Y axis
- sprintf(defname, "X %s", devinfo->name());
- devinfo->device()->add_item(defname, ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &devinfo->lightgun.lX);
-
- sprintf(defname, "Y %s", devinfo->name());
- devinfo->device()->add_item(defname, ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &devinfo->lightgun.lY);
-
- // Save the device id
- devinfo->x11_state.deviceid = info->id;
+ // register ourself to handle events from event manager
+ int const event_types[] = { motion_type, button_press_type, button_release_type };
+ osd_printf_verbose("Events types to register: motion:%d, press:%d, release:%d\n", motion_type, button_press_type, button_release_type);
+ subscribe(x11_event_manager::instance(), event_types);
- // Register this device to receive event notifications
- int events_registered = register_events(m_display, info, m_lightgun_map.map[index].name.c_str(), 0);
- osd_printf_verbose("Device %i: Registered %i events.\n", static_cast<int>(info->id), events_registered);
+ osd_printf_verbose("Lightgun: End initialization\n");
+ }
- // register ourself to handle events from event manager
- int event_types[] = { motion_type, button_press_type, button_release_type };
- osd_printf_verbose("Events types to register: motion:%d, press:%d, release:%d\n", motion_type, button_press_type, button_release_type);
- x11_event_manager::instance().subscribe(event_types, ARRAY_LENGTH(event_types), this);
- }
+ virtual void exit() override
+ {
+ // unsubscribe from events
+ unsubscribe();
- osd_printf_verbose("Lightgun: End initialization\n");
+ input_module_impl<x11_input_device, osd_common_t>::exit();
}
- bool should_poll_devices(running_machine &machine) override
+ virtual bool should_poll_devices() override
{
- return sdl_event_manager::instance().has_focus();
+ return osd().has_focus();
}
- void before_poll(running_machine &machine) override
+ virtual void before_poll() override
{
- if (!should_poll_devices(machine))
- return;
+ // trigger the SDL event manager so it can process window events
+ input_module_impl<x11_input_device, osd_common_t>::before_poll();
// Tell the event manager to process events and push them to the devices
- x11_event_manager::instance().process_events(machine);
-
- // Also trigger the SDL event manager so it can process window events
- sdl_event_manager::instance().process_events(machine);
+ if (should_poll_devices())
+ x11_event_manager::instance().process_events();
}
- void handle_event(XEvent &xevent) override
+ virtual void handle_event(XEvent const &xevent) override
{
XID deviceid;
if (xevent.type == motion_type)
{
- XDeviceMotionEvent *motion = reinterpret_cast<XDeviceMotionEvent *>(&xevent);
+ auto const motion = reinterpret_cast<XDeviceMotionEvent const *>(&xevent);
deviceid = motion->deviceid;
}
else if (xevent.type == button_press_type || xevent.type == button_release_type)
{
- XDeviceButtonEvent *button = reinterpret_cast<XDeviceButtonEvent *>(&xevent);
+ auto const button = reinterpret_cast<XDeviceButtonEvent const *>(&xevent);
deviceid = button->deviceid;
}
else
@@ -550,97 +702,27 @@ public:
}
// Figure out which lightgun this event id destined for
- auto target_device = std::find_if(devicelist()->begin(), devicelist()->end(), [deviceid](auto &device)
- {
- std::unique_ptr<device_info> &ptr = device;
- return downcast<x11_input_device*>(ptr.get())->x11_state.deviceid == deviceid;
- });
+ auto target_device = std::find_if(
+ devicelist().begin(),
+ devicelist().end(),
+ [&deviceid] (auto &device) { return device->device_id() == deviceid; });
// If we find a matching lightgun, dispatch the event to the lightgun
- if (target_device != devicelist()->end())
- {
- downcast<x11_input_device*>((*target_device).get())->queue_events(&xevent, 1);
- }
+ if (target_device != devicelist().end())
+ (*target_device)->queue_events(&xevent, 1);
}
+};
-private:
- x11_lightgun_device* create_lightgun_device(running_machine &machine, int index)
- {
- char tempname[20];
-
- if (m_lightgun_map.map[index].name.length() == 0)
- {
- if (m_lightgun_map.initialized)
- {
- snprintf(tempname, ARRAY_LENGTH(tempname), "NC%d", index);
- devicelist()->create_device<x11_lightgun_device>(machine, tempname, tempname, *this);
- }
-
- return nullptr;
- }
-
- return devicelist()->create_device<x11_lightgun_device>(machine, m_lightgun_map.map[index].name.c_str(), m_lightgun_map.map[index].name.c_str(), *this);
- }
-
- void add_lightgun_buttons(XAnyClassPtr first_info_class, int num_classes, x11_lightgun_device *devinfo) const
- {
- XAnyClassPtr any = first_info_class;
-
- for (int i = 0; i < num_classes; i++)
- {
- switch (any->c_class)
- {
- case ButtonClass:
- XButtonInfoPtr b = reinterpret_cast<XButtonInfoPtr>(any);
- for (int button = 0; button < b->num_buttons; button++)
- {
- input_item_id itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + button);
- devinfo->device()->add_item(default_button_name(button), itemid, generic_button_get_state<std::int32_t>, &devinfo->lightgun.buttons[button]);
- }
- break;
- }
+} // anonymous namespace
- any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length);
- }
- }
+} // namespace osd
- void set_lightgun_axis_props(XAnyClassPtr first_info_class, int num_classes, x11_lightgun_device *devinfo) const
- {
- XAnyClassPtr any = first_info_class;
- for (int i = 0; i < num_classes; i++)
- {
- switch (any->c_class)
- {
- case ValuatorClass:
- XValuatorInfoPtr valuator_info = reinterpret_cast<XValuatorInfoPtr>(any);
- XAxisInfoPtr axis_info = reinterpret_cast<XAxisInfoPtr>(reinterpret_cast<char *>(valuator_info) + sizeof(XValuatorInfo));
- for (int j = 0; j < valuator_info->num_axes; j++, axis_info++)
- {
- if (j == 0)
- {
- XI_DBG("Set minx=%d, maxx=%d\n", axis_info->min_value, axis_info->max_value);
- devinfo->x11_state.maxx = axis_info->max_value;
- devinfo->x11_state.minx = axis_info->min_value;
- }
+#else // defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT
- if (j == 1)
- {
- XI_DBG("Set miny=%d, maxy=%d\n", axis_info->min_value, axis_info->max_value);
- devinfo->x11_state.maxy = axis_info->max_value;
- devinfo->x11_state.miny = axis_info->min_value;
- }
- }
- break;
- }
+namespace osd { namespace { MODULE_NOT_SUPPORTED(x11_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "x11") } }
- any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length);
- }
- }
-};
+#endif // defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT
-#else
-MODULE_NOT_SUPPORTED(x11_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "x11")
-#endif
-MODULE_DEFINITION(LIGHTGUN_X11, x11_lightgun_module)
+MODULE_DEFINITION(LIGHTGUN_X11, osd::x11_lightgun_module)
diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp
index 67348ebe162..474ef6fe7ff 100644
--- a/src/osd/modules/input/input_xinput.cpp
+++ b/src/osd/modules/input/input_xinput.cpp
@@ -1,223 +1,2997 @@
// license:BSD-3-Clause
-// copyright-holders:Brad Hughes
+// copyright-holders:Brad Hughes, Vas Crabb
//============================================================
//
// input_xinput.cpp - XInput API input support for Windows
//
//============================================================
+/*
-#include "input_module.h"
-#include "modules/osdmodule.h"
+XInput is infamously inflexible. It currently only supports a single
+controller type (game controller) with a fixed report format. The
+report format includes:
+* 16-bit field for single-bit buttons and D-pad directions (two bits
+ undefined)
+* Two 8-bit axis values, used for triggers, pedals, buttons or throttle
+ and rudder controls, depending on the controller
+* Four 16-bit axis values, used for joysticks, steering wheels or hat
+ switches, depending on the controller
+
+Some button names have changed at various times:
+* Face buttons White and Black became shoulder buttons LB and RB,
+ respectively.
+* Navigation buttons Start and Back became Menu and View, respectively.
+
+Subtype Gamepad Wheel Arcade stick Arcade pad Flight stick Dance pad Guitar Drum kit
+
+D-pad D-pad D-pad Joystick D-pad Foot switch D-Pad/Strum D-pad
+L trigger Trigger Brake Button Button Rudder Pickup select^
+R trigger Trigger Accelerator Button Button Throttle ^
+L stick X Stick Wheel Roll
+L stick Y Stick Pitch
+R stick X Stick Hat left/right Whammy bar
+R stick Y Stick Hat up/down Orientation
+
+A Button Button Button Button Primary fire Foot switch Fret 1 Floor tom
+B Button Button Button Button Secondary fire Foot switch Fret 2 Snare
+X Button Button Button Button Button Foot switch Fret 4 Low tom
+Y Button Button Button Button Button Foot switch Fret 3 High tom
+LB Button Button Button^ Button Button^ Fret 5 Bass drum
+RB Button Button Button^ Button Button^ Button^ Button^
+LSB Button Button^ Button^ Button^ Button^ Fret modifier^ Button^
+RSB Button Button^ Button^ Button^ Button^ Button^ Button^
+
+^ optional
+
+
+At least the vast majority of controllers report 8-bit trigger
+resolution and 10-bit stick resolution, even when the physical controls
+use digital switches. Resolution can't be used to reliably detect
+nominal analog axes controlled by switches.
+
+Some arcade sticks report unknown or gamepad subtype, but have a single
+digital joystick with a switch to select between controlling the D-pad,
+left stick and right stick. You can't assume that all three can be
+controlled at the same time.
+
+Many controllers don't correctly report the absence of analog sticks.
+
+
+Some controllers use capabilities to indicate extended controller type
+information:
+
+ Type Sub LSX LSY RSX
+Band Hero Wireless Guitar 0x01 0x07 0x1430 0x0705 0x0001
+Band Hero Wireless Drum Kit 0x01 0x08 0x1430 0x0805 0x0001
+DJ Hero 2 Turntable 0x01 0x17 0x1430 0x1705 0x0001
+Rock Band 3 Wireless Keyboard 0x01 0x0f 0x1bad 0x1330 0x0004
+
+
+There are multiple physical button layouts for arcade sticks, for
+example:
+
+Gamester Xbox Arcade Stick, Hori Real Arcade Pro EX
+LT X Y LB
+RT A B RB
+
+PXN 0082 Arcade Stick
+LB X Y RB
+LT A B RT
+
+Mortal Kombat Tournament Edition Arcade Stick
+ X Y
+ RT
+LB A B
+
+Hori Fighting Stick EX2
+ B X Y
+A LT RT
+
+Hori Real Arcade Pro VX-SA Kai, Razer Atrox
+ B X Y LB
+A LT RT RB
+
+Hori Real Arcade Pro.V Kai, Mad Catz EGO Arcade Stick, Mayflash F300, Mayflash F500
+ X Y RB LB
+A B RT LT
+
+Mad Catz WWE All Stars Brawl Stick
+ X Y LB LT
+A B RB RT
+
+Arcade pads typically have six face buttons, and come with different
+layouts corresponding to the latter two arcade stick layouts, with the
+rightmost column on the shoulder buttons. Examples of face button
+layouts:
+
+Hori Fighting Commander OCTA, Mad Catz Street Fighter IV FightPad, PowerA FUSION Wired FightPad
+X Y RB
+A B RT
-#if defined(OSD_WINDOWS)
+Hori Pad EX Turbo 2, Mad Catz WWE All Stars BrawlPad, Mortal Kombat X Fight Pad, PDP Versus Fighting Pad
+X Y LB
+A B RB
-// standard windows headers
-#include <windows.h>
-// XInput header
-#include <xinput.h>
+Dance mats usually have this layout:
+BK ST
+B U A
+L R
+Y D X
-#undef interface
+This layout seems somewhat unusual:
+BK ST
+A U B
+L R
+X D Y
-// MAME headers
-#include "emu.h"
+This layout is also available but rare:
+BK ST
+A U B
+L R
+Y D X
-// MAMEOS headers
-#include "winutil.h"
-#include "winmain.h"
-#include "input_common.h"
-#include "input_windows.h"
+Drum kits also have multiple layouts.
+
+Rock Band:
+B Y X A
+ LB
+
+Guitar Hero:
+ Y RB
+A X B
+ LB
+
+
+Rock Band keyboards use axes as bit fields:
+
+LT 7 C
+LT 6 C#
+LT 5 D
+LT 4 D#
+LT 3 E
+LT 2 F
+LT 1 F#
+LT 0 G
+RT 7 G#
+RT 6 A
+RT 5 A#
+RT 4 B
+RT 3 C
+RT 2 C#
+RT 1 D
+RT 0 D#
+LSX 7 E
+LSX 6 F
+LSX 5 F#
+LSX 4 G
+LSX 3 G#
+LSX 2 A
+LSX 1 A#
+LSX 0 B
+LSX 15 C
+
+*/
+
+#include "modules/osdmodule.h"
+
+#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
#include "input_xinput.h"
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#include "assignmenthelper.h"
+
+#include "interface/inputseq.h"
+#include "modules/lib/osdobj_common.h"
+
+// emu
+#include "inpttype.h"
+
+// lib/util
+#include "util/coretmpl.h"
+
+#include "eminline.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <string>
+#include <tuple>
+#include <utility>
+
+
#define XINPUT_LIBRARIES { "xinput1_4.dll", "xinput9_1_0.dll" }
-#else
-#define XINPUT_LIBRARIES { "xinput1_4.dll" }
-#endif
-int xinput_api_helper::initialize()
+#define XINPUT_AXIS_MINVALUE (-32'767)
+#define XINPUT_AXIS_MAXVALUE (32'767)
+
+namespace osd {
+
+namespace {
+
+using util::BIT;
+
+
+char const *const AXIS_NAMES_GAMEPAD[]{
+ "LSX",
+ "LSY",
+ "RSX",
+ "RSY",
+ "LT",
+ "RT" };
+
+char const *const AXIS_NAMES_WHEEL[]{
+ "Wheel",
+ "LSY",
+ "RSX",
+ "RSY",
+ "Brake",
+ "Accelerator" };
+
+char const *const AXIS_NAMES_GUITAR[]{
+ "LSX",
+ "LSY",
+ "Whammy Bar",
+ "Orientation",
+ "Pickup Selector",
+ "RT" };
+
+input_item_id const AXIS_IDS_GAMEPAD[]{
+ ITEM_ID_XAXIS,
+ ITEM_ID_YAXIS,
+ ITEM_ID_ZAXIS,
+ ITEM_ID_RZAXIS,
+ ITEM_ID_SLIDER1,
+ ITEM_ID_SLIDER2 };
+
+char const *const HAT_NAMES_GAMEPAD[]{
+ "D-pad Up",
+ "D-pad Down",
+ "D-pad Left",
+ "D-pad Right",
+ "POV Hat Up",
+ "POV Hat Down",
+ "POV Hat Left",
+ "POV Hat Right" };
+
+char const *const HAT_NAMES_ARCADE_STICK[]{
+ "Joystick Up",
+ "Joystick Down",
+ "Joystick Left",
+ "Joystick Right",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr };
+
+char const *const HAT_NAMES_GUITAR[]{
+ "Strum/D-pad Up",
+ "Strum/D-pad Down",
+ "D-pad Left",
+ "D-pad Right" };
+
+char const *const BUTTON_NAMES_GAMEPAD[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "LT",
+ "RT",
+ "LB",
+ "RB",
+ "LSB",
+ "RSB" };
+
+char const *const BUTTON_NAMES_FLIGHT_STICK[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "LB",
+ "RB",
+ "LSB",
+ "RSB" };
+
+char const *const BUTTON_NAMES_GUITAR[]{
+ "Fret 1",
+ "Fret 2",
+ "Fret 3",
+ "Fret 4",
+ "Fret 5",
+ "Fret Modifier",
+ "RB",
+ "RSB" };
+
+char const *const BUTTON_NAMES_DRUMKIT[]{
+ "Green", // floor tom
+ "Red", // snare
+ "Blue", // low tom
+ "Yellow", // Rock Band high tom, Guitar Hero hi-hat
+ "Orange", // Guitar Hero crash cymbal
+ "Bass Drum",
+ "LSB",
+ "RSB" };
+
+char const *const BUTTON_NAMES_KEYBOARD[]{
+ "A",
+ "B",
+ "X",
+ "Y",
+ "LB",
+ "RB",
+ "LSB",
+ "RSB" };
+
+
+
+//============================================================
+// base class for XInput controller handlers
+//============================================================
+
+class xinput_device_base : public device_info, protected joystick_assignment_helper
{
- m_xinput_dll = osd::dynamic_module::open(XINPUT_LIBRARIES);
+protected:
+ xinput_device_base(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
- XInputGetState = m_xinput_dll->bind<xinput_get_state_fn>("XInputGetState");
- XInputGetCapabilities = m_xinput_dll->bind<xinput_get_caps_fn>("XInputGetCapabilities");
+ // capabilities
+ BYTE device_type() const { return m_capabilities.Type; }
+ BYTE device_subtype() const { return m_capabilities.SubType; }
+ bool has_button(WORD mask) const { return (m_capabilities.Gamepad.wButtons & mask) != 0; }
+ bool has_trigger_left() const { return m_capabilities.Gamepad.bLeftTrigger != 0; }
+ bool has_trigger_right() const { return m_capabilities.Gamepad.bRightTrigger != 0; }
+ bool has_thumb_left_x() const { return m_capabilities.Gamepad.sThumbLX != 0; }
+ bool has_thumb_left_y() const { return m_capabilities.Gamepad.sThumbLY != 0; }
+ bool has_thumb_right_x() const { return m_capabilities.Gamepad.sThumbRX != 0; }
+ bool has_thumb_right_y() const { return m_capabilities.Gamepad.sThumbRY != 0; }
- if (!XInputGetState || !XInputGetCapabilities)
+ // device state
+ WORD buttons() const { return m_xinput_state.Gamepad.wButtons; }
+ BYTE trigger_left() const { return m_xinput_state.Gamepad.bLeftTrigger; }
+ BYTE trigger_right() const { return m_xinput_state.Gamepad.bRightTrigger; }
+ SHORT thumb_left_x() const { return m_xinput_state.Gamepad.sThumbLX; }
+ SHORT thumb_left_y() const { return m_xinput_state.Gamepad.sThumbLY; }
+ SHORT thumb_right_x() const { return m_xinput_state.Gamepad.sThumbRX; }
+ SHORT thumb_right_y() const { return m_xinput_state.Gamepad.sThumbRY; }
+
+ bool read_state();
+ bool is_reset() const { return m_reset; }
+ void set_reset() { m_reset = true; }
+
+protected:
+ template <unsigned M, unsigned N>
+ static bool assign_ui_button(
+ input_device::assignment_vector &assignments,
+ ioport_type type,
+ unsigned preferred,
+ input_item_id (&switch_ids)[M],
+ unsigned const (&numbered_buttons)[N],
+ unsigned button_count);
+
+ template <unsigned M, unsigned N>
+ static void assign_ui_actions(
+ input_device::assignment_vector &assignments,
+ unsigned preferred_back,
+ unsigned preferred_clear,
+ unsigned preferred_help,
+ unsigned start,
+ unsigned back,
+ input_item_id (&switch_ids)[M],
+ unsigned const (&numbered_buttons)[N],
+ unsigned button_count);
+
+private:
+ bool probe_extended_type();
+
+ u32 const m_player_index;
+ XINPUT_CAPABILITIES m_capabilities;
+ XINPUT_STATE m_xinput_state;
+ bool m_reset;
+
+ xinput_api_helper const &m_xinput_helper;
+};
+
+
+xinput_device_base::xinput_device_base(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ device_info(std::move(name), std::move(id), module),
+ m_player_index(player),
+ m_capabilities(caps),
+ m_xinput_state{ 0 },
+ m_reset(true),
+ m_xinput_helper(helper)
+{
+ // get friendly names for controller type and subtype
+ char const *type_name = "unsupported";
+ char const *subtype_name = "unsupported";
+ switch (m_capabilities.Type)
{
- osd_printf_verbose("Could not find XInput. Please try to reinstall DirectX runtime package.\n");
- return -1;
+ case XINPUT_DEVTYPE_GAMEPAD:
+ type_name = "game controller";
+ switch (m_capabilities.SubType)
+ {
+ case 0x00: // XINPUT_DEVSUBTYPE_UNKNOWN: work around MinGW header issues
+ subtype_name = "unknown";
+ break;
+ case XINPUT_DEVSUBTYPE_GAMEPAD:
+ subtype_name = "gamepad";
+ break;
+ case XINPUT_DEVSUBTYPE_WHEEL:
+ subtype_name = "wheel";
+ break;
+ case XINPUT_DEVSUBTYPE_ARCADE_STICK:
+ subtype_name = "arcade stick";
+ break;
+ case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues
+ subtype_name = "flight stick";
+ break;
+ case XINPUT_DEVSUBTYPE_DANCE_PAD:
+ subtype_name = "dance pad";
+ break;
+ case XINPUT_DEVSUBTYPE_GUITAR:
+ subtype_name = "guitar";
+ break;
+ case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
+ subtype_name = "alternate guitar";
+ break;
+ case XINPUT_DEVSUBTYPE_DRUM_KIT:
+ subtype_name = "drum kit";
+ break;
+ case XINPUT_DEVSUBTYPE_GUITAR_BASS:
+ subtype_name = "bass guitar";
+ break;
+ case XINPUT_DEVSUBTYPE_ARCADE_PAD:
+ subtype_name = "arcade pad";
+ break;
+ }
+ break;
}
- return 0;
+ // detect invalid axis resolutions
+ bool const ltcap_bad = m_capabilities.Gamepad.bLeftTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bLeftTrigger << 24);
+ bool const rtcap_bad = m_capabilities.Gamepad.bRightTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bRightTrigger << 24);
+ bool const lsxcap_bad = m_capabilities.Gamepad.sThumbLX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLX << 16);
+ bool const lsycap_bad = m_capabilities.Gamepad.sThumbLY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLY << 16);
+ bool const rsxcap_bad = m_capabilities.Gamepad.sThumbRX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRX << 16);
+ bool const rsycap_bad = m_capabilities.Gamepad.sThumbRY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRY << 16);
+
+ // log some diagnostic information
+ osd_printf_verbose(
+ "XInput: Configuring player %d type 0x%02X (%s) sub type 0x%02X (%s).\n",
+ m_player_index + 1,
+ m_capabilities.Type,
+ type_name,
+ m_capabilities.SubType,
+ subtype_name);
+ osd_printf_verbose(
+ "XInput: Switch capabilities A=%d B=%d X=%d Y=%d LB=%d RB=%d LSB=%d RSB=%d Start=%d Back=%d Up=%d Down=%d Left=%d Right=%d.\n",
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1 : 0,
+ (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1 : 0);
+ osd_printf_verbose(
+ "XInput: Axis capabilities LT=0x%02X (%d-bit%s) RT=0x%02X (%d-bit%s) LSX=0x%04X (%d-bit%s) LSY=0x%04X (%d-bit%s) RSX=0x%04X (%d-bit%s) RSY=0x%04X (%d-bit%s).\n",
+ m_capabilities.Gamepad.bLeftTrigger,
+ count_leading_ones_32(u32(m_capabilities.Gamepad.bLeftTrigger) << 24),
+ ltcap_bad ? ", invalid" : "",
+ m_capabilities.Gamepad.bRightTrigger,
+ count_leading_ones_32(u32(m_capabilities.Gamepad.bRightTrigger) << 24),
+ rtcap_bad ? ", invalid" : "",
+ m_capabilities.Gamepad.sThumbLX,
+ count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbLX)) << 16),
+ lsxcap_bad ? ", invalid" : "",
+ m_capabilities.Gamepad.sThumbLY,
+ count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbLY)) << 16),
+ lsycap_bad ? ", invalid" : "",
+ m_capabilities.Gamepad.sThumbRX,
+ count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbRX)) << 16),
+ rsxcap_bad ? ", invalid" : "",
+ m_capabilities.Gamepad.sThumbRY,
+ count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbRY)) << 16),
+ rsycap_bad ? ", invalid" : "");
+
+ // ignore capabilities if invalid
+ if (!probe_extended_type())
+ {
+ bool ignore_caps = false;
+ if (ltcap_bad || rtcap_bad || lsxcap_bad || lsycap_bad || rsxcap_bad || rsycap_bad)
+ {
+ // Retro-Bit Sega Saturn Control Pad reports garbage for axis resolutions and absence of several buttons
+ osd_printf_verbose("XInput: Ignoring invalid capabilities (invalid axis resolution).\n");
+ ignore_caps = true;
+ }
+ else if (!m_capabilities.Gamepad.wButtons && !m_capabilities.Gamepad.bLeftTrigger && !m_capabilities.Gamepad.bRightTrigger && !m_capabilities.Gamepad.sThumbLX && !m_capabilities.Gamepad.sThumbLY && !m_capabilities.Gamepad.sThumbRX && !m_capabilities.Gamepad.sThumbRY)
+ {
+ // 8BitDo SN30 Pro V1 reports no controls at all, which would be completely useless
+ osd_printf_verbose("XInput: Ignoring invalid capabilities (no controls reported).\n");
+ ignore_caps = true;
+ }
+ if (ignore_caps)
+ {
+ m_capabilities.Gamepad.wButtons = 0xf3ff;
+ m_capabilities.Gamepad.bLeftTrigger = 0xff;
+ m_capabilities.Gamepad.bRightTrigger = 0xff;
+ m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0));
+ m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0));
+ m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0));
+ m_capabilities.Gamepad.sThumbRY = s16(u16(0xffc0));
+ }
+ }
}
+
+bool xinput_device_base::read_state()
+{
+ // save previous packet number and try to read peripheral state
+ DWORD const prevpacket = m_xinput_state.dwPacketNumber;
+ HRESULT const result = m_xinput_helper.xinput_get_state(m_player_index, &m_xinput_state);
+
+ // only update if it succeeded and the packed number changed
+ if (FAILED(result))
+ {
+ return false;
+ }
+ else if (m_reset)
+ {
+ m_reset = false;
+ return true;
+ }
+ else
+ {
+ return prevpacket != m_xinput_state.dwPacketNumber;
+ }
+}
+
+
+template <unsigned M, unsigned N>
+bool xinput_device_base::assign_ui_button(
+ input_device::assignment_vector &assignments,
+ ioport_type type,
+ unsigned preferred,
+ input_item_id (&switch_ids)[M],
+ unsigned const (&numbered_buttons)[N],
+ unsigned button_count)
+{
+ assert(N >= button_count);
+
+ // use preferred button if available
+ if (add_button_assignment(assignments, type, { switch_ids[preferred] }))
+ {
+ switch_ids[preferred] = ITEM_ID_INVALID;
+ return true;
+ }
+
+ // otherwise find next available button
+ for (unsigned i = 0; button_count > i; ++i)
+ {
+ if (add_button_assignment(assignments, type, { switch_ids[numbered_buttons[i]] }))
+ {
+ switch_ids[numbered_buttons[i]] = ITEM_ID_INVALID;
+ return true;
+ }
+ }
+
+ // didn't find a suitable button
+ return false;
+}
+
+
+template <unsigned M, unsigned N>
+void xinput_device_base::assign_ui_actions(
+ input_device::assignment_vector &assignments,
+ unsigned preferred_back,
+ unsigned preferred_clear,
+ unsigned preferred_help,
+ unsigned start,
+ unsigned back,
+ input_item_id (&switch_ids)[M],
+ unsigned const (&numbered_buttons)[N],
+ unsigned button_count)
+{
+ // the first button is always UI select if present, or we can fall back to start
+ if (1U <= button_count)
+ {
+ add_button_assignment(assignments, IPT_UI_SELECT, { switch_ids[numbered_buttons[0]] });
+ switch_ids[numbered_buttons[0]] = ITEM_ID_INVALID;
+ }
+ else if (add_button_assignment(assignments, IPT_UI_SELECT, { switch_ids[start] }))
+ {
+ switch_ids[start] = ITEM_ID_INVALID;
+ }
+
+ // UI clear is usually X
+ assign_ui_button(
+ assignments,
+ IPT_UI_CLEAR,
+ preferred_clear,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+
+ // UI back can fall back from B to the back button
+ bool const assigned_back = assign_ui_button(
+ assignments,
+ IPT_UI_BACK,
+ preferred_back,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+ if (!assigned_back)
+ {
+ if (add_button_assignment(assignments, IPT_UI_BACK, { switch_ids[back] }))
+ switch_ids[back] = ITEM_ID_INVALID;
+ }
+
+ // help takes Y if present
+ assign_ui_button(
+ assignments,
+ IPT_UI_HELP,
+ preferred_help,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+}
+
+
+bool xinput_device_base::probe_extended_type()
+{
+ switch (m_capabilities.Gamepad.sThumbLX)
+ {
+ case 0x1430:
+ switch (m_capabilities.Gamepad.sThumbLY)
+ {
+ case 0x0705:
+ osd_printf_verbose("XInput: Detected Band Hero guitar controller.\n");
+ m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0)); // neck slider
+ m_capabilities.Gamepad.sThumbLY = 0;
+ m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // whammy bar
+ return true;
+ case 0x0805:
+ osd_printf_verbose("XInput: Detected Band Hero drum kit controller.\n");
+ m_capabilities.Gamepad.sThumbLX = 0;
+ m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); // green/red velocity
+ m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // blue/yellow velocity
+ return true;
+ case 0x1705:
+ osd_printf_verbose("XInput: Detected DJ Hero turntable controller.\n");
+ m_capabilities.Gamepad.sThumbLX = 0;
+ m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); // turntable
+ m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // effects dial
+ return true;
+ }
+ break;
+ case 0x1bad:
+ switch (m_capabilities.Gamepad.sThumbLY)
+ {
+ case 0x1330:
+ osd_printf_verbose("XInput: Detected Rock Band keyboard controller.\n");
+ m_capabilities.Gamepad.sThumbLX = 0; // keys, velocity
+ m_capabilities.Gamepad.sThumbLY = 0; // not present?
+ m_capabilities.Gamepad.sThumbRX = 0; // not present?
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+
+
//============================================================
-// create_xinput_device
+// general XInput controller handler
//============================================================
-xinput_joystick_device * xinput_api_helper::create_xinput_device(running_machine &machine, UINT index, wininput_module &module)
+class xinput_joystick_device : public xinput_device_base
{
- xinput_joystick_device *devinfo;
+public:
+ xinput_joystick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
- XINPUT_CAPABILITIES caps = { 0 };
- if (FAILED(xinput_get_capabilities(index, 0, &caps)))
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ static inline constexpr USHORT SWITCH_BITS[] =
{
- // If we can't get the capabilities skip this device
- return nullptr;
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+
+ enum
+ {
+ SWITCH_A, // button bits
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_LB,
+ SWITCH_RB,
+ SWITCH_LSB,
+ SWITCH_RSB,
+ SWITCH_START,
+ SWITCH_BACK,
+
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_LT, // for arcade stick/pad with LT/RT buttons
+ SWITCH_RT,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_LT, // half-axes for triggers
+ AXIS_RT,
+
+ AXIS_LSX, // full-precision axes
+ AXIS_LSY,
+ AXIS_RSX,
+ AXIS_RSY,
+
+ AXIS_TOTAL
+ };
+
+ static bool assign_pedal(
+ input_device::assignment_vector &assignments,
+ bool fallback_shoulder,
+ ioport_type type,
+ input_item_id preferred_axis,
+ input_item_id fallback_axis1,
+ input_item_id fallback_axis2,
+ input_item_modifier fallback_axis_modifier,
+ input_item_id trigger_button,
+ input_item_id shoulder_button,
+ input_item_id numbered_button);
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+};
+
+
+xinput_joystick_device::xinput_joystick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_joystick_device::poll(bool relative_reset)
+{
+ // poll the device first, and skip if nothing changed
+ if (!read_state())
+ return;
+
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
+
+ // translate the triggers onto the negative side of the axes
+ m_axes[AXIS_LT] = -normalize_absolute_axis(trigger_left(), -255, 255);
+ m_axes[AXIS_RT] = -normalize_absolute_axis(trigger_right(), -255, 255);
+
+ // translate full-precision axes - Y direction is opposite to what MAME uses
+ m_axes[AXIS_LSX] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_RSX] = normalize_absolute_axis(thumb_right_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_RSY] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+
+ // translate LT/RT switches for arcade sticks/pads
+ m_switches[SWITCH_LT] = (0x80 <= trigger_left()) ? 0xff : 0x00;
+ m_switches[SWITCH_RT] = (0x80 <= trigger_right()) ? 0xff : 0x00;
+}
+
+
+void xinput_joystick_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_joystick_device::configure(input_device &device)
+{
+ // TODO: proper support for dance mat controllers
+
+ // default characteristics for a gamepad
+ bool button_diamond = true;
+ bool pedal_fallback_shoulder = true;
+ bool lt_rt_button = false;
+ char const *const *axis_names = AXIS_NAMES_GAMEPAD;
+ input_item_id const *preferred_axis_ids = AXIS_IDS_GAMEPAD;
+ char const *const *hat_names = HAT_NAMES_GAMEPAD;
+ char const *const *button_names = BUTTON_NAMES_GAMEPAD;
+
+ // consider the device type to decide how to map controls
+ switch (device_type())
+ {
+ case XINPUT_DEVTYPE_GAMEPAD:
+ switch (device_subtype())
+ {
+ case XINPUT_DEVSUBTYPE_WHEEL:
+ pedal_fallback_shoulder = false;
+ axis_names = AXIS_NAMES_WHEEL;
+ break;
+ case XINPUT_DEVSUBTYPE_ARCADE_STICK:
+ button_diamond = false;
+ lt_rt_button = true;
+ hat_names = HAT_NAMES_ARCADE_STICK;
+ break;
+ case XINPUT_DEVSUBTYPE_DANCE_PAD:
+ // TODO: proper support
+ button_diamond = false;
+ break;
+ case XINPUT_DEVSUBTYPE_ARCADE_PAD:
+ button_diamond = false;
+ lt_rt_button = true;
+ break;
+ }
+ break;
}
- char device_name[16];
- snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1);
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id axis_ids[AXIS_TOTAL];
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
- // allocate the device object
- devinfo = module.devicelist()->create_device<xinput_joystick_device>(machine, device_name, device_name, module, shared_from_this());
+ // add bidirectional axes
+ bool const axis_caps[]{
+ has_thumb_left_x(),
+ has_thumb_left_y(),
+ has_thumb_right_x(),
+ has_thumb_right_y() };
+ for (unsigned i = 0; std::size(axis_caps) > i; ++i)
+ {
+ if (axis_caps[i])
+ {
+ axis_ids[AXIS_LSX + i] = device.add_item(
+ axis_names[i],
+ std::string_view(),
+ preferred_axis_ids[i],
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_LSX + i]);
+ }
+ else
+ {
+ axis_ids[AXIS_LSX + i] = ITEM_ID_INVALID;
+ }
+ }
- // Set the player ID
- devinfo->xinput_state.player_index = index;
+ // add hats
+ bool const hat_caps[]{
+ has_button(XINPUT_GAMEPAD_DPAD_UP),
+ has_button(XINPUT_GAMEPAD_DPAD_DOWN),
+ has_button(XINPUT_GAMEPAD_DPAD_LEFT),
+ has_button(XINPUT_GAMEPAD_DPAD_RIGHT) };
+ for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
+ {
+ if (hat_caps[i])
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ hat_names[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
+ }
- // Assign the caps we captured earlier
- devinfo->xinput_state.caps = caps;
+ // add buttons
+ std::pair<unsigned, bool> const button_caps[]{
+ { SWITCH_A, has_button(XINPUT_GAMEPAD_A) },
+ { SWITCH_B, has_button(XINPUT_GAMEPAD_B) },
+ { SWITCH_X, has_button(XINPUT_GAMEPAD_X) },
+ { SWITCH_Y, has_button(XINPUT_GAMEPAD_Y) },
+ { SWITCH_LT, lt_rt_button && has_trigger_left() },
+ { SWITCH_RT, lt_rt_button && has_trigger_right() },
+ { SWITCH_LB, has_button(XINPUT_GAMEPAD_LEFT_SHOULDER) },
+ { SWITCH_RB, has_button(XINPUT_GAMEPAD_RIGHT_SHOULDER) },
+ { SWITCH_LSB, has_button(XINPUT_GAMEPAD_LEFT_THUMB) },
+ { SWITCH_RSB, has_button(XINPUT_GAMEPAD_RIGHT_THUMB) } };
+ input_item_id button_id = ITEM_ID_BUTTON1;
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1];
+ for (unsigned i = 0; std::size(button_caps) > i; ++i)
+ {
+ auto const [offset, supported] = button_caps[i];
+ if (supported)
+ {
+ switch_ids[offset] = device.add_item(
+ button_names[i],
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[offset]);
+ numbered_buttons[button_count] = offset;
+
+ // use these for automatically numbered buttons
+ assignments.emplace_back(
+ ioport_type(IPT_BUTTON1 + button_count++),
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[offset])));
+ }
+ }
+
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
+ {
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
- return devinfo;
+ // add triggers/pedals
+ if (!lt_rt_button)
+ {
+ for (unsigned i = 0; (AXIS_RT - AXIS_LT) >= i; ++i)
+ {
+ if (i ? has_trigger_right() : has_trigger_left())
+ {
+ axis_ids[AXIS_LT + i] = device.add_item(
+ axis_names[std::size(axis_caps) + i],
+ std::string_view(),
+ preferred_axis_ids[std::size(axis_caps) + i],
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_LT + i]);
+ }
+ else
+ {
+ axis_ids[AXIS_LT + i] = ITEM_ID_INVALID;
+ }
+ }
+ }
+
+ // try to get a "complete" joystick for primary movement controls
+ input_item_id directional_axes[2][2];
+ choose_primary_stick(
+ directional_axes,
+ axis_ids[AXIS_LSX],
+ axis_ids[AXIS_LSY],
+ axis_ids[AXIS_RSX],
+ axis_ids[AXIS_RSY]);
+
+ // now set up controls using the primary joystick
+ add_directional_assignments(
+ assignments,
+ directional_axes[0][0],
+ directional_axes[0][1],
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN]);
+
+ // assign a secondary stick axis to joystick Z if available
+ bool const stick_z = add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { directional_axes[1][1], directional_axes[1][0] });
+ if (!stick_z)
+ {
+ // if both triggers are present, combine them, or failing that, fall back to a pair of buttons
+ if ((ITEM_ID_INVALID != axis_ids[AXIS_LT]) && (ITEM_ID_INVALID != axis_ids[AXIS_RT]))
+ {
+ assignments.emplace_back(
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ input_seq(
+ make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axis_ids[AXIS_LT]),
+ make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_REVERSE, axis_ids[AXIS_RT])));
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB]))
+ {
+ // took shoulder buttons
+ }
+ else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, switch_ids[SWITCH_LT], switch_ids[SWITCH_RT]))
+ {
+ // took trigger buttons
+ }
+ }
+
+ // prefer trigger axes for pedals, otherwise take half axes and buttons
+ unsigned pedal_button = 0;
+ bool const pedal1_numbered_button = assign_pedal(
+ assignments,
+ pedal_fallback_shoulder,
+ IPT_PEDAL,
+ axis_ids[AXIS_RT],
+ directional_axes[1][1],
+ directional_axes[0][1],
+ ITEM_MODIFIER_NEG,
+ switch_ids[SWITCH_RT],
+ switch_ids[SWITCH_RB],
+ (pedal_button < button_count)
+ ? switch_ids[numbered_buttons[pedal_button]]
+ : ITEM_ID_INVALID);
+ if (pedal1_numbered_button)
+ ++pedal_button;
+ bool const pedal2_numbered_button = assign_pedal(
+ assignments,
+ pedal_fallback_shoulder,
+ IPT_PEDAL2,
+ axis_ids[AXIS_LT],
+ directional_axes[1][1],
+ directional_axes[0][1],
+ ITEM_MODIFIER_POS,
+ switch_ids[SWITCH_LT],
+ switch_ids[SWITCH_LB],
+ (pedal_button < button_count)
+ ? switch_ids[numbered_buttons[pedal_button]]
+ : ITEM_ID_INVALID);
+ if (pedal2_numbered_button)
+ ++pedal_button;
+ if (pedal_button < button_count)
+ {
+ input_item_id const pedal_button_id = switch_ids[numbered_buttons[pedal_button]];
+ assignments.emplace_back(
+ IPT_PEDAL3,
+ SEQ_TYPE_INCREMENT,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, pedal_button_id)));
+ }
+
+ // potentially use thumb sticks and/or D-pad and A/B/X/Y diamond for twin sticks
+ add_twin_stick_assignments(
+ assignments,
+ axis_ids[AXIS_LSX],
+ axis_ids[AXIS_LSY],
+ axis_ids[AXIS_RSX],
+ axis_ids[AXIS_RSY],
+ button_diamond ? switch_ids[SWITCH_DPAD_LEFT] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_DPAD_RIGHT] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_DPAD_UP] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_DPAD_DOWN] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_X] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_B] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_Y] : ITEM_ID_INVALID,
+ button_diamond ? switch_ids[SWITCH_A] : ITEM_ID_INVALID);
+
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+
+ // try to get a matching pair of buttons for previous/next group
+ if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_LT], switch_ids[SWITCH_RT]))
+ {
+ // took digital triggers
+ }
+ else if (consume_trigger_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_LT], axis_ids[AXIS_RT]))
+ {
+ // took analog triggers
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, directional_axes[1][1]))
+ {
+ // took secondary Y
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, directional_axes[1][0]))
+ {
+ // took secondary X
+ }
+
+ // try to assign secondary stick to page up/down
+ consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, directional_axes[1][1]);
+
+ // put focus previous/next on the shoulder buttons if available - this can be overloaded with zoom
+ if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB]))
+ {
+ // took shoulder buttons
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, directional_axes[1][0]))
+ {
+ // took secondary X
+ }
+
+ // put zoom on the secondary stick if available, or fall back to shoulder buttons
+ if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, directional_axes[1][0]))
+ {
+ // took secondary X
+ if (axis_ids[AXIS_LSX] == directional_axes[1][0])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { switch_ids[SWITCH_LSB] });
+ else if (axis_ids[AXIS_RSX] == directional_axes[1][0])
+ add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { switch_ids[SWITCH_RSB] });
+ directional_axes[1][0] = ITEM_ID_INVALID;
+ }
+ else if (consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB]))
+ {
+ // took shoulder buttons
+ }
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
+}
+
+
+bool xinput_joystick_device::assign_pedal(
+ input_device::assignment_vector &assignments,
+ bool fallback_shoulder,
+ ioport_type type,
+ input_item_id preferred_axis,
+ input_item_id fallback_axis1,
+ input_item_id fallback_axis2,
+ input_item_modifier fallback_axis_modifier,
+ input_item_id trigger_button,
+ input_item_id shoulder_button,
+ input_item_id numbered_button)
+{
+ // first try the preferred trigger/pedal axis
+ if (ITEM_ID_INVALID != preferred_axis)
+ {
+ assignments.emplace_back(
+ type,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, preferred_axis)));
+ return false;
+ }
+
+ // try adding half a joystick axis
+ add_assignment(
+ assignments,
+ type,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ fallback_axis_modifier,
+ { fallback_axis1, fallback_axis2 });
+
+ // try a trigger button
+ if (ITEM_ID_INVALID != trigger_button)
+ {
+ assignments.emplace_back(
+ type,
+ SEQ_TYPE_INCREMENT,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, trigger_button)));
+ return false;
+ }
+
+ // try a shoulder button if appropriate
+ if (fallback_shoulder && (ITEM_ID_INVALID != shoulder_button))
+ {
+ assignments.emplace_back(
+ type,
+ SEQ_TYPE_INCREMENT,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, shoulder_button)));
+ return false;
+ }
+
+ // if no numbered button, nothing can be done
+ if (ITEM_ID_INVALID == numbered_button)
+ return false;
+
+ // last resort
+ assignments.emplace_back(
+ type,
+ SEQ_TYPE_INCREMENT,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, numbered_button)));
+ return true;
}
+
+
//============================================================
-// xinput_joystick_device
+// XInput flight stick handler
//============================================================
-xinput_joystick_device::xinput_joystick_device(running_machine &machine, const char *name, char const *id, input_module &module, std::shared_ptr<xinput_api_helper> helper)
- : device_info(machine, name, id, DEVICE_CLASS_JOYSTICK, module),
- gamepad({{0}}),
- xinput_state({0}),
- m_xinput_helper(helper),
- m_configured(false)
+class xinput_flight_stick_device : public xinput_device_base
{
+public:
+ xinput_flight_stick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ static inline constexpr USHORT SWITCH_BITS[] =
+ {
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+
+ enum
+ {
+ SWITCH_A, // button bits
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_LB,
+ SWITCH_RB,
+ SWITCH_LSB,
+ SWITCH_RSB,
+ SWITCH_START,
+ SWITCH_BACK,
+
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_HAT_UP, // for POV hat as right stick
+ SWITCH_HAT_DOWN,
+ SWITCH_HAT_LEFT,
+ SWITCH_HAT_RIGHT,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_RUDDER, // LT/RT mapped as bidirectional axes
+ AXIS_THROTTLE,
+
+ AXIS_X, // full-precision axes
+ AXIS_Y,
+
+ AXIS_TOTAL
+ };
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+};
+
+
+xinput_flight_stick_device::xinput_flight_stick_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
}
-void xinput_joystick_device::poll()
+
+void xinput_flight_stick_device::poll(bool relative_reset)
{
- if (!m_configured)
+ // poll the device first, and skip if nothing changed
+ if (!read_state())
return;
- // poll the device first
- HRESULT result = m_xinput_helper->xinput_get_state(xinput_state.player_index, &xinput_state.xstate);
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
- // If we can't poll the device, skip
- if (FAILED(result))
- return;
+ // translate rudder and throttle
+ m_axes[AXIS_RUDDER] = normalize_absolute_axis(trigger_left(), 0, 255);
+ m_axes[AXIS_THROTTLE] = normalize_absolute_axis(trigger_right(), 0, 255);
+
+ // translate full-precision axes - Y direction is opposite to what MAME uses
+ m_axes[AXIS_X] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_Y] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+
+ // translate right stick as POV hat
+ m_switches[SWITCH_HAT_UP] = (16'384 <= thumb_right_y()) ? 0xff : 0x00;
+ m_switches[SWITCH_HAT_DOWN] = (-16'384 >= thumb_right_y()) ? 0xff : 0x00;
+ m_switches[SWITCH_HAT_LEFT] = (-16'384 >= thumb_right_x()) ? 0xff : 0x00;
+ m_switches[SWITCH_HAT_RIGHT] = (16'384 <= thumb_right_x()) ? 0xff : 0x00;
+}
+
+
+void xinput_flight_stick_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_flight_stick_device::configure(input_device &device)
+{
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
+
+ // add bidirectional axes
+ std::tuple<input_item_id, char const *, bool> const axis_caps[]{
+ { ITEM_ID_RZAXIS, "Rudder", has_trigger_left() },
+ { ITEM_ID_ZAXIS, "Throttle", has_trigger_right() },
+ { ITEM_ID_XAXIS, "Joystick X", has_thumb_left_x() },
+ { ITEM_ID_YAXIS, "Joystick Y", has_thumb_left_y() } };
+ input_item_id axis_ids[AXIS_TOTAL];
+ for (unsigned i = 0; AXIS_TOTAL > i; ++i)
+ {
+ auto const [id, name, supported] = axis_caps[i];
+ if (supported)
+ {
+ axis_ids[i] = device.add_item(
+ name,
+ std::string_view(),
+ id,
+ generic_axis_get_state<s32>,
+ &m_axes[i]);
+ }
+ else
+ {
+ axis_ids[i] = ITEM_ID_INVALID;
+ }
+ }
+
+ // add hats
+ bool const hat_caps[]{
+ has_button(XINPUT_GAMEPAD_DPAD_UP),
+ has_button(XINPUT_GAMEPAD_DPAD_DOWN),
+ has_button(XINPUT_GAMEPAD_DPAD_LEFT),
+ has_button(XINPUT_GAMEPAD_DPAD_RIGHT),
+ has_thumb_right_x(),
+ has_thumb_right_x(),
+ has_thumb_right_y(),
+ has_thumb_right_y() };
+ for (unsigned i = 0; (SWITCH_HAT_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
+ {
+ if (hat_caps[i])
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ HAT_NAMES_GAMEPAD[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
+ }
+
+ // add buttons
+ input_item_id button_id = ITEM_ID_BUTTON1;
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1];
+ for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[i]))
+ {
+ switch_ids[SWITCH_A + i] = device.add_item(
+ BUTTON_NAMES_FLIGHT_STICK[i],
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_A + i]);
+ numbered_buttons[button_count] = SWITCH_A + i;
+
+ // use these for automatically numbered buttons and pedals
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i]));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq);
+ if (3 > button_count)
+ assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq);
+ ++button_count;
+ }
+ }
+
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
+ {
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
- // Copy the XState into State
- // Start with the POV (DPAD)
- for (int povindex = 0; povindex < XINPUT_MAX_POV; povindex++)
+ // use throttle for joystick Z, or rudder if it isn't available
+ add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { axis_ids[AXIS_THROTTLE], axis_ids[AXIS_RUDDER] });
+
+ // if throttle is available, use it for first two pedals, too
+ if (ITEM_ID_INVALID != axis_ids[AXIS_THROTTLE])
{
- int currentPov = xinput_pov_dir[povindex];
- gamepad.povs[povindex] = (xinput_state.xstate.Gamepad.wButtons & currentPov) ? 0xFF : 0;
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axis_ids[AXIS_THROTTLE])));
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axis_ids[AXIS_THROTTLE])));
}
- // Now do the buttons
- for (int buttonindex = 0; buttonindex < XINPUT_MAX_BUTTONS; buttonindex++)
+ // find something to use for directional controls and navigation
+ bool const axis_missing = (ITEM_ID_INVALID == axis_ids[AXIS_X]) || (ITEM_ID_INVALID == axis_ids[AXIS_Y]);
+ bool const hat_complete = has_thumb_right_x() && has_thumb_right_y();
+ if (axis_missing && hat_complete)
+ {
+ // X or Y missing - rely on POV hat
+ add_directional_assignments(
+ assignments,
+ axis_ids[AXIS_X],
+ axis_ids[AXIS_Y],
+ switch_ids[SWITCH_HAT_LEFT],
+ switch_ids[SWITCH_HAT_RIGHT],
+ switch_ids[SWITCH_HAT_UP],
+ switch_ids[SWITCH_HAT_DOWN]);
+
+ // choose something for previous/next group
+ if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_RUDDER]))
+ {
+ // took rudder
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_THROTTLE]))
+ {
+ // took throttle
+ }
+
+ // choose something for zoom and focus previous/next
+ if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_THROTTLE]))
+ {
+ if (!add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER]))
+ add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_THROTTLE]);
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_RUDDER]))
+ {
+ add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER]);
+ }
+ else if (add_button_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ consume_button_pair(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]);
+ }
+ }
+ else
{
- int currentButton = xinput_buttons[buttonindex];
- gamepad.buttons[buttonindex] = (xinput_state.xstate.Gamepad.wButtons & currentButton) ? 0xFF : 0;
+ // only use stick for the primary directional controls
+ add_directional_assignments(
+ assignments,
+ axis_ids[AXIS_X],
+ axis_ids[AXIS_Y],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+
+ // assign the POV hat differently depending on whether rudder and/or throttle are present
+ if ((ITEM_ID_INVALID != axis_ids[AXIS_RUDDER]) || (ITEM_ID_INVALID != axis_ids[AXIS_THROTTLE]))
+ {
+ // previous/next group
+ if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT]))
+ {
+ // took hat left/right
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN]))
+ {
+ // took hat up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_RUDDER]))
+ {
+ // took rudder
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_THROTTLE]))
+ {
+ // took throttle
+ }
+
+ // page up/down
+ if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN]))
+ {
+ // took hat up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+
+ // home/end
+ if (consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+
+ // assign something for zoom - this can overlap with focus previous/next
+ if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_THROTTLE]))
+ {
+ // took throttle
+ }
+ else if (add_button_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_RUDDER]))
+ {
+ // took rudder
+ }
+
+ // assign something for focus previous/next
+ if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER]))
+ {
+ // took rudder
+ }
+ else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+ else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_THROTTLE]))
+ {
+ // took throttle
+ }
+ }
+ else
+ {
+ // previous/next group
+ if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN]))
+ {
+ // took hat up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT]))
+ {
+ // took hat left/right
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+
+ // try to choose something for focus previous/next
+ if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT]))
+ {
+ // took hat left/right - use it for zoom as well
+ consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT]);
+ }
+ else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right - use it for zoom as well
+ consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]);
+ }
+ else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad left/right - use it for zoom as well
+ consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]);
+ }
+
+ // use D-pad for page up/down and home/end if it's still available
+ if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]))
+ {
+ // took D-pad up/down
+ }
+ else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]))
+ {
+ // took D-pad left/right
+ }
+ consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]);
+ }
}
- // Now grab the axis values
- // Each of the thumbstick axis members is a signed value between -32768 and 32767 describing the position of the thumbstick
- // However, the Y axis values are inverted from what MAME expects, so negate the value
- gamepad.left_thumb_x = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
- gamepad.left_thumb_y = normalize_absolute_axis(-xinput_state.xstate.Gamepad.sThumbLY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
- gamepad.right_thumb_x = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
- gamepad.right_thumb_y = normalize_absolute_axis(-xinput_state.xstate.Gamepad.sThumbRY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ button_count);
- // Now the triggers, place them on half-axes (negative side)
- gamepad.left_trigger = -normalize_absolute_axis(xinput_state.xstate.Gamepad.bLeftTrigger, -255, 255);
- gamepad.right_trigger = -normalize_absolute_axis(xinput_state.xstate.Gamepad.bRightTrigger, -255, 255);
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
}
-void xinput_joystick_device::reset()
+
+
+//============================================================
+// XInput guitar handler
+//============================================================
+
+class xinput_guitar_device : public xinput_device_base
{
- memset(&gamepad, 0, sizeof(gamepad));
+public:
+ xinput_guitar_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ static inline constexpr USHORT SWITCH_BITS[] =
+ {
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+
+ enum
+ {
+ SWITCH_FRET1,
+ SWITCH_FRET2,
+ SWITCH_FRET3,
+ SWITCH_FRET4,
+ SWITCH_FRET5,
+ SWITCH_FRET_MOD, // indicates a "solo fret" is pressed, not a separate button
+ SWITCH_RB, // often reported present but no physical button
+ SWITCH_RSB, // usually reported absent
+ SWITCH_START,
+ SWITCH_BACK, // also used for "Star Power" in some games
+
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_SLIDER, // LSX, positive toward bridge
+ AXIS_LSY, // not used on guitar controllers?
+ AXIS_WHAMMY, // RSX, single-ended, neutral at negative extreme
+
+ AXIS_ORIENT_NECK, // RSY
+ AXIS_ORIENT_BRIDGE, // LT, toward zero with frets up
+ AXIS_ORIENT_BODY, // RT, toward zero in right-handed orientation
+
+ AXIS_PICKUP, // LT, positive extreme at one end
+
+ AXIS_TOTAL
+ };
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+};
+
+
+xinput_guitar_device::xinput_guitar_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_guitar_device::poll(bool relative_reset)
+{
+ // poll the device first, and skip if nothing changed
+ if (!read_state())
+ return;
+
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_FRET1 + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
+
+ // translate miscellaneous axes
+ m_axes[AXIS_SLIDER] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_WHAMMY] = -normalize_absolute_axis(s32(thumb_right_x()) + 32'768, -65'535, 65'535);
+
+ // translate orientation sensors
+ m_axes[AXIS_ORIENT_NECK] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ m_axes[AXIS_ORIENT_BRIDGE] = normalize_absolute_axis(trigger_left(), 0, 255);
+ m_axes[AXIS_ORIENT_BODY] = normalize_absolute_axis(trigger_right(), 0, 255);
+
+ // translate pickup selector
+ m_axes[AXIS_PICKUP] = -normalize_absolute_axis(trigger_left(), -255, 255);
+}
+
+
+void xinput_guitar_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_guitar_device::configure(input_device &device)
+{
+ // TODO: does subtype 0x06 indicate digital neck orientation sensor or lack of three-axis accelerometer?
+
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
+
+ // add axes
+ std::tuple<input_item_id, char const *, bool> const axis_caps[]{
+ { ITEM_ID_RXAXIS, "Neck Slider", has_thumb_left_x() },
+ { ITEM_ID_RYAXIS, "LSY", has_thumb_left_y() },
+ { ITEM_ID_SLIDER1, "Whammy Bar", has_thumb_right_x() },
+ { ITEM_ID_YAXIS, "Neck Orientation", has_thumb_right_y() },
+ { ITEM_ID_XAXIS, "Bridge Orientation", has_trigger_left() && has_trigger_right() },
+ { ITEM_ID_ZAXIS, "Body Orientation", has_trigger_right() },
+ { ITEM_ID_SLIDER2, "Pickup Selector", has_trigger_left() && !has_trigger_right() } };
+ input_item_id axis_ids[AXIS_TOTAL];
+ for (unsigned i = 0; AXIS_TOTAL > i; ++i)
+ {
+ auto const [item, name, supported] = axis_caps[i];
+ if (supported)
+ {
+ axis_ids[i] = device.add_item(
+ name,
+ std::string_view(),
+ item,
+ generic_axis_get_state<s32>,
+ &m_axes[i]);
+ }
+ else
+ {
+ axis_ids[i] = ITEM_ID_INVALID;
+ }
+ }
+
+ // add hats
+ for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i]))
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ HAT_NAMES_GUITAR[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
+ }
+
+ // add buttons
+ input_item_id button_id = ITEM_ID_BUTTON1;
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_FRET5 - SWITCH_FRET1 + 1];
+ for (unsigned i = 0; (SWITCH_RSB - SWITCH_FRET1) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[i]))
+ {
+ switch_ids[SWITCH_FRET1 + i] = device.add_item(
+ BUTTON_NAMES_GUITAR[i],
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_FRET1 + i]);
+
+ // use fret buttons for automatically numbered buttons and pedals
+ if ((SWITCH_FRET5 - SWITCH_FRET1) >= i)
+ {
+ numbered_buttons[button_count] = SWITCH_FRET1 + i;
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_FRET1 + i]));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq);
+ if (((ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY]) ? 2 : 3) > button_count)
+ {
+ ioport_type const first_pedal = (ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY])
+ ? IPT_PEDAL2
+ : IPT_PEDAL;
+ assignments.emplace_back(
+ ioport_type(first_pedal + button_count),
+ SEQ_TYPE_INCREMENT,
+ seq);
+ }
+ ++button_count;
+ }
+ }
+ }
+
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
+ {
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
+
+ // use the D-pad for directional controls - accelerometers are an annoyance
+ add_directional_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN]);
+
+ // use the whammy bar for the first pedal and focus next if present
+ if (ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axis_ids[AXIS_WHAMMY])));
+ assignments.emplace_back(
+ IPT_UI_FOCUS_NEXT,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis_ids[AXIS_WHAMMY])));
+ }
+
+ // use the neck slider for a couple of things if it's present
+ if (ITEM_ID_INVALID != axis_ids[AXIS_SLIDER])
+ {
+ input_seq const seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axis_ids[AXIS_SLIDER]));
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, seq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, seq);
+ }
+
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_FRET2,
+ SWITCH_FRET3,
+ SWITCH_FRET4,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
}
-void xinput_joystick_device::configure()
+
+
+//============================================================
+// XInput drum kit handler
+//============================================================
+
+class xinput_drumkit_device : public xinput_device_base
{
- std::lock_guard<std::mutex> scope_lock(m_device_lock);
+public:
+ xinput_drumkit_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ static inline constexpr USHORT SWITCH_BITS[] =
+ {
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+
+ enum
+ {
+ SWITCH_GREEN, // button bits
+ SWITCH_RED,
+ SWITCH_BLUE,
+ SWITCH_YELLOW,
+ SWITCH_ORANGE,
+ SWITCH_BASS_DRUM,
+ SWITCH_LSB,
+ SWITCH_RSB,
+ SWITCH_START,
+ SWITCH_BACK,
- if (m_configured)
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_GREEN, // LSY low
+ AXIS_RED, // LSY high
+ AXIS_BLUE, // RSX high
+ AXIS_YELLOW, // RSX low
+ AXIS_ORANGE, // RSY low
+ AXIS_BASS_DRUM, // RSY high
+
+ AXIS_TOTAL
+ };
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+};
+
+
+xinput_drumkit_device::xinput_drumkit_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_drumkit_device::poll(bool relative_reset)
+{
+ // poll the device first, and skip if nothing changed
+ if (!read_state())
return;
- // Add the axes
- for (int axisnum = 0; axisnum < XINPUT_MAX_AXIS; axisnum++)
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_GREEN + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
+
+ // translate axes
+ m_axes[AXIS_GREEN] = -normalize_absolute_axis(BIT(u16(thumb_left_y()), 0, 8), -255, 255);
+ m_axes[AXIS_RED] = -normalize_absolute_axis(BIT(u16(thumb_left_y()), 8, 8), -255, 255);
+ m_axes[AXIS_BLUE] = -normalize_absolute_axis(BIT(u16(thumb_right_x()), 8, 8), -255, 255);
+ m_axes[AXIS_YELLOW] = -normalize_absolute_axis(BIT(u16(thumb_right_x()), 0, 8), -255, 255);
+ m_axes[AXIS_ORANGE] = -normalize_absolute_axis(BIT(u16(thumb_right_y()), 0, 8), -255, 255);
+ m_axes[AXIS_BASS_DRUM] = -normalize_absolute_axis(BIT(u16(thumb_right_y()), 8, 8), -255, 255);
+}
+
+
+void xinput_drumkit_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_drumkit_device::configure(input_device &device)
+{
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
+
+ // add axes
+ std::tuple<input_item_id, char const *, bool> const axis_caps[]{
+ { ITEM_ID_XAXIS, "Green Velocity", has_thumb_left_y() },
+ { ITEM_ID_YAXIS, "Red Velocity", has_thumb_left_y() },
+ { ITEM_ID_ZAXIS, "Blue Velocity", has_thumb_right_x() },
+ { ITEM_ID_RXAXIS, "Yellow Velocity", has_thumb_right_x() },
+ { ITEM_ID_RYAXIS, "Orange Velocity", has_thumb_right_y() },
+ { ITEM_ID_RZAXIS, "Bass Drum Velocity", has_thumb_right_y() } };
+ for (unsigned i = 0; (AXIS_BASS_DRUM - AXIS_GREEN) >= i; ++i)
{
- device()->add_item(
- xinput_axis_name[axisnum],
- xinput_axis_ids[axisnum],
- generic_axis_get_state<LONG>,
- &gamepad.left_thumb_x + axisnum);
+ auto const [item, name, supported] = axis_caps[i];
+ if (supported)
+ {
+ device.add_item(
+ name,
+ std::string_view(),
+ item,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_GREEN + i]);
+ }
}
- // Populate the POVs
- // For XBOX, we treat the DPAD as a hat switch
- for (int povnum = 0; povnum < XINPUT_MAX_POV; povnum++)
+ // add hats
+ for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
{
- device()->add_item(
- xinput_pov_names[povnum],
- ITEM_ID_OTHER_SWITCH,
- generic_button_get_state<BYTE>,
- &gamepad.povs[povnum]);
+ if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i]))
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ HAT_NAMES_GAMEPAD[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
}
- // populate the buttons
- for (int butnum = 0; butnum < XINPUT_MAX_BUTTONS; butnum++)
+ // add buttons
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_RSB - SWITCH_GREEN + 1];
+ for (unsigned i = 0; (SWITCH_RSB - SWITCH_GREEN) >= i; ++i)
{
- device()->add_item(
- xinput_button_names[butnum],
- static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &gamepad.buttons[butnum]);
+ if (has_button(SWITCH_BITS[i]))
+ {
+ switch_ids[SWITCH_GREEN + i] = device.add_item(
+ BUTTON_NAMES_DRUMKIT[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + button_count),
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_GREEN + i]);
+ numbered_buttons[button_count] = SWITCH_GREEN + i;
+
+ // use these for automatically numbered buttons and pedals
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_GREEN + i]));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq);
+ if (3 > button_count)
+ assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq);
+ ++button_count;
+ }
}
- device()->add_item(
- "RT",
- ITEM_ID_ZAXIS,
- generic_axis_get_state<LONG>,
- &gamepad.right_trigger);
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
+ {
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
- device()->add_item(
- "LT",
- ITEM_ID_RZAXIS,
- generic_axis_get_state<LONG>,
- &gamepad.left_trigger);
+ // use the D-pad for directional controls
+ add_directional_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN]);
+
+ // use the D-pad and A/B/X/Y diamond for twin sticks
+ add_twin_stick_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN],
+ switch_ids[SWITCH_BLUE],
+ switch_ids[SWITCH_RED],
+ switch_ids[SWITCH_YELLOW],
+ switch_ids[SWITCH_GREEN]);
- m_configured = true;
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_RED,
+ SWITCH_BLUE,
+ SWITCH_YELLOW,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+
+ // use bass drum pedal for focus next if available to make the system selection menu usable
+ if (add_button_assignment(assignments, IPT_UI_FOCUS_NEXT, { switch_ids[SWITCH_BASS_DRUM] }))
+ switch_ids[SWITCH_BASS_DRUM] = ITEM_ID_INVALID;
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
}
+
+
//============================================================
-// xinput_joystick_module
+// XInput turntable handler
//============================================================
-class xinput_joystick_module : public wininput_module
+class xinput_turntable_device : public xinput_device_base
{
+public:
+ xinput_turntable_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
private:
- std::shared_ptr<xinput_api_helper> m_xinput_helper;
+ static inline constexpr USHORT SWITCH_BITS[] =
+ {
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+ enum
+ {
+ SWITCH_A, // button bits
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_LB,
+ SWITCH_RB,
+ SWITCH_LSB,
+ SWITCH_RSB,
+ SWITCH_START,
+ SWITCH_BACK,
+
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_GREEN, // RT bits
+ SWITCH_RED,
+ SWITCH_BLUE,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_TURNTABLE, // LSY
+ AXIS_EFFECT, // RSX
+ AXIS_CROSSFADE, // RSY
+
+ AXIS_TOTAL
+ };
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+ u16 m_prev_effect;
+};
+
+
+xinput_turntable_device::xinput_turntable_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper),
+ m_prev_effect(0)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_turntable_device::poll(bool relative_reset)
+{
+ // poll the device first
+ bool const was_reset = is_reset();
+ if (read_state())
+ {
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
+
+ // translate RT bits
+ for (unsigned i = 0; (SWITCH_BLUE - SWITCH_GREEN) >= i; ++i)
+ m_switches[SWITCH_GREEN + i] = BIT(trigger_right(), i) ? 0xff : 0x00;
+
+ // translate axes
+ m_axes[AXIS_TURNTABLE] = s32(thumb_left_y()) * input_device::RELATIVE_PER_PIXEL * 2;
+ m_axes[AXIS_CROSSFADE] = normalize_absolute_axis(thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE);
+ }
+
+ // handle effect dial
+ if (was_reset)
+ {
+ // just grab the current count after regaining focus
+ m_prev_effect = u16(thumb_right_x());
+ }
+ else if (relative_reset)
+ {
+ // convert value to relative displacement
+ s32 effect_delta = s32(u32(u16(thumb_right_x()))) - s32(u32(m_prev_effect));
+ m_prev_effect = u16(thumb_right_x());
+ if (0x8000 < effect_delta)
+ effect_delta -= 0x1'0000;
+ else if (-0x8000 > effect_delta)
+ effect_delta += 0x1'0000;
+ m_axes[AXIS_EFFECT] = effect_delta * input_device::RELATIVE_PER_PIXEL / 128;
+ }
+}
+
+
+void xinput_turntable_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_turntable_device::configure(input_device &device)
+{
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
+
+ // add axes
+ input_item_id const turntable_id = device.add_item(
+ "Turntable",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE1,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_TURNTABLE]);
+ input_item_id const effect_id = device.add_item(
+ "Effect",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_EFFECT]);
+ input_item_id const crossfade_id = device.add_item(
+ "Crossfade",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_CROSSFADE]);
+
+ // add hats
+ for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i]))
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ HAT_NAMES_GAMEPAD[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
+ }
+
+ // add buttons
+ input_item_id button_id = ITEM_ID_BUTTON1;
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1];
+ for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[i]))
+ {
+ switch_ids[SWITCH_A + i] = device.add_item(
+ BUTTON_NAMES_KEYBOARD[i],
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_A + i]);
+ numbered_buttons[button_count] = SWITCH_A + i;
+
+ // use these for automatically numbered buttons and pedals
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i]));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq);
+ if (3 > button_count)
+ assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq);
+ ++button_count;
+ }
+ }
+
+ // turntable buttons activate these as well as A/B/X
+ switch_ids[SWITCH_GREEN] = device.add_item(
+ "Green",
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_GREEN]);
+ switch_ids[SWITCH_RED] = device.add_item(
+ "Red",
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_RED]);
+ switch_ids[SWITCH_BLUE] = device.add_item(
+ "Blue",
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BLUE]);
+
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
+ {
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
+
+ // use D-pad and A/B/X/Y diamond for twin sticks
+ add_twin_stick_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN],
+ switch_ids[SWITCH_X],
+ switch_ids[SWITCH_B],
+ switch_ids[SWITCH_Y],
+ switch_ids[SWITCH_A]);
+
+ // for most analog player controls, use turntable for X and effect for Y
+ input_seq const turntable_seq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, turntable_id));
+ input_seq const effect_seq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, effect_id));
+ input_seq const crossfade_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, crossfade_id));
+ input_seq joystick_left_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, turntable_id));
+ input_seq joystick_right_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, turntable_id));
+ input_seq joystick_up_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, effect_id));
+ input_seq joystick_down_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, effect_id));
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_STANDARD, crossfade_seq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, crossfade_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, effect_seq);
+ assignments.emplace_back(IPT_MOUSE_X, SEQ_TYPE_STANDARD, turntable_seq);
+ assignments.emplace_back(IPT_MOUSE_Y, SEQ_TYPE_STANDARD, effect_seq);
+
+ // use D-pad for analog controls as well if present
+ bool const have_dpad_left = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_LEFT];
+ bool const have_dpad_right = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_RIGHT];
+ bool const have_dpad_up = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_UP];
+ bool const have_dpad_down = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_DOWN];
+ if (have_dpad_left)
+ {
+ input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT]));
+ input_seq const left_seq(code);
+ joystick_left_seq += input_seq::or_code;
+ joystick_left_seq += code;
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_DECREMENT, left_seq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_DECREMENT, left_seq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_DECREMENT, left_seq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_DECREMENT, left_seq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_DECREMENT, left_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_DECREMENT, left_seq);
+ }
+ if (have_dpad_right)
+ {
+ input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT]));
+ input_seq const right_seq(code);
+ joystick_right_seq += input_seq::or_code;
+ joystick_right_seq += code;
+ assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_INCREMENT, right_seq);
+ assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_INCREMENT, right_seq);
+ assignments.emplace_back(IPT_DIAL, SEQ_TYPE_INCREMENT, right_seq);
+ assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_INCREMENT, right_seq);
+ assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_INCREMENT, right_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_INCREMENT, right_seq);
+ }
+ if (have_dpad_up)
+ {
+ input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP]));
+ input_seq const up_seq(code);
+ joystick_up_seq += input_seq::or_code;
+ joystick_up_seq += code;
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_DECREMENT, up_seq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_DECREMENT, up_seq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_DECREMENT, up_seq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_DECREMENT, up_seq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_DECREMENT, up_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_DECREMENT, up_seq);
+ }
+ if (have_dpad_down)
+ {
+ input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN]));
+ input_seq const down_seq(code);
+ joystick_down_seq += input_seq::or_code;
+ joystick_down_seq += code;
+ assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_INCREMENT, down_seq);
+ assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_INCREMENT, down_seq);
+ assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_INCREMENT, down_seq);
+ assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_INCREMENT, down_seq);
+ assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_INCREMENT, down_seq);
+ assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_INCREMENT, down_seq);
+ }
+ assignments.emplace_back(IPT_JOYSTICK_LEFT, SEQ_TYPE_STANDARD, joystick_left_seq);
+ assignments.emplace_back(IPT_JOYSTICK_RIGHT, SEQ_TYPE_STANDARD, joystick_right_seq);
+ assignments.emplace_back(IPT_JOYSTICK_UP, SEQ_TYPE_STANDARD, joystick_up_seq);
+ assignments.emplace_back(IPT_JOYSTICK_DOWN, SEQ_TYPE_STANDARD, joystick_down_seq);
+
+ // choose navigation controls
+ input_seq ui_up_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, turntable_id));
+ input_seq ui_down_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, turntable_id));
+ input_seq ui_left_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, effect_id));
+ input_seq ui_right_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, effect_id));
+ if ((have_dpad_up && have_dpad_down) || !have_dpad_left || !have_dpad_right)
+ {
+ if (have_dpad_up)
+ {
+ ui_up_seq += input_seq::or_code;
+ ui_up_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP]);
+ }
+ if (have_dpad_down)
+ {
+ ui_down_seq += input_seq::or_code;
+ ui_down_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN]);
+ }
+ if (have_dpad_left)
+ {
+ ui_left_seq += input_seq::or_code;
+ ui_left_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT]);
+ }
+ if (have_dpad_right)
+ {
+ ui_right_seq += input_seq::or_code;
+ ui_right_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT]);
+ }
+ }
+ else
+ {
+ if (have_dpad_left)
+ {
+ ui_up_seq += input_seq::or_code;
+ ui_up_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT]);
+ }
+ if (have_dpad_right)
+ {
+ ui_down_seq += input_seq::or_code;
+ ui_down_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT]);
+ }
+ if (have_dpad_up)
+ {
+ ui_left_seq += input_seq::or_code;
+ ui_left_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP]);
+ }
+ if (have_dpad_down)
+ {
+ ui_right_seq += input_seq::or_code;
+ ui_right_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN]);
+ }
+ }
+ assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, ui_up_seq);
+ assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, ui_down_seq);
+ assignments.emplace_back(IPT_UI_LEFT, SEQ_TYPE_STANDARD, ui_left_seq);
+ assignments.emplace_back(IPT_UI_RIGHT, SEQ_TYPE_STANDARD, ui_right_seq);
+
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ std::min<unsigned>(button_count, 4));
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
+}
+
+
+
+//============================================================
+// XInput keyboard handler
+//============================================================
+
+class xinput_keyboard_device : public xinput_device_base
+{
public:
- xinput_joystick_module()
- : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "xinput"),
- m_xinput_helper(nullptr)
+ xinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper);
+
+ virtual void poll(bool relative_reset) override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ static inline constexpr USHORT SWITCH_BITS[] =
{
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_BACK,
+
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT
+ };
+
+ enum
+ {
+ SWITCH_A, // button bits
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_LB,
+ SWITCH_RB,
+ SWITCH_LSB,
+ SWITCH_RSB,
+ SWITCH_START,
+ SWITCH_BACK,
+
+ SWITCH_DPAD_UP, // D-pad bits
+ SWITCH_DPAD_DOWN,
+ SWITCH_DPAD_LEFT,
+ SWITCH_DPAD_RIGHT,
+
+ SWITCH_C1,
+ SWITCH_C3 = SWITCH_C1 + 24,
+
+ SWITCH_TOTAL
+ };
+
+ enum
+ {
+ AXIS_VELOCITY, // field in LSX
+ AXIS_PEDAL, // RSY, most positive value neutral
+
+ AXIS_TOTAL
+ };
+
+ u8 m_switches[SWITCH_TOTAL];
+ s32 m_axes[AXIS_TOTAL];
+};
+
+
+xinput_keyboard_device::xinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ u32 player,
+ XINPUT_CAPABILITIES const &caps,
+ xinput_api_helper const &helper) :
+ xinput_device_base(std::move(name), std::move(id), module, player, caps, helper)
+{
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_keyboard_device::poll(bool relative_reset)
+{
+ // TODO: how many bits are really velocity?
+ // TODO: how are touch strip and overdrive read?
+
+ // poll the device first, and skip if nothing changed
+ if (!read_state())
+ return;
+
+ // translate button bits
+ for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i)
+ m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00;
+
+ // translate keys
+ for (unsigned i = 0; 8 > i; ++i)
+ {
+ m_switches[SWITCH_C1 + i] = BIT(trigger_left(), 7 - i) ? 0xff : 0x00;
+ m_switches[SWITCH_C1 + 8 + i] = BIT(trigger_right(), 7 - i) ? 0xff : 0x00;
+ m_switches[SWITCH_C1 + 16 + i] = BIT(thumb_left_x(), 7 - i) ? 0xff : 0x00;
}
+ m_switches[SWITCH_C3] = BIT(thumb_left_x(), 15) ? 0xff : 0x00;
- int init(const osd_options &options) override
+ // translate axes
+ m_axes[AXIS_VELOCITY] = -normalize_absolute_axis(BIT(thumb_left_x(), 8, 7), -127, 127);
+ m_axes[AXIS_PEDAL] = -normalize_absolute_axis(32'767 - thumb_right_y(), -32'767, 32'767);
+}
+
+
+void xinput_keyboard_device::reset()
+{
+ set_reset();
+ std::fill(std::begin(m_switches), std::end(m_switches), 0);
+ std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+void xinput_keyboard_device::configure(input_device &device)
+{
+ // track item IDs for setting up default assignments
+ input_device::assignment_vector assignments;
+ input_item_id switch_ids[SWITCH_TOTAL];
+ std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID);
+
+ // add axes
+ device.add_item(
+ "Velocity",
+ std::string_view(),
+ ITEM_ID_SLIDER1,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_VELOCITY]);
+ input_item_id const pedal_id = device.add_item(
+ "Pedal",
+ std::string_view(),
+ ITEM_ID_SLIDER2,
+ generic_axis_get_state<s32>,
+ &m_axes[AXIS_PEDAL]);
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedal_id)));
+
+ // add hats
+ for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i]))
+ {
+ switch_ids[SWITCH_DPAD_UP + i] = device.add_item(
+ HAT_NAMES_GAMEPAD[i],
+ std::string_view(),
+ input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_DPAD_UP + i]);
+ }
+ }
+
+ // add buttons
+ input_item_id button_id = ITEM_ID_BUTTON1;
+ unsigned button_count = 0;
+ unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1];
+ for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i)
+ {
+ if (has_button(SWITCH_BITS[i]))
+ {
+ switch_ids[SWITCH_A + i] = device.add_item(
+ BUTTON_NAMES_KEYBOARD[i],
+ std::string_view(),
+ button_id++,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_A + i]);
+ numbered_buttons[button_count] = SWITCH_A + i;
+
+ // use these for automatically numbered buttons and pedals
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i]));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq);
+ if (3 > button_count)
+ assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq);
+ ++button_count;
+ }
+ }
+
+ // add keys
+ char const *const key_formats[]{
+ "C %d", "C# %d", "D %d", "D# %d", "E %d", "F %d", "F# %d", "G %d", "G# %d", "A %d", "A# %d", "B %d" };
+ std::pair<ioport_type, ioport_type> const key_ids[]{
+ { IPT_MAHJONG_A, IPT_HANAFUDA_A }, // C
+ { IPT_MAHJONG_SCORE, IPT_INVALID }, // C#
+ { IPT_MAHJONG_B, IPT_HANAFUDA_B }, // D
+ { IPT_MAHJONG_DOUBLE_UP, IPT_INVALID }, // D#
+ { IPT_MAHJONG_C, IPT_HANAFUDA_C }, // E
+ { IPT_MAHJONG_D, IPT_HANAFUDA_D }, // F
+ { IPT_MAHJONG_BIG, IPT_INVALID }, // F#
+ { IPT_MAHJONG_E, IPT_HANAFUDA_E }, // G
+ { IPT_MAHJONG_SMALL, IPT_INVALID }, // G#
+ { IPT_MAHJONG_F, IPT_HANAFUDA_F }, // A
+ { IPT_MAHJONG_LAST_CHANCE, IPT_INVALID }, // A#
+ { IPT_MAHJONG_G, IPT_HANAFUDA_G }, // B
+ { IPT_MAHJONG_H, IPT_HANAFUDA_H }, // C
+ { IPT_MAHJONG_KAN, IPT_INVALID }, // C#
+ { IPT_MAHJONG_I, IPT_INVALID }, // D
+ { IPT_MAHJONG_PON, IPT_INVALID }, // D#
+ { IPT_MAHJONG_J, IPT_INVALID }, // E
+ { IPT_MAHJONG_K, IPT_INVALID }, // F
+ { IPT_MAHJONG_CHI, IPT_INVALID }, // F#
+ { IPT_MAHJONG_L, IPT_INVALID }, // G
+ { IPT_MAHJONG_REACH, IPT_INVALID }, // G#
+ { IPT_MAHJONG_M, IPT_HANAFUDA_YES }, // A
+ { IPT_MAHJONG_RON, IPT_INVALID }, // A#
+ { IPT_MAHJONG_N, IPT_HANAFUDA_NO }, // B
+ { IPT_MAHJONG_O, IPT_INVALID } }; // C
+ for (unsigned i = 0; (SWITCH_C3 - SWITCH_C1) >= i; ++i)
+ {
+ switch_ids[SWITCH_C1 + i] = device.add_item(
+ util::string_format(key_formats[i % 12], (i / 12) + 1),
+ std::string_view(),
+ (ITEM_ID_BUTTON32 >= button_id) ? button_id++ : ITEM_ID_OTHER_SWITCH,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_C1 + i]);
+
+ // add mahjong/hanafuda control assignments
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_C1 + i]));
+ if (IPT_INVALID != key_ids[i].first)
+ assignments.emplace_back(key_ids[i].first, SEQ_TYPE_STANDARD, seq);
+ if (IPT_INVALID != key_ids[i].second)
+ assignments.emplace_back(key_ids[i].second, SEQ_TYPE_STANDARD, seq);
+ }
+
+ // add start/back
+ if (has_button(XINPUT_GAMEPAD_START))
{
+ switch_ids[SWITCH_START] = device.add_item(
+ "Start",
+ std::string_view(),
+ ITEM_ID_START,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_START]);
+ add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] });
+ }
+ if (has_button(XINPUT_GAMEPAD_BACK))
+ {
+ switch_ids[SWITCH_BACK] = device.add_item(
+ "Back",
+ std::string_view(),
+ ITEM_ID_SELECT,
+ generic_button_get_state<u8>,
+ &m_switches[SWITCH_BACK]);
+ add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] });
+ }
+
+ // use the D-pad for directional controls
+ add_directional_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN]);
+
+ // use the D-pad and A/B/X/Y diamond for twin sticks
+ add_twin_stick_assignments(
+ assignments,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ switch_ids[SWITCH_DPAD_LEFT],
+ switch_ids[SWITCH_DPAD_RIGHT],
+ switch_ids[SWITCH_DPAD_UP],
+ switch_ids[SWITCH_DPAD_DOWN],
+ switch_ids[SWITCH_X],
+ switch_ids[SWITCH_B],
+ switch_ids[SWITCH_Y],
+ switch_ids[SWITCH_A]);
+
+ // assign UI select/back/clear/help
+ assign_ui_actions(
+ assignments,
+ SWITCH_B,
+ SWITCH_X,
+ SWITCH_Y,
+ SWITCH_START,
+ SWITCH_BACK,
+ switch_ids,
+ numbered_buttons,
+ button_count);
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
+}
+
+
+
+//============================================================
+// XInput joystick module
+//============================================================
+
+class xinput_joystick_module : public input_module_impl<device_info, osd_common_t>
+{
+public:
+ xinput_joystick_module() : input_module_impl<device_info, osd_common_t>(OSD_JOYSTICKINPUT_PROVIDER, "xinput")
+ {
+ }
+
+ virtual int init(osd_interface &osd, const osd_options &options) override
+ {
+ int status;
+
// Call the base
- int status = wininput_module::init(options);
+ status = input_module_impl<device_info, osd_common_t>::init(osd, options);
if (status != 0)
return status;
// Create and initialize our helper
- m_xinput_helper = std::make_shared<xinput_api_helper>();
+ m_xinput_helper = std::make_unique<xinput_api_helper>();
status = m_xinput_helper->initialize();
if (status != 0)
{
@@ -228,32 +3002,140 @@ public:
return 0;
}
-protected:
virtual void input_init(running_machine &machine) override
{
- xinput_joystick_device *devinfo;
+ input_module_impl<device_info, osd_common_t>::input_init(machine);
// Loop through each gamepad to determine if they are connected
for (UINT i = 0; i < XUSER_MAX_COUNT; i++)
{
- XINPUT_STATE state = {0};
+ // allocate and link in a new device
+ auto devinfo = m_xinput_helper->create_xinput_device(i, *this);
+ if (devinfo)
+ add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo));
+ }
+ }
- if (m_xinput_helper->xinput_get_state(i, &state) == ERROR_SUCCESS)
- {
- // allocate and link in a new device
- devinfo = m_xinput_helper->create_xinput_device(machine, i, *this);
- if (devinfo == nullptr)
- continue;
+ virtual void exit() override
+ {
+ input_module_impl<device_info, osd_common_t>::exit();
- // Configure each gamepad to add buttons and Axes, etc.
- devinfo->configure();
- }
- }
+ m_xinput_helper.reset();
}
+
+private:
+ std::unique_ptr<xinput_api_helper> m_xinput_helper;
};
-#else
-MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "xinput")
-#endif
+} // anonymous namespace
+
+
+
+int xinput_api_helper::initialize()
+{
+ m_xinput_dll = dynamic_module::open(XINPUT_LIBRARIES);
+
+ XInputGetState = m_xinput_dll->bind<xinput_get_state_fn>("XInputGetState");
+ XInputGetCapabilities = m_xinput_dll->bind<xinput_get_caps_fn>("XInputGetCapabilities");
+
+ if (!XInputGetState || !XInputGetCapabilities)
+ {
+ osd_printf_error("XInput: Could not find API functions.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+//============================================================
+// create_xinput_device
+//============================================================
+
+std::unique_ptr<device_info> xinput_api_helper::create_xinput_device(
+ UINT index,
+ input_module_base &module)
+{
+ // If we can't get the capabilities skip this device
+ XINPUT_STATE state{ 0 };
+ if (xinput_get_state(index, &state) != ERROR_SUCCESS)
+ return nullptr;
+ XINPUT_CAPABILITIES caps{ 0 };
+ if (FAILED(xinput_get_capabilities(index, 0, &caps)))
+ return nullptr;
+
+ char device_name[16];
+ snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1);
+
+ // allocate specialised device objects
+ switch (caps.Type)
+ {
+ case XINPUT_DEVTYPE_GAMEPAD:
+ switch (caps.SubType)
+ {
+ case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues
+ return std::make_unique<xinput_flight_stick_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+ case XINPUT_DEVSUBTYPE_GUITAR:
+ case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
+ case XINPUT_DEVSUBTYPE_GUITAR_BASS:
+ return std::make_unique<xinput_guitar_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+ case XINPUT_DEVSUBTYPE_DRUM_KIT:
+ return std::make_unique<xinput_drumkit_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+ case 0x0f:
+ return std::make_unique<xinput_keyboard_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+ case 0x17:
+ return std::make_unique<xinput_turntable_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+ }
+ }
+
+ // create default general-purpose device
+ return std::make_unique<xinput_joystick_device>(
+ device_name,
+ device_name,
+ module,
+ index,
+ caps,
+ *this);
+}
+
+} // namespace osd
+
+#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
+
+#include "input_module.h"
+
+namespace osd { namespace { MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "xinput") } }
+
+#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
-MODULE_DEFINITION(JOYSTICKINPUT_XINPUT, xinput_joystick_module)
+MODULE_DEFINITION(JOYSTICKINPUT_XINPUT, osd::xinput_joystick_module)
diff --git a/src/osd/modules/input/input_xinput.h b/src/osd/modules/input/input_xinput.h
index 9772e941bb7..fa36b7a2622 100644
--- a/src/osd/modules/input/input_xinput.h
+++ b/src/osd/modules/input/input_xinput.h
@@ -1,111 +1,30 @@
-#ifndef INPUT_XINPUT_H_
-#define INPUT_XINPUT_H_
+// license:BSD-3-Clause
+// copyright-holders:Brad Hughes, Vas Crabb
+#ifndef MAME_OSD_INPUT_INPUT_XINPUT_H
+#define MAME_OSD_INPUT_INPUT_XINPUT_H
-#include <mutex>
+#pragma once
-#include "modules/lib/osdlib.h"
-
-#define XINPUT_MAX_POV 4
-#define XINPUT_MAX_BUTTONS 10
-#define XINPUT_MAX_AXIS 4
-
-#define XINPUT_AXIS_MINVALUE -32767
-#define XINPUT_AXIS_MAXVALUE 32767
-
-class xinput_joystick_device;
-// default axis names
-static const char *const xinput_axis_name[] =
-{
- "LSX",
- "LSY",
- "RSX",
- "RSY"
-};
-
-static const input_item_id xinput_axis_ids[] =
-{
- ITEM_ID_XAXIS,
- ITEM_ID_YAXIS,
- ITEM_ID_RXAXIS,
- ITEM_ID_RYAXIS
-};
-
-static const USHORT xinput_pov_dir[] = {
- XINPUT_GAMEPAD_DPAD_UP,
- XINPUT_GAMEPAD_DPAD_DOWN,
- XINPUT_GAMEPAD_DPAD_LEFT,
- XINPUT_GAMEPAD_DPAD_RIGHT
-};
-
-static const char *const xinput_pov_names[] = {
- "DPAD Up",
- "DPAD Down",
- "DPAD Left",
- "DPAD Right"
-};
+#include "input_common.h"
-static const USHORT xinput_buttons[] = {
- XINPUT_GAMEPAD_A,
- XINPUT_GAMEPAD_B,
- XINPUT_GAMEPAD_X,
- XINPUT_GAMEPAD_Y,
- XINPUT_GAMEPAD_LEFT_SHOULDER,
- XINPUT_GAMEPAD_RIGHT_SHOULDER,
- XINPUT_GAMEPAD_START,
- XINPUT_GAMEPAD_BACK,
- XINPUT_GAMEPAD_LEFT_THUMB,
- XINPUT_GAMEPAD_RIGHT_THUMB,
-};
+#include "modules/lib/osdlib.h"
-static const char *const xinput_button_names[] = {
- "A",
- "B",
- "X",
- "Y",
- "LB",
- "RB",
- "Start",
- "Back",
- "LS",
- "RS"
-};
+#include <memory>
-struct gamepad_state
-{
- BYTE buttons[XINPUT_MAX_BUTTONS];
- BYTE povs[XINPUT_MAX_POV];
- LONG left_trigger;
- LONG right_trigger;
- LONG left_thumb_x;
- LONG left_thumb_y;
- LONG right_thumb_x;
- LONG right_thumb_y;
-};
+#include <windows.h>
+#include <xinput.h>
-// state information for a gamepad; state must be first element
-struct xinput_api_state
-{
- uint32_t player_index;
- XINPUT_STATE xstate;
- XINPUT_CAPABILITIES caps;
-};
-// Typedefs for dynamically loaded functions
-typedef DWORD (WINAPI *xinput_get_state_fn)(DWORD, XINPUT_STATE *);
-typedef DWORD (WINAPI *xinput_get_caps_fn)(DWORD, DWORD, XINPUT_CAPABILITIES *);
+namespace osd {
-class xinput_api_helper : public std::enable_shared_from_this<xinput_api_helper>
+class xinput_api_helper
{
public:
- xinput_api_helper()
- : m_xinput_dll(nullptr),
- XInputGetState(nullptr),
- XInputGetCapabilities(nullptr)
- {
- }
+ xinput_api_helper() { }
int initialize();
- xinput_joystick_device * create_xinput_device(running_machine &machine, UINT index, wininput_module &module);
+
+ std::unique_ptr<device_info> create_xinput_device(UINT index, input_module_base &module);
DWORD xinput_get_state(DWORD dwUserindex, XINPUT_STATE *pState) const
{
@@ -118,28 +37,15 @@ public:
}
private:
- osd::dynamic_module::ptr m_xinput_dll;
- xinput_get_state_fn XInputGetState;
- xinput_get_caps_fn XInputGetCapabilities;
-};
-
-class xinput_joystick_device : public device_info
-{
-public:
- gamepad_state gamepad;
- xinput_api_state xinput_state;
-
-private:
- std::shared_ptr<xinput_api_helper> m_xinput_helper;
- std::mutex m_device_lock;
- bool m_configured;
-
-public:
- xinput_joystick_device(running_machine &machine, const char *name, const char *id, input_module &module, std::shared_ptr<xinput_api_helper> helper);
+ // Typedefs for dynamically loaded functions
+ typedef DWORD (WINAPI *xinput_get_state_fn)(DWORD, XINPUT_STATE *);
+ typedef DWORD (WINAPI *xinput_get_caps_fn)(DWORD, DWORD, XINPUT_CAPABILITIES *);
- void poll() override;
- void reset() override;
- void configure();
+ dynamic_module::ptr m_xinput_dll = nullptr;
+ xinput_get_state_fn XInputGetState = nullptr;
+ xinput_get_caps_fn XInputGetCapabilities = nullptr;
};
-#endif
+} // namespace osd
+
+#endif // MAME_OSD_INPUT_INPUT_XINPUT_H