It turns out that understanding what a command is doing gets really complicated.
The key idea to untangling it all is this:
  • Commands have duration, and that duration is the amount of time you “hold down” the button or key that actuates the command.
  • Not all commands do things for the entire duration.
An example will clarify this:
  • When you press and hold down the ‘p’ key to pause the sim, the sim pauses (or unpauses) instantly the moment you press the p key.  Holding the p key down for a long time does not change this.  Pause is a “momentary” command.
  • You have to keep the starter button/key held down for a few seconds to start an engine. If you press it and release it, the starter motor will only run for a fraction of a second, which is not enough time to start an aircraft engine.  Engine start is a “duration” command.
Virtual Datarefs
A “virtual dataref” isn’t really a dataref at all – it’s a string you can enter into an OBJ or generic instrument that looks like a dataref, but actually is something else.  There is one “set” of virtual datarefs in X-Plane right now.
In X-Plane, you can enter

CMND=some/command/name

into a generic instrument or obj animation – this creates a “virtual dataref” around the command’s “activation status”.  The virtual dataref acts like a read-only integer-type dataref whose value is 0 if the command is not being pressed right now and 1 if it is.
For example, if you animate a push button using CMND=sim/starters/engage_starter_1 (key framing the animation to be “out” when the virtual dataref is 0 and 1 when it is in) then you will have a button that appears to be pressed whenever the starters are engaged.  This will happen no matter how the starter is engaged – your animation will happen whether the user presses a joystick button, holds down a key, clicks on a manipulator, or a plugin runs the command.
Basically, virtual datarefs that provide “activation status” of commands exist so that you can animate the buttons in your virtual cockpit to match what the user is doing with the mouse, keyboard, etc.  These datarefs are read-only; use trigger generic instruments and command manipulators to actually run the command.
For Programmers
In that last section I pointed out that a virtual dataref is not a real dataref at least 3 times. Why am I harping on this?  Well, programmers, you cannot use XPLMFindDataRef to access virtual datarefs yourself.  Sorry.
Virtual datarefs exist to give authors of OBJs and panels access to command activation status, something they would not normally have.
Plugin programmers don’t need this – if you are programming a plugin you simply use XPLMRegisterCommandHandler and you will be told when the command is being actuated and released.  Using a command handler is a lot more efficient than reading a dataref because you will receive a call only when the command is pressed, instead of having to check the dataref value every frame.  If you need to monitor a lot of commands, the callbacks are a lot more efficient.
Command Activation Is Not The Same As System Activation
Let’s go back to the case of the “pause” command and review.  Here’s what we know about the pause command.
  • The command sim/operation/pause_toggle will change the sim’s pause state.  The instant this command is pressed, the sim will pause (if it is runnning) or unpause (if it is paused).
  • Holding down the sim/operation/pause_toggle command has no effect beyond its initial press. Hold it down for an hour, it doesn’t matter.
  • The virtual dataref CMND=sim/operation/pause_toggle tells you if the pause command is being held down at any instant.
Here’s what we know about the pause dataref.
  • The dataref sim/time/paused is 1 if the sim is paused, 0 if it is not.
  • This dataref cannot be written!
So here’s what I mean when I say: command activation is not the same as system activation.
  • CMND=sim/operation/pause_toggle tells you if the pause button is being held down.
  • sim/time/paused tells you if the sim is actually paused or not.
  • They are simply not the same!
This becomes important when you start to model the buttons in airplanes.  Take for example an autopilot button for altitude hold.  In many planes, the button can be pushed in, and the moment you do, the altitude hold “arm” light will turn on.  Keep the button held in and it doesn’t have any more effect.  Altitude hold arm is a momentary command, but it has a system status indicator light.
I receive a number of questions about how to model this – and the answer is: take advantage of the fact that command activation (is the button pushed in) and system activation (is the light on) are not the same.
  • You would use a read-only dataref for the autopilot state to turn on the indicator light.  (There are two ways to do this: use ATTR_light_level to turn a _LIT texture on and off, or use panel texture and map a generic instrument like a rotary or an annunciator.)
  • You would use a command to make the button work.  Probably you’d use a command manipulator on the button mesh.
  • You would key frame the button’s animation based on the virtual dataref wrapped around the command you are using with the manipulator.
The result will be a button that lights up when the AP is armed but pushes in while it is being pressed (whether via the mouse in the 3-d cockpit or a joystick button press or a keyboard button press).
In my final post, I’ll comment on how much overlap there is between datarefs and commands.

About Ben Supnik

Ben is a software engineer who works on X-Plane; he spends most of his days drinking coffee and swearing at the computer -- sometimes at the same time.