mirror of
https://github.com/holub/mame
synced 2025-04-26 18:23:08 +03:00

* Feed mouse/pen/touch pointer events through UI input manager with Win32 and SDL. * Started migrating UI code to use new API and reworking mouse/touch interaction. * emu/render.cpp: Support pressing multiple clickable layout items simultaneously. * emu/render.cpp: Allow UI elements to be drawn in any window. * emu/rendlay.cpp, luaengine_render.cpp: Added layout view events for pointer input. * ui/ui.cpp: Allow the UI handler to control pointer display. * ui/analogipt.cpp: Added mouse/touch and more keys for navigating field state list. * ui/menu.cpp: Use vertical swipe to scroll and horizontal swipe to adjust. * ui/menu.cpp: Draw after processing input - greatly improves responsiveness. * ui/menu.cpp: Ignore keyboard/gamepad input during pointer actions. * ui/selmenu.cpp: Made left/right info pane arrows repeat when held. * ui/selmenu.cpp: Use middle click to move keyboard focus. * ui/selmenu.cpp: Let filter list scroll if it's too tall, and use a bit of horizontal padding. * ui/selmenu.cpp: Improved divider sizing. * ui/state.cpp: Don't allow clicks to pass through the confirm deletion prompt to the menu. * ui/simpleselgame.cpp: Fixed error message display and graphics/sound status not showing. * ui/simpleselgame.cpp: Allow tap/click to dismiss error message. * ui/utils.cpp: Show UI for choice filters when there are no choices - it's less confusing. * modules/input/input_sdl.cpp: Made scaling for mouse scroll better match RawInput and DirectInput. * modules/input/input_rawinput.cpp: Added support for horizontal scroll axis. * modules/input/input_win32.cpp: Added support for scroll axes and more buttons to mouse/lightgun. * modules/debugger/debugimgui.cpp: Don't fight over events with the UI manager - it breaks menus. * osd/windows/window.cpp: Translate mouse position to window cooridinates for scroll wheel events. * osd/sdl/window.cpp: Supply last mouse position for scroll wheel events if possible. * scripts/build/complay.py: Made zero input mask an error - it was only being used to block clicks.
787 lines
38 KiB
ReStructuredText
787 lines
38 KiB
ReStructuredText
.. _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 functions that will be called after
|
||
certain events, or when certain data is required.
|
||
|
||
Layout scripting requires the :ref:`layout plugin <plugins-layout>` to be
|
||
enabled. For example, to run BWB Double Take with the Lua script in the layout
|
||
enabled, you might use this command::
|
||
|
||
mame -plugins -plugin layout v4dbltak
|
||
|
||
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. See :ref:`plugins` for
|
||
more information about using plugins with MAME.
|
||
|
||
|
||
.. _layscript-examples:
|
||
|
||
Practical examples
|
||
------------------
|
||
|
||
Before diving into the technical details of how it works, we’ll start with some
|
||
example layout files using Lua script for enhancement. It’s assumed that you’re
|
||
familiar with MAME’s artwork system and have a basic understanding of Lua
|
||
scripting. For details on MAME’s layout file, see :ref:`layfile`; for detailed
|
||
descriptions of MAME’s Lua interface, see :ref:`luascript`.
|
||
|
||
.. _layscript-examples-espial:
|
||
|
||
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 the 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 to its :ref:`layout file <luascript-ref-renderlayfile>`.
|
||
|
||
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 :ref:`I/O ports <luascript-ref-ioport>` 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 :ref:`view items <luascript-ref-renderlayitem>` 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 when drawing a frame.
|
||
* 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 shuffles the bits into the order needed by the joystick
|
||
element.
|
||
|
||
.. _layscript-examples-starwars:
|
||
|
||
Star Wars: animation on two axes
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
We’ll make a layout that shows the position of the flight yoke for Atari Star
|
||
Wars. The input ports are straightforward – each analog axis produces a value
|
||
in the range from 0x00 (0) to 0xff (255), inclusive:
|
||
|
||
.. code-block:: C++
|
||
|
||
PORT_START("STICKY")
|
||
PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_SENSITIVITY(70) PORT_KEYDELTA(30)
|
||
|
||
PORT_START("STICKX")
|
||
PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(30)
|
||
|
||
Here’s our layout file:
|
||
|
||
.. code-block:: XML
|
||
|
||
<?xml version="1.0"?>
|
||
<mamelayout version="2">
|
||
|
||
<!-- a square with a white outline 1% of its width -->
|
||
<element name="outline">
|
||
<rect><bounds x="0.00" y="0.00" width="1.00" height="0.01" /></rect>
|
||
<rect><bounds x="0.00" y="0.99" width="1.00" height="0.01" /></rect>
|
||
<rect><bounds x="0.00" y="0.00" width="0.01" height="1.00" /></rect>
|
||
<rect><bounds x="0.99" y="0.00" width="0.01" height="1.00" /></rect>
|
||
</element>
|
||
|
||
<!-- a rectangle with a vertical line 10% of its width down the middle -->
|
||
<element name="line">
|
||
<!-- use a transparent rectangle to force element dimensions -->
|
||
<rect>
|
||
<bounds x="0" y="0" width="0.1" height="1" />
|
||
<color alpha="0" />
|
||
</rect>
|
||
<!-- this is the visible white line -->
|
||
<rect><bounds x="0.045" y="0" width="0.01" height="1" /></rect>
|
||
</element>
|
||
|
||
<!-- an outlined square inset by 20% with lines 10% of the element width/height -->
|
||
<element name="box">
|
||
<!-- use a transparent rectangle to force element dimensions -->
|
||
<rect>
|
||
<bounds x="0" y="0" width="0.1" height="0.1" />
|
||
<color alpha="0" />
|
||
</rect>
|
||
<!-- draw the outline of a square -->
|
||
<rect><bounds x="0.02" y="0.02" width="0.06" height="0.01" /></rect>
|
||
<rect><bounds x="0.02" y="0.07" width="0.06" height="0.01" /></rect>
|
||
<rect><bounds x="0.02" y="0.02" width="0.01" height="0.06" /></rect>
|
||
<rect><bounds x="0.07" y="0.02" width="0.01" height="0.06" /></rect>
|
||
</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 flight yoke position -->
|
||
<view name="Analog Control Display">
|
||
<!-- draw the screen with correct aspect ratio -->
|
||
<screen index="0">
|
||
<bounds x="0" y="0" width="4" height="3" />
|
||
</screen>
|
||
|
||
<!-- draw the white outlined square to the right of the screen near the bottom -->
|
||
<!-- the script uses the size of this item to determine movement ranges -->
|
||
<element id="outline" ref="outline">
|
||
<bounds x="4.1" y="1.9" width="1.0" height="1.0" />
|
||
</element>
|
||
|
||
<!-- vertical line for displaying X axis input -->
|
||
<element id="vertical" ref="line">
|
||
<!-- element draws a vertical line, no need to rotate it -->
|
||
<orientation rotate="0" />
|
||
<!-- centre it in the square horizontally, using the full height -->
|
||
<bounds x="4.55" y="1.9" width="0.1" height="1" />
|
||
</element>
|
||
|
||
<!-- horizontal line for displaying Y axis input -->
|
||
<element id="horizontal" ref="line">
|
||
<!-- rotate the element by 90 degrees to get a horizontal line -->
|
||
<orientation rotate="90" />
|
||
<!-- centre it in the square vertically, using the full width -->
|
||
<bounds x="4.1" y="2.35" width="1" height="0.1" />
|
||
</element>
|
||
|
||
<!-- draw a small box at the intersection of the vertical and horizontal lines -->
|
||
<element id="box" ref="box">
|
||
<bounds x="4.55" y="2.35" width="0.1" height="0.1" />
|
||
</element>
|
||
|
||
<!-- draw the warning text over the screen near the bottom -->
|
||
<element id="warning" ref="warning">
|
||
<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 starwars
|
||
-- find the analog axis inputs
|
||
local x_input = file.device:ioport("STICKX")
|
||
local y_input = file.device:ioport("STICKY")
|
||
|
||
-- find the outline item
|
||
local outline_item = file.views["Analog Control Display"].items["outline"]
|
||
|
||
-- variables for keeping state across callbacks
|
||
local outline_bounds -- bounds of the outlined square
|
||
local width, height -- width and height for animated items
|
||
local x_scale, y_scale -- ratios of axis units to render coordinates
|
||
local x_pos, y_pos -- display positions for the animated items
|
||
|
||
-- set a function to call when view dimensions have been recalculated
|
||
-- this can happen when when the window is resized or scaling options are changed
|
||
file.views["Analog Control Display"]:set_recomputed_callback(
|
||
function ()
|
||
-- get the bounds of the outlined square
|
||
outline_bounds = outline_item.bounds
|
||
-- animated items use 10% of the width/height of the square
|
||
width = outline_bounds.width * 0.1
|
||
height = outline_bounds.height * 0.1
|
||
-- calculate ratios of axis units to render coordinates
|
||
-- animated items leave 90% of the width/height for the movement range
|
||
-- the end of the range of each axis is at 0xff
|
||
x_scale = outline_bounds.width * 0.9 / 0xff
|
||
y_scale = outline_bounds.height * 0.9 / 0xff
|
||
end)
|
||
|
||
-- set a function to call before adding the view items to the render target
|
||
file.views["Analog Control Display"]:set_prepare_items_callback(
|
||
function ()
|
||
-- read analog axes, reverse Y axis as zero is at the bottom
|
||
local x = x_input:read() & 0xff
|
||
local y = 0xff - (y_input:read() & 0xff)
|
||
-- convert the input values to layout coordinates
|
||
-- use the top left corner of the outlined square as the origin
|
||
x_pos = outline_bounds.x0 + (x * x_scale)
|
||
y_pos = outline_bounds.y0 + (y * y_scale)
|
||
end)
|
||
|
||
-- set a function to supply the bounds for the vertical line
|
||
file.views["Analog Control Display"].items["vertical"]:set_bounds_callback(
|
||
function ()
|
||
-- create a new render bounds object (starts as a unit square)
|
||
local result = emu.render_bounds()
|
||
-- set left, top, width and height
|
||
result:set_wh(
|
||
x_pos, -- calculated X position for animated items
|
||
outline_bounds.y0, -- top of outlined square
|
||
width, -- 10% of width of outlined square
|
||
outline_bounds.height) -- full height of outlined square
|
||
return result
|
||
end)
|
||
|
||
-- set a function to supply the bounds for the horizontal line
|
||
file.views["Analog Control Display"].items["horizontal"]:set_bounds_callback(
|
||
function ()
|
||
-- create a new render bounds object (starts as a unit square)
|
||
local result = emu.render_bounds()
|
||
-- set left, top, width and height
|
||
result:set_wh(
|
||
outline_bounds.x0, -- left of outlined square
|
||
y_pos, -- calculated Y position for animated items
|
||
outline_bounds.width, -- full width of outlined square
|
||
height) -- 10% of height of outlined square
|
||
return result
|
||
end)
|
||
|
||
-- set a function to supply the bounds for the box at the intersection of the lines
|
||
file.views["Analog Control Display"].items["box"]:set_bounds_callback(
|
||
function ()
|
||
-- create a new render bounds object (starts as a unit square)
|
||
local result = emu.render_bounds()
|
||
-- set left, top, width and height
|
||
result:set_wh(
|
||
x_pos, -- calculated X position for animated items
|
||
y_pos, -- calculated Y position for animated items
|
||
width, -- 10% of width of outlined square
|
||
height) -- 10% of height of outlined square
|
||
return result
|
||
end)
|
||
|
||
-- hide the warning, since if we got here the script is running
|
||
file.views["Analog Control Display"].items["warning"]:set_state(0)
|
||
end)
|
||
]]></script>
|
||
|
||
</mamelayout>
|
||
|
||
The layout has a ``script`` element containing the Lua script, to be called as a
|
||
function by the layout plugin when the layout file is loaded. This happens
|
||
after the layout views have been build, but before the emulated system has
|
||
finished starting. The :ref:`layout file <luascript-ref-renderlayfile>` object
|
||
is supplied to the script in the ``file`` variable.
|
||
|
||
We supply a function to be called after tags in the layout file have been
|
||
resolved. This function does the following:
|
||
|
||
* Looks up the analog axis :ref:`inputs <luascript-ref-ioport>`.
|
||
* Looks up the :ref:`view item <luascript-ref-renderlayitem>` that draws the
|
||
outline of area where the yoke position is displayed.
|
||
* Declares some variables to hold calculated values across function calls.
|
||
* Supplies a function to be called when the view’s dimensions have been
|
||
recomputed.
|
||
* Supplies a function to be called before adding view items to the render
|
||
container when drawing a frame.
|
||
* Supplies functions that will supply the bounds for the animated items.
|
||
* 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 view is looked up by name (value of its ``name`` attribute), and items
|
||
within the view are looked up by ID (values of their ``id`` attributes).
|
||
|
||
Layout view dimensions are recomputed in response to several events, including
|
||
the window being resized, entering/leaving full screen mode, toggling visibility
|
||
of item collections, and changing the zoom to screen area setting. When this
|
||
happens, we need to update our size and animation scale factors. We get the
|
||
bounds of the square where the yoke position is displayed, calculate the size
|
||
for the animated items, and calculate the ratios of axis units to render target
|
||
coordinates in each direction. It’s more efficient to do these calculations
|
||
only when the results may change.
|
||
|
||
Before view items are added to the render target, we read the analog axis inputs
|
||
and convert the values to coordinates positions for the animated items. The Y
|
||
axis input uses larger values to aim higher, so we need to reverse the value by
|
||
subtracting it from 0xff (255). We add in the coordinates of the top left
|
||
corner of the square where we’re displaying the yoke position. We do this once
|
||
each time the layout is drawn for efficiency, since we can use the values for
|
||
all three animated items.
|
||
|
||
Finally, we supply bounds for the animated items when required. These functions
|
||
need to return ``render_bounds`` objects giving the position and size of the
|
||
items in render target coordinates.
|
||
|
||
(Since the vertical and horizontal line elements each only move on a single
|
||
axis, it would be possible to animate them using the layout file format’s item
|
||
animation features. Only the box at the intersection of the line actually
|
||
requires scripting. It’s done entirely using scripting here for illustrative
|
||
purposes.)
|
||
|
||
|
||
.. _layscript-environment:
|
||
|
||
The layout script environment
|
||
-----------------------------
|
||
|
||
The Lua environment is provided by the layout plugin. It’s fairly minimal, only
|
||
providing what’s needed:
|
||
|
||
* ``file`` giving the script’s :ref:`layout file <luascript-ref-renderlayfile>`
|
||
object. Has a ``device`` property for obtaining the :ref:`device
|
||
<luascript-ref-device>` that caused the layout file to be loaded, and a
|
||
``views`` property for obtaining the layout’s :ref:`views
|
||
<luascript-ref-renderlayview>` (indexed by name).
|
||
* ``machine`` giving MAME’s current :ref:`running machine
|
||
<luascript-ref-machine>`.
|
||
* ``emu.device_enumerator``, ``emu.palette_enumerator``,
|
||
``emu.screen_enumerator``, ``emu.cassette_enumerator``,
|
||
``emu.image_enumerator`` and ``emu.slot_enumerator`` functions for obtaining
|
||
specific device interfaces.
|
||
* ``emu.attotime``, ``emu.render_bounds`` and ``emu.render_color`` functions for
|
||
creating :ref:`attotime <luascript-ref-attotime>`, :ref:`bounds
|
||
<luascript-ref-renderbounds>` and :ref:`colour <luascript-ref-rendercolor>`
|
||
objects.
|
||
* ``emu.bitmap_ind8``, ``emu.bitmap_ind16``, ``emu.bitmap_ind32``,
|
||
``emu.bitmap_ind64``, ``emu.bitmap_yuy16``, ``emu.bitmap_rgb32`` and
|
||
``emu.bitmap_argb32`` objects for creating
|
||
:ref:`bitmaps <luascript-ref-bitmap>`.
|
||
* ``emu.print_verbose``, ``emu.print_error``, ``emu.print_warning``,
|
||
``emu.print_info`` and ``emu.print_debug`` functions for diagnostic output.
|
||
* Standard Lua ``tonumber``, ``tostring``, ``pairs`` and ``ipairs`` functions,
|
||
and ``table`` and ``string`` objects for manipulating strings, tables and
|
||
other containers.
|
||
* Standard Lua ``print`` function for text output to the console.
|
||
|
||
|
||
.. _layscript-events:
|
||
|
||
Layout events
|
||
-------------
|
||
|
||
MAME layout scripting uses an event-based model. Scripts can supply functions
|
||
to be called after events occur, or when data is needed. There are three levels
|
||
of events: layout file events, layout view events, and layout view item events.
|
||
|
||
.. _layscript-events-file:
|
||
|
||
Layout file events
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
Layout file events apply to the file as a whole, and not to an individual view.
|
||
|
||
Resolve tags
|
||
``file:set_resolve_tags_callback(cb)``
|
||
|
||
Called after the emulated system has finished starting, input and output
|
||
tags in the layout have been resolved, and default item callbacks have been
|
||
set up. This is a good time to look up inputs and set up view item event
|
||
handlers.
|
||
|
||
The callback function has no return value and takes no parameters. Call
|
||
with ``nil`` as the argument to remove the event handler.
|
||
|
||
.. _layscript-events-view:
|
||
|
||
Layout view events
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
Layout view events apply to an individual view.
|
||
|
||
Prepare items
|
||
``view:set_prepare_items_callback(cb)``
|
||
|
||
Called before the view’s items are added to the render target in preparation
|
||
for drawing a video frame.
|
||
|
||
The callback function has no return value and takes no parameters. Call
|
||
with ``nil`` as the argument to remove the event handler.
|
||
Preload
|
||
``view:set_preload_callback(cb)``
|
||
|
||
Called after pre-loading visible view elements. This can happen when the
|
||
view is selected for the first time in a session, or when the user toggles
|
||
visibility of an element collection on. Be aware that this can be called
|
||
multiple times in a session and avoid repeating expensive tasks.
|
||
|
||
The callback function has no return value and takes no parameters. Call
|
||
with ``nil`` as the argument to remove the event handler.
|
||
Dimensions recomputed
|
||
``view:set_recomputed_callback(cb)``
|
||
|
||
Called after view dimensions are recomputed. This happens in several
|
||
situations, including the window being resized, entering or leaving full
|
||
screen mode, toggling visibility of item collections, and changes to the
|
||
rotation and zoom to screen area settings. If you’re animating the position
|
||
of view items, this is a good time to calculate positions and scale factors.
|
||
|
||
The callback function has no return value and takes no parameters. Call
|
||
with ``nil`` as the argument to remove the event handler.
|
||
Pointer updated
|
||
``view:set_pointer_updated_callback(cb)``
|
||
|
||
Called when a pointer enters, moves or changes button state over the view.
|
||
|
||
The callback function is passed nine arguments:
|
||
|
||
* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
|
||
or ``unknown``, and will not change for the lifetime of a pointer.
|
||
* The pointer ID. This will be a non-negative integer that will not change
|
||
for the lifetime of a pointer. Pointer ID values are recycled
|
||
aggressively.
|
||
* The device ID. This will be a non-negative integer that can be used to
|
||
group pointers for recognising multi-touch gestures.
|
||
* The horizontal position of the pointer in layout coordinates.
|
||
* The vertical position of the pointer in layout coordinates.
|
||
* A bit mask representing the currently pressed buttons. The primary button
|
||
is the least significant bit.
|
||
* A bit mask representing the buttons that were pressed in this update. The
|
||
primary button is the least significant bit.
|
||
* A bit mask representing the buttons that were released in this update.
|
||
The primary button is the least significant bit.
|
||
* The click count. This is positive for multi-click actions, or negative if
|
||
a click is turned into a hold or drag. This only applies to the primary
|
||
button.
|
||
|
||
The callback function has no return value. Call with ``nil`` as the
|
||
argument to remove the event handler.
|
||
Pointer left
|
||
``view:set_pointer_left_callback(cb)``
|
||
|
||
Called when a pointer leaves the view normally. After receiving this event,
|
||
the pointer ID may be reused for a new pointer.
|
||
|
||
The callback function is passed seven arguments:
|
||
|
||
* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
|
||
or ``unknown``, and will not change for the lifetime of a pointer.
|
||
* The pointer ID. This will be a non-negative integer that will not change
|
||
for the lifetime of a pointer. Pointer ID values are recycled
|
||
aggressively.
|
||
* The device ID. This will be a non-negative integer that can be used to
|
||
group pointers for recognising multi-touch gestures.
|
||
* The horizontal position of the pointer in layout coordinates.
|
||
* The vertical position of the pointer in layout coordinates.
|
||
* A bit mask representing the buttons that were released in this update.
|
||
The primary button is the least significant bit.
|
||
* The click count. This is positive for multi-click actions, or negative if
|
||
a click is turned into a hold or drag. This only applies to the primary
|
||
button.
|
||
|
||
The callback function has no return value. Call with ``nil`` as the
|
||
argument to remove the event handler.
|
||
Pointer aborted
|
||
``view:set_pointer_aborted_callback(cb)``
|
||
|
||
Called when a pointer leaves the view abnormally. After receiving this
|
||
event, the pointer ID may be reused for a new pointer.
|
||
|
||
The callback function is passed seven arguments:
|
||
|
||
* The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
|
||
or ``unknown``, and will not change for the lifetime of a pointer.
|
||
* The pointer ID. This will be a non-negative integer that will not change
|
||
for the lifetime of a pointer. Pointer ID values are recycled
|
||
aggressively.
|
||
* The device ID. This will be a non-negative integer that can be used to
|
||
group pointers for recognising multi-touch gestures.
|
||
* The horizontal position of the pointer in layout coordinates.
|
||
* The vertical position of the pointer in layout coordinates.
|
||
* A bit mask representing the buttons that were released in this update.
|
||
The primary button is the least significant bit.
|
||
* The click count. This is positive for multi-click actions, or negative if
|
||
a click is turned into a hold or drag. This only applies to the primary
|
||
button.
|
||
|
||
The callback function has no return value. Call with ``nil`` as the
|
||
argument to remove the event handler.
|
||
Forget pointers
|
||
``view:set_forget_pointers_callback(cb)``
|
||
|
||
Called when the view should stop processing pointer input. This can happen
|
||
in a number of situations, including:
|
||
|
||
* The user activated a menu.
|
||
* The view configuration will change.
|
||
* The view will be deactivated.
|
||
|
||
The callback function has no return value and takes no parameters. Call
|
||
with ``nil`` as the argument to remove the event handler.
|
||
|
||
.. _layscript-events-item:
|
||
|
||
Layout view item events
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Layout view item callbacks apply to individual items within a view. They are
|
||
used to override items’ default element state, animation state, bounds and
|
||
colour behaviour.
|
||
|
||
Get element state
|
||
``item:set_element_state_callback(cb)``
|
||
|
||
Set callback for getting the item’s element state. This controls how the
|
||
item’s element is drawn, for components that change appearance depending on
|
||
state, conditionally-drawn components, and component bounds/colour
|
||
animation. Do not attempt to access the item’s ``element_state`` property
|
||
from the callback, as it will result in infinite recursion.
|
||
|
||
The callback function must return an integer, and takes no parameters. Call
|
||
with ``nil`` as the argument to restore the default element state
|
||
handler (based on the item’s XML attributes).
|
||
Get animation state
|
||
``item:set_animation_state_callback(cb)``
|
||
|
||
Set callback for getting the item’s animation state. This is used for item
|
||
bounds/colour animation. Do not attempt to access the item’s
|
||
``animation_state`` property from the callback, as it will result in
|
||
infinite recursion.
|
||
|
||
The callback function must return an integer, and takes no parameters. Call
|
||
with ``nil`` as the argument to restore the default animation state handler
|
||
(based on the item’s XML attributes and ``animate`` child element).
|
||
Get item bounds
|
||
``item:set_bounds_callback(cb)``
|
||
|
||
Set callback for getting the item’s bounds (position and size). Do not
|
||
attempt to access the item’s ``bounds`` property from the callback, as it
|
||
will result in infinite recursion.
|
||
|
||
The callback function must return a render bounds object representing the
|
||
item’s bounds in render target coordinates (usually created by calling
|
||
``emu.render_bounds``), and takes no parameters. Call with ``nil`` as the
|
||
argument to restore the default bounds handler (based on the item’s
|
||
animation state and ``bounds`` child elements).
|
||
Get item colour
|
||
``item:set_color_callback(cb)``
|
||
|
||
Set callback for getting the item’s colour (the element texture’s colours
|
||
multiplied by this colour). Do not attempt to access the item’s ``color``
|
||
property from the callback, as it will result in infinite recursion.
|
||
|
||
The callback function must return a render colour object representing the
|
||
ARGB colour (usually created by calling ``emu.render_color``), and takes no
|
||
parameters. Call with ``nil`` as the argument to restore the default colour
|
||
handler (based on the item’s animation state and ``color`` child elements).
|
||
Get item horizontal scroll window size
|
||
``item:set_scroll_size_x_callback(cb)``
|
||
|
||
Set callback for getting the item’s horizontal scroll window size. This
|
||
allows the script to control how much of the element is displayed by the
|
||
item. Do not attempt to access the item’s ``scroll_size_x`` property from
|
||
the callback, as it will result in infinite recursion.
|
||
|
||
The callback function must return a floating-point number representing the
|
||
horizontal window size as a proportion of the associated element’s width,
|
||
and takes no parameters. A value of 1.0 will display the entire width of
|
||
the element; smaller values will display proportionally smaller parts of the
|
||
element. Call with ``nil`` as the argument to restore the default
|
||
horizontal scroll window size handler (based on the ``xscroll`` child
|
||
element).
|
||
Get item vertical scroll window size
|
||
``item:set_scroll_size_y_callback(cb)``
|
||
|
||
Set callback for getting the item’s vertical scroll window size. This
|
||
allows the script to control how much of the element is displayed by the
|
||
item. Do not attempt to access the item’s ``scroll_size_y`` property from
|
||
the callback, as it will result in infinite recursion.
|
||
|
||
The callback function must return a floating-point number representing the
|
||
vertical window size as a proportion of the associated element’s height, and
|
||
takes no parameters. A value of 1.0 will display the entire height of the
|
||
element; smaller values will display proportionally smaller parts of the
|
||
element. Call with ``nil`` as the argument to restore the default
|
||
vertical scroll window size handler (based on the ``xscroll`` child
|
||
element).
|
||
Get item horizontal scroll position
|
||
``item:set_scroll_pos_x_callback(cb)``
|
||
|
||
Set callback for getting the item’s horizontal scroll position. This allows
|
||
the script to control which part of the element is displayed by the item.
|
||
Do not attempt to access the item’s ``scroll_pos_x`` property from the
|
||
callback, as this will result in infinite recursion.
|
||
|
||
The callback must return a floating-point number, and takes no parameters.
|
||
A value of 0.0 aligns the left edge of the element with the left edge of the
|
||
item; larger values pan right. Call with ``nil`` as the argument to restore
|
||
the default horizontal scroll position handler (based on bindings in the
|
||
``xscroll`` child element).
|
||
Get item vertical scroll position
|
||
``item:set_scroll_pos_y_callback(cb)``
|
||
|
||
Set callback for getting the item’s vertical scroll position. This allows
|
||
the script to control which part of the element is displayed by the item.
|
||
Do not attempt to access the item’s ``scroll_pos_y`` property from the
|
||
callback, as this will result in infinite recursion.
|
||
|
||
The callback must return a floating-point number, and takes no parameters.
|
||
A value of 0.0 aligns the top edge of the element with the top edge of the
|
||
item; larger values pan down. Call with ``nil`` as the argument to restore
|
||
the default vertical scroll position handler (based on bindings in the
|
||
``yscroll`` child element).
|
||
|
||
.. _layscript-events-element:
|
||
|
||
Layout element events
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Layout element events apply to an individual visual element definition.
|
||
|
||
Draw
|
||
``element:set_draw_callback(cb)``
|
||
|
||
Set callback for additional drawing after the element’s components have been
|
||
drawn. This gives the script direct control over the final texture when an
|
||
element item is drawn.
|
||
|
||
The callback is passed two arguments: the element state (an integer) and the
|
||
32-bit ARGB bitmap at the required size. The callback must not attempt to
|
||
resize the bitmap. Call with ``nil`` as the argument to remove the event
|
||
handler.
|