Despite that openMSX now has an internal GUI that can control most of the emulator's functionality, it is still possible for debugger GUIs, launcher GUIs, etc., to be external programs that control openMSX. This document explains you how you can control openMSX from your own application.
There are multiple ways to connect to openMSX. The first (and oldest) way
is using a pipe. Non-Windows systems use stdio, in Windows you can use a named pipe.
To enable this, start openMSX like this:
or for Windows:
The second method is using a socket. Connecting on non-Windows systems
is done with a UNIX domain socket. openMSX puts the socket in
/tmp/openmsx-<username>/socket.<pid>. The
/tmp/ dir can be overridden by environment variables
TMPDIR, TMP or TEMP (in that order).
On Windows, openMSX uses a TCP socket on localhost instead. The port
number is random between 9938 and 10001. This is done to enforce applications
to deal with multiple running openMSX processes. The port number will be put
in the file socket.<pid> in the folder
openmsx-default in your temp folder (%TMP%), normally:
Unlike a UNIX domain socket, a TCP socket can be connected to by any
process on the computer, also by processes of other users. As the commands
sent to openMSX can for instance read and write files, openMSX only accepts
connections from the same Windows user that is running openMSX. This is
checked with SSPI:
directly after connecting, the client must authenticate itself using the SSPI
Negotiate
package. The client starts, and each token is sent as a 4-byte big-endian
length followed by the token data. Only after a successful authentication,
openMSX accepts the XML input described below; otherwise it closes the
connection. An example implementation in C++ can be found in
src/openmsx/SspiNegotiateClient.cpp of the old, separate openMSX
debugger (no longer maintained, as its functionality is now part of the
openMSX GUI). If your
application starts openMSX itself, it is easier to use a named pipe
(-control pipe, see above), which does not need this.
After connecting, openMSX expects XML input on the channel and it will also give you output. This is explained in the next section.
After connecting, openMSX expects XML input on the channel (pipe or socket) and it will also give you output in XML format. The first output it gives is this:
<openmsx-output>
On non-Windows systems you can easily try it out by just starting openMSX
via the stdio method, as explained above. You give XML
commands via the keyboard in the terminal and openMSX will print its
responses on the terminal as well.
This first output is the opening tag (<openmsx-output>).
All messages that are normally printed on stdout in the
terminal from which you start openMSX are in a <log> tag.
The level can be "info", "warning", "error" or "progress" (for long
operations, then the message ends with the percentage done) and the message
is in the text node itself.
When you want to start communicating back, you always have to start with the opening tag first:
When starting openMSX with the -control option, it will not show a
window: it starts with the 'none' renderer. So, a nice example (if you're
still experimenting on the command line) would be to type this:
With the <command> tag you can give any openMSX console
command to openMSX. The commands are documented in the Console Commands Reference.
Every <command> will result in a reply from openMSX. In
the above case it will be:
<reply result="ok">SDL</reply>
The order is maintained, i.e. the replies will be in the same order as the commands you gave to openMSX. In this reply example, you see that the command succeeded (result=ok) and it also gives you the actual result text that would be printed on the console. In this case, the value of the renderer setting. When a command fails, you get something like this:
<command>biep</command> <reply result="nok">invalid command name "biep" </reply>
"biep" is not a valid command, and openMSX tells you this via a "nok" reply with the error message in the text node.
The next important thing is events. When you use this interface to control openMSX, you want to know when things change. For this, you can enable events for certain event classes.
An example:
This command will enable updates about LED events. When a LED changes, you'll get messages such as:
<update type="led" machine="machine1" name="power">on</update>
<update> tags are openMSX's way of telling you that something changed. In this case, it is a LED update, for the machine with ID "machine1". The name of the LED is "power" and the value is in the text node: on.
Here is a list of the currently available event types and when they are sent:
hardware |
hardware changes occurred, like a change of machine |
led |
LED status changed |
media |
media (disk images, cartridges, etc.) changed |
plug |
a pluggable got plugged or unplugged (empty value) |
setting |
the value of a setting changed |
setting-info |
the properties of a setting changed (e.g. number of options changed) |
status |
status changed, currently only pause and debug break status |
extension |
extensions changed (add/remove) |
sounddevice |
sounddevices changed (add/remove) |
connector |
connectors changed (add/remove) |
debug |
breakpoints, watchpoints or conditions changed (add/remove), the name is their ID |
Someone changed machines from Boosted MSX2 to Toshiba HX-10 at run time:
<update type="hardware" name="machine2">add</update> <update type="hardware" machine="machine2" name="carta">add</update> <update type="hardware" machine="machine2" name="cartb">add</update> <update type="hardware" machine="machine2" name="cassetteplayer">add</update> <update type="hardware" machine="machine1" name="diskb">remove</update> <update type="hardware" machine="machine1" name="diska">remove</update> <update type="hardware" machine="machine1" name="carta">remove</update> <update type="hardware" machine="machine1" name="cartb">remove</update> <update type="hardware" machine="machine1" name="cartc">remove</update> <update type="hardware" machine="machine1" name="cassetteplayer">remove</update> <update type="hardware" name="machine1">remove</update> <update type="hardware" name="machine2">select</update>
CAPS LED went to OFF:
<update type="led" machine="machine2" name="caps">off</update>
A tape was inserted in the cassette player:
<update type="media" machine="machine2" name="cassetteplayer">/home/manuel/msx-soft/tapes/Zoids.zip</update>
The cassetteplayer got into play mode:
<update type="status" machine="machine2" name="cassetteplayer">play</update>
Someone plugged in a joystick:
<update type="plug" machine="machine2" name="joyporta">msxjoystick1</update>
And unplugged it again:
<update type="plug" machine="machine2" name="joyporta"></update>
The maxframeskip setting was set to 12:
<update type="setting" name="maxframeskip">12</update>
openMSX got paused:
<update type="status" name="paused">true</update>
openMSX entered a debug break state:
<update type="status" machine="machine1" name="cpu">suspended</update>
openMSX exited the debug break state:
<update type="status" machine="machine1" name="cpu">running</update>
A Philips NMS-1205 Music Module was inserted:
<update type="sounddevice" machine="machine2" name="Philips NMS 1205 Music Module MSX-Audio 8-bit DAC">add</update> <update type="connector" machine="machine2" name="audiokeyboardport">add</update> <update type="sounddevice" machine="machine2" name="Philips NMS 1205 Music Module MSX-Audio DAC">add</update> <update type="sounddevice" machine="machine2" name="Philips NMS 1205 Music Module MSX-Audio">add</update> <update type="extension" machine="machine2" name="Philips_NMS_1205">add</update>
And with this, you should have all info that you need to make any external application that can control openMSX.
More real world examples can be found here: