mirror of
https://github.com/holub/mame
synced 2025-04-19 07:00:31 +03:00
-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:
parent
06568860e7
commit
e008c7b1b1
@ -9,6 +9,7 @@ MAME’s source or working on scripts that run within the MAME framework.
|
||||
|
||||
naming
|
||||
layout_files
|
||||
layout_script
|
||||
object_finders
|
||||
device_memory_interface
|
||||
device_rom_interface
|
||||
|
@ -1,10 +1,12 @@
|
||||
.. _layfile:
|
||||
|
||||
MAME Layout Files
|
||||
=================
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
|
||||
.. _layout-intro:
|
||||
.. _layfile-intro:
|
||||
|
||||
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-concepts:
|
||||
.. _layfile-concepts:
|
||||
|
||||
Core concepts
|
||||
-------------
|
||||
|
||||
.. _layout-concepts-numbers:
|
||||
.. _layfile-concepts-numbers:
|
||||
|
||||
Numbers
|
||||
~~~~~~~
|
||||
@ -53,7 +55,7 @@ found, the number will be interpreted as an integer.
|
||||
Numbers are parsed using the "C" locale for portability.
|
||||
|
||||
|
||||
.. _layout-concepts-coordinates:
|
||||
.. _layfile-concepts-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``.
|
||||
|
||||
|
||||
.. _layout-concepts-colours:
|
||||
.. _layfile-concepts-colours:
|
||||
|
||||
Colours
|
||||
~~~~~~~
|
||||
@ -128,7 +130,7 @@ is an error if any channel value falls outside the range of 0.0 to 1.0
|
||||
(inclusive).
|
||||
|
||||
|
||||
.. _layout-concepts-params:
|
||||
.. _layfile-concepts-params:
|
||||
|
||||
Parameters
|
||||
~~~~~~~~~~
|
||||
@ -183,7 +185,7 @@ Here’s an example assigning the value “4” to the value parameter “firstd
|
||||
Generator parameters are assigned using a ``param`` element with ``name`` and
|
||||
``start`` attributes, and ``increment``, ``lshift`` and/or ``rshift``
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
.. _layout-concepts-predef-params:
|
||||
.. _layfile-concepts-predef-params:
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _layout-parts:
|
||||
.. _layfile-parts:
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
element
|
||||
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
|
||||
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
|
||||
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.
|
||||
view
|
||||
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
|
||||
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
|
||||
~~~~~~~~
|
||||
@ -400,7 +403,7 @@ multiple times within a view.
|
||||
|
||||
An element’s appearance depends on its *state*. The state is an integer which
|
||||
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
|
||||
only drawing when the element’s state is a particular value. Some components
|
||||
(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``
|
||||
attributes are present, or if the ``statemask`` attribute’s value is zero.)
|
||||
* Each component may have a ``bounds`` child element specifying its position and
|
||||
size (see :ref:`layout-concepts-coordinates`). If no such element is present,
|
||||
the bounds default to a unit square (width and height of 1.0) with the top
|
||||
left corner at (0,0).
|
||||
size (see :ref:`layfile-concepts-coordinates`). If no such element is
|
||||
present, the bounds default to a unit square (width and height of 1.0) with
|
||||
the top left corner at (0,0).
|
||||
|
||||
A component’s position and/or size may be animated according to the element’s
|
||||
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
|
||||
interpolated linearly.
|
||||
* 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
|
||||
the colour of geometric, algorithmically drawn, or textual components. For
|
||||
``image`` components, the colour of the image pixels is multiplied by the
|
||||
(see :ref:`layfile-concepts-colours` for details). This can be used to
|
||||
control the colour of geometric, algorithmically drawn, or textual components.
|
||||
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
|
||||
opaque white.
|
||||
|
||||
@ -485,10 +488,14 @@ disk
|
||||
image
|
||||
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
|
||||
using the required ``file`` attribute. Additionally, an optional
|
||||
``alphafile`` attribute may be used to specify the name of a PNG file
|
||||
(including the file name extension) to load into the alpha channel of the
|
||||
image.
|
||||
using the ``file`` attribute. Additionally, an optional ``alphafile``
|
||||
attribute may be used to specify the name of a PNG file (including the file
|
||||
name extension) to load into the alpha channel of the 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
|
||||
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)
|
||||
corresponding to fully opaque, and black corresponding to fully transparent.
|
||||
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
|
||||
layout file. Image file formats are detected by examining the content of
|
||||
@ -671,7 +679,7 @@ neutral position:
|
||||
</element>
|
||||
|
||||
|
||||
.. _layout-parts-views:
|
||||
.. _layfile-parts-views:
|
||||
|
||||
Views
|
||||
~~~~~
|
||||
@ -713,7 +721,7 @@ The following child elements are allowed inside a ``view`` element:
|
||||
|
||||
bounds
|
||||
Sets the origin and size of the view’s 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
|
||||
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
|
||||
@ -721,18 +729,18 @@ bounds
|
||||
output window or screen.
|
||||
param
|
||||
Defines or reassigns a value parameter in the view’s scope. See
|
||||
:ref:`layout-concepts-params` for details.
|
||||
:ref:`layfile-concepts-params` for details.
|
||||
element
|
||||
Adds an element to the view (see :ref:`layout-parts-elements`). The name of
|
||||
the element to add is specified using the required ``ref`` attribute. It is
|
||||
an error if no element with this name is defined in the layout file. Within
|
||||
a view, elements are drawn in the order they appear in the layout file, from
|
||||
front to back. See below for more details.
|
||||
Adds an element to the view (see :ref:`layfile-parts-elements`). The name
|
||||
of the element to add is specified using the required ``ref`` attribute. It
|
||||
is an error if no element with this name is defined in the layout file.
|
||||
Within a view, elements are drawn in the order they appear in the layout
|
||||
file, from front to back. See below for more details.
|
||||
|
||||
May optionally be connected to an emulated I/O port using ``inputtag`` and
|
||||
``inputmask`` attributes, and/or an emulated output using a ``name``
|
||||
attribute. See :ref:`layout-interact-clickable` for details. See
|
||||
:ref:`layout-interact-elemstate` for details on supplying a state value to
|
||||
attribute. See :ref:`layfile-interact-clickable` for details. See
|
||||
:ref:`layfile-interact-elemstate` for details on supplying a state value to
|
||||
the instantiated element.
|
||||
screen
|
||||
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
|
||||
``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
|
||||
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
|
||||
collections per view.
|
||||
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``
|
||||
attribute. It is an error if no group with this name is defined in the
|
||||
layout file. See below for more details on positioning.
|
||||
@ -762,9 +770,15 @@ repeat
|
||||
attribute. The ``count`` attribute must be a positive integer. A
|
||||
``repeat`` element in a view may contain ``element``, ``screen``, ``group``,
|
||||
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.
|
||||
|
||||
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
|
||||
(``group`` elements) may have their orientation altered using an ``orientation``
|
||||
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
|
||||
(``group`` elements) may be positioned and sized using a ``bounds`` child
|
||||
element (see :ref:`layout-concepts-coordinates` for details). In the absence of
|
||||
a ``bounds`` child element, screens’ and layout elements’ bounds default to a
|
||||
element (see :ref:`layfile-concepts-coordinates` for details). In the absence
|
||||
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
|
||||
absence of a ``bounds`` child element, groups are expanded with no
|
||||
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
|
||||
(``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.
|
||||
|
||||
Screens (``screen`` elements) and layout elements (``element`` elements) may
|
||||
have their colour and position/size animated by supplying multiple ``color``
|
||||
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
|
||||
~~~~~~~~~~~
|
||||
@ -862,13 +876,14 @@ to be hidden by the user:
|
||||
|
||||
A collection creates a nested parameter scope. Any ``param`` elements inside
|
||||
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
|
||||
collection’s name and default visibility are not part of its content, and any
|
||||
parameter references in the ``name`` and ``visible`` attributes themselves will
|
||||
be substituted using parameter values from the collection’s parent’s scope.)
|
||||
See :ref:`layfile-concepts-params` for more detail on parameters. (Note that
|
||||
the collection’s name and default visibility are not part of its content, and
|
||||
any parameter references in the ``name`` and ``visible`` attributes themselves
|
||||
will be substituted using parameter values from the collection’s parent’s
|
||||
scope.)
|
||||
|
||||
|
||||
.. _layout-parts-groups:
|
||||
.. _layfile-parts-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.
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
transform when instantiating a group. A group may position screens and/or
|
||||
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
|
||||
element set parameters in the local scope for the group instantiation. Local
|
||||
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
|
||||
group’s 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
|
||||
definition appears in the top-level ``mamelayout`` element’s scope.)
|
||||
|
||||
|
||||
.. _layout-parts-repeats:
|
||||
.. _layfile-parts-repeats:
|
||||
|
||||
Repeating blocks
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Repeating blocks provide a concise way to generate or arrange large numbers of
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
:ref:`layout-parts-views`). A repeating block creates a nested parameter scope
|
||||
child elements are used (:ref:`layfile-parts`, :ref:`layfile-parts-groups`, and
|
||||
:ref:`layfile-parts-views`). A repeating block creates a nested parameter scope
|
||||
inside the parameter scope of its lexical (DOM) parent element.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _layout-interact:
|
||||
.. _layfile-interact:
|
||||
|
||||
Interactivity
|
||||
-------------
|
||||
@ -1135,23 +1150,23 @@ Clickable items
|
||||
State-dependent components
|
||||
Some components will be drawn differently depending on the containing
|
||||
element’s 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.
|
||||
Conditionally-drawn components
|
||||
Components may be conditionally drawn or hidden depending on the containing
|
||||
element’s state by supplying ``state`` and/or ``statemask`` attributes. See
|
||||
:ref:`layout-parts-elements` for details.
|
||||
:ref:`layfile-parts-elements` for details.
|
||||
Component parameter animation
|
||||
Components’ colour and position/size within their containing element may be
|
||||
animated according the element’s state by providing multiple ``color``
|
||||
and/or ``bounds`` elements with ``state`` attributes. See
|
||||
:ref:`layout-parts-elements` for details.
|
||||
:ref:`layfile-parts-elements` for details.
|
||||
Item parameter animation
|
||||
Items’ colour and position/size within their containing view may be animated
|
||||
according to their animation state.
|
||||
|
||||
|
||||
.. _layout-interact-clickable:
|
||||
.. _layfile-interact-clickable:
|
||||
|
||||
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
|
||||
will remain active as long as the mouse 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:`layout-interact-itemanim`).
|
||||
the item’s animation state, see :ref:`layfile-interact-itemanim`).
|
||||
|
||||
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
|
||||
@ -1185,14 +1200,14 @@ and only activates the first clickable item whose area includes the location of
|
||||
the mouse pointer.
|
||||
|
||||
|
||||
.. _layout-interact-elemstate:
|
||||
.. _layfile-interact-elemstate:
|
||||
|
||||
Element state
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
:ref:`layout-parts-elements` for details on how an element’s state affects its
|
||||
:ref:`layfile-parts-elements` for details on how an element’s state affects its
|
||||
appearance.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _layout-interact-itemanim:
|
||||
.. _layfile-interact-itemanim:
|
||||
|
||||
View item animation
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@ -1265,7 +1280,7 @@ An item’s animation state may be bound to an emulated output or input port by
|
||||
supplying an ``animate`` child element. If present, the ``animate`` element
|
||||
must have either an ``inputtag`` attribute or a ``name`` attribute (but not
|
||||
both). If the ``animate`` child element is not present, the item’s 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``
|
||||
attribute, the item’s animation state will be taken from the value of the
|
||||
@ -1330,7 +1345,7 @@ their positions:
|
||||
</repeat>
|
||||
|
||||
|
||||
.. _layout-errors:
|
||||
.. _layfile-errors:
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
@ -1346,7 +1361,7 @@ Error handling
|
||||
screens are considered unviable and not available to the user.
|
||||
|
||||
|
||||
.. _layout-autogen:
|
||||
.. _layfile-autogen:
|
||||
|
||||
Automatically-generated views
|
||||
-----------------------------
|
||||
@ -1389,7 +1404,7 @@ The following views will be automatically generated:
|
||||
will be displayed at physical aspect ratio, with rotation applied.
|
||||
|
||||
|
||||
.. _layout-complay:
|
||||
.. _layfile-complay:
|
||||
|
||||
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**
|
||||
|
||||
|
||||
.. _layout-examples:
|
||||
.. _layfile-examples:
|
||||
|
||||
Example layout files
|
||||
--------------------
|
||||
|
203
docs/source/techspecs/layout_script.rst
Normal file
203
docs/source/techspecs/layout_script.rst
Normal 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 there’s 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, we’ll 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.
|
||||
|
||||
There’s no layout file syntax to build element state using bits from multiple
|
||||
I/O ports. It’s also inconvenient if each joystick needs to be defined as a
|
||||
separate element because the bits for the switches aren’t 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, it’s 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.
|
@ -108,26 +108,27 @@ end
|
||||
-- Borrowed from the cheat plugin
|
||||
local function poll_for_hotkey()
|
||||
local input = manager:machine():input()
|
||||
local poller = input:sequence_poller()
|
||||
manager:machine():popmessage(_('Press button for hotkey or wait to leave unchanged'))
|
||||
manager:machine():video():frame_update(true)
|
||||
input:seq_poll_start('switch')
|
||||
poller:start('switch')
|
||||
local time = os.clock()
|
||||
local clearmsg = true
|
||||
while (not input:seq_poll()) and (input:seq_poll_modified() or (os.clock() < time + 1)) do
|
||||
if input:seq_poll_modified() then
|
||||
if not input:seq_poll_valid() then
|
||||
while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do
|
||||
if poller.modified then
|
||||
if not poller.valid then
|
||||
manager:machine():popmessage(_("Invalid sequence entered"))
|
||||
clearmsg = false
|
||||
break
|
||||
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)
|
||||
end
|
||||
end
|
||||
if clearmsg then
|
||||
manager:machine():popmessage()
|
||||
end
|
||||
return input:seq_poll_valid() and input:seq_poll_final() or nil
|
||||
return poller.valid and poller.sequence or nil
|
||||
end
|
||||
|
||||
local function handle_configure_menu(index, event)
|
||||
|
@ -610,24 +610,25 @@ function cheat.startplugin()
|
||||
|
||||
local function hkcbfunc(cheat)
|
||||
local input = manager:machine():input()
|
||||
local poller = input:sequence_poller()
|
||||
manager:machine():popmessage(_("Press button for hotkey or wait to clear"))
|
||||
manager:machine():video():frame_update(true)
|
||||
input:seq_poll_start("switch")
|
||||
poller:start("switch")
|
||||
local time = os.clock()
|
||||
local clearmsg = true
|
||||
while (not input:seq_poll()) and (input.seq_poll_modified() or (os.clock() < time + 1)) do
|
||||
if input:seq_poll_modified() then
|
||||
if not input:seq_poll_valid() then
|
||||
while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do
|
||||
if poller.modified then
|
||||
if not poller.valid then
|
||||
manager:machine():popmessage(_("Invalid sequence entered"))
|
||||
clearmsg = false
|
||||
break
|
||||
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)
|
||||
end
|
||||
end
|
||||
if input:seq_poll_valid() then
|
||||
cheat.hotkeys = {pressed = false, keys = input:seq_poll_final()}
|
||||
if poller.valid then
|
||||
cheat.hotkeys = { pressed = false, keys = poller.sequence }
|
||||
else
|
||||
cheat.hotkeys = nil
|
||||
end
|
||||
@ -748,7 +749,8 @@ function cheat.startplugin()
|
||||
return chg
|
||||
else
|
||||
if not cheat.is_oneshot then
|
||||
return cheat:set_enabled(false)
|
||||
local state, chg = cheat:set_enabled(false)
|
||||
return chg
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
@ -1044,7 +1044,7 @@ function cheatfind.startplugin()
|
||||
end
|
||||
emu.register_menu(menu_callback, menu_populate, _("Cheat Finder"))
|
||||
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()
|
||||
for num, watch in ipairs(watches) do
|
||||
screen:draw_text("left", num * height, string.format(watch.format, watch.addr, watch.func()))
|
||||
|
@ -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)
|
||||
{
|
||||
if (!machine || (machine != m_machine))
|
||||
m_seq_poll.reset();
|
||||
m_machine = machine;
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iptseqpoll.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@ -35,10 +33,10 @@ class lua_engine
|
||||
public:
|
||||
// helper structures
|
||||
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> 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
|
||||
lua_engine();
|
||||
@ -146,7 +144,6 @@ private:
|
||||
lua_State *m_lua_state;
|
||||
std::unique_ptr<sol::state_view> m_sol_state;
|
||||
running_machine *m_machine;
|
||||
std::unique_ptr<input_sequence_poller> m_seq_poll;
|
||||
|
||||
std::vector<std::string> m_menu;
|
||||
|
||||
|
@ -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>
|
||||
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);
|
||||
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::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> >;
|
||||
|
||||
|
||||
// tag_object_ptr_map is_container
|
||||
template <typename T> struct is_container<lua_engine::tag_object_ptr_map<T> > : std::true_type { };
|
||||
template <typename T>
|
||||
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>
|
||||
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>
|
||||
{
|
||||
|
@ -11,11 +11,140 @@
|
||||
#include "emu.h"
|
||||
#include "luaengine.ipp"
|
||||
|
||||
#include "iptseqpoll.h"
|
||||
|
||||
#include "inputdev.h"
|
||||
#include "natkeyboard.h"
|
||||
#include "render.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
|
||||
@ -41,86 +170,123 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
* ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player
|
||||
*
|
||||
* 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);
|
||||
ioport_manager_type.set("count_players", &ioport_manager::count_players);
|
||||
ioport_manager_type.set("natkeyboard", &ioport_manager::natkeyboard);
|
||||
ioport_manager_type.set("type_group", [](ioport_manager &im, ioport_type type, int player) {
|
||||
return im.type_group(type, player);
|
||||
});
|
||||
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);
|
||||
});
|
||||
auto ioport_manager_type = sol().registry().new_usertype<ioport_manager>("ioport", sol::no_constructor);
|
||||
ioport_manager_type["count_players"] = &ioport_manager::count_players;
|
||||
ioport_manager_type["type_group"] = &ioport_manager::type_group;
|
||||
ioport_manager_type["type_seq"] = &ioport_manager::type_seq;
|
||||
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);
|
||||
|
||||
|
||||
/* natural_keyboard library
|
||||
*
|
||||
* manager:machine():ioport():natkeyboard()
|
||||
* manager:machine():ioport().natkeyboard
|
||||
*
|
||||
* natkeyboard:paste() - paste clipboard data
|
||||
* natkeyboard:post() - post data to natural keyboard
|
||||
* natkeyboard:post_coded() - post data to natural keyboard
|
||||
* natkeyboard:post(text) - post data to natural keyboard
|
||||
* natkeyboard:post_coded(text) - 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.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);
|
||||
natkeyboard_type.set("empty", sol::property(&natural_keyboard::empty));
|
||||
natkeyboard_type.set("in_use", sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use));
|
||||
natkeyboard_type.set("paste", &natural_keyboard::paste);
|
||||
natkeyboard_type.set("post", [](natural_keyboard &nat, const std::string &text) { nat.post_utf8(text); });
|
||||
natkeyboard_type.set("post_coded", [](natural_keyboard &nat, const std::string &text) { nat.post_coded(text); });
|
||||
auto natkeyboard_type = sol().registry().new_usertype<natural_keyboard>("natkeyboard", sol::no_constructor);
|
||||
natkeyboard_type["post"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_utf8(text); };
|
||||
natkeyboard_type["post_coded"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_coded(text); };
|
||||
natkeyboard_type["paste"] = &natural_keyboard::paste;
|
||||
natkeyboard_type["dump"] = static_cast<std::string (natural_keyboard::*)() const>(&natural_keyboard::dump);
|
||||
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
|
||||
*
|
||||
* 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: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.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)
|
||||
*/
|
||||
|
||||
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.set("active", &ioport_port::active);
|
||||
ioport_port_type.set("live", &ioport_port::live);
|
||||
ioport_port_type.set("read", &ioport_port::read);
|
||||
ioport_port_type.set("write", &ioport_port::write);
|
||||
ioport_port_type.set("field", &ioport_port::field);
|
||||
ioport_port_type.set("fields", sol::property([this](ioport_port &p){
|
||||
sol::table f_table = sol().create_table();
|
||||
// parse twice for custom and default names, default has priority
|
||||
for(ioport_field &field : p.fields())
|
||||
ioport_port_type["read"] = &ioport_port::read;
|
||||
ioport_port_type["write"] = &ioport_port::write;
|
||||
ioport_port_type["field"] = &ioport_port::field;
|
||||
ioport_port_type["device"] = sol::property(&ioport_port::device);
|
||||
ioport_port_type["tag"] = sol::property(&ioport_port::tag);
|
||||
ioport_port_type["active"] = sol::property(&ioport_port::active);
|
||||
ioport_port_type["live"] = sol::property(&ioport_port::live);
|
||||
ioport_port_type["fields"] = sol::property(
|
||||
[this] (ioport_port &p)
|
||||
{
|
||||
if (field.type_class() != INPUT_CLASS_INTERNAL)
|
||||
f_table[field.name()] = &field;
|
||||
}
|
||||
for(ioport_field &field : p.fields())
|
||||
{
|
||||
if (field.type_class() != INPUT_CLASS_INTERNAL)
|
||||
sol::table f_table = sol().create_table();
|
||||
// parse twice for custom and default names, default has priority
|
||||
for (ioport_field &field : p.fields())
|
||||
{
|
||||
if(field.specific_name())
|
||||
f_table[field.specific_name()] = &field;
|
||||
else
|
||||
f_table[field.manager().type_name(field.type(), field.player())] = &field;
|
||||
if (field.type_class() != INPUT_CLASS_INTERNAL)
|
||||
f_table[field.name()] = &field;
|
||||
}
|
||||
}
|
||||
return f_table;
|
||||
}));
|
||||
for (ioport_field &field : p.fields())
|
||||
{
|
||||
if (field.type_class() != INPUT_CLASS_INTERNAL)
|
||||
{
|
||||
if (field.specific_name())
|
||||
f_table[field.specific_name()] = &field;
|
||||
else
|
||||
f_table[field.manager().type_name(field.type(), field.player())] = &field;
|
||||
}
|
||||
}
|
||||
return f_table;
|
||||
});
|
||||
|
||||
|
||||
/* ioport_field library
|
||||
@ -165,92 +331,110 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
* 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);
|
||||
ioport_field_type.set("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) {
|
||||
auto ioport_field_type = sol().registry().new_usertype<ioport_field>("ioport_field", sol::no_constructor);
|
||||
ioport_field_type["set_value"] = &ioport_field::set_value;
|
||||
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);
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
settings.seq[seq_type] = seq;
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
int index = 1;
|
||||
for (char32_t code : f.keyboard_codes(which))
|
||||
result[index++] = code;
|
||||
return result;
|
||||
});
|
||||
ioport_field_type.set("device", sol::property(&ioport_field::device));
|
||||
ioport_field_type.set("port", sol::property(&ioport_field::port));
|
||||
ioport_field_type.set("name", sol::property(&ioport_field::name));
|
||||
ioport_field_type.set("default_name", sol::property([](ioport_field &f) {
|
||||
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.set("mask", sol::property(&ioport_field::mask));
|
||||
ioport_field_type.set("defvalue", sol::property(&ioport_field::defvalue));
|
||||
ioport_field_type.set("sensitivity", sol::property(&ioport_field::sensitivity));
|
||||
ioport_field_type.set("way", sol::property(&ioport_field::way));
|
||||
ioport_field_type.set("type_class", sol::property([](ioport_field &f) {
|
||||
switch (f.type_class())
|
||||
};
|
||||
ioport_field_type["device"] = sol::property(&ioport_field::device);
|
||||
ioport_field_type["port"] = sol::property(&ioport_field::port);
|
||||
ioport_field_type["name"] = sol::property(&ioport_field::name);
|
||||
ioport_field_type["default_name"] = sol::property(
|
||||
[] (ioport_field &f)
|
||||
{
|
||||
case INPUT_CLASS_KEYBOARD: return "keyboard";
|
||||
case INPUT_CLASS_CONTROLLER: return "controller";
|
||||
case INPUT_CLASS_CONFIG: return "config";
|
||||
case INPUT_CLASS_DIPSWITCH: return "dipswitch";
|
||||
case INPUT_CLASS_MISC: return "misc";
|
||||
default: break;
|
||||
}
|
||||
throw false;
|
||||
}));
|
||||
ioport_field_type.set("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.set("enabled", sol::property(&ioport_field::enabled));
|
||||
ioport_field_type.set("optional", sol::property(&ioport_field::optional));
|
||||
ioport_field_type.set("cocktail", sol::property(&ioport_field::cocktail));
|
||||
ioport_field_type.set("toggle", sol::property(&ioport_field::toggle));
|
||||
ioport_field_type.set("rotated", sol::property(&ioport_field::rotated));
|
||||
ioport_field_type.set("analog_reverse", sol::property(&ioport_field::analog_reverse));
|
||||
ioport_field_type.set("analog_reset", sol::property(&ioport_field::analog_reset));
|
||||
ioport_field_type.set("analog_wraps", sol::property(&ioport_field::analog_wraps));
|
||||
ioport_field_type.set("analog_invert", sol::property(&ioport_field::analog_invert));
|
||||
ioport_field_type.set("impulse", sol::property(&ioport_field::impulse));
|
||||
ioport_field_type.set("type", sol::property(&ioport_field::type));
|
||||
ioport_field_type.set("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.set("crosshair_offset", sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset));
|
||||
ioport_field_type.set("user_value", sol::property(
|
||||
[](ioport_field &f) {
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
return settings.value;
|
||||
},
|
||||
[](ioport_field &f, ioport_value val) {
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
settings.value = val;
|
||||
f.set_user_settings(settings);
|
||||
}));
|
||||
ioport_field_type.set("settings", sol::property([this](ioport_field &f) {
|
||||
sol::table result = sol().create_table();
|
||||
for (ioport_setting &setting : f.settings())
|
||||
if (setting.enabled())
|
||||
result[setting.value()] = setting.name();
|
||||
return result;
|
||||
}));
|
||||
return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player());
|
||||
});
|
||||
ioport_field_type["player"] = sol::property(&ioport_field::player, &ioport_field::set_player);
|
||||
ioport_field_type["mask"] = sol::property(&ioport_field::mask);
|
||||
ioport_field_type["defvalue"] = sol::property(&ioport_field::defvalue);
|
||||
ioport_field_type["sensitivity"] = sol::property(&ioport_field::sensitivity);
|
||||
ioport_field_type["way"] = sol::property(&ioport_field::way);
|
||||
ioport_field_type["type_class"] = sol::property(
|
||||
[] (ioport_field &f)
|
||||
{
|
||||
switch (f.type_class())
|
||||
{
|
||||
case INPUT_CLASS_KEYBOARD: return "keyboard";
|
||||
case INPUT_CLASS_CONTROLLER: return "controller";
|
||||
case INPUT_CLASS_CONFIG: return "config";
|
||||
case INPUT_CLASS_DIPSWITCH: return "dipswitch";
|
||||
case INPUT_CLASS_MISC: return "misc";
|
||||
default: break;
|
||||
}
|
||||
throw false;
|
||||
});
|
||||
ioport_field_type["is_analog"] = sol::property(&ioport_field::is_analog);
|
||||
ioport_field_type["is_digital_joystick"] = sol::property(&ioport_field::is_digital_joystick);
|
||||
ioport_field_type["enabled"] = sol::property(&ioport_field::enabled);
|
||||
ioport_field_type["optional"] = sol::property(&ioport_field::optional);
|
||||
ioport_field_type["cocktail"] = sol::property(&ioport_field::cocktail);
|
||||
ioport_field_type["toggle"] = sol::property(&ioport_field::toggle);
|
||||
ioport_field_type["rotated"] = sol::property(&ioport_field::rotated);
|
||||
ioport_field_type["analog_reverse"] = sol::property(&ioport_field::analog_reverse);
|
||||
ioport_field_type["analog_reset"] = sol::property(&ioport_field::analog_reset);
|
||||
ioport_field_type["analog_wraps"] = sol::property(&ioport_field::analog_wraps);
|
||||
ioport_field_type["analog_invert"] = sol::property(&ioport_field::analog_invert);
|
||||
ioport_field_type["impulse"] = sol::property(&ioport_field::impulse);
|
||||
ioport_field_type["type"] = sol::property(&ioport_field::type);
|
||||
ioport_field_type["live"] = sol::property(&ioport_field::live);
|
||||
ioport_field_type["crosshair_scale"] = sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale);
|
||||
ioport_field_type["crosshair_offset"] = sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset);
|
||||
ioport_field_type["user_value"] = sol::property(
|
||||
[] (ioport_field &f)
|
||||
{
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
return settings.value;
|
||||
},
|
||||
[] (ioport_field &f, ioport_value val)
|
||||
{
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
settings.value = val;
|
||||
f.set_user_settings(settings);
|
||||
});
|
||||
ioport_field_type["settings"] = sol::property(
|
||||
[this] (ioport_field &f)
|
||||
{
|
||||
sol::table result = sol().create_table();
|
||||
for (ioport_setting &setting : f.settings())
|
||||
if (setting.enabled())
|
||||
result[setting.value()] = setting.name();
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
/* ioport_field_live library
|
||||
@ -260,44 +444,79 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
* live.name
|
||||
*/
|
||||
|
||||
sol().registry().new_usertype<ioport_field_live>("ioport_field_live", "new", sol::no_constructor,
|
||||
"name", &ioport_field_live::name);
|
||||
auto ioport_field_live_type = sol().registry().new_usertype<ioport_field_live>("ioport_field_live", sol::no_constructor);
|
||||
ioport_field_live_type["name"] = &ioport_field_live::name;
|
||||
|
||||
|
||||
/* input_manager library
|
||||
*
|
||||
* 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_to_token(code) - get KEYCODE_* string token for code
|
||||
* input:code_pressed_once(code) -
|
||||
* 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_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_poll_start(class, [opt] start_seq) - start polling for input_item_class passed as string
|
||||
* (switch/abs[olute]/rel[ative]/max[imum])
|
||||
* input:seq_poll() - poll once, returns true if input was fetched
|
||||
* input:seq_poll_final() - get final input_seq
|
||||
* input.device_classes - returns device classes
|
||||
* input:seq_name(seq) - get seq friendly name
|
||||
* input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq
|
||||
* input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens
|
||||
* input:sequence_poller() - get an input sequence poller
|
||||
*
|
||||
* 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);
|
||||
input_type.set("code_from_token", [](input_manager &input, const char *token) { return input.code_from_token(token); });
|
||||
input_type.set("code_pressed", [](input_manager &input, const input_code &code) { return input.code_pressed(code); });
|
||||
input_type.set("code_to_token", [](input_manager &input, const input_code &code) { return input.code_to_token(code); });
|
||||
input_type.set("code_name", [](input_manager &input, const input_code &code) { return input.code_name(code); });
|
||||
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.set("seq_pressed", [](input_manager &input, const input_seq &seq) { return input.seq_pressed(seq); });
|
||||
input_type.set("seq_to_tokens", [](input_manager &input, const input_seq &seq) { return input.seq_to_tokens(seq); });
|
||||
input_type.set("seq_name", [](input_manager &input, const input_seq &seq) { return input.seq_name(seq); });
|
||||
input_type.set("seq_clean", [](input_manager &input, const input_seq &seq) { return input.seq_clean(seq); });
|
||||
input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) {
|
||||
if (!m_seq_poll)
|
||||
m_seq_poll.reset(new input_sequence_poller(input));
|
||||
auto input_type = sol().registry().new_usertype<input_manager>("input", sol::no_constructor);
|
||||
input_type["code_value"] = &input_manager::code_value;
|
||||
input_type["code_pressed"] = &input_manager::code_pressed;
|
||||
input_type["code_pressed_once"] = &input_manager::code_pressed_once;
|
||||
input_type["code_name"] = &input_manager::code_name;
|
||||
input_type["code_to_token"] = &input_manager::code_to_token;
|
||||
input_type["code_from_token"] = &input_manager::code_from_token;
|
||||
input_type["seq_pressed"] = &input_manager::seq_pressed;
|
||||
input_type["seq_clean"] = &input_manager::seq_clean;
|
||||
input_type["seq_name"] = &input_manager::seq_name;
|
||||
input_type["seq_to_tokens"] = &input_manager::seq_to_tokens;
|
||||
input_type["seq_from_tokens"] =
|
||||
[] (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;
|
||||
if (!strcmp(cls_string, "switch"))
|
||||
cls = ITEM_CLASS_SWITCH;
|
||||
@ -311,44 +530,14 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
cls = ITEM_CLASS_INVALID;
|
||||
|
||||
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
|
||||
m_seq_poll->start(cls);
|
||||
});
|
||||
input_type.set("seq_poll", [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->poll());
|
||||
});
|
||||
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;
|
||||
}));
|
||||
poller.start(cls);
|
||||
};
|
||||
seqpoll_type["poll"] = &input_sequence_poller::poll;
|
||||
seqpoll_type["sequence"] = sol::property(&input_sequence_poller::sequence);
|
||||
seqpoll_type["valid"] = sol::property(&input_sequence_poller::valid);
|
||||
seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified);
|
||||
|
||||
|
||||
/* input_class library
|
||||
@ -362,61 +551,67 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
*/
|
||||
|
||||
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.set("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.set("devices", sol::property([this](input_class &devclass) {
|
||||
sol::table result = sol().create_table();
|
||||
int index = 1;
|
||||
for (int devindex = 0; devindex <= devclass.maxindex(); devindex++)
|
||||
input_class_type["name"] = sol::property(&input_class::name);
|
||||
input_class_type["enabled"] = sol::property(&input_class::enabled, &input_class::enable);
|
||||
input_class_type["multi"] = sol::property(&input_class::multi, &input_class::set_multi);
|
||||
input_class_type["devices"] = sol::property(
|
||||
[this] (input_class &devclass)
|
||||
{
|
||||
input_device *dev = devclass.device(devindex);
|
||||
if (dev)
|
||||
result[index++] = dev;
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
sol::table result = sol().create_table();
|
||||
int index = 1;
|
||||
for (int devindex = 0; devindex <= devclass.maxindex(); devindex++)
|
||||
{
|
||||
input_device *dev = devclass.device(devindex);
|
||||
if (dev)
|
||||
result[index++] = dev;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
/* input_device library
|
||||
*
|
||||
* manager:machine():input().device_classes[devclass].devices[index]
|
||||
* device.name
|
||||
* device.id
|
||||
* device.devindex
|
||||
* device.items[]
|
||||
*
|
||||
* device.name -
|
||||
* device.id -
|
||||
* device.devindex -
|
||||
* device.items[] -
|
||||
*/
|
||||
|
||||
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.set("id", sol::property(&input_device::id));
|
||||
input_device_type.set("devindex", sol::property(&input_device::devindex));
|
||||
input_device_type.set("items", sol::property([this](input_device &dev) {
|
||||
sol::table result = sol().create_table();
|
||||
for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
|
||||
input_device_type["name"] = sol::property(&input_device::name);
|
||||
input_device_type["id"] = sol::property(&input_device::id);
|
||||
input_device_type["devindex"] = sol::property(&input_device::devindex);
|
||||
input_device_type["items"] = sol::property(
|
||||
[this] (input_device &dev)
|
||||
{
|
||||
input_device_item *item = dev.item(id);
|
||||
if (item)
|
||||
result[id] = dev.item(id);
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
sol::table result = sol().create_table();
|
||||
for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
|
||||
{
|
||||
input_device_item *item = dev.item(id);
|
||||
if (item)
|
||||
result[id] = dev.item(id);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
/* input_device_item library
|
||||
*
|
||||
* manager:machine():input().device_classes[devclass].devices[index].items[item_id]
|
||||
* item.name
|
||||
* item.token
|
||||
* item:code()
|
||||
*
|
||||
* item.name -
|
||||
* 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);
|
||||
input_device_item_type.set("name", sol::property(&input_device_item::name));
|
||||
input_device_item_type.set("token", sol::property(&input_device_item::token));
|
||||
input_device_item_type.set("code", [](input_device_item &item) {
|
||||
return input_code(item.device().devclass(), item.device().devindex(), item.itemclass(), ITEM_MODIFIER_NONE, item.itemid());
|
||||
});
|
||||
input_device_item_type["name"] = sol::property(&input_device_item::name);
|
||||
input_device_item_type["code"] = sol::property(&input_device_item::code);
|
||||
input_device_item_type["token"] = sol::property(&input_device_item::token);
|
||||
input_device_item_type["current"] = sol::property(&input_device_item::current);
|
||||
|
||||
|
||||
/* ui_input_manager library
|
||||
|
@ -33,21 +33,13 @@ struct layout_view_items
|
||||
layout_view &view;
|
||||
};
|
||||
|
||||
|
||||
struct render_manager_targets
|
||||
{
|
||||
render_manager_targets(render_manager &m) : targets(m.targets()) { }
|
||||
|
||||
simple_list<render_target> const &targets;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
namespace sol {
|
||||
|
||||
template <> struct is_container<layout_file_views> : std::true_type { };
|
||||
template <> struct is_container<layout_view_items> : std::true_type { };
|
||||
template <> struct is_container<render_manager_targets> : std::true_type { };
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -619,6 +541,6 @@ void lua_engine::initialize_render(sol::table &emu)
|
||||
render_type["max_update_rate"] = &render_manager::max_update_rate;
|
||||
render_type["ui_target"] = &render_manager::ui_target;
|
||||
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()); });
|
||||
|
||||
}
|
||||
|
@ -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(0x0e80, 0x0e80).w(FUNC(bwidow_state::earom_control_w));
|
||||
map(0x0f00, 0x0f3f).w(FUNC(bwidow_state::earom_write));
|
||||
map(0x1000, 0x13ff).rw("pokey1", FUNC(pokey_device::read), FUNC(pokey_device::write));
|
||||
map(0x1400, 0x17ff).rw("pokey2", 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, 0x14ff).mirror(0x0300).rw("pokey2", FUNC(pokey_device::read), FUNC(pokey_device::write));
|
||||
map(0x2000, 0x27ff).ram(); // vector RAM
|
||||
map(0x2800, 0x3fff).rom(); // vector ROM
|
||||
map(0x4000, 0xffff).rom();
|
||||
|
@ -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( 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_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( 0x00, DEF_STR( On ) )
|
||||
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 )
|
||||
@ -1855,8 +1855,7 @@ static INPUT_PORTS_START( birdiy )
|
||||
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) 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( 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( 0x40, IP_ACTIVE_LOW, IPT_START2 )
|
||||
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
|
||||
@ -1874,8 +1873,8 @@ static INPUT_PORTS_START( birdiy )
|
||||
PORT_DIPSETTING( 0x0c, "4" )
|
||||
PORT_DIPNAME( 0x10, 0x00, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW:5")
|
||||
PORT_DIPSETTING( 0x00, DEF_STR( Upright ) )
|
||||
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_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_DIPSETTING( 0x20, DEF_STR( Off ) )
|
||||
PORT_DIPSETTING( 0x00, DEF_STR( On ) )
|
||||
PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW:7")
|
||||
|
Loading…
Reference in New Issue
Block a user