-Lua engine cleanup, input edition:

* Modernised and cleaned up Lua bindings for input classes.
* Exposed the input_sequence_poller class to Lua and updated the
  autofire and cheat plugins to use it, rather than continuing to
  pretend it's part of the input manager.
* Exposed more of the natural keyboard manager, including the ability
  to enable/disable individual keyboard and keypad devices like you
  can from the keyboard mode menu.
* Exposed a few more things on ioport_port and input_device.

-plugins/cheat: Fixed menu item not updating visually when disabling a
 cheat with UI Left.

-plugins/cheatfind: Fixed not finding the first screen after screen
 enumerator was exposed as an object rather than using a table.

-bwidow.cpp, pacman.cpp: Minor cleanup to recent changes.
This commit is contained in:
Vas Crabb 2020-12-09 01:01:22 +11:00
parent 06568860e7
commit e008c7b1b1
13 changed files with 816 additions and 406 deletions

View File

@ -9,6 +9,7 @@ MAMEs source or working on scripts that run within the MAME framework.
naming naming
layout_files layout_files
layout_script
object_finders object_finders
device_memory_interface device_memory_interface
device_rom_interface device_rom_interface

View File

@ -1,10 +1,12 @@
.. _layfile:
MAME Layout Files MAME Layout Files
================= =================
.. contents:: :local: .. contents:: :local:
.. _layout-intro: .. _layfile-intro:
Introduction Introduction
------------ ------------
@ -18,12 +20,12 @@ screens, built and linked into the MAME binary, or provided externally. MAME
layout files are an XML application, using the ``.lay`` filename extension. layout files are an XML application, using the ``.lay`` filename extension.
.. _layout-concepts: .. _layfile-concepts:
Core concepts Core concepts
------------- -------------
.. _layout-concepts-numbers: .. _layfile-concepts-numbers:
Numbers Numbers
~~~~~~~ ~~~~~~~
@ -53,7 +55,7 @@ found, the number will be interpreted as an integer.
Numbers are parsed using the "C" locale for portability. Numbers are parsed using the "C" locale for portability.
.. _layout-concepts-coordinates: .. _layfile-concepts-coordinates:
Coordinates Coordinates
~~~~~~~~~~~ ~~~~~~~~~~~
@ -103,7 +105,7 @@ It is an error if ``width`` or ``height`` are negative, if ``right`` is less
than ``left``, or if ``bottom`` is less than ``top``. than ``left``, or if ``bottom`` is less than ``top``.
.. _layout-concepts-colours: .. _layfile-concepts-colours:
Colours Colours
~~~~~~~ ~~~~~~~
@ -128,7 +130,7 @@ is an error if any channel value falls outside the range of 0.0 to 1.0
(inclusive). (inclusive).
.. _layout-concepts-params: .. _layfile-concepts-params:
Parameters Parameters
~~~~~~~~~~ ~~~~~~~~~~
@ -183,7 +185,7 @@ Heres an example assigning the value “4” to the value parameter “firstd
Generator parameters are assigned using a ``param`` element with ``name`` and Generator parameters are assigned using a ``param`` element with ``name`` and
``start`` attributes, and ``increment``, ``lshift`` and/or ``rshift`` ``start`` attributes, and ``increment``, ``lshift`` and/or ``rshift``
attributes. Generator parameters may only appear inside ``repeat`` elements attributes. Generator parameters may only appear inside ``repeat`` elements
(see :ref:`layout-parts-repeats` for details). A generator parameter must not (see :ref:`layfile-parts-repeats` for details). A generator parameter must not
be reassigned in the same scope (an identically named parameter may be defined be reassigned in the same scope (an identically named parameter may be defined
in a child scope). Here are some example generator parameters: in a child scope). Here are some example generator parameters:
@ -230,7 +232,7 @@ innermost scope. It is not possible to define or reassign parameters in a
containing scope. containing scope.
.. _layout-concepts-predef-params: .. _layfile-concepts-predef-params:
Pre-defined parameters Pre-defined parameters
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -340,7 +342,7 @@ end of configuration. Values are not updated and layouts are not recomputed if
the system reconfigures the screen while running. the system reconfigures the screen while running.
.. _layout-parts: .. _layfile-parts:
Parts of a layout Parts of a layout
----------------- -----------------
@ -368,26 +370,27 @@ and groups that appear after them.
The following elements are allowed inside the top-level ``mamelayout`` element: The following elements are allowed inside the top-level ``mamelayout`` element:
param param
Defines or reassigns a value parameter. See :ref:`layout-concepts-params` Defines or reassigns a value parameter. See :ref:`layfile-concepts-params`
for details. for details.
element element
Defines an element one of the basic objects that can be arranged in a Defines an element one of the basic objects that can be arranged in a
view. See :ref:`layout-parts-elements` for details. view. See :ref:`layfile-parts-elements` for details.
group group
Defines a reusable group of elements/screens that may be referenced from Defines a reusable group of elements/screens that may be referenced from
views or other groups. See :ref:`layout-parts-groups` for details. views or other groups. See :ref:`layfile-parts-groups` for details.
repeat repeat
A repeating group of elements may contain ``param``, ``element``, A repeating group of elements may contain ``param``, ``element``,
``group``, and ``repeat`` elements. See :ref:`layout-parts-repeats` for ``group``, and ``repeat`` elements. See :ref:`layfile-parts-repeats` for
details. details.
view view
An arrangement of elements and/or screens that can be displayed on an output An arrangement of elements and/or screens that can be displayed on an output
device (a host screen/window). See :ref:`layout-parts-views` for details. device (a host screen/window). See :ref:`layfile-parts-views` for details.
script script
Allows lua script to be supplied for enhanced interactive layouts. Allows Lua script to be supplied for enhanced interactive layouts. See
:ref:`layscript` for details.
.. _layout-parts-elements: .. _layfile-parts-elements:
Elements Elements
~~~~~~~~ ~~~~~~~~
@ -400,7 +403,7 @@ multiple times within a view.
An elements appearance depends on its *state*. The state is an integer which An elements appearance depends on its *state*. The state is an integer which
usually comes from an I/O port field or an emulated output (see usually comes from an I/O port field or an emulated output (see
:ref:`layout-interact-elemstate` for information on connecting an element to an :ref:`layfile-interact-elemstate` for information on connecting an element to an
emulated I/O port or output). Any component of an element may be restricted to emulated I/O port or output). Any component of an element may be restricted to
only drawing when the elements state is a particular value. Some components only drawing when the elements state is a particular value. Some components
(e.g. multi-segment displays and reels) use the state directly to determine (e.g. multi-segment displays and reels) use the state directly to determine
@ -438,9 +441,9 @@ them). All components support a few common features:
(The component will always be drawn if neither ``state`` nor ``statemask`` (The component will always be drawn if neither ``state`` nor ``statemask``
attributes are present, or if the ``statemask`` attributes value is zero.) attributes are present, or if the ``statemask`` attributes value is zero.)
* Each component may have a ``bounds`` child element specifying its position and * Each component may have a ``bounds`` child element specifying its position and
size (see :ref:`layout-concepts-coordinates`). If no such element is present, size (see :ref:`layfile-concepts-coordinates`). If no such element is
the bounds default to a unit square (width and height of 1.0) with the top present, the bounds default to a unit square (width and height of 1.0) with
left corner at (0,0). the top left corner at (0,0).
A components position and/or size may be animated according to the elements A components position and/or size may be animated according to the elements
state by supplying multiple ``bounds`` child elements with ``state`` state by supplying multiple ``bounds`` child elements with ``state``
@ -457,9 +460,9 @@ them). All components support a few common features:
values of two ``bounds`` child elements, the position/size will be values of two ``bounds`` child elements, the position/size will be
interpolated linearly. interpolated linearly.
* Each component may have a ``color`` child element specifying an RGBA colour * Each component may have a ``color`` child element specifying an RGBA colour
(see :ref:`layout-concepts-colours` for details). This can be used to control (see :ref:`layfile-concepts-colours` for details). This can be used to
the colour of geometric, algorithmically drawn, or textual components. For control the colour of geometric, algorithmically drawn, or textual components.
``image`` components, the colour of the image pixels is multiplied by the For ``image`` components, the colour of the image pixels is multiplied by the
specified colour. If no such element is present, the colour defaults to specified colour. If no such element is present, the colour defaults to
opaque white. opaque white.
@ -485,10 +488,14 @@ disk
image image
Draws an image loaded from a PNG, JPEG, Windows DIB (BMP) or SVG file. The Draws an image loaded from a PNG, JPEG, Windows DIB (BMP) or SVG file. The
name of the file to load (including the file name extension) is supplied name of the file to load (including the file name extension) is supplied
using the required ``file`` attribute. Additionally, an optional using the ``file`` attribute. Additionally, an optional ``alphafile``
``alphafile`` attribute may be used to specify the name of a PNG file attribute may be used to specify the name of a PNG file (including the file
(including the file name extension) to load into the alpha channel of the name extension) to load into the alpha channel of the image.
image.
Alternatively, image data may be supplied in the layout file itself using a
``data`` child element. This can be useful for supplying simple,
human-readable SVG graphics. A ``file`` attribute or ``data`` child element
must be supplied; it is an error if neither or both are supplied.
If the ``alphafile`` attribute refers refers to a file, it must have the If the ``alphafile`` attribute refers refers to a file, it must have the
same dimensions (in pixels) as the file referred to by the ``file`` same dimensions (in pixels) as the file referred to by the ``file``
@ -497,7 +504,8 @@ image
alpha channel, with full intensity (white in a greyscale image) alpha channel, with full intensity (white in a greyscale image)
corresponding to fully opaque, and black corresponding to fully transparent. corresponding to fully opaque, and black corresponding to fully transparent.
The ``alphafile`` attribute will be ignored if the ``file`` attribute refers The ``alphafile`` attribute will be ignored if the ``file`` attribute refers
to an SVG image; it is only used in conjunction with bitmap images. to an SVG image or the ``data`` child element contains SVG data; it is only
used in conjunction with bitmap images.
The image file(s) should be placed in the same directory/archive as the The image file(s) should be placed in the same directory/archive as the
layout file. Image file formats are detected by examining the content of layout file. Image file formats are detected by examining the content of
@ -671,7 +679,7 @@ neutral position:
</element> </element>
.. _layout-parts-views: .. _layfile-parts-views:
Views Views
~~~~~ ~~~~~
@ -713,7 +721,7 @@ The following child elements are allowed inside a ``view`` element:
bounds bounds
Sets the origin and size of the views internal coordinate system if Sets the origin and size of the views internal coordinate system if
present. See :ref:`layout-concepts-coordinates` for details. If absent, present. See :ref:`layfile-concepts-coordinates` for details. If absent,
the bounds of the view are computed as the union of the bounds of all the bounds of the view are computed as the union of the bounds of all
screens and elements within the view. It only makes sense to have one screens and elements within the view. It only makes sense to have one
``bounds`` as a direct child of a view element. Any content outside the ``bounds`` as a direct child of a view element. Any content outside the
@ -721,18 +729,18 @@ bounds
output window or screen. output window or screen.
param param
Defines or reassigns a value parameter in the views scope. See Defines or reassigns a value parameter in the views scope. See
:ref:`layout-concepts-params` for details. :ref:`layfile-concepts-params` for details.
element element
Adds an element to the view (see :ref:`layout-parts-elements`). The name of Adds an element to the view (see :ref:`layfile-parts-elements`). The name
the element to add is specified using the required ``ref`` attribute. It is of the element to add is specified using the required ``ref`` attribute. It
an error if no element with this name is defined in the layout file. Within is an error if no element with this name is defined in the layout file.
a view, elements are drawn in the order they appear in the layout file, from Within a view, elements are drawn in the order they appear in the layout
front to back. See below for more details. file, from front to back. See below for more details.
May optionally be connected to an emulated I/O port using ``inputtag`` and May optionally be connected to an emulated I/O port using ``inputtag`` and
``inputmask`` attributes, and/or an emulated output using a ``name`` ``inputmask`` attributes, and/or an emulated output using a ``name``
attribute. See :ref:`layout-interact-clickable` for details. See attribute. See :ref:`layfile-interact-clickable` for details. See
:ref:`layout-interact-elemstate` for details on supplying a state value to :ref:`layfile-interact-elemstate` for details on supplying a state value to
the instantiated element. the instantiated element.
screen screen
Adds an emulated screen image to the view. The screen must be identified Adds an emulated screen image to the view. The screen must be identified
@ -746,14 +754,14 @@ screen
May optionally be connected to an emulated I/O port using ``inputtag`` and May optionally be connected to an emulated I/O port using ``inputtag`` and
``inputmask`` attributes, and/or an emulated output using a ``name`` ``inputmask`` attributes, and/or an emulated output using a ``name``
attribute. See :ref:`layout-interact-clickable` for details. attribute. See :ref:`layfile-interact-clickable` for details.
collection collection
Adds screens and/or items in a collection that can be shown or hidden by the Adds screens and/or items in a collection that can be shown or hidden by the
user (see :ref:`layout-parts-collections`). The name of the collection is user (see :ref:`layfile-parts-collections`). The name of the collection is
specified using the required ``name`` attribute.. There is a limit of 32 specified using the required ``name`` attribute.. There is a limit of 32
collections per view. collections per view.
group group
Adds the content of the group to the view (see :ref:`layout-parts-groups`). Adds the content of the group to the view (see :ref:`layfile-parts-groups`).
The name of the group to add is specified using the required ``ref`` The name of the group to add is specified using the required ``ref``
attribute. It is an error if no group with this name is defined in the attribute. It is an error if no group with this name is defined in the
layout file. See below for more details on positioning. layout file. See below for more details on positioning.
@ -762,9 +770,15 @@ repeat
attribute. The ``count`` attribute must be a positive integer. A attribute. The ``count`` attribute must be a positive integer. A
``repeat`` element in a view may contain ``element``, ``screen``, ``group``, ``repeat`` element in a view may contain ``element``, ``screen``, ``group``,
and further ``repeat`` elements, which function the same way they do when and further ``repeat`` elements, which function the same way they do when
placed in a view directly. See :ref:`layout-parts-repeats` for discussion placed in a view directly. See :ref:`layfile-parts-repeats` for discussion
on using ``repeat`` elements. on using ``repeat`` elements.
Screens (``screen`` elements) and layout elements (``element`` elements) may
have an ``id`` attribute. If present, the ``id`` attribute must not be empty,
and must be unique within a view, including screens and elements instantiated
via reusable groups and repeating blocks. Screens and layout elements with
``id`` attributes can be looked up by Lua scripts (see :ref:`layscript`).
Screens (``screen`` elements), layout elements (``element`` elements) and groups Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may have their orientation altered using an ``orientation`` (``group`` elements) may have their orientation altered using an ``orientation``
child element. For screens, the orientation modifiers are applied in addition child element. For screens, the orientation modifiers are applied in addition
@ -798,8 +812,8 @@ layout elements is alpha blending.
Screens (``screen`` elements), layout elements (``element`` elements) and groups Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may be positioned and sized using a ``bounds`` child (``group`` elements) may be positioned and sized using a ``bounds`` child
element (see :ref:`layout-concepts-coordinates` for details). In the absence of element (see :ref:`layfile-concepts-coordinates` for details). In the absence
a ``bounds`` child element, screens and layout elements bounds default to a of a ``bounds`` child element, screens and layout elements bounds default to a
unit square (origin at 0,0 and height and width both equal to 1). In the unit square (origin at 0,0 and height and width both equal to 1). In the
absence of a ``bounds`` child element, groups are expanded with no absence of a ``bounds`` child element, groups are expanded with no
translation/scaling (note that groups may position screens/elements outside translation/scaling (note that groups may position screens/elements outside
@ -817,16 +831,16 @@ screen, an individual layout element, and two element groups:
Screens (``screen`` elements), layout elements (``element`` elements) and groups Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may have a ``color`` child element (see (``group`` elements) may have a ``color`` child element (see
:ref:`layout-concepts-colours`) specifying a modifier colour. The component :ref:`layfile-concepts-colours`) specifying a modifier colour. The component
colours of the screen or layout element(s) are multiplied by this colour. colours of the screen or layout element(s) are multiplied by this colour.
Screens (``screen`` elements) and layout elements (``element`` elements) may Screens (``screen`` elements) and layout elements (``element`` elements) may
have their colour and position/size animated by supplying multiple ``color`` have their colour and position/size animated by supplying multiple ``color``
and/or ``bounds`` child elements with ``state`` attributes. See and/or ``bounds`` child elements with ``state`` attributes. See
:ref:`layout-interact-itemanim` for details. :ref:`layfile-interact-itemanim` for details.
.. _layout-parts-collections: .. _layfile-parts-collections:
Collections Collections
~~~~~~~~~~~ ~~~~~~~~~~~
@ -862,13 +876,14 @@ to be hidden by the user:
A collection creates a nested parameter scope. Any ``param`` elements inside A collection creates a nested parameter scope. Any ``param`` elements inside
the collection element set parameters in the local scope for the collection. the collection element set parameters in the local scope for the collection.
See :ref:`layout-concepts-params` for more detail on parameters. (Note that the See :ref:`layfile-concepts-params` for more detail on parameters. (Note that
collections name and default visibility are not part of its content, and any the collections name and default visibility are not part of its content, and
parameter references in the ``name`` and ``visible`` attributes themselves will any parameter references in the ``name`` and ``visible`` attributes themselves
be substituted using parameter values from the collections parents scope.) will be substituted using parameter values from the collections parents
scope.)
.. _layout-parts-groups: .. _layfile-parts-groups:
Reusable groups Reusable groups
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
@ -902,7 +917,7 @@ instantiate in this example, destination bounds are supplied:
Group definition elements allow all the same child elements as views. Group definition elements allow all the same child elements as views.
Positioning and orienting screens, layout elements and nested groups works the Positioning and orienting screens, layout elements and nested groups works the
same way as for views. See :ref:`layout-parts-views` for details. A group may same way as for views. See :ref:`layfile-parts-views` for details. A group may
instantiate other groups, but recursive loops are not permitted. It is an error instantiate other groups, but recursive loops are not permitted. It is an error
if a group directly or indirectly instantiates itself. if a group directly or indirectly instantiates itself.
@ -910,7 +925,7 @@ Groups have their own internal coordinate systems. If a group definition
element has no ``bounds`` element as a direct child, its bounds are computed as element has no ``bounds`` element as a direct child, its bounds are computed as
the union of the bounds of all the screens, layout elements and/or nested groups the union of the bounds of all the screens, layout elements and/or nested groups
it instantiates. A ``bounds`` child element may be used to explicitly specify it instantiates. A ``bounds`` child element may be used to explicitly specify
group bounds (see :ref:`layout-concepts-coordinates` for details). Note that group bounds (see :ref:`layfile-concepts-coordinates` for details). Note that
groups bounds are only used for the purpose of calculating the coordinate groups bounds are only used for the purpose of calculating the coordinate
transform when instantiating a group. A group may position screens and/or transform when instantiating a group. A group may position screens and/or
elements outside its bounds, and they will not be cropped. elements outside its bounds, and they will not be cropped.
@ -970,20 +985,20 @@ the group is instantiated (*not* its lexical parent, the top-level
``mamelayout`` element). Any ``param`` elements inside the group definition ``mamelayout`` element). Any ``param`` elements inside the group definition
element set parameters in the local scope for the group instantiation. Local element set parameters in the local scope for the group instantiation. Local
parameters do not persist across multiple instantiations. See parameters do not persist across multiple instantiations. See
:ref:`layout-concepts-params` for more detail on parameters. (Note that the :ref:`layfile-concepts-params` for more detail on parameters. (Note that the
groups name is not part of its content, and any parameter references in the groups name is not part of its content, and any parameter references in the
``name`` attribute itself will be substituted at the point where the group ``name`` attribute itself will be substituted at the point where the group
definition appears in the top-level ``mamelayout`` elements scope.) definition appears in the top-level ``mamelayout`` elements scope.)
.. _layout-parts-repeats: .. _layfile-parts-repeats:
Repeating blocks Repeating blocks
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Repeating blocks provide a concise way to generate or arrange large numbers of Repeating blocks provide a concise way to generate or arrange large numbers of
similar elements. Repeating blocks are generally used in conjunction with similar elements. Repeating blocks are generally used in conjunction with
generator parameters (see :ref:`layout-concepts-params`). Repeating blocks may generator parameters (see :ref:`layfile-concepts-params`). Repeating blocks may
be nested for more complex arrangements. be nested for more complex arrangements.
Repeating blocks are created with ``repeat`` elements. Each ``repeat`` element Repeating blocks are created with ``repeat`` elements. Each ``repeat`` element
@ -1001,8 +1016,8 @@ elements allowed inside a ``repeat`` element depend on where it appears:
A repeating block effectively repeats its contents the number of times specified A repeating block effectively repeats its contents the number of times specified
by its ``count`` attribute. See the relevant sections for details on how the by its ``count`` attribute. See the relevant sections for details on how the
child elements are used (:ref:`layout-parts`, :ref:`layout-parts-groups`, and child elements are used (:ref:`layfile-parts`, :ref:`layfile-parts-groups`, and
:ref:`layout-parts-views`). A repeating block creates a nested parameter scope :ref:`layfile-parts-views`). A repeating block creates a nested parameter scope
inside the parameter scope of its lexical (DOM) parent element. inside the parameter scope of its lexical (DOM) parent element.
Generating white number labels from zero to eleven named ``label_0``, Generating white number labels from zero to eleven named ``label_0``,
@ -1121,7 +1136,7 @@ tiles on each iteration. Rows are connected to I/O ports ``board:IN.7`` at the
top to ``board.IN.0`` at the bottom. top to ``board.IN.0`` at the bottom.
.. _layout-interact: .. _layfile-interact:
Interactivity Interactivity
------------- -------------
@ -1135,23 +1150,23 @@ Clickable items
State-dependent components State-dependent components
Some components will be drawn differently depending on the containing Some components will be drawn differently depending on the containing
elements state. These include the dot matrix, multi-segment LED display, elements state. These include the dot matrix, multi-segment LED display,
simple counter and reel elements. See :ref:`layout-parts-elements` for simple counter and reel elements. See :ref:`layfile-parts-elements` for
details. details.
Conditionally-drawn components Conditionally-drawn components
Components may be conditionally drawn or hidden depending on the containing Components may be conditionally drawn or hidden depending on the containing
elements state by supplying ``state`` and/or ``statemask`` attributes. See elements state by supplying ``state`` and/or ``statemask`` attributes. See
:ref:`layout-parts-elements` for details. :ref:`layfile-parts-elements` for details.
Component parameter animation Component parameter animation
Components colour and position/size within their containing element may be Components colour and position/size within their containing element may be
animated according the elements state by providing multiple ``color`` animated according the elements state by providing multiple ``color``
and/or ``bounds`` elements with ``state`` attributes. See and/or ``bounds`` elements with ``state`` attributes. See
:ref:`layout-parts-elements` for details. :ref:`layfile-parts-elements` for details.
Item parameter animation Item parameter animation
Items colour and position/size within their containing view may be animated Items colour and position/size within their containing view may be animated
according to their animation state. according to their animation state.
.. _layout-interact-clickable: .. _layfile-interact-clickable:
Clickable items Clickable items
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
@ -1161,7 +1176,7 @@ If a view item (``element`` or ``screen`` element) has ``inputtag`` and
emulated system, clicking the element will activate the switch. The switch 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 mouse button is held down and the pointer is
within the items current bounds. (Note that the bounds may change depending on within the items current bounds. (Note that the bounds may change depending on
the items animation state, see :ref:`layout-interact-itemanim`). the items animation state, see :ref:`layfile-interact-itemanim`).
The ``inputtag`` attribute specifies the tag path of an I/O port relative to the The ``inputtag`` attribute specifies the tag path of an I/O port relative to the
device that caused the layout file to be loaded. The ``inputmask`` attribute device that caused the layout file to be loaded. The ``inputmask`` attribute
@ -1185,14 +1200,14 @@ and only activates the first clickable item whose area includes the location of
the mouse pointer. the mouse pointer.
.. _layout-interact-elemstate: .. _layfile-interact-elemstate:
Element state Element state
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
A view item that instantiates an element (``element`` element) may supply a A view item that instantiates an element (``element`` element) may supply a
state value to the element from an emulated I/O port or output. See state value to the element from an emulated I/O port or output. See
:ref:`layout-parts-elements` for details on how an elements state affects its :ref:`layfile-parts-elements` for details on how an elements state affects its
appearance. appearance.
If the ``element`` element has a ``name`` attribute, the element state value If the ``element`` element has a ``name`` attribute, the element state value
@ -1232,7 +1247,7 @@ in the value being shifted four bits to the right). This is useful for
obtaining the value of analog or positional inputs. obtaining the value of analog or positional inputs.
.. _layout-interact-itemanim: .. _layfile-interact-itemanim:
View item animation View item animation
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
@ -1265,7 +1280,7 @@ An items animation state may be bound to an emulated output or input port by
supplying an ``animate`` child element. If present, the ``animate`` element supplying an ``animate`` child element. If present, the ``animate`` element
must have either an ``inputtag`` attribute or a ``name`` attribute (but not must have either an ``inputtag`` attribute or a ``name`` attribute (but not
both). If the ``animate`` child element is not present, the items animation both). If the ``animate`` child element is not present, the items animation
state is the same as its element state (see :ref:`layout-interact-elemstate`). state is the same as its element state (see :ref:`layfile-interact-elemstate`).
If the ``animate`` child element is present and has an ``inputtag`` If the ``animate`` child element is present and has an ``inputtag``
attribute, the items animation state will be taken from the value of the attribute, the items animation state will be taken from the value of the
@ -1330,7 +1345,7 @@ their positions:
</repeat> </repeat>
.. _layout-errors: .. _layfile-errors:
Error handling Error handling
-------------- --------------
@ -1346,7 +1361,7 @@ Error handling
screens are considered unviable and not available to the user. screens are considered unviable and not available to the user.
.. _layout-autogen: .. _layfile-autogen:
Automatically-generated views Automatically-generated views
----------------------------- -----------------------------
@ -1389,7 +1404,7 @@ The following views will be automatically generated:
will be displayed at physical aspect ratio, with rotation applied. will be displayed at physical aspect ratio, with rotation applied.
.. _layout-complay: .. _layfile-complay:
Using complay.py Using complay.py
---------------- ----------------
@ -1424,7 +1439,7 @@ file to check and no output file name or base variable name. For example:
**python scripts/build/complay.py artwork/dino/default.lay** **python scripts/build/complay.py artwork/dino/default.lay**
.. _layout-examples: .. _layfile-examples:
Example layout files Example layout files
-------------------- --------------------

View File

@ -0,0 +1,203 @@
.. _layscript:
MAME Layout Scripting
=====================
.. contents:: :local:
.. _layscript-intro:
Introduction
------------
MAME layout files can embed Lua script to provide enhanced functionality.
Although theres a lot you can do with conditionally drawn components and
parameter animation, some things can only be done with scripting. MAME uses an
event-based model. Scripts can supply function that will be called after
certain events, or when certain data is required.
Layout scripting requires the layout plugin to be enabled. For example, to run
BWB Touble Take with the Lua script in the layout enabled, you might use this
command::
mame64 -plugins -plugin layout v4dbltak
If you may want to add the settings to enable the layout plugin to an INI file
to save having to enable it every time you start a system.
.. _layscript-examples:
Practical examples
------------------
Before diving into the technical details of how it works, well start with some
complete examples using Lua script to enhance layouts.
Espial: joystick split across ports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Take a look at the player input definitions for Espial:
.. code-block:: C++
PORT_START("IN1")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 )
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_COCKTAIL
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_COCKTAIL
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_COCKTAIL
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_COCKTAIL
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL
PORT_START("IN2")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN )
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN1 )
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN )
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 )
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY
There are two joysticks, one used for both players on an upright cabinet or the
first player on a cocktail cabinet, and one used for the second player on a
cocktail cabinet. Notice that the switches for the first joystick are split
across the two I/O ports.
Theres no layout file syntax to build element state using bits from multiple
I/O ports. Its also inconvenient if each joystick needs to be defined as a
separate element because the bits for the switches arent arranged the same
way.
We can overcome these limitations using a script to read the player inputs and
set the items element state:
.. code-block:: XML
<?xml version="1.0"?>
<mamelayout version="2">
<!-- element for drawing a joystick -->
<!-- up = 1 (bit 0), down = 2 (bit 1), left = 4 (bit 2), right = 8 (bit 3) -->
<element name="stick" defstate="0">
<image state="0x0" file="stick_c.svg" />
<image state="0x1" file="stick_u.svg" />
<image state="0x9" file="stick_ur.svg" />
<image state="0x8" file="stick_r.svg" />
<image state="0xa" file="stick_dr.svg" />
<image state="0x2" file="stick_d.svg" />
<image state="0x6" file="stick_dl.svg" />
<image state="0x4" file="stick_l.svg" />
<image state="0x5" file="stick_ul.svg" />
</element>
<!-- we'll warn the user if the layout plugin isn't enabled -->
<!-- draw only when state is 1, and set the default state to 1 so warning is visible initially -->
<element name="warning" defstate="1">
<text state="1" string="This view requires the layout plugin." />
</element>
<!-- view showing the screen and joysticks on a cocktail cabinet -->
<view name="Joystick Display">
<!-- draw the screen with correct aspect ratio -->
<screen index="0">
<bounds x="0" y="0" width="4" height="3" />
</screen>
<!-- first joystick, id attribute allows script to find item -->
<!-- no bindings, state will be set by the script -->
<element id="joy_p1" ref="stick">
<!-- position below the screen -->
<bounds xc="2" yc="3.35" width="0.5" height="0.5" />
</element>
<!-- second joystick, id attribute allows script to find item -->
<!-- no bindings, state will be set by the script -->
<element id="joy_p2" ref="stick">
<!-- screen is flipped for second player, so rotate by 180 degrees -->
<orientation rotate="180" />
<!-- position above the screen -->
<bounds xc="2" yc="-0.35" width="0.5" height="0.5" />
</element>
<!-- warning text item also has id attribute so the script can find it -->
<element id="warning" ref="warning">
<!-- position over the screen near the bottom -->
<bounds x="0.2" y="2.6" width="3.6" height="0.2" />
</element>
</view>
<!-- the content of the script element will be called as a function by the layout plugin -->
<!-- use CDATA block to avoid the need to escape angle brackets and ampersands -->
<script><![CDATA[
-- file is the layout file object
-- set a function to call after resolving tags
file:set_resolve_tags_callback(
function ()
-- file.device is the device that caused the layout to be loaded
-- in this case, it's the root machine driver for espial
-- look up the two I/O ports we need to be able to read
local in1 = file.device:ioport("IN1")
local in2 = file.device:ioport("IN2")
-- look up the view items for showing the joystick state
local p1_stick = file.views["Joystick Display"].items["joy_p1"]
local p2_stick = file.views["Joystick Display"].items["joy_p2"]
-- set a function to call before adding the view items to the render target
file.views["Joystick Display"]:set_prepare_items_callback(
function ()
-- read the two player input I/O ports
local in1_val = in1:read()
local in2_val = in2:read()
-- set element state for first joystick
p1_stick:set_state(
((in2_val & 0x10) >> 4) | -- shift up from IN2 bit 4 to bit 0
((in1_val & 0x20) >> 4) | -- shift down from IN1 bit 5 to bit 1
((in2_val & 0x80) >> 5) | -- shift left from IN2 bit 7 to bit 2
(in2_val & 0x08)) -- right is in IN2 bit 3
-- set element state for second joystick
p2_stick:set_state(
((in1_val & 0x10) >> 4) | -- shift up from IN1 bit 4 to bit 0
((in1_val & 0x40) >> 5) | -- shift down from IN1 bit 6 to bit 1
(in1_val & 0x04) | -- left is in IN1 bit 2
(in1_val & 0x08)) -- right is in IN1 bit 3
end)
-- hide the warning, since if we got here the script is running
file.views["Joystick Display"].items["warning"]:set_state(0)
end)
]]></script>
</mamelayout>
The layout has a ``script`` element containing the Lua script. This is called
as a function by the layout plugin when the layout file is loaded. The layout
views have been built at this point, but the emulated system has not finished
starting. In particular, its not safe to access inputs and outputs at this
time. The key variable in the script environment is ``file``, which gives the
script access its layout file.
We supply a function to be called after tags in the layout file have been
resolved. At this point, the emulated system will have completed starting.
This function does the following tasks
* Looks up the two I/O ports used for player input. I/O ports can be looked up
by tag relative to the device that caused the layout file to be loaded.
* Looks up the two view items used to display joystick state. Views can be
looked up by name (i.e. value of the ``name`` attribute), and items within a
view can be looked up by ID (i.e. the value of the ``id`` attribute).
* Supplies a function to be called before view items are added to the render
target.
* Hides the warning that reminds the user to enable the layout plugin by setting
the element state for the item to 0 (the text component is only drawn when
the element state is 1).
The function called before view items are added to the render target reads the
player inputs, and shuffle the bits into the order needed by the joystick
element.

View File

@ -108,26 +108,27 @@ end
-- Borrowed from the cheat plugin -- Borrowed from the cheat plugin
local function poll_for_hotkey() local function poll_for_hotkey()
local input = manager:machine():input() local input = manager:machine():input()
local poller = input:sequence_poller()
manager:machine():popmessage(_('Press button for hotkey or wait to leave unchanged')) manager:machine():popmessage(_('Press button for hotkey or wait to leave unchanged'))
manager:machine():video():frame_update(true) manager:machine():video():frame_update(true)
input:seq_poll_start('switch') poller:start('switch')
local time = os.clock() local time = os.clock()
local clearmsg = true local clearmsg = true
while (not input:seq_poll()) and (input:seq_poll_modified() or (os.clock() < time + 1)) do while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do
if input:seq_poll_modified() then if poller.modified then
if not input:seq_poll_valid() then if not poller.valid then
manager:machine():popmessage(_("Invalid sequence entered")) manager:machine():popmessage(_("Invalid sequence entered"))
clearmsg = false clearmsg = false
break break
end end
manager:machine():popmessage(input:seq_name(input:seq_poll_sequence())) manager:machine():popmessage(input:seq_name(poller.sequence))
manager:machine():video():frame_update(true) manager:machine():video():frame_update(true)
end end
end end
if clearmsg then if clearmsg then
manager:machine():popmessage() manager:machine():popmessage()
end end
return input:seq_poll_valid() and input:seq_poll_final() or nil return poller.valid and poller.sequence or nil
end end
local function handle_configure_menu(index, event) local function handle_configure_menu(index, event)

View File

@ -610,24 +610,25 @@ function cheat.startplugin()
local function hkcbfunc(cheat) local function hkcbfunc(cheat)
local input = manager:machine():input() local input = manager:machine():input()
local poller = input:sequence_poller()
manager:machine():popmessage(_("Press button for hotkey or wait to clear")) manager:machine():popmessage(_("Press button for hotkey or wait to clear"))
manager:machine():video():frame_update(true) manager:machine():video():frame_update(true)
input:seq_poll_start("switch") poller:start("switch")
local time = os.clock() local time = os.clock()
local clearmsg = true local clearmsg = true
while (not input:seq_poll()) and (input.seq_poll_modified() or (os.clock() < time + 1)) do while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do
if input:seq_poll_modified() then if poller.modified then
if not input:seq_poll_valid() then if not poller.valid then
manager:machine():popmessage(_("Invalid sequence entered")) manager:machine():popmessage(_("Invalid sequence entered"))
clearmsg = false clearmsg = false
break break
end end
manager:machine():popmessage(input:seq_name(input:seq_poll_sequence())) manager:machine():popmessage(input:seq_name(poller.sequence))
manager:machine():video():frame_update(true) manager:machine():video():frame_update(true)
end end
end end
if input:seq_poll_valid() then if poller.valid then
cheat.hotkeys = {pressed = false, keys = input:seq_poll_final()} cheat.hotkeys = { pressed = false, keys = poller.sequence }
else else
cheat.hotkeys = nil cheat.hotkeys = nil
end end
@ -748,7 +749,8 @@ function cheat.startplugin()
return chg return chg
else else
if not cheat.is_oneshot then if not cheat.is_oneshot then
return cheat:set_enabled(false) local state, chg = cheat:set_enabled(false)
return chg
end end
return false return false
end end

View File

@ -1044,7 +1044,7 @@ function cheatfind.startplugin()
end end
emu.register_menu(menu_callback, menu_populate, _("Cheat Finder")) emu.register_menu(menu_callback, menu_populate, _("Cheat Finder"))
emu.register_frame_done(function () emu.register_frame_done(function ()
local tag, screen = next(manager:machine().screens) local screen = manager:machine().screens:at(1)
local height = mame_manager:ui():get_line_height() local height = mame_manager:ui():get_line_height()
for num, watch in ipairs(watches) do for num, watch in ipairs(watches) do
screen:draw_text("left", num * height, string.format(watch.format, watch.addr, watch.func())) screen:draw_text("left", num * height, string.format(watch.format, watch.addr, watch.func()))

View File

@ -378,8 +378,6 @@ bool lua_engine::menu_callback(const std::string &menu, int index, const std::st
void lua_engine::set_machine(running_machine *machine) void lua_engine::set_machine(running_machine *machine)
{ {
if (!machine || (machine != m_machine))
m_seq_poll.reset();
m_machine = machine; m_machine = machine;
} }

View File

@ -12,8 +12,6 @@
#pragma once #pragma once
#include "iptseqpoll.h"
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
#include <map> #include <map>
@ -35,10 +33,10 @@ class lua_engine
public: public:
// helper structures // helper structures
template <typename T> struct devenum; template <typename T> struct devenum;
template <typename T, typename C, typename I = typename C::iterator> struct immutable_container_helper; template <typename T> struct simple_list_wrapper;
template <typename T> struct tag_object_ptr_map; template <typename T> struct tag_object_ptr_map;
template <typename T> using standard_tag_object_ptr_map = tag_object_ptr_map<std::unordered_map<std::string, std::unique_ptr<T> > >; template <typename T> using standard_tag_object_ptr_map = tag_object_ptr_map<std::unordered_map<std::string, std::unique_ptr<T> > >;
template <typename T, typename C, typename I = typename C::iterator> struct immutable_container_helper;
// construction/destruction // construction/destruction
lua_engine(); lua_engine();
@ -146,7 +144,6 @@ private:
lua_State *m_lua_state; lua_State *m_lua_state;
std::unique_ptr<sol::state_view> m_sol_state; std::unique_ptr<sol::state_view> m_sol_state;
running_machine *m_machine; running_machine *m_machine;
std::unique_ptr<input_sequence_poller> m_seq_poll;
std::vector<std::string> m_menu; std::vector<std::string> m_menu;

View File

@ -18,6 +18,15 @@
template <typename T>
struct lua_engine::simple_list_wrapper
{
simple_list_wrapper(simple_list<T> const &l) : list(l) { }
simple_list<T> const &list;
};
template <typename T> template <typename T>
struct lua_engine::tag_object_ptr_map struct lua_engine::tag_object_ptr_map
{ {
@ -75,15 +84,83 @@ template <> struct is_container<core_options> : std::false_type { };
sol::buffer *sol_lua_get(sol::types<buffer *>, lua_State *L, int index, sol::stack::record &tracking); sol::buffer *sol_lua_get(sol::types<buffer *>, lua_State *L, int index, sol::stack::record &tracking);
int sol_lua_push(sol::types<buffer *>, lua_State *L, buffer *value); int sol_lua_push(sol::types<buffer *>, lua_State *L, buffer *value);
// lua_engine::devenum customisation
// these things should be treated as containers
template <typename T> struct is_container<lua_engine::devenum<T> > : std::true_type { }; template <typename T> struct is_container<lua_engine::devenum<T> > : std::true_type { };
template <typename T> struct is_container<lua_engine::simple_list_wrapper<T> > : std::true_type { };
template <typename T> struct is_container<lua_engine::tag_object_ptr_map<T> > : std::true_type { };
template <typename T> struct usertype_container<lua_engine::devenum<T> >; template <typename T> struct usertype_container<lua_engine::devenum<T> >;
// tag_object_ptr_map is_container template <typename T>
template <typename T> struct is_container<lua_engine::tag_object_ptr_map<T> > : std::true_type { }; struct usertype_container<lua_engine::simple_list_wrapper<T> > : lua_engine::immutable_container_helper<lua_engine::simple_list_wrapper<T>, simple_list<T> const, typename simple_list<T>::auto_iterator>
{
private:
static int next_pairs(lua_State *L)
{
typename usertype_container::indexed_iterator &i(stack::unqualified_get<user<typename usertype_container::indexed_iterator> >(L, 1));
if (i.src.end() == i.it)
return stack::push(L, lua_nil);
int result;
result = stack::push(L, i.ix + 1);
result += stack::push_reference(L, *i.it);
++i;
return result;
}
public:
static int at(lua_State *L)
{
lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
if ((0 >= index) || (self.list.count() < index))
return stack::push(L, lua_nil);
else
return stack::push_reference(L, *self.list.find(index - 1));
}
static int get(lua_State *L) { return at(L); }
static int index_get(lua_State *L) { return at(L); }
static int index_of(lua_State *L)
{
lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
T &target(stack::unqualified_get<T>(L, 2));
int const found(self.list.indexof(target));
if (0 > found)
return stack::push(L, lua_nil);
else
return stack::push(L, found + 1);
}
static int size(lua_State *L)
{
lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
return stack::push(L, self.list.count());
}
static int empty(lua_State *L)
{
lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
return stack::push(L, self.list.empty());
}
static int next(lua_State *L) { return stack::push(L, next_pairs); }
static int pairs(lua_State *L) { return ipairs(L); }
static int ipairs(lua_State *L)
{
lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
stack::push(L, next_pairs);
stack::push<user<typename usertype_container::indexed_iterator> >(L, self.list, self.list.begin());
stack::push(L, lua_nil);
return 3;
}
};
// tag_object_ptr_map usertype_container
template <typename T> template <typename T>
struct usertype_container<lua_engine::tag_object_ptr_map<T> > : lua_engine::immutable_container_helper<lua_engine::tag_object_ptr_map<T>, T const, typename T::const_iterator> struct usertype_container<lua_engine::tag_object_ptr_map<T> > : lua_engine::immutable_container_helper<lua_engine::tag_object_ptr_map<T>, T const, typename T::const_iterator>
{ {

View File

@ -11,11 +11,140 @@
#include "emu.h" #include "emu.h"
#include "luaengine.ipp" #include "luaengine.ipp"
#include "iptseqpoll.h"
#include "inputdev.h" #include "inputdev.h"
#include "natkeyboard.h" #include "natkeyboard.h"
#include "render.h" #include "render.h"
#include "uiinput.h" #include "uiinput.h"
#include <cstring>
namespace {
struct natkbd_kbd_dev
{
natkbd_kbd_dev(natural_keyboard &m, std::size_t i) : manager(m), index(i) { }
natural_keyboard &manager;
std::size_t index;
};
struct natkbd_kbd_list
{
natkbd_kbd_list(natural_keyboard &m) : manager(m) { }
natural_keyboard &manager;
};
} // anonymous namespace
namespace sol {
template <> struct is_container<natkbd_kbd_list> : std::true_type { };
template <>
struct usertype_container<natkbd_kbd_list>
{
private:
static natkbd_kbd_list &get_self(lua_State *L)
{
auto p(sol::stack::unqualified_check_get<natkbd_kbd_list *>(L, 1));
if (!p)
luaL_error(L, "sol: 'self' is not of type 'natkbd_kbd_list' (pass 'self' as first argument with ':' or call on proper type)");
if (!*p)
luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a 'natkbd_kbd_list' type");
return **p;
}
template <bool Indexed>
static int next_pairs(lua_State *L)
{
natkbd_kbd_dev &i(stack::unqualified_get<user<natkbd_kbd_dev> >(L, 1));
if (i.manager.keyboard_count() <= i.index)
return stack::push(L, lua_nil);
int result;
if constexpr (Indexed)
result = stack::push(L, i.index + 1);
else
result = stack::push(L, i.manager.keyboard_device(i.index).tag());
result += stack::push(L, i);
++i.index;
return result;
}
template <bool Indexed>
static int start_pairs(lua_State *L)
{
natkbd_kbd_list &self(get_self(L));
stack::push(L, next_pairs<Indexed>);
stack::push<user<natkbd_kbd_dev> >(L, self.manager, 0);
stack::push(L, lua_nil);
return 3;
}
public:
static int at(lua_State *L)
{
natkbd_kbd_list &self(get_self(L));
std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
if ((0 < index) && (self.manager.keyboard_count() >= index))
return stack::push(L, natkbd_kbd_dev(self.manager, index - 1));
else
return stack::push(L, lua_nil);
}
static int get(lua_State *L)
{
natkbd_kbd_list &self(get_self(L));
char const *const tag(stack::unqualified_get<char const *>(L));
for (std::size_t i = 0; self.manager.keyboard_count() > i; ++i)
{
if (!std::strcmp(self.manager.keyboard_device(i).tag(), tag))
return stack::push(L, natkbd_kbd_dev(self.manager, i));
}
return stack::push(L, lua_nil);
}
static int index_get(lua_State *L)
{
return get(L);
}
static int size(lua_State *L)
{
natkbd_kbd_list &self(get_self(L));
return stack::push(L, self.manager.keyboard_count());
}
static int empty(lua_State *L)
{
natkbd_kbd_list &self(get_self(L));
return stack::push(L, !self.manager.keyboard_count());
}
// produce errors for unsupported operations
static int set(lua_State *L) { return luaL_error(L, "sol: cannot call 'set(key, value)' on type 'natkbd_kbd_list': container is not modifiable"); }
static int index_set(lua_State *L) { return luaL_error(L, "sol: cannot call 'container[key] = value' on type 'natkbd_kbd_list': container is not modifiable"); }
static int add(lua_State *L) { return luaL_error(L, "sol: cannot call 'add' on type 'natkbd_kbd_list': container is not modifiable"); }
static int insert(lua_State *L) { return luaL_error(L, "sol: cannot call 'insert' on type 'natkbd_kbd_list': container is not modifiable"); }
static int find(lua_State *L) { return luaL_error(L, "sol: cannot call 'find' on type 'natkbd_kbd_list': no supported comparison operator for the value type"); }
static int index_of(lua_State *L) { return luaL_error(L, "sol: cannot call 'index_of' on type 'natkbd_kbd_list': no supported comparison operator for the value type"); }
static int clear(lua_State *L) { return luaL_error(L, "sol: cannot call 'clear' on type 'natkbd_kbd_list': container is not modifiable"); }
static int erase(lua_State *L) { return luaL_error(L, "sol: cannot call 'erase' on type 'natkbd_kbd_list': container is not modifiable"); }
// support for iteration with pairs and ipairs
static int next(lua_State *L) { return stack::push(L, next_pairs<false>); }
static int pairs(lua_State *L) { return start_pairs<false>(L); }
static int ipairs(lua_State *L) { return start_pairs<true>(L); }
};
} // namespace sol
//------------------------------------------------- //-------------------------------------------------
// initialize_input - register input user types // initialize_input - register input user types
@ -41,67 +170,104 @@ void lua_engine::initialize_input(sol::table &emu)
* ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player * ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player
* *
* ioport.ports[] - ioports table (k=tag, v=ioport_port) * ioport.ports[] - ioports table (k=tag, v=ioport_port)
* ioport.natkeyboard - get natural keyboard manager
*/ */
auto ioport_manager_type = sol().registry().new_usertype<ioport_manager>("ioport", "new", sol::no_constructor); auto ioport_manager_type = sol().registry().new_usertype<ioport_manager>("ioport", sol::no_constructor);
ioport_manager_type.set("count_players", &ioport_manager::count_players); ioport_manager_type["count_players"] = &ioport_manager::count_players;
ioport_manager_type.set("natkeyboard", &ioport_manager::natkeyboard); ioport_manager_type["type_group"] = &ioport_manager::type_group;
ioport_manager_type.set("type_group", [](ioport_manager &im, ioport_type type, int player) { ioport_manager_type["type_seq"] = &ioport_manager::type_seq;
return im.type_group(type, player); ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map<ioport_list>(im.ports()); });
}); ioport_manager_type["natkeyboard"] = sol::property(&ioport_manager::natkeyboard);
ioport_manager_type.set("ports", sol::property([this](ioport_manager &im) {
sol::table port_table = sol().create_table();
for (auto &port : im.ports())
port_table[port.second->tag()] = port.second.get();
return port_table;
}));
ioport_manager_type.set("type_seq", [](ioport_manager &m, ioport_type type, int player, input_seq_type seqtype) {
return m.type_seq(type, player, seqtype);
});
/* natural_keyboard library /* natural_keyboard library
* *
* manager:machine():ioport():natkeyboard() * manager:machine():ioport().natkeyboard
* *
* natkeyboard:paste() - paste clipboard data * natkeyboard:post(text) - post data to natural keyboard
* natkeyboard:post() - post data to natural keyboard * natkeyboard:post_coded(text) - post data to natural keyboard
* natkeyboard:post_coded() - post data to natural keyboard * natkeyboard:paste() - paste host clipboard text
* natkeyboard:dump() - returns human-readable description of character mappings
* *
* natkeyboard.empty - is the natural keyboard buffer empty? * natkeyboard.empty - is the natural keyboard buffer empty?
* natkeyboard.in_use - is the natural keyboard in use? * natkeyboard.full - is the natural keyboard buffer full?
* natkeyboard.can_post - does the system support posting characters via natural keyboard?
* natkeyboard.is_posting - is a post operation currently in progress?
* natkeyboard.in_use - is natural keyboard mode enabled (read/write)?
* natkeyboard.keyboards[] - get keyboard devices in system (k=tag, v=natkbd_kbd_dev)
*/ */
auto natkeyboard_type = sol().registry().new_usertype<natural_keyboard>("natkeyboard", "new", sol::no_constructor); auto natkeyboard_type = sol().registry().new_usertype<natural_keyboard>("natkeyboard", sol::no_constructor);
natkeyboard_type.set("empty", sol::property(&natural_keyboard::empty)); natkeyboard_type["post"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_utf8(text); };
natkeyboard_type.set("in_use", sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use)); natkeyboard_type["post_coded"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_coded(text); };
natkeyboard_type.set("paste", &natural_keyboard::paste); natkeyboard_type["paste"] = &natural_keyboard::paste;
natkeyboard_type.set("post", [](natural_keyboard &nat, const std::string &text) { nat.post_utf8(text); }); natkeyboard_type["dump"] = static_cast<std::string (natural_keyboard::*)() const>(&natural_keyboard::dump);
natkeyboard_type.set("post_coded", [](natural_keyboard &nat, const std::string &text) { nat.post_coded(text); }); natkeyboard_type["empty"] = sol::property(&natural_keyboard::empty);
natkeyboard_type["full"] = sol::property(&natural_keyboard::full);
natkeyboard_type["can_post"] = sol::property(&natural_keyboard::can_post);
natkeyboard_type["is_posting"] = sol::property(&natural_keyboard::is_posting);
natkeyboard_type["in_use"] = sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use);
natkeyboard_type["keyboards"] = sol::property([] (natural_keyboard &nat) { return natkbd_kbd_list(nat); });
/* natkbd_kbd_dev library
*
* manager:machine():ioport().natkeyboard.keyboards[tag]
*
* keyboard.device - underlying device that the inputs belong to
* keyboard.tag - absolute tag of the device
* keyboard.basetag - last component of the device tag ("root" for root device)
* keyboard.name - device type full name
* keyboard.shortname - device type short name
* keyboard.is_keypad - does the device have keypad inputs but no keyboard inputs?
* keyboard.enabled - are the device's keyboard/keypad inputs enabled (read/write)?
*/
auto natkbddev_type = sol().registry().new_usertype<natkbd_kbd_dev>("natkeyboard_device", sol::no_constructor);
natkbddev_type["device"] = sol::property([] (natkbd_kbd_dev const &kbd) -> device_t & { return kbd.manager.keyboard_device(kbd.index); });
natkbddev_type["tag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).tag(); });
natkbddev_type["basetag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).basetag(); });
natkbddev_type["name"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).name(); });
natkbddev_type["shortname"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).shortname(); });
natkbddev_type["is_keypad"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_is_keypad(kbd.index); });
natkbddev_type["enabled"] = sol::property(
[] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_enabled(kbd.index); },
[] (natkbd_kbd_dev &kbd, bool enable)
{
if (enable)
kbd.manager.enable_keyboard(kbd.index);
else
kbd.manager.disable_keyboard(kbd.index);
});
/* ioport_port library /* ioport_port library
* *
* manager:machine():ioport().ports[port_tag] * manager:machine():ioport().ports[port_tag]
* *
* port:tag() - get port tag
* port:active() - get port status
* port:live() - get port ioport_port_live (TODO: not usable from lua as of now)
* port:read() - get port value * port:read() - get port value
* port:write(val, mask) - set port to value & mask (output fields only, for other fields use field:set_value(val)) * port:write(val, mask) - set port to value & mask (output fields only, for other fields use field:set_value(val))
* port:field(mask) - get ioport_field for port and mask * port:field(mask) - get ioport_field for port and mask
* *
* port.device - get device that the port belongs to
* port.tag - get port tag
* port.active - get port status
* port.live - get port ioport_port_live (TODO: not usable from lua as of now)
* port.fields[] - get ioport_field table (k=name, v=ioport_field) * port.fields[] - get ioport_field table (k=name, v=ioport_field)
*/ */
auto ioport_port_type = sol().registry().new_usertype<ioport_port>("ioport_port", "new", sol::no_constructor); auto ioport_port_type = sol().registry().new_usertype<ioport_port>("ioport_port", "new", sol::no_constructor);
ioport_port_type.set("tag", &ioport_port::tag); ioport_port_type["read"] = &ioport_port::read;
ioport_port_type.set("active", &ioport_port::active); ioport_port_type["write"] = &ioport_port::write;
ioport_port_type.set("live", &ioport_port::live); ioport_port_type["field"] = &ioport_port::field;
ioport_port_type.set("read", &ioport_port::read); ioport_port_type["device"] = sol::property(&ioport_port::device);
ioport_port_type.set("write", &ioport_port::write); ioport_port_type["tag"] = sol::property(&ioport_port::tag);
ioport_port_type.set("field", &ioport_port::field); ioport_port_type["active"] = sol::property(&ioport_port::active);
ioport_port_type.set("fields", sol::property([this](ioport_port &p){ ioport_port_type["live"] = sol::property(&ioport_port::live);
ioport_port_type["fields"] = sol::property(
[this] (ioport_port &p)
{
sol::table f_table = sol().create_table(); sol::table f_table = sol().create_table();
// parse twice for custom and default names, default has priority // parse twice for custom and default names, default has priority
for (ioport_field &field : p.fields()) for (ioport_field &field : p.fields())
@ -120,7 +286,7 @@ void lua_engine::initialize_input(sol::table &emu)
} }
} }
return f_table; return f_table;
})); });
/* ioport_field library /* ioport_field library
@ -165,46 +331,60 @@ void lua_engine::initialize_input(sol::table &emu)
* field.settings[] - ioport_setting table (k=value, v=name) * field.settings[] - ioport_setting table (k=value, v=name)
*/ */
auto ioport_field_type = sol().registry().new_usertype<ioport_field>("ioport_field", "new", sol::no_constructor); auto ioport_field_type = sol().registry().new_usertype<ioport_field>("ioport_field", sol::no_constructor);
ioport_field_type.set("set_value", &ioport_field::set_value); ioport_field_type["set_value"] = &ioport_field::set_value;
ioport_field_type.set("set_input_seq", [](ioport_field &f, const std::string &seq_type_string, const input_seq &seq) { ioport_field_type["set_input_seq"] =
[] (ioport_field &f, std::string const &seq_type_string, const input_seq &seq)
{
input_seq_type seq_type = s_seq_type_parser(seq_type_string); input_seq_type seq_type = s_seq_type_parser(seq_type_string);
ioport_field::user_settings settings; ioport_field::user_settings settings;
f.get_user_settings(settings); f.get_user_settings(settings);
settings.seq[seq_type] = seq; settings.seq[seq_type] = seq;
f.set_user_settings(settings); f.set_user_settings(settings);
}); };
ioport_field_type.set("input_seq", [](ioport_field &f, const std::string &seq_type_string) { ioport_field_type["input_seq"] =
[] (ioport_field &f, std::string const &seq_type_string)
{
input_seq_type seq_type = s_seq_type_parser(seq_type_string); input_seq_type seq_type = s_seq_type_parser(seq_type_string);
return f.seq(seq_type); return f.seq(seq_type);
}); };
ioport_field_type.set("set_default_input_seq", [](ioport_field &f, const std::string &seq_type_string, const input_seq &seq) { ioport_field_type["set_default_input_seq"] =
[] (ioport_field &f, std::string const &seq_type_string, input_seq const &seq)
{
input_seq_type seq_type = s_seq_type_parser(seq_type_string); input_seq_type seq_type = s_seq_type_parser(seq_type_string);
f.set_defseq(seq_type, seq); f.set_defseq(seq_type, seq);
}); };
ioport_field_type.set("default_input_seq", [](ioport_field &f, const std::string &seq_type_string) { ioport_field_type["default_input_seq"] =
[] (ioport_field &f, const std::string &seq_type_string)
{
input_seq_type seq_type = s_seq_type_parser(seq_type_string); input_seq_type seq_type = s_seq_type_parser(seq_type_string);
return f.defseq(seq_type); return f.defseq(seq_type);
}); };
ioport_field_type.set("keyboard_codes", [this](ioport_field &f, int which) { ioport_field_type["keyboard_codes"] =
[this] (ioport_field &f, int which)
{
sol::table result = sol().create_table(); sol::table result = sol().create_table();
int index = 1; int index = 1;
for (char32_t code : f.keyboard_codes(which)) for (char32_t code : f.keyboard_codes(which))
result[index++] = code; result[index++] = code;
return result; return result;
}); };
ioport_field_type.set("device", sol::property(&ioport_field::device)); ioport_field_type["device"] = sol::property(&ioport_field::device);
ioport_field_type.set("port", sol::property(&ioport_field::port)); ioport_field_type["port"] = sol::property(&ioport_field::port);
ioport_field_type.set("name", sol::property(&ioport_field::name)); ioport_field_type["name"] = sol::property(&ioport_field::name);
ioport_field_type.set("default_name", sol::property([](ioport_field &f) { ioport_field_type["default_name"] = sol::property(
[] (ioport_field &f)
{
return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player()); return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player());
})); });
ioport_field_type.set("player", sol::property(&ioport_field::player, &ioport_field::set_player)); ioport_field_type["player"] = sol::property(&ioport_field::player, &ioport_field::set_player);
ioport_field_type.set("mask", sol::property(&ioport_field::mask)); ioport_field_type["mask"] = sol::property(&ioport_field::mask);
ioport_field_type.set("defvalue", sol::property(&ioport_field::defvalue)); ioport_field_type["defvalue"] = sol::property(&ioport_field::defvalue);
ioport_field_type.set("sensitivity", sol::property(&ioport_field::sensitivity)); ioport_field_type["sensitivity"] = sol::property(&ioport_field::sensitivity);
ioport_field_type.set("way", sol::property(&ioport_field::way)); ioport_field_type["way"] = sol::property(&ioport_field::way);
ioport_field_type.set("type_class", sol::property([](ioport_field &f) { ioport_field_type["type_class"] = sol::property(
[] (ioport_field &f)
{
switch (f.type_class()) switch (f.type_class())
{ {
case INPUT_CLASS_KEYBOARD: return "keyboard"; case INPUT_CLASS_KEYBOARD: return "keyboard";
@ -215,42 +395,46 @@ void lua_engine::initialize_input(sol::table &emu)
default: break; default: break;
} }
throw false; throw false;
})); });
ioport_field_type.set("is_analog", sol::property(&ioport_field::is_analog)); ioport_field_type["is_analog"] = sol::property(&ioport_field::is_analog);
ioport_field_type.set("is_digital_joystick", sol::property(&ioport_field::is_digital_joystick)); ioport_field_type["is_digital_joystick"] = sol::property(&ioport_field::is_digital_joystick);
ioport_field_type.set("enabled", sol::property(&ioport_field::enabled)); ioport_field_type["enabled"] = sol::property(&ioport_field::enabled);
ioport_field_type.set("optional", sol::property(&ioport_field::optional)); ioport_field_type["optional"] = sol::property(&ioport_field::optional);
ioport_field_type.set("cocktail", sol::property(&ioport_field::cocktail)); ioport_field_type["cocktail"] = sol::property(&ioport_field::cocktail);
ioport_field_type.set("toggle", sol::property(&ioport_field::toggle)); ioport_field_type["toggle"] = sol::property(&ioport_field::toggle);
ioport_field_type.set("rotated", sol::property(&ioport_field::rotated)); ioport_field_type["rotated"] = sol::property(&ioport_field::rotated);
ioport_field_type.set("analog_reverse", sol::property(&ioport_field::analog_reverse)); ioport_field_type["analog_reverse"] = sol::property(&ioport_field::analog_reverse);
ioport_field_type.set("analog_reset", sol::property(&ioport_field::analog_reset)); ioport_field_type["analog_reset"] = sol::property(&ioport_field::analog_reset);
ioport_field_type.set("analog_wraps", sol::property(&ioport_field::analog_wraps)); ioport_field_type["analog_wraps"] = sol::property(&ioport_field::analog_wraps);
ioport_field_type.set("analog_invert", sol::property(&ioport_field::analog_invert)); ioport_field_type["analog_invert"] = sol::property(&ioport_field::analog_invert);
ioport_field_type.set("impulse", sol::property(&ioport_field::impulse)); ioport_field_type["impulse"] = sol::property(&ioport_field::impulse);
ioport_field_type.set("type", sol::property(&ioport_field::type)); ioport_field_type["type"] = sol::property(&ioport_field::type);
ioport_field_type.set("live", sol::property(&ioport_field::live)); ioport_field_type["live"] = sol::property(&ioport_field::live);
ioport_field_type.set("crosshair_scale", sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale)); ioport_field_type["crosshair_scale"] = sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale);
ioport_field_type.set("crosshair_offset", sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset)); ioport_field_type["crosshair_offset"] = sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset);
ioport_field_type.set("user_value", sol::property( ioport_field_type["user_value"] = sol::property(
[](ioport_field &f) { [] (ioport_field &f)
{
ioport_field::user_settings settings; ioport_field::user_settings settings;
f.get_user_settings(settings); f.get_user_settings(settings);
return settings.value; return settings.value;
}, },
[](ioport_field &f, ioport_value val) { [] (ioport_field &f, ioport_value val)
{
ioport_field::user_settings settings; ioport_field::user_settings settings;
f.get_user_settings(settings); f.get_user_settings(settings);
settings.value = val; settings.value = val;
f.set_user_settings(settings); f.set_user_settings(settings);
})); });
ioport_field_type.set("settings", sol::property([this](ioport_field &f) { ioport_field_type["settings"] = sol::property(
[this] (ioport_field &f)
{
sol::table result = sol().create_table(); sol::table result = sol().create_table();
for (ioport_setting &setting : f.settings()) for (ioport_setting &setting : f.settings())
if (setting.enabled()) if (setting.enabled())
result[setting.value()] = setting.name(); result[setting.value()] = setting.name();
return result; return result;
})); });
/* ioport_field_live library /* ioport_field_live library
@ -260,44 +444,79 @@ void lua_engine::initialize_input(sol::table &emu)
* live.name * live.name
*/ */
sol().registry().new_usertype<ioport_field_live>("ioport_field_live", "new", sol::no_constructor, auto ioport_field_live_type = sol().registry().new_usertype<ioport_field_live>("ioport_field_live", sol::no_constructor);
"name", &ioport_field_live::name); ioport_field_live_type["name"] = &ioport_field_live::name;
/* input_manager library /* input_manager library
* *
* manager:machine():input() * manager:machine():input()
* *
* input:code_from_token(token) - get input_code for KEYCODE_* string token * input:code_value(code) -
* input:code_pressed(code) - get pressed state for input_code * input:code_pressed(code) - get pressed state for input_code
* input:code_to_token(code) - get KEYCODE_* string token for code * input:code_pressed_once(code) -
* input:code_name(code) - get code friendly name * input:code_name(code) - get code friendly name
* input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens * input:code_to_token(code) - get KEYCODE_* string token for code
* input:code_from_token(token) - get input_code for KEYCODE_* string token
* input:seq_pressed(seq) - get pressed state for input_seq * input:seq_pressed(seq) - get pressed state for input_seq
* input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq
* input:seq_name(seq) - get seq friendly name
* input:seq_clean(seq) - clean the seq and remove invalid elements * input:seq_clean(seq) - clean the seq and remove invalid elements
* input:seq_poll_start(class, [opt] start_seq) - start polling for input_item_class passed as string * input:seq_name(seq) - get seq friendly name
* (switch/abs[olute]/rel[ative]/max[imum]) * input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq
* input:seq_poll() - poll once, returns true if input was fetched * input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens
* input:seq_poll_final() - get final input_seq * input:sequence_poller() - get an input sequence poller
* input.device_classes - returns device classes *
* input.device_classes[] - returns device classes (k=name, v=input_device_class)
*/ */
auto input_type = sol().registry().new_usertype<input_manager>("input", "new", sol::no_constructor); auto input_type = sol().registry().new_usertype<input_manager>("input", sol::no_constructor);
input_type.set("code_from_token", [](input_manager &input, const char *token) { return input.code_from_token(token); }); input_type["code_value"] = &input_manager::code_value;
input_type.set("code_pressed", [](input_manager &input, const input_code &code) { return input.code_pressed(code); }); input_type["code_pressed"] = &input_manager::code_pressed;
input_type.set("code_to_token", [](input_manager &input, const input_code &code) { return input.code_to_token(code); }); input_type["code_pressed_once"] = &input_manager::code_pressed_once;
input_type.set("code_name", [](input_manager &input, const input_code &code) { return input.code_name(code); }); input_type["code_name"] = &input_manager::code_name;
input_type.set("seq_from_tokens", [](input_manager &input, const char *tokens) { input_seq seq; input.seq_from_tokens(seq, tokens); return seq; }); input_type["code_to_token"] = &input_manager::code_to_token;
input_type.set("seq_pressed", [](input_manager &input, const input_seq &seq) { return input.seq_pressed(seq); }); input_type["code_from_token"] = &input_manager::code_from_token;
input_type.set("seq_to_tokens", [](input_manager &input, const input_seq &seq) { return input.seq_to_tokens(seq); }); input_type["seq_pressed"] = &input_manager::seq_pressed;
input_type.set("seq_name", [](input_manager &input, const input_seq &seq) { return input.seq_name(seq); }); input_type["seq_clean"] = &input_manager::seq_clean;
input_type.set("seq_clean", [](input_manager &input, const input_seq &seq) { return input.seq_clean(seq); }); input_type["seq_name"] = &input_manager::seq_name;
input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) { input_type["seq_to_tokens"] = &input_manager::seq_to_tokens;
if (!m_seq_poll) input_type["seq_from_tokens"] =
m_seq_poll.reset(new input_sequence_poller(input)); [] (input_manager &input, const char *tokens)
{
input_seq seq;
input.seq_from_tokens(seq, tokens);
return seq;
};
input_type["sequence_poller"] = [] (input_manager &input) { return input_sequence_poller(input); };
input_type["device_classes"] = sol::property(
[this] (input_manager &input)
{
sol::table result = sol().create_table();
for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++)
{
input_class &devclass = input.device_class(devclass_id);
result[devclass.name()] = &devclass;
}
return result;
});
/* input_sequence_poller library
*
* manager:machine():input():seq_poll()
*
* poller:start(class, [opt] start_seq) - start polling for input_item_class passed as string
* (switch/abs[olute]/rel[ative]/max[imum])
* poller:poll() - poll once, returns true if input was fetched
*
* poller.sequence - get current input_seq
* poller.valid - true if input sequence is valid
* poller.modified - true if input sequence was modified
*/
auto seqpoll_type = sol().registry().new_usertype<input_sequence_poller>("input_seq_poller", sol::no_constructor);
seqpoll_type["start"] =
[this] (input_sequence_poller &poller, char const *cls_string, sol::object seq)
{
input_item_class cls; input_item_class cls;
if (!strcmp(cls_string, "switch")) if (!strcmp(cls_string, "switch"))
cls = ITEM_CLASS_SWITCH; cls = ITEM_CLASS_SWITCH;
@ -311,44 +530,14 @@ void lua_engine::initialize_input(sol::table &emu)
cls = ITEM_CLASS_INVALID; cls = ITEM_CLASS_INVALID;
if (seq.is<sol::user<input_seq>>()) if (seq.is<sol::user<input_seq>>())
m_seq_poll->start(cls, seq.as<sol::user<input_seq>>()); poller.start(cls, seq.as<input_seq>());
else else
m_seq_poll->start(cls); poller.start(cls);
}); };
input_type.set("seq_poll", [this](input_manager &input) -> sol::object { seqpoll_type["poll"] = &input_sequence_poller::poll;
if (!m_seq_poll) seqpoll_type["sequence"] = sol::property(&input_sequence_poller::sequence);
return sol::make_object(sol(), sol::lua_nil); seqpoll_type["valid"] = sol::property(&input_sequence_poller::valid);
return sol::make_object(sol(), m_seq_poll->poll()); seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified);
});
input_type.set("seq_poll_final", [this](input_manager &input) -> sol::object {
if (!m_seq_poll)
return sol::make_object(sol(), sol::lua_nil);
return sol::make_object(sol(), m_seq_poll->valid() ? m_seq_poll->sequence() : input_seq());
});
input_type.set("seq_poll_modified", [this](input_manager &input) -> sol::object {
if (!m_seq_poll)
return sol::make_object(sol(), sol::lua_nil);
return sol::make_object(sol(), m_seq_poll->modified());
});
input_type.set("seq_poll_valid", [this](input_manager &input) -> sol::object {
if (!m_seq_poll)
return sol::make_object(sol(), sol::lua_nil);
return sol::make_object(sol(), m_seq_poll->valid());
});
input_type.set("seq_poll_sequence", [this](input_manager &input) -> sol::object {
if (!m_seq_poll)
return sol::make_object(sol(), sol::lua_nil);
return sol::make_object(sol(), m_seq_poll->sequence());
});
input_type.set("device_classes", sol::property([this](input_manager &input) {
sol::table result = sol().create_table();
for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++)
{
input_class &devclass = input.device_class(devclass_id);
result[devclass.name()] = &devclass;
}
return result;
}));
/* input_class library /* input_class library
@ -362,10 +551,12 @@ void lua_engine::initialize_input(sol::table &emu)
*/ */
auto input_class_type = sol().registry().new_usertype<input_class>("input_class", "new", sol::no_constructor); auto input_class_type = sol().registry().new_usertype<input_class>("input_class", "new", sol::no_constructor);
input_class_type.set("name", sol::property(&input_class::name)); input_class_type["name"] = sol::property(&input_class::name);
input_class_type.set("enabled", sol::property(&input_class::enabled, &input_class::enable)); input_class_type["enabled"] = sol::property(&input_class::enabled, &input_class::enable);
input_class_type.set("multi", sol::property(&input_class::multi, &input_class::set_multi)); input_class_type["multi"] = sol::property(&input_class::multi, &input_class::set_multi);
input_class_type.set("devices", sol::property([this](input_class &devclass) { input_class_type["devices"] = sol::property(
[this] (input_class &devclass)
{
sol::table result = sol().create_table(); sol::table result = sol().create_table();
int index = 1; int index = 1;
for (int devindex = 0; devindex <= devclass.maxindex(); devindex++) for (int devindex = 0; devindex <= devclass.maxindex(); devindex++)
@ -375,23 +566,26 @@ void lua_engine::initialize_input(sol::table &emu)
result[index++] = dev; result[index++] = dev;
} }
return result; return result;
})); });
/* input_device library /* input_device library
* *
* manager:machine():input().device_classes[devclass].devices[index] * manager:machine():input().device_classes[devclass].devices[index]
* device.name *
* device.id * device.name -
* device.devindex * device.id -
* device.items[] * device.devindex -
* device.items[] -
*/ */
auto input_device_type = sol().registry().new_usertype<input_device>("input_device", "new", sol::no_constructor); auto input_device_type = sol().registry().new_usertype<input_device>("input_device", "new", sol::no_constructor);
input_device_type.set("name", sol::property(&input_device::name)); input_device_type["name"] = sol::property(&input_device::name);
input_device_type.set("id", sol::property(&input_device::id)); input_device_type["id"] = sol::property(&input_device::id);
input_device_type.set("devindex", sol::property(&input_device::devindex)); input_device_type["devindex"] = sol::property(&input_device::devindex);
input_device_type.set("items", sol::property([this](input_device &dev) { input_device_type["items"] = sol::property(
[this] (input_device &dev)
{
sol::table result = sol().create_table(); sol::table result = sol().create_table();
for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++) for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
{ {
@ -400,23 +594,24 @@ void lua_engine::initialize_input(sol::table &emu)
result[id] = dev.item(id); result[id] = dev.item(id);
} }
return result; return result;
})); });
/* input_device_item library /* input_device_item library
* *
* manager:machine():input().device_classes[devclass].devices[index].items[item_id] * manager:machine():input().device_classes[devclass].devices[index].items[item_id]
* item.name *
* item.token * item.name -
* item:code() * item.code -
* item.token -
* item.current -
*/ */
auto input_device_item_type = sol().registry().new_usertype<input_device_item>("input_device_item", "new", sol::no_constructor); auto input_device_item_type = sol().registry().new_usertype<input_device_item>("input_device_item", "new", sol::no_constructor);
input_device_item_type.set("name", sol::property(&input_device_item::name)); input_device_item_type["name"] = sol::property(&input_device_item::name);
input_device_item_type.set("token", sol::property(&input_device_item::token)); input_device_item_type["code"] = sol::property(&input_device_item::code);
input_device_item_type.set("code", [](input_device_item &item) { input_device_item_type["token"] = sol::property(&input_device_item::token);
return input_code(item.device().devclass(), item.device().devindex(), item.itemclass(), ITEM_MODIFIER_NONE, item.itemid()); input_device_item_type["current"] = sol::property(&input_device_item::current);
});
/* ui_input_manager library /* ui_input_manager library

View File

@ -33,21 +33,13 @@ struct layout_view_items
layout_view &view; layout_view &view;
}; };
struct render_manager_targets
{
render_manager_targets(render_manager &m) : targets(m.targets()) { }
simple_list<render_target> const &targets;
};
} // anonymous namespace } // anonymous namespace
namespace sol { namespace sol {
template <> struct is_container<layout_file_views> : std::true_type { }; template <> struct is_container<layout_file_views> : std::true_type { };
template <> struct is_container<layout_view_items> : std::true_type { }; template <> struct is_container<layout_view_items> : std::true_type { };
template <> struct is_container<render_manager_targets> : std::true_type { };
template <> template <>
@ -239,76 +231,6 @@ public:
} }
}; };
template <>
struct usertype_container<render_manager_targets> : lua_engine::immutable_container_helper<render_manager_targets, simple_list<render_target> const, simple_list<render_target>::auto_iterator>
{
private:
using target_list = simple_list<render_target>;
static int next_pairs(lua_State *L)
{
indexed_iterator &i(stack::unqualified_get<user<indexed_iterator> >(L, 1));
if (i.src.end() == i.it)
return stack::push(L, lua_nil);
int result;
result = stack::push(L, i.ix + 1);
result += stack::push_reference(L, *i.it);
++i.it;
++i.ix;
return result;
}
public:
static int at(lua_State *L)
{
render_manager_targets &self(get_self(L));
std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
if ((0 >= index) || (self.targets.count() < index))
return stack::push(L, lua_nil);
else
return stack::push_reference(L, *self.targets.find(index - 1));
}
static int get(lua_State *L) { return at(L); }
static int index_get(lua_State *L) { return at(L); }
static int index_of(lua_State *L)
{
render_manager_targets &self(get_self(L));
render_target &target(stack::unqualified_get<render_target>(L, 2));
int const found(self.targets.indexof(target));
if (0 > found)
return stack::push(L, lua_nil);
else
return stack::push(L, found + 1);
}
static int size(lua_State *L)
{
render_manager_targets &self(get_self(L));
return stack::push(L, self.targets.count());
}
static int empty(lua_State *L)
{
render_manager_targets &self(get_self(L));
return stack::push(L, self.targets.empty());
}
static int next(lua_State *L) { return stack::push(L, next_pairs); }
static int pairs(lua_State *L) { return ipairs(L); }
static int ipairs(lua_State *L)
{
render_manager_targets &self(get_self(L));
stack::push(L, next_pairs);
stack::push<user<indexed_iterator> >(L, self.targets, self.targets.begin());
stack::push(L, lua_nil);
return 3;
}
};
} // namespace sol } // namespace sol
@ -619,6 +541,6 @@ void lua_engine::initialize_render(sol::table &emu)
render_type["max_update_rate"] = &render_manager::max_update_rate; render_type["max_update_rate"] = &render_manager::max_update_rate;
render_type["ui_target"] = &render_manager::ui_target; render_type["ui_target"] = &render_manager::ui_target;
render_type["ui_container"] = &render_manager::ui_container; render_type["ui_container"] = &render_manager::ui_container;
render_type["targets"] = sol::property([] (render_manager &m) { return render_manager_targets(m); }); render_type["targets"] = sol::property([] (render_manager &m) { return simple_list_wrapper<render_target>(m.targets()); });
} }

View File

@ -460,8 +460,8 @@ void bwidow_state::spacduel_map(address_map &map)
map(0x0e00, 0x0e00).w(FUNC(bwidow_state::irq_ack_w)); // interrupt acknowledge map(0x0e00, 0x0e00).w(FUNC(bwidow_state::irq_ack_w)); // interrupt acknowledge
map(0x0e80, 0x0e80).w(FUNC(bwidow_state::earom_control_w)); map(0x0e80, 0x0e80).w(FUNC(bwidow_state::earom_control_w));
map(0x0f00, 0x0f3f).w(FUNC(bwidow_state::earom_write)); map(0x0f00, 0x0f3f).w(FUNC(bwidow_state::earom_write));
map(0x1000, 0x13ff).rw("pokey1", FUNC(pokey_device::read), FUNC(pokey_device::write)); map(0x1000, 0x10ff).mirror(0x0300).rw("pokey1", FUNC(pokey_device::read), FUNC(pokey_device::write));
map(0x1400, 0x17ff).rw("pokey2", FUNC(pokey_device::read), FUNC(pokey_device::write)); map(0x1400, 0x14ff).mirror(0x0300).rw("pokey2", FUNC(pokey_device::read), FUNC(pokey_device::write));
map(0x2000, 0x27ff).ram(); // vector RAM map(0x2000, 0x27ff).ram(); // vector RAM
map(0x2800, 0x3fff).rom(); // vector ROM map(0x2800, 0x3fff).rom(); // vector ROM
map(0x4000, 0xffff).rom(); map(0x4000, 0xffff).rom();

View File

@ -1843,7 +1843,7 @@ static INPUT_PORTS_START( birdiy )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_4WAY PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_4WAY
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_4WAY PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_4WAY
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_4WAY PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_4WAY
PORT_DIPNAME( 0x10, 0x10, "Test mode?" ) /* Some kind of test / debug mode */ PORT_DIPNAME( 0x10, 0x10, "Test mode?" ) // Some kind of test/debug mode
PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) )
PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) )
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 )
@ -1856,7 +1856,6 @@ static INPUT_PORTS_START( birdiy )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
@ -1875,7 +1874,7 @@ static INPUT_PORTS_START( birdiy )
PORT_DIPNAME( 0x10, 0x00, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW:5") PORT_DIPNAME( 0x10, 0x00, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW:5")
PORT_DIPSETTING( 0x00, DEF_STR( Upright ) ) PORT_DIPSETTING( 0x00, DEF_STR( Upright ) )
PORT_DIPSETTING( 0x10, DEF_STR( Cocktail ) ) PORT_DIPSETTING( 0x10, DEF_STR( Cocktail ) )
PORT_DIPNAME( 0x20, 0x20, "Skip Screen" ) PORT_DIPLOCATION("SW:7") /* End level after the first worm fed to your baby */ PORT_DIPNAME( 0x20, 0x20, "Skip Screen" ) PORT_DIPLOCATION("SW:7") // End level after the first worm fed to your baby
PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) )
PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) )
PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW:7") PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW:7")