Skip to content

Commit

Permalink
Initial touch support:
Browse files Browse the repository at this point in the history
* Feed mouse/pen/touch pointer events through UI input manager with Win32 and SDL.
* Started migrating UI code to use new API and reworking mouse/touch interaction.
* emu/render.cpp: Support pressing multiple clickable layout items simultaneously.
* emu/render.cpp: Allow UI elements to be drawn in any window.
* emu/rendlay.cpp, luaengine_render.cpp: Added layout view events for pointer input.
* ui/ui.cpp: Allow the UI handler to control pointer display.
* ui/analogipt.cpp: Added mouse/touch and more keys for navigating field state list.
* ui/menu.cpp: Use vertical swipe to scroll and horizontal swipe to adjust.
* ui/menu.cpp: Draw after processing input - greatly improves responsiveness.
* ui/menu.cpp: Ignore keyboard/gamepad input during pointer actions.
* ui/selmenu.cpp: Made left/right info pane arrows repeat when held.
* ui/selmenu.cpp: Use middle click to move keyboard focus.
* ui/selmenu.cpp: Let filter list scroll if it's too tall, and use a bit of horizontal padding.
* ui/selmenu.cpp: Improved divider sizing.
* ui/state.cpp: Don't allow clicks to pass through the confirm deletion prompt to the menu.
* ui/simpleselgame.cpp: Fixed error message display and graphics/sound status not showing.
* ui/simpleselgame.cpp: Allow tap/click to dismiss error message.
* ui/utils.cpp: Show UI for choice filters when there are no choices - it's less confusing.
* modules/input/input_sdl.cpp: Made scaling for mouse scroll better match RawInput and DirectInput.
* modules/input/input_rawinput.cpp: Added support for horizontal scroll axis.
* modules/input/input_win32.cpp: Added support for scroll axes and more buttons to mouse/lightgun.
* osd/windows/window.cpp: Translate mouse position to window cooridinates for scroll wheel events.
* osd/sdl/window.cpp: Supply last mouse position for scroll wheel events if possible.
* scripts/build/complay.py: Made zero input mask an error - it was only being used to block clicks.
  • Loading branch information
cuavas committed Apr 11, 2024
1 parent 78ef444 commit 8465345
Show file tree
Hide file tree
Showing 115 changed files with 7,874 additions and 3,176 deletions.
10 changes: 9 additions & 1 deletion docs/source/commandline/sdlconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ SDL Keyboard Mapping
Keymap file name. Default is ``keymap.dat``.


SDL Joystick Mapping
SDL Input Options
--------------------

.. _mame-scommandline-enabletouch:

**-enable_touch**

Enable support for touch input. If this option is switched off, mouse input
simulated from touch devices will be used instead. Default is OFF
(**-noenable_touch**)

.. _mame-scommandline-sixaxis:

**-sixaxis**
Expand Down
10 changes: 0 additions & 10 deletions docs/source/luascript/ref-input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -750,16 +750,6 @@ uiinput:reset()
Clears pending events and UI input states. Should be called when leaving a
modal state where input is handled directly (e.g. configuring an input
combination).
uiinput:find_mouse()
Returns host system mouse pointer X position, Y position, button state, and
the :ref:`render target <luascript-ref-rendertarget>` it falls in. The
position is in host pixels, where zero is at the top/left. The button state
is a Boolean indicating whether the primary mouse button is pressed.

If the mouse pointer is not over one of MAME’s windows, this may return the
position and render target from when the mouse pointer was most recently
over one of MAME’s windows. The render target may be ``nil`` if the mouse
pointer is not over one of MAME’s windows.
uiinput:pressed(type)
Returns a Boolean indicating whether the specified UI input has been
pressed. The input type is an enumerated value.
Expand Down
60 changes: 60 additions & 0 deletions docs/source/luascript/ref-render.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,66 @@ view:set_recomputed_callback(cb)
View coordinates are recomputed in various events, including the window
being resized, entering or leaving full-screen mode, and changing the zoom
to screen area setting.
view:set_pointer_updated_callback(cb)
Set a function to receive notifications when a pointer enters, moves or
changes button states over the view. The function must accept nine
arguments:

* The pointer type (``mouse``, ``pen``, ``touch`` or ``unknown``).
* The pointer ID (a non-negative integer that will not change for the
lifetime of a pointer).
* The device ID for grouping pointers to recognise multi-touch gestures
(non-negative integer).
* Horizontal position in layout coordinates.
* Vertical position in layout coordinates.
* A bit mask representing the currently pressed buttons.
* A bit mask representing the buttons that were pressed in this update.
* A bit mask representing the buttons that were released in this update.
* The click count (positive for multi-click actions, or negative if a click
is turned into a hold or drag).

Call with ``nil`` to remove the callback.
view:set_pointer_left_callback(cb)
Set a function to receive notifications when a pointer leaves the view
normally. The function must accept seven arguments:

* The pointer type (``mouse``, ``pen``, ``touch`` or ``unknown``).
* The pointer ID (a non-negative integer that will not change for the
lifetime of a pointer). The ID may be reused for a new pointer after
receiving this notification.
* The device ID for grouping pointers to recognise multi-touch gestures
(non-negative integer).
* Horizontal position in layout coordinates.
* Vertical position in layout coordinates.
* A bit mask representing the buttons that were released in this update.
* The click count (positive for multi-click actions, or negative if a click
is turned into a hold or drag).

Call with ``nil`` to remove the callback.
view:set_pointer_aborted_callback(cb)
Set a function to receive notifications when a pointer leaves the view
abnormally. The function must accept seven arguments:

* The pointer type (``mouse``, ``pen``, ``touch`` or ``unknown``).
* The pointer ID (a non-negative integer that will not change for the
lifetime of a pointer). The ID may be reused for a new pointer after
receiving this notification.
* The device ID for grouping pointers to recognise multi-touch gestures
(non-negative integer).
* Horizontal position in layout coordinates.
* Vertical position in layout coordinates.
* A bit mask representing the buttons that were released in this update.
* The click count (positive for multi-click actions, or negative if a click
is turned into a hold or drag).

Call with ``nil`` to remove the callback.
view:set_forget_pointers_callback(cb)
Set a function to receive notifications when the view should stop processing
pointer input. The function must accept no arguments. Call with ``nil`` to
remove the callback.

This can happen in a number of situations, including the view configuration
changing or a menu taking over input handling.

Properties
~~~~~~~~~~
Expand Down
13 changes: 9 additions & 4 deletions docs/source/techspecs/layout_files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ Clickable items
If a view item (``element`` or ``screen`` element) has ``inputtag`` and
``inputmask`` attribute values that correspond to a digital switch field in the
emulated system, clicking the element will activate the switch. The switch
will remain active as long as the mouse button is held down and the pointer is
will remain active as long as the primary button is held down and the pointer is
within the item’s current bounds. (Note that the bounds may change depending on
the item’s animation state, see :ref:`layfile-interact-itemanim`).

Expand All @@ -1197,6 +1197,12 @@ device that caused the layout file to be loaded. The ``inputmask`` attribute
must be an integer specifying the bits of the I/O port field that the item
should activate. This sample shows instantiation of clickable buttons:

The ``clickthrough`` attribute controls whether clicks can pass through the view
item to other view items drawn above it. The ``clickthrough`` attribute must be
``yes`` or ``no`` if present. The default is ``no`` (clicks do not pass
through) for view items with ``inputtag`` and ``inputmask`` attributes, and
``yes`` (clicks pass through) for other view items.

.. code-block:: XML
<element ref="btn_3" inputtag="X2" inputmask="0x10">
Expand All @@ -1209,9 +1215,8 @@ should activate. This sample shows instantiation of clickable buttons:
<bounds x="1.775" y="5.375" width="1.0" height="1.0" />
</element>
When handling mouse input, MAME treats all layout elements as being rectangular,
and only activates the first clickable item whose area includes the location of
the mouse pointer.
When handling pointer input, MAME treats all layout elements as being
rectangular.


.. _layfile-interact-elemstate:
Expand Down
93 changes: 92 additions & 1 deletion docs/source/techspecs/layout_script.rst
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,96 @@ Dimensions recomputed
rotation and zoom to screen area settings. If you’re animating the position
of view items, this is a good time to calculate positions and scale factors.

The callback function has no return value and takes no parameters. Call
with ``nil`` as the argument to remove the event handler.
Pointer updated
``view:set_pointer_updated_callback(cb)``

Called when a pointer enters, moves or changes button state over the view.

The callback function is passed nine arguments:

* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
or ``unknown``, and will not change for the lifetime of a pointer.
* The pointer ID. This will be a non-negative integer that will not change
for the lifetime of a pointer. Pointer ID values are recycled
aggressively.
* The device ID. This will be a non-negative integer that can be used to
group pointers for recognising multi-touch gestures.
* The horizontal position of the pointer in layout coordinates.
* The vertical position of the pointer in layout coordinates.
* A bit mask representing the currently pressed buttons. The primary button
is the least significant bit.
* A bit mask representing the buttons that were pressed in this update. The
primary button is the least significant bit.
* A bit mask representing the buttons that were released in this update.
The primary button is the least significant bit.
* The click count. This is positive for multi-click actions, or negative if
a click is turned into a hold or drag. This only applies to the primary
button.

The callback function has no return value. Call with ``nil`` as the
argument to remove the event handler.
Pointer left
``view:set_pointer_left_callback(cb)``

Called when a pointer leaves the view normally. After receiving this event,
the pointer ID may be reused for a new pointer.

The callback function is passed seven arguments:

* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
or ``unknown``, and will not change for the lifetime of a pointer.
* The pointer ID. This will be a non-negative integer that will not change
for the lifetime of a pointer. Pointer ID values are recycled
aggressively.
* The device ID. This will be a non-negative integer that can be used to
group pointers for recognising multi-touch gestures.
* The horizontal position of the pointer in layout coordinates.
* The vertical position of the pointer in layout coordinates.
* A bit mask representing the buttons that were released in this update.
The primary button is the least significant bit.
* The click count. This is positive for multi-click actions, or negative if
a click is turned into a hold or drag. This only applies to the primary
button.

The callback function has no return value. Call with ``nil`` as the
argument to remove the event handler.
Pointer aborted
``view:set_pointer_aborted_callback(cb)``

Called when a pointer leaves the view abnormally. After receiving this
event, the pointer ID may be reused for a new pointer.

The callback function is passed seven arguments:

* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
or ``unknown``, and will not change for the lifetime of a pointer.
* The pointer ID. This will be a non-negative integer that will not change
for the lifetime of a pointer. Pointer ID values are recycled
aggressively.
* The device ID. This will be a non-negative integer that can be used to
group pointers for recognising multi-touch gestures.
* The horizontal position of the pointer in layout coordinates.
* The vertical position of the pointer in layout coordinates.
* A bit mask representing the buttons that were released in this update.
The primary button is the least significant bit.
* The click count. This is positive for multi-click actions, or negative if
a click is turned into a hold or drag. This only applies to the primary
button.

The callback function has no return value. Call with ``nil`` as the
argument to remove the event handler.
Forget pointers
``view:set_forget_pointers_callback(cb)``

Called when the view should stop processing pointer input. This can happen
in a number of situations, including:

* The user activated a menu.
* The view configuration will change.
* The view will be deactivated.

The callback function has no return value and takes no parameters. Call
with ``nil`` as the argument to remove the event handler.

Expand Down Expand Up @@ -692,4 +782,5 @@ Draw

The callback is passed two arguments: the element state (an integer) and the
32-bit ARGB bitmap at the required size. The callback must not attempt to
resize the bitmap.
resize the bitmap. Call with ``nil`` as the argument to remove the event
handler.
30 changes: 27 additions & 3 deletions docs/source/usingmame/ui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ pointing device:
* Click menu items to highlight them.
* Double-click menu items to select them.
* Click the left- or right-pointing triangle to adjust settings.
* For menus with too many items to fit on the screen, click the upward- or
downward-pointing triangle at the top or bottom to scroll up or down by one
screen at a time.
* For menus or text boxes with too many items or lines to fit on the screen,
press on the upward- or downward-pointing triangle at the top or bottom to
scroll up or down.
* Use vertical scrolling gestures to scroll menus or text boxes with too many
items or lines to fit on the screen.
* Click toolbar items to select them, or hover over them to see a description.
Expand All @@ -148,6 +148,28 @@ combinations to the **Show/Hide Menu**, **Pause**, **UI Back** and/or
**UI Cancel** inputs to make it possible to use MAME without a keyboard.


.. _ui-menus-touch:

Using a touch screen
~~~~~~~~~~~~~~~~~~~~

MAME has basic support for navigating menus using a touch screen:

* Tap menu items to highlight them.
* Double-tap menu items to select them.
* Swipe left or right (horizontally) on the highlighted menu item to adjust the
setting if applicable.
* Swipe up or down (vertically) to scroll menus or text boxes with too many
items to fit on the screen.
* For menus or text boxes with too many items or lines to fit on the screen,
press on the upward- or downward-pointing triangle at the top or bottom to
scroll up or down.

Note that for SDL-based MAME, the
:ref:`enable_touch <mame-scommandline-enabletouch>` option must be switched on
to use touch screen support.


.. _ui-inptcfg:

Configuring inputs
Expand Down Expand Up @@ -493,6 +515,8 @@ or info source with left/right. When focus is on the info/image tabs,
left/right switch between tabs. When focus is on the image/info tabs or source,
you can scroll the info using up, down, page up, page down, home and end.

You can move focus to an area by clicking on it with the middle mouse button.


.. _ui-simpleselmenu:

Expand Down
21 changes: 15 additions & 6 deletions scripts/build/complay.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ def check_numeric_attribute(self, name, attrs, key, default):
self.handle_error('Element %s attribute %s "%s" is not a number' % (name, key, val))
return None

def check_bool_attribute(self, name, attrs, key, default):
if key not in attrs:
return default
val = attrs[key]
if self.VARPATTERN.match(val):
return None
elif val in self.YESNO:
return 'yes' == val
self.handle_error('Element %s attribute %s "%s" is not "yes" or "no"' % (name, key, val))
return None

def check_parameter(self, attrs):
if 'name' not in attrs:
self.handle_error('Element param missing attribute name')
Expand Down Expand Up @@ -253,8 +264,7 @@ def check_orientation(self, attrs):
if self.check_int_attribute('orientation', attrs, 'rotate', 0) not in self.ORIENTATIONS:
self.handle_error('Element orientation attribute rotate "%s" is unsupported' % (attrs['rotate'], ))
for name in ('swapxy', 'flipx', 'flipy'):
if (attrs.get(name, 'no') not in self.YESNO) and (not self.VARPATTERN.match(attrs[name])):
self.handle_error('Element orientation attribute %s "%s" is not "yes" or "no"' % (name, attrs[name]))
self.check_bool_attribute('orientation', attrs, name, None)

def check_color(self, attrs):
self.check_color_channel(attrs, 'red')
Expand Down Expand Up @@ -324,8 +334,8 @@ def check_view_item(self, name, attrs):
self.handle_error('Element %s has inputraw attribute without inputtag attribute' % (name, ))
inputmask = self.check_int_attribute(name, attrs, 'inputmask', None)
if (inputmask is not None) and (not inputmask):
if (inputraw is None) or (not inputraw):
self.handle_error('Element %s attribute inputmask "%s" is zero' % (name, attrs['inputmask']))
self.handle_error('Element %s attribute inputmask "%s" is zero' % (name, attrs['inputmask']))
self.check_bool_attribute(name, attrs, 'clickthrough', None)

def startViewItem(self, name):
self.handlers.append((self.viewItemStartHandler, self.viewItemEndHandler))
Expand Down Expand Up @@ -676,8 +686,7 @@ def viewItemStartHandler(self, name, attrs):
else:
have_scroll[-1] = self.format_location()
self.check_float_attribute(name, attrs, 'size', 1.0)
if (attrs.get('wrap', 'no') not in self.YESNO) and (not self.VARPATTERN.match(attrs['wrap'])):
self.handle_error('Element %s attribute wrap "%s" is not "yes" or "no"' % (name, attrs['wrap']))
self.check_bool_attribute(name, attrs, 'wrap', False)
if 'inputtag' in attrs:
if 'name' in attrs:
self.handle_error('Element %s has both attribute inputtag and attribute name' % (name, ))
Expand Down
1 change: 1 addition & 0 deletions scripts/src/osd/modules.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function osdmodulesbuild()
MAME_DIR .. "src/osd/interface/midiport.h",
MAME_DIR .. "src/osd/interface/nethandler.cpp",
MAME_DIR .. "src/osd/interface/nethandler.h",
MAME_DIR .. "src/osd/interface/uievents.h",
MAME_DIR .. "src/osd/modules/debugger/debug_module.h",
MAME_DIR .. "src/osd/modules/debugger/debuggdbstub.cpp",
MAME_DIR .. "src/osd/modules/debugger/debugimgui.cpp",
Expand Down
2 changes: 1 addition & 1 deletion scripts/src/osd/windows_cfg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ if _OPTIONS["MODERN_WIN_API"]=="1" then
}
else
defines {
"_WIN32_WINNT=0x0600",
"_WIN32_WINNT=0x0602",
"NTDDI_VERSION=0x06000000",
}
end
Expand Down
Loading

0 comments on commit 8465345

Please sign in to comment.