<?xml version="1.0"?> <!-- generic.xml - FlightGear driver for the DragonRise Inc. Generic USB Joystick Copyright (C) 2013 Florent Rougon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ****************************************************************************** * Axes and Buttons Layout * ****************************************************************************** In the following ASCII-art representation of the joypad: - a single digit represents a button (and gives the button number); - a digit followed by a + or - sign represents an axis direction (and gives the axis number). The Top View below shows 4 real axes (0 and 1 on the left stick, 3 and 4 on the right stick) and 2 pseudo-axes (5 and 6). Pseudo-axes are simple buttons in the hardware. Contrary to a real axis, each pseudo-axis only allows 3 values: -1, 0, 1. Don't forget to put the joypad in analog mode! This changes the center point of axes, for one. Top View ~~~~~~~~~ 6- 0 5- 5+ 8 (SELECT) (START) 9 3 1 6+ ANALOG 2 1- 4- 0- 0+ 3- 3+ 1+ 4+ (buttons and axes numbers obtained on Linux) Bottom View ~~~~~~~~~~~ 5 4 7 6 (buttons numbers obtained on Linux) ****************************************************************************** * Buttons and Axes Bindings * ****************************************************************************** Modifiers ~~~~~~~~~ The two pseudo-axes (5 and 6) are used to define three variables (mod1, mod2, mod3) that act as modifiers for other buttons and axes. Initially, mod1 == mod2 == mod3 == 0. Pressing button 6- toggles mod1 between the values 0 and 1. Pressing button 6+ toggles mod2 between the values 0 and 1 (mod2 is currently unused). When neither 5- nor 5+ is pressed: mod3 == 0 As long as 5- is pressed and 5+ is released: mod3 == -1 As long as 5+ is pressed and 5- is released: mod3 == 1 (the result of holding both 5- and 5+ at the same time is undefined) Bindings that don't depend on modifier states ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Axis 0 Aileron Axis 1 Elevator Axis 3 Rudder Button 4 View Cycle (backward) Button 5 View Cycle (forward) Button 6 View Decrease Button 7 View Increase When mod3 == 0 ~~~~~~~~~~~~~~ [if mod1 == 0] Axis 4 Throttle Button 0 View Up Button 1 View Right Button 2 View Down Button 3 View Left Button 8 Apply Brakes Button 9 Select Initial View and Recenter [if mod1 == 1] Button 3 Trigger (weapon) Button 1 Trigger1 (other weapon) When mod3 == -1 ~~~~~~~~~~~~~~~ Axis 4 Elevator Trim Button 0 Flaps Up Button 2 Flaps Down Button 3 Brake Left Button 1 Brake Right Button 8 Aileron and Rudder Dance (for kids!) Button 9 Reset Zoom to Default When mod3 == 1 ~~~~~~~~~~~~~~ Axis 4 Mixture Control Button 0 Select First Engine Button 1 Select Second Engine Button 2 Select Third Engine Button 3 Select Fourth Engine Button 9 Select All Engines --> <PropertyList> <name>DragonRise Inc. Generic USB Joystick </name> <nasal> <script> var (mod1, mod2, mod3) = (0, 0, 0); # These variables are used to emulate non-repeatable buttons when # mod3 != 0. var (button0_with_mod3, button1_with_mod3, button2_with_mod3, button3_with_mod3) = (0, 0, 0, 0); var initial_goal_heading_offset_deg = getprop("/sim/current-view/goal-heading-offset-deg") or 0.0; var initial_goal_pitch_offset_deg = getprop("/sim/current-view/goal-pitch-offset-deg") or 0.0; </script> </nasal> <axis n="0"> <desc>Aileron</desc> <binding> <command>property-scale</command> <property>/controls/flight/aileron</property> <!-- I need the following offset when the joypad is in analog mode. --> <offset type="double">-0.01028</offset> <factor type="double">0.5</factor> </binding> </axis> <axis n="1"> <desc>Elevator</desc> <binding> <command>property-scale</command> <property>/controls/flight/elevator</property> <!-- I need the following offset when the joypad is in analog mode. --> <offset type="double">-0.01028</offset> <factor type="double">-0.5</factor> </binding> </axis> <axis n="3"> <desc>Rudder</desc> <binding> <command>property-scale</command> <property>/controls/flight/rudder</property> <!-- I need the following offset when the joypad is in analog mode. --> <offset type="double">-0.01028</offset> </binding> </axis> <axis n="4"> <desc>Throttle [mod3=0] / Mixture Control [mod3=1] / Elevator Trim [mod3=-1]</desc> <high> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { controls.adjThrottle(-1.0); } elsif (mod3 == 1) { controls.adjMixture(-1); } else { setprop("/controls/flight/elevator-trim", -0.001 + getprop("/controls/flight/elevator-trim")); } </script> </binding> </high> <low> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { controls.adjThrottle(1.0); } elsif (mod3 == 1) { controls.adjMixture(1); } else { setprop("/controls/flight/elevator-trim", 0.001 + getprop("/controls/flight/elevator-trim")); } </script> </binding> </low> </axis> <axis n="5"> <name>mod3_switch</name> <desc>Switch mod3 between the values -1, 0, 1</desc> <binding> <command>nasal</command> <script> mod3 = cmdarg().getNode("setting").getValue(); # gui.popupTip("DragonRise.mod3 = " ~ mod3, 2); </script> </binding> </axis> <axis n="6"> <name>mod1_mod2_toggle</name> <desc>Toggle mod1 and mod2</desc> <low> <binding> <command>nasal</command> <script> mod1 = !mod1; gui.popupTip("DragonRise.mod1 = " ~ mod1, 2); </script> </binding> </low> <high> <binding> <command>nasal</command> <script> mod2 = !mod2; gui.popupTip("DragonRise.mod2 = " ~ mod2, 2); </script> </binding> </high> </axis> <button n="0"> <desc>View Up [mod3=0] / Select first engine [mod3=1] / Flaps Up [mod3=-1]</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { setprop("/sim/current-view/goal-pitch-offset-deg", 1.0 + getprop("/sim/current-view/goal-pitch-offset-deg")); return; } # Emulate a non-repeatable button for mod3 != 0 if (button0_with_mod3) return; button0_with_mod3 = 1; if (mod3 == -1) { controls.flapsDown(-1); } else { controls.selectEngine(0); } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> button0_with_mod3 = 0; </script> </binding> </mod-up> </button> <button n="1"> <desc>View Right / Brake Right [mod3=-1] / Select second engine [mod3=1] / Trigger1 [mod3=0 and mod1]</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { if (mod1) { # This seems the most common for "trigger" setprop("/controls/armament/trigger1", 1); # Many planes use this instead [also true for "trigger1"?] setprop("/ai/submodels/trigger1", 1); } else { setprop("/sim/current-view/goal-heading-offset-deg", -1.0 + getprop("/sim/current-view/goal-heading-offset-deg")); } return; } # Emulate a non-repeatable button for mod3 != 0 if (button1_with_mod3) return; button1_with_mod3 = 1; if (mod3 == -1) { controls.applyBrakes(1, 1); } else { controls.selectEngine(1); } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> button1_with_mod3 = 0; controls.applyBrakes(0, 1); # This seems the most common for "trigger" setprop("/controls/armament/trigger1", 0); # Many planes use this instead [also true for "trigger1"?] setprop("/ai/submodels/trigger1", 0); </script> </binding> </mod-up> </button> <button n="2"> <desc>View Down [mod3=0] / Select third engine [mod3=1] / Flaps Down [mod3=-1]</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { setprop("/sim/current-view/goal-pitch-offset-deg", -1.0 + getprop("/sim/current-view/goal-pitch-offset-deg")); return; } # Emulate a non-repeatable button for mod3 != 0 if (button2_with_mod3) return; button2_with_mod3 = 1; if (mod3 == -1) { controls.flapsDown(1); } else { controls.selectEngine(2); } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> button2_with_mod3 = 0; </script> </binding> </mod-up> </button> <button n="3"> <desc>View Left / Brake Left [mod3=-1] / Select fourth engine [mod3=1] / Trigger [mod3=0 and mod1]</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script> if (mod3 == 0) { if (mod1) { # This seems the most common setprop("/controls/armament/trigger", 1); # Many planes use this instead setprop("/ai/submodels/trigger", 1); } else { setprop("/sim/current-view/goal-heading-offset-deg", 1.0 + getprop("/sim/current-view/goal-heading-offset-deg")); } return; } # Emulate a non-repeatable button for mod3 != 0 if (button3_with_mod3) return; button3_with_mod3 = 1; if (mod3 == -1) { controls.applyBrakes(1, -1); } else { controls.selectEngine(3); } </script> </binding> <mod-up> <binding> <command>nasal</command> <script> button3_with_mod3 = 0; controls.applyBrakes(0, -1); # This seems the most common setprop("/controls/armament/trigger", 0); # Many planes use this instead setprop("/ai/submodels/trigger", 0); </script> </binding> </mod-up> </button> <button n="4"> <desc>View Cycle (backward)</desc> <repeatable type="bool">false</repeatable> <binding> <command>nasal</command> <script>view.stepView(-1)</script> </binding> </button> <button n="5"> <desc>View Cycle (forward)</desc> <repeatable type="bool">false</repeatable> <binding> <command>nasal</command> <script>view.stepView(1)</script> </binding> </button> <button n="6"> <desc>View Decrease</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>view.decrease(0.75)</script> </binding> </button> <button n="7"> <desc>View Increase</desc> <repeatable type="bool">true</repeatable> <binding> <command>nasal</command> <script>view.increase(0.75)</script> </binding> </button> <button n="8"> <desc>Brakes [mod3=0] / Aileron and Rudder Dance [mod3=-1]</desc> <binding> <command>nasal</command> <script> if (mod3 == -1) { interpolate("/controls/flight/aileron", -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1); interpolate("/controls/flight/rudder", -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1, -0.5, 1, 0.5, 1); } elsif (mod3 == 0) { controls.applyBrakes(1); } </script> </binding> <mod-up> <binding> <command>nasal</command> <script>controls.applyBrakes(0)</script> </binding> </mod-up> </button> <button n="9"> <desc>Select Initial View and Recenter [mod3=0] / Reset Zoom to Default [mod3=-1] / Select all engines [mod3=1]</desc> <binding> <command>nasal</command> <script> if (mod3 == -1) { setprop("/sim/current-view/field-of-view", getprop("/sim/view/config/default-field-of-view-deg")); } elsif (mod3 == 1) { controls.selectAllEngines(); } else { setprop("/sim/current-view/view-number", 0); setprop("/sim/current-view/goal-heading-offset-deg", initial_goal_heading_offset_deg); setprop("/sim/current-view/goal-pitch-offset-deg", initial_goal_pitch_offset_deg); } </script> </binding> </button> </PropertyList> <!-- end of generic.xml -->