510 lines
14 KiB
XML
510 lines
14 KiB
XML
<?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 -->
|