Using a Nasal file with a joystick Part 2

From FlightGear wiki
Jump to navigation Jump to search

In Using a Nasal file with a joystick you saw how to get started. This article goes into it in more depth.


First Steps

Backup your xml file. Print it, you need to know which button does what. If you have a Nasal file, back it up too. If you haven't read the first part, read it and do the basic implementation now.


Modifying your xml file

This seems like a lot of work, but it is worth it in the end. You need to use the labels of the buttons on your joystick. You will use these labels for the name of the Nasal function to call in your nasal file. For each button in your xml file, change your code. Here, the button is labelled "1". And remember, we are talking about the label printed on the joystick, not the button number

assigned by the operating system.

  <button n="xxx">
    <!-- Labled as 1 -->
    <desc>Whatever it does</desc>
    <binding>
      <command>nasal</command>
      <script>nasfilename.button1()</script>
    </binding>
  </button>

Of course, nasfilename is the name of your Nasal file.


If you have a modifier button, named mymod, then do this

  <button n="xxx">
    <!-- Labled as 1 -->
    <desc>Whatever it does</desc>
    <binding>
      <command>nasal</command>
      <script>nasfilename.button1(mymod)</script>
    </binding>
  </button>


If you use the keyboard Shift, Ctrl or Alt buttons as a modifier, you do this

  <button n="xxx">
    <!-- Labled as 1 -->
    <desc>Whatever it does</desc>
    <binding>
      <command>nasal</command>
      <script>nasfilename.button1(1)</script>
    </binding>
    <mod-shift>
      <binding>
      <command>nasal</command>
      <script>nasfilename.button1(2)</script>
      </binding>
    </mod-shift>
  </button>


If you use 2 keyboard buttons then you would need another <mod-...> section, and pass the value 3. To help you remember, add a comment at the top of your Nasal file to remind you what the values mean.


If your button needs a mod-up, then do this

  <button n="xxx">
    <!-- Labled as 1 -->
    <desc>Whatever it does</desc>
    <binding>
      <command>nasal</command>
      <script>nasfilename.button1()</script>
    </binding>
    <mod-up>
      <binding>
        <command>nasal</command>
        <script>nasfilename.button1Up()</script>
      </binding>
    </mod-up>
  </button>

but this is very seldom needed. Of course, if you use modifiers you need to pass the number of the modifier in the brackets.


You could use 2 modifiers simultaneously. The Saitek Pro Flight Yoke has a Mode Switch, which needs special methods to access it - see Using Saitek Pro Flight Yoke Mode Switch, then could have a case where you have these modes (which simulate using the keyboard Shift, Ctrl and Alt keys) as well as a modifier button on the joystick.


In such a case do this

  <button n="6">
    <!-- Labled as C1 -->
    <desc>Whatever</desc>
    <repeatable>true</repeatable>
    <binding>
      <command>nasal</command>
      <script>saitekyoke.buttonC1(saitekAlter, 1)</script>
    </binding>
    <mod-shift>
      <binding>
        <command>nasal</command>
        <script>saitekyoke.buttonC2(saitekAlter, 2)</script>
      </binding>
    </mod-shift>
    <mod-ctrl>
      <binding>
        <command>nasal</command>
        <script>saitekyoke.buttonC1(saitekAlter, 3)</script>
      </binding>
    </mod-ctrl>
  </button>

Here, saitekAlter is the value (0 or 1) of the modifier button, and the 1, 2 and 3 represent the position of the Mode Switch.


Now do this for all the buttons.


Contents of the Nasal file

The blank file

For each button you need a function defined. In our simplest example above you need

  var button1 = func {
        ... code goes here ...
  }


If you pass the value of a modifier button then it is slightly different.

Assuming that your modifier button is 0 for not pressed and 1 for pressed, we can make use of the fact that in Nasal 0 represents false, and any other value represents true.

  var button1 = func(mod) {
    if (mod) {
      ... code for modifier button pressed goes here ...
    } else {
      ... code for modifier not pressed goes here ...
    }
  }


When have values other than just 0 and 1 then you need specific if clauses

  var button1 = func(mod) {
    if (mod == 1) {
      ... code for modifier value 1 goes here ...
    } 
    if (mod == 2) {
      ... code for modifier value 2 goes here ...
    }
  }


If you have 2 modifiers, as in the case of the Saitek Yoke with modes and a modifier button, then you need to nest the if clauses.

  var button1 = func(alter, mode) {
    if (mode == 1) {
       if (alter) {
         ... Mode 1, mod button pressed ...
       } else {
         ... Mode 1, mod button not pressed ...
       }
    } 
    if (mode == 2) {
      if (alter) {
         ... Mode 2, mod button pressed ...
      } else {
         ... Mode 2, mod button not pressed ...
      }
    }
  }
    if (mode == 3) {
      if (alter) {
         ... Mode 3, mod button pressed ...
      } else {
         ... Mode 3, mod button not pressed ...
      }
    }
  }


Populating the Nasal file

Now you need to put in all the code. Have the printout of the xml file handy, it is easier than switching between files. The equivalent code for most actions is available in numerous places, but some equivalents will be supplied below. And you can always ask on the Hardware forum for the Nasal equivalents of xml code.

Enter all the code.


Testing your code

If you use an editor such as gedit (multi-platform, free) which has line-numbers, it makes the task soooo much easier.

Run FG, keeping an eye on the "black window." If you don't see Loading myfilename.nas, then there is a mistake in the xml file. If you get a parse error when starting FG, then you have a basic mistake in your Nasal file. The first entry after parse error is the line number to look for. If you get a parse when pressing a button, then the error is in the code for that button.


Nasal equivalents to xml code

If your xml code has

  <command>nasal</command>
  <script>blah blah</script>

then you can use the bit between <script> and </script> as is.


Elevator Trim

  <command>property-scale</command>
  <property>/controls/flight/elevator</property>
  <factor type="double">1.0</factor>
  <squared type="bool">true</squared>

becomes

  controls.elevatorTrim(1);

The 1 becomes -1 for the other direction. Just substitute ruder and aileron for the others.


View Direction

  <command>property-adjust</command>
  <property>/sim/current-view/goal-heading-offset-deg</property>
  <step type="double">1.0</step>

becomes

  controls.slewProp("/sim/current-view/goal-heading-offset-deg", 15);

You can fiddle with the 15 to make it slower or faster. Make it -15 for the other direction. Change goal-heading to goal-pitch for up and down.


Zoom

  <command>property-adjust</command>
  <property>/sim/current-view/field-of-view</property>
  <step type="double">0.5</step>

becomes

  view.increase(0.5);

Use view.decrease to zoom out.