2012-08-24 21:53:21 +00:00
|
|
|
# Joystick configuration library.
|
|
|
|
var DIALOGROOT = "/sim/gui/dialogs/joystick-config";
|
|
|
|
var MAX_AXES = 8;
|
2013-04-12 19:24:53 +00:00
|
|
|
var MAX_NASALS = 8;
|
2012-08-24 21:53:21 +00:00
|
|
|
var MAX_BUTTONS = 24;
|
|
|
|
|
|
|
|
# Hash of the custom axis/buttons
|
|
|
|
var custom_bindings = {};
|
|
|
|
|
|
|
|
# Class for an individual joystick axis binding
|
|
|
|
var Axis = {
|
|
|
|
new: func(name, prop, invertable) {
|
|
|
|
var m = { parents: [Axis] };
|
|
|
|
m.name = name;
|
|
|
|
m.prop = prop;
|
|
|
|
m.invertable = invertable;
|
|
|
|
m.inverted = 0;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [Axis] };
|
|
|
|
m.name = me.name;
|
|
|
|
m.prop = me.prop;
|
|
|
|
m.invertable = me.invertable;
|
|
|
|
m.inverted = me.inverted;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
parse: func(prop) { },
|
|
|
|
|
|
|
|
readProps: func(props) {},
|
|
|
|
|
|
|
|
getName: func() { return me.name; },
|
|
|
|
getBinding: func(axis) { return props.Node.new(); },
|
|
|
|
isInvertable: func() { return me.invertable; },
|
|
|
|
isInverted: func() { return me.inverted; },
|
|
|
|
|
|
|
|
setInverted: func(b) {
|
|
|
|
if (me.invertable) me.inverted = b;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var CustomAxis = {
|
|
|
|
new: func() {
|
|
|
|
var m = { parents: [CustomAxis, Axis.new("Custom", "", 0) ] };
|
|
|
|
me.custom_binding = nil;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [CustomAxis, Axis.new("Custom", "", 0) ] };
|
|
|
|
m.custom_binding = me.custom_binding;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var p = props.Node.new();
|
|
|
|
|
|
|
|
if (prop.getNode("binding") != nil) {
|
|
|
|
props.copy(prop.getNode("binding"), p);
|
|
|
|
me.custom_binding = p;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(axis) {
|
|
|
|
var p = props.Node.new().getNode("axis[" ~ axis ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
props.copy(me.custom_binding, p.getNode("binding", 1));
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var UnboundAxis = {
|
|
|
|
new: func() {
|
|
|
|
var m = { parents: [UnboundAxis, Axis.new("None", "", 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [UnboundAxis, Axis.new("None", "", 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
return 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(axis) {
|
|
|
|
return nil;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var PropertyScaleAxis = {
|
|
|
|
new: func(name, prop, deadband=0, factor=1, offset=0) {
|
|
|
|
var m = { parents: [PropertyScaleAxis, Axis.new(name, prop, 1) ] };
|
|
|
|
m.prop=prop;
|
|
|
|
m.deadband = deadband;
|
|
|
|
m.factor = factor;
|
|
|
|
m.offset = offset;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [PropertyScaleAxis, Axis.new(me.name, me.prop, 1) ] };
|
|
|
|
|
|
|
|
m.inverted = me.inverted;
|
|
|
|
|
|
|
|
m.prop= me.prop;
|
|
|
|
m.deadband = me.deadband;
|
|
|
|
m.factor = me.factor;
|
|
|
|
m.offset = me.offset;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var cmd = prop.getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("binding", 1).getNode("property", 1).getValue();
|
|
|
|
return ((cmd == "property-scale") and (p == me.prop));
|
|
|
|
},
|
|
|
|
|
|
|
|
parse: func(p) {
|
|
|
|
me.deadband = p.getNode("binding", 1).getNode("dead-band", 1).getValue();
|
Fix nuking of property-scale's default factor by joystick-config dialog
As can be seen in do_property_scale()'s definition in
flightgear/src/Main/fg_commands.cxx, property-scale rightfully uses a
default factor of 1.0. However, if a joystick axis' property-scale
binding has no 'factor' node defined, and one opens the joystick
configuration dialog, then PropertyScaleAxis.parse() creates an empty
'factor' node that implicitely gets a value of 0. This method is called
by joystick.readConfig() when the joystick-config dialog is opened. This
has the effect of rendering the corresponding joystick axis inoperant.
How to reproduce the bug:
- take a joystick such as the SAITEK CYBORG 3D USB, with its default
binding file from
fgdata/Input/Joysticks/Saitek/Cyborg-Gold-3d-USB.xml (this file uses
property-scale for the aileron, with no explicitely defined factor);
- start FlightGear; move the joystick left or right while looking at
the plane wings -> the ailerons move, it works fine;
- now, open the joystick-config dialog and do the same test -> the
ailerons don't move anymore and the 'Aileron' value at the bottom of
the dialog stays at 0 (0.0 or -0.0...). Just opening the dialog to
test the joystick has "corrupted" its setup! This is very confusing
for users.
This fix corrects the problem by avoiding the apparently unneeded
creation of an empty 'factor' node when there is none inside the
<binding>. An alternative would be to create a 'factor' node with value
1.0. In any case, if someone later expands the joystick-config dialog to
allow modification of property-scale's factor, he should make sure to
use a default value of 1.0!
2016-01-08 11:04:39 +00:00
|
|
|
# Don't create a null 'factor' node if it doesn't exist!
|
|
|
|
# (value 0 wouldn't be appropriate for a default factor)
|
|
|
|
factorNode = p.getNode("binding", 1).getNode("factor", 0);
|
|
|
|
if (factorNode != nil)
|
|
|
|
me.inverted = (factorNode.getValue() < 0);
|
|
|
|
|
2012-08-24 21:53:21 +00:00
|
|
|
me.offset = p.getNode("binding", 1).getNode("offset", 1).getValue();
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(axis) {
|
|
|
|
var p = props.Node.new();
|
|
|
|
p = p.getNode("axis[" ~ axis ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("property-scale");
|
|
|
|
p.getNode("binding", 1).getNode("property", 1).setValue(me.prop);
|
|
|
|
p.getNode("binding", 1).getNode("dead-band", 1).setValue(me.deadband);
|
|
|
|
if (me.inverted) {
|
|
|
|
p.getNode("binding", 1).getNode("factor", 1).setValue(0 - me.factor);
|
|
|
|
} else {
|
|
|
|
p.getNode("binding", 1).getNode("factor", 1).setValue(me.factor);
|
|
|
|
}
|
|
|
|
p.getNode("binding", 1).getNode("offset", 1).setValue(me.offset);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
var NasalScaleAxis = {
|
|
|
|
new: func(name, script, prop) {
|
|
|
|
var m = { parents: [NasalScaleAxis, Axis.new(name, prop, 0) ] };
|
|
|
|
m.script = script;
|
|
|
|
m.prop = prop;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [NasalScaleAxis, Axis.new(me.name, me.prop, 0) ] };
|
|
|
|
|
|
|
|
m.script = me.script;
|
|
|
|
m.prop = me.prop;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var cmd = prop.getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("binding", 1).getNode("script", 1).getValue();
|
|
|
|
if ((p != nil) and (cmd == "nasal")) {
|
|
|
|
p = string.trim(p);
|
|
|
|
p = string.replace(p, ";", "");
|
|
|
|
p = p ~ ";";
|
|
|
|
return (p == me.script);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(axis) {
|
|
|
|
var p = props.Node.new().getNode("axis[" ~ axis ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
p.getNode("binding", 1).getNode("script", 1).setValue(me.script);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var NasalLowHighAxis = {
|
|
|
|
new: func(name, lowscript, highscript, prop) {
|
|
|
|
var m = { parents: [NasalLowHighAxis, Axis.new(name, prop, 1) ] };
|
|
|
|
m.lowscript = lowscript;
|
|
|
|
m.highscript = highscript;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [NasalLowHighAxis, Axis.new(me.name, me.prop, 1) ] };
|
|
|
|
|
|
|
|
m.inverted = me.inverted;
|
|
|
|
|
|
|
|
m.lowscript = me.lowscript;
|
|
|
|
m.highscript = me.highscript;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var cmd = prop.getNode("low", 1).getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("low", 1).getNode("binding", 1).getNode("script", 1).getValue();
|
|
|
|
|
|
|
|
if ((p == nil) or (cmd != "nasal")) return 0;
|
|
|
|
p = string.trim(p);
|
|
|
|
p = string.replace(p, ";", "");
|
|
|
|
p = p ~ ";";
|
|
|
|
|
|
|
|
if (p == me.lowscript) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == me.highscript) {
|
|
|
|
me.inverted = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(axis) {
|
|
|
|
var p = props.Node.new().getNode("axis[" ~ axis ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
|
|
|
|
p.getNode("low", 1).getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
p.getNode("high", 1).getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
|
|
|
|
if (me.inverted) {
|
|
|
|
p.getNode("low", 1).getNode("binding", 1).getNode("script", 1).setValue(me.highscript);
|
|
|
|
p.getNode("high", 1).getNode("binding", 1).getNode("script", 1).setValue(me.lowscript);
|
|
|
|
} else {
|
|
|
|
p.getNode("low", 1).getNode("binding", 1).getNode("script", 1).setValue(me.lowscript);
|
|
|
|
p.getNode("high", 1).getNode("binding", 1).getNode("script", 1).setValue(me.highscript);
|
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var axisBindings = [
|
|
|
|
PropertyScaleAxis.new("Aileron", "/controls/flight/aileron"),
|
|
|
|
PropertyScaleAxis.new("Elevator", "/controls/flight/elevator"),
|
|
|
|
PropertyScaleAxis.new("Rudder", "/controls/flight/rudder"),
|
|
|
|
NasalScaleAxis.new("Throttle", "controls.throttleAxis();", "/controls/engines/engine[0]/throttle") ,
|
|
|
|
NasalScaleAxis.new("Mixture", "controls.mixtureAxis();", "/controls/engines/engine[0]/mixture") ,
|
|
|
|
NasalScaleAxis.new("Propeller", "controls.propellerAxis();", "/controls/engines/engine[0]/propeller-pitch") ,
|
|
|
|
NasalLowHighAxis.new("View (horizontal)",
|
|
|
|
"setprop(\"/sim/current-view/goal-heading-offset-deg\", getprop(\"/sim/current-view/goal-heading-offset-deg\") + 30);",
|
|
|
|
"setprop(\"/sim/current-view/goal-heading-offset-deg\", getprop(\"/sim/current-view/goal-heading-offset-deg\") - 30);",
|
|
|
|
"/sim/current-view/goal-heading-offset-deg"),
|
|
|
|
NasalLowHighAxis.new("View (vertical)",
|
|
|
|
"setprop(\"/sim/current-view/goal-pitch-offset-deg\", getprop(\"/sim/current-view/goal-pitch-offset-deg\") - 20);",
|
|
|
|
"setprop(\"/sim/current-view/goal-pitch-offset-deg\", getprop(\"/sim/current-view/goal-pitch-offset-deg\") + 20);",
|
|
|
|
"/sim/current-view/goal-heading-offset-deg"),
|
|
|
|
# PropertyScaleAxis.new("Aileron Trim", "/controls/flight/aileron-trim"),
|
|
|
|
# PropertyScaleAxis.new("Elevator Trim", "/controls/flight/elevator-trim"),
|
|
|
|
# PropertyScaleAxis.new("Rudder Trim", "/controls/flight/rudder-trim"),
|
|
|
|
PropertyScaleAxis.new("Brake Left", "/controls/gear/brake-left", 0, 0.5, 1.0),
|
|
|
|
PropertyScaleAxis.new("Brake Right", "/controls/gear/brake-right", 0, 0.5, 1.0),
|
|
|
|
NasalLowHighAxis.new("Aileron Trim", "controls.aileronTrim(-1);", "controls.aileronTrim(1);", "/controls/flight/aileron-trim"),
|
|
|
|
NasalLowHighAxis.new("Elevator Trim", "controls.elevatorTrim(-1);", "controls.elevatorTrim(1);", "/controls/flight/elevator-trim"),
|
|
|
|
NasalLowHighAxis.new("Rudder Trim", "controls.rudderTrim(-1);", "controls.rudderTrim(1);", "/controls/flight/rudder-trim"),
|
|
|
|
CustomAxis.new(),
|
|
|
|
UnboundAxis.new(),
|
|
|
|
];
|
|
|
|
|
|
|
|
# Button bindings
|
|
|
|
var ButtonBinding = {
|
|
|
|
new: func(name, binding, repeatable) {
|
|
|
|
var m = { parents: [ButtonBinding] };
|
|
|
|
m.name = name;
|
|
|
|
m.binding = binding;
|
|
|
|
m.repeatable = repeatable;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [ButtonBinding] };
|
|
|
|
m.name = me.name;
|
|
|
|
m.binding= me.binding;
|
|
|
|
m.repeatable = me.repeatable;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
getName: func() { return me.name; },
|
|
|
|
getBinding: func(button) { return nil; },
|
|
|
|
isRepeatable: func() { return me.repeatable; }
|
|
|
|
};
|
|
|
|
|
|
|
|
var CustomButton = {
|
|
|
|
new: func() {
|
|
|
|
var m = { parents: [CustomButton, ButtonBinding.new("Custom", "", 0) ] };
|
|
|
|
m.custom_binding = nil;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [CustomButton, ButtonBinding.new("Custom", "", 0) ] };
|
|
|
|
m.custom_binding = me.custom_binding;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
if (prop.getNode("binding") != nil) {
|
|
|
|
var p = props.Node.new();
|
|
|
|
props.copy(prop.getNode("binding"), p);
|
|
|
|
me.custom_binding = p;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
props.copy(me.custom_binding, p.getNode("binding", 1));
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var UnboundButton = {
|
|
|
|
new: func() {
|
|
|
|
var m = { parents: [UnboundButton, ButtonBinding.new("None", "", 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [UnboundButton, ButtonBinding.new("None", "", 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
return (prop.getNode("binding") != nil);
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var PropertyToggleButton = {
|
|
|
|
new: func(name, prop) {
|
|
|
|
var m = { parents: [PropertyToggleButton, ButtonBinding.new(name, prop, 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [PropertyToggleButton, ButtonBinding.new(me.name, me.binding, 0) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
|
|
|
|
var c = prop.getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("binding", 1).getNode("property", 1).getValue();
|
|
|
|
return ((c == "property-toggle") and (p == me.prop));
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("property-toggle");
|
|
|
|
p.getNode("binding", 1).getNode("property", 1).setValue(me.binding);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var PropertyAdjustButton = {
|
|
|
|
new: func(name, prop, step) {
|
|
|
|
var m = { parents: [PropertyAdjustButton, ButtonBinding.new(name, prop, 0) ] };
|
|
|
|
m.step = step;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [PropertyAdjustButton, ButtonBinding.new(me.name, me.binding, 0) ] };
|
|
|
|
m.step = me.step;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var c = prop.getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("binding", 1).getNode("property", 1).getValue();
|
|
|
|
var s = prop.getNode("binding", 1).getNode("step", 1).getValue();
|
|
|
|
return ((c == "property-adjust") and (p == me.binding) and (s == me.step));
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("property-adjust");
|
|
|
|
p.getNode("binding", 1).getNode("property", 1).setValue(me.binding);
|
|
|
|
p.getNode("binding", 1).getNode("step", 1).setValue(me.step);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var NasalButton = {
|
|
|
|
new: func(name, script, repeatable) {
|
|
|
|
var m = { parents: [NasalButton, ButtonBinding.new(name, script, repeatable) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [NasalButton, ButtonBinding.new(me.name, me.binding, me.repeatable) ] };
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
var c = prop.getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p = prop.getNode("binding", 1).getNode("script", 1).getValue();
|
|
|
|
|
|
|
|
if (p == nil) { return 0; }
|
|
|
|
|
|
|
|
p = string.trim(p);
|
|
|
|
p = string.replace(p, ";", "");
|
|
|
|
p = p ~ ";";
|
|
|
|
|
|
|
|
return ((c == "nasal") and (p == me.binding));
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
p.getNode("binding", 1).getNode("script", 1).setValue(me.binding);
|
|
|
|
p.getNode("repeatable", 1).setValue(me.repeatable);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var NasalHoldButton = {
|
|
|
|
new: func(name, script, scriptUp) {
|
|
|
|
var m = { parents: [NasalHoldButton, ButtonBinding.new(name, script, 0) ] };
|
|
|
|
m.scriptUp = scriptUp;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
clone: func() {
|
|
|
|
var m = { parents: [NasalHoldButton, ButtonBinding.new(me.name, me.binding, 0) ] };
|
|
|
|
m.scriptUp = me.scriptUp;
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
match: func(prop) {
|
|
|
|
|
|
|
|
var c = prop.getNode("mod-up", 1).getNode("binding", 1).getNode("command", 1).getValue();
|
|
|
|
var p1 = prop.getNode("binding", 1).getNode("script", 1).getValue();
|
|
|
|
var p2 = prop.getNode("mod-up", 1).getNode("binding", 1).getNode("script", 1).getValue();
|
|
|
|
|
|
|
|
if (p2 == nil) { return 0; }
|
|
|
|
|
|
|
|
p1 = string.trim(p1);
|
|
|
|
p1 = string.replace(p1, ";", "");
|
|
|
|
p1 = p1 ~ ";";
|
|
|
|
|
|
|
|
return ((c == "nasal") and (p1 == me.binding));
|
|
|
|
},
|
|
|
|
|
|
|
|
getBinding: func(button) {
|
|
|
|
var p = props.Node.new().getNode("button[" ~ button ~ "]", 1);
|
|
|
|
p.getNode("desc", 1).setValue(me.name);
|
|
|
|
p.getNode("repeatable", 1).setValue("false");
|
|
|
|
p.getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
p.getNode("binding", 1).getNode("script", 1).setValue(me.binding);
|
|
|
|
p.getNode("mod-up", 1).getNode("binding", 1).getNode("command", 1).setValue("nasal");
|
|
|
|
p.getNode("mod-up", 1).getNode("binding", 1).getNode("script", 1).setValue(me.scriptUp);
|
|
|
|
return p;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var buttonBindings = [
|
|
|
|
NasalButton.new("Elevator Trim Up", "controls.elevatorTrim(-1);", 1),
|
|
|
|
NasalButton.new("Elevator Trim Down", "controls.elevatorTrim(1);", 1),
|
|
|
|
NasalButton.new("Rudder Trim Left", "controls.rudderTrim(-1);", 1),
|
|
|
|
NasalButton.new("Rudder Trim Right", "controls.rudderTrim(1);", 1),
|
|
|
|
NasalButton.new("Aileron Trim Left", "controls.aileronTrim(-1);", 1),
|
|
|
|
NasalButton.new("Aileron Trim Right", "controls.aileronTrim(1);", 1),
|
2013-09-24 11:29:25 +00:00
|
|
|
NasalHoldButton.new("FGCom PTT", "controls.ptt(1);", "controls.ptt(0);"),
|
2012-08-24 21:53:21 +00:00
|
|
|
NasalHoldButton.new("Trigger", "controls.trigger(1);", "controls.trigger(0);"),
|
|
|
|
NasalHoldButton.new("Flaps Up", "controls.flapsDown(-1);", "controls.flapsDown(0);"),
|
|
|
|
NasalHoldButton.new("Flaps Down", "controls.flapsDown(1);", "controls.flapsDown(0);"),
|
|
|
|
NasalHoldButton.new("Gear Up", "controls.gearDown(-1);", "controls.gearDown(0);"),
|
|
|
|
NasalHoldButton.new("Gear Down", "controls.gearDown(1);", "controls.gearDown(0);"),
|
|
|
|
NasalHoldButton.new("Spoilers Retract", "controls.stepSpoilers(-1);", "controls.stepSpoilers(0);"),
|
|
|
|
NasalHoldButton.new("Spoilers Deploy", "controls.stepSpoilers(1);", "controls.stepSpoilers(0);"),
|
|
|
|
NasalHoldButton.new("Brakes", "controls.applyBrakes(1);", "controls.applyBrakes(0);"),
|
|
|
|
|
|
|
|
NasalButton.new("View Decrease", "view.decrease(0.75);", 1),
|
|
|
|
NasalButton.new("View Increase", "view.increase(0.75);", 1),
|
|
|
|
NasalButton.new("View Cycle Forwards", "view.stepView(1);", 0),
|
|
|
|
NasalButton.new("View Cycle Backwards", "view.stepView(-1);", 0),
|
|
|
|
PropertyAdjustButton.new("View Left", "/sim/current-view/goal-heading-offset-deg", "30.0"),
|
|
|
|
PropertyAdjustButton.new("View Right", "/sim/current-view/goal-heading-offset-deg", "-30.0"),
|
|
|
|
PropertyAdjustButton.new("View Up", "/sim/current-view/goal-pitch-offset-deg", "20.0"),
|
|
|
|
PropertyAdjustButton.new("View Down", "/sim/current-view/goal-pitch-offset-deg", "-20.0"),
|
|
|
|
CustomButton.new(),
|
|
|
|
];
|
|
|
|
|
|
|
|
# Parse config from the /input tree and write it to the
|
|
|
|
# dialog_root.
|
|
|
|
var readConfig = func(dialog_root="/sim/gui/dialogs/joystick-config") {
|
|
|
|
|
|
|
|
var js_name = getprop(dialog_root ~ "/selected-joystick");
|
|
|
|
var joysticks = props.globals.getNode("/input/joysticks").getChildren("js");
|
|
|
|
|
2012-08-26 20:48:53 +00:00
|
|
|
if (size(joysticks) == 0) { return 0; }
|
2012-08-24 21:53:21 +00:00
|
|
|
|
|
|
|
if (js_name == nil) {
|
|
|
|
js_name = joysticks[0].getNode("id").getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
var js = nil;
|
|
|
|
|
|
|
|
forindex (var i; joysticks) {
|
|
|
|
if ((joysticks[i].getNode("id") != nil) and
|
|
|
|
(joysticks[i].getNode("id").getValue() == js_name))
|
|
|
|
{
|
|
|
|
js = joysticks[i];
|
|
|
|
setprop(dialog_root ~ "/selected-joystick", js_name);
|
|
|
|
setprop(dialog_root ~ "/selected-joystick-index", i);
|
|
|
|
setprop(dialog_root ~ "/selected-joystick-config", joysticks[i].getNode("source").getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-26 20:48:53 +00:00
|
|
|
if (js == nil) {
|
|
|
|
# We didn't find the joystick we expected - default to the first
|
|
|
|
setprop(dialog_root ~ "/selected-joystick", joysticks[0].getNode("id").getValue());
|
|
|
|
setprop(dialog_root ~ "/selected-joystick-index", 0);
|
|
|
|
setprop(dialog_root ~ "/selected-joystick-config", joysticks[0].getNode("source").getValue());
|
|
|
|
}
|
|
|
|
|
2012-08-24 21:53:21 +00:00
|
|
|
# Set up the axes assignments
|
|
|
|
var axes = js.getChildren("axis");
|
|
|
|
|
|
|
|
for (var axis = 0; axis < MAX_AXES; axis = axis +1) {
|
|
|
|
|
|
|
|
var p = props.globals.getNode(dialog_root ~ "/axis[" ~ axis ~ "]", 1);
|
|
|
|
p.remove();
|
|
|
|
p = props.globals.getNode(dialog_root ~ "/axis[" ~ axis ~ "]", 1);
|
|
|
|
|
|
|
|
# Note that we can't simply use an index into the axes array
|
|
|
|
# as that doesn't work for a sparsley populated set of axes.
|
|
|
|
# E.g. one with n="3"
|
|
|
|
var a = js.getNode("axis[" ~ axis ~ "]");
|
|
|
|
|
|
|
|
if (a != nil) {
|
|
|
|
# Read properties from bindings
|
|
|
|
props.copy(a, p.getNode("original_binding", 1));
|
|
|
|
|
|
|
|
var binding = nil;
|
|
|
|
foreach (var b; joystick.axisBindings) {
|
|
|
|
if ((binding == nil) and (a != nil) and b.match(a)) {
|
|
|
|
binding = b.clone();
|
|
|
|
binding.parse(a);
|
|
|
|
p.getNode("binding", 1).setValue(binding.getName());
|
|
|
|
p.getNode("invertable", 1).setValue(binding.isInvertable());
|
|
|
|
p.getNode("inverted", 1).setValue(binding.isInverted());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (binding == nil) {
|
|
|
|
# No binding for this axis
|
|
|
|
p.getNode("binding", 1).setValue("None");
|
|
|
|
p.getNode("invertable", 1).setValue(0);
|
|
|
|
p.getNode("inverted", 1).setValue(0);
|
|
|
|
p.removeChild("original_binding");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p.getNode("binding", 1).setValue("None");
|
|
|
|
p.getNode("invertable", 1).setValue(0);
|
|
|
|
p.getNode("inverted", 1).setValue(0);
|
|
|
|
p.removeChild("original_binding");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set up button assignment.
|
|
|
|
var buttons = js.getChildren("button");
|
|
|
|
|
|
|
|
for (var button = 0; button < MAX_BUTTONS; button = button + 1) {
|
|
|
|
var btn = props.globals.getNode(dialog_root ~ "/button[" ~ button ~ "]", 1);
|
|
|
|
btn.remove();
|
|
|
|
btn = props.globals.getNode(dialog_root ~ "/button[" ~ button ~ "]", 1);
|
|
|
|
|
|
|
|
# Note that we can't simply use an index into the buttons array
|
|
|
|
# as that doesn't work for a sparsley populated set of buttons.
|
|
|
|
# E.g. one with n="3"
|
|
|
|
var a = js.getNode("button[" ~ button ~ "]");
|
|
|
|
|
|
|
|
if (a != nil) {
|
|
|
|
# Read properties from bindings
|
2012-09-29 20:24:12 +00:00
|
|
|
props.copy(a, btn.getNode("original_binding", 1));
|
2012-08-24 21:53:21 +00:00
|
|
|
var binding = nil;
|
|
|
|
foreach (var b; joystick.buttonBindings) {
|
|
|
|
if ((binding == nil) and (a != nil) and b.match(a)) {
|
|
|
|
binding = b.clone();
|
|
|
|
btn.getNode("binding", 1).setValue(binding.getName());
|
|
|
|
props.copy(b.getBinding(button), btn.getNode("original_binding", 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b == nil) {
|
|
|
|
btn.getNode("binding", 1).setValue("None");
|
|
|
|
btn.removeChild("original_binding");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
btn.getNode("binding", 1).setValue("None");
|
|
|
|
btn.removeChild("original_binding");
|
|
|
|
}
|
2013-04-12 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# Set up Nasal code.
|
|
|
|
var nasals = js.getChildren("nasal");
|
|
|
|
|
|
|
|
for (var nasal = 0; nasal < MAX_NASALS; nasal = nasal + 1) {
|
|
|
|
var nas = props.globals.getNode(dialog_root ~ "/nasal[" ~ nasal ~ "]", 1);
|
|
|
|
nas.remove();
|
|
|
|
nas = props.globals.getNode(dialog_root ~ "/nasal[" ~ nasal ~ "]", 1);
|
|
|
|
|
|
|
|
# Note that we can't simply use an index into the buttons array
|
|
|
|
# as that doesn't work for a sparsley populated set of buttons.
|
|
|
|
# E.g. one with n="3"
|
|
|
|
var a = js.getNode("nasal[" ~ nasal ~ "]");
|
|
|
|
if (a != nil) {
|
|
|
|
props.copy(a, nas.getNode("original_script", 1));
|
|
|
|
}
|
|
|
|
}
|
2012-08-24 21:53:21 +00:00
|
|
|
}
|
|
|
|
|
2012-12-07 22:42:18 +00:00
|
|
|
var writeConfig = func(dialog_root="/sim/gui/dialogs/joystick-config", reset=0) {
|
2012-08-24 21:53:21 +00:00
|
|
|
|
|
|
|
# Write out the joystick file.
|
|
|
|
var config = props.Node.new();
|
|
|
|
var id = getprop(dialog_root ~ "/selected-joystick");
|
2012-12-07 22:42:18 +00:00
|
|
|
|
|
|
|
if (reset == 1) {
|
|
|
|
# We've been asked to reset the joystick config to the default. As we can't
|
|
|
|
# delete the configuration file, we achieve this by setting an invalid name
|
|
|
|
# tag that won't match.
|
|
|
|
config.getNode("name", 1).setValue("UNUSED INVALID CONFIG");
|
|
|
|
} else {
|
|
|
|
config.getNode("name", 1).setValue(id);
|
|
|
|
}
|
2012-08-24 21:53:21 +00:00
|
|
|
|
2013-04-12 19:24:53 +00:00
|
|
|
var nasals = props.globals.getNode(dialog_root).getChildren("nasal");
|
|
|
|
forindex (var nas; nasals) {
|
|
|
|
var nasalscript = config.getNode("nasal[" ~ nas ~ "]", 1);
|
|
|
|
props.copy(props.globals.getNode(dialog_root ~ "/nasal[" ~ nas ~ "]/original_script", 1), nasalscript);
|
|
|
|
}
|
|
|
|
|
2012-08-24 21:53:21 +00:00
|
|
|
var axes = props.globals.getNode(dialog_root).getChildren("axis");
|
|
|
|
forindex (var axis; axes) {
|
|
|
|
var name = getprop(dialog_root ~ "/axis[" ~ axis ~ "]/binding");
|
|
|
|
|
|
|
|
if (name != "None") {
|
|
|
|
foreach (var binding; axisBindings) {
|
|
|
|
if (binding.getName() == name) {
|
|
|
|
var b = binding.clone();
|
|
|
|
b.setInverted(getprop(dialog_root ~ "/axis[" ~ axis ~ "]/inverted"));
|
|
|
|
|
|
|
|
# Generate the axis and binding
|
|
|
|
var axisnode = config.getNode("axis[" ~ axis ~ "]", 1);
|
|
|
|
if (name == "Custom") {
|
|
|
|
props.copy(props.globals.getNode(dialog_root ~ "/axis[" ~ axis ~ "]/original_binding", 1), axisnode);
|
|
|
|
} else {
|
|
|
|
props.copy(b.getBinding(axis), axisnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var buttons = props.globals.getNode(dialog_root).getChildren("button");
|
|
|
|
forindex (var btn; buttons) {
|
|
|
|
var name = getprop(dialog_root ~ "/button[" ~ btn ~ "]/binding");
|
|
|
|
|
|
|
|
if (name != "None") {
|
|
|
|
foreach (var binding; buttonBindings) {
|
|
|
|
if (binding.getName() == name) {
|
|
|
|
var b = binding.clone();
|
|
|
|
# Generate the axis and binding
|
|
|
|
var buttonprop = config.getNode("button[" ~ btn ~ "]", 1);
|
|
|
|
if (name == "Custom") {
|
|
|
|
props.copy(props.globals.getNode(dialog_root ~ "/button[" ~ btn ~ "]/original_binding", 1), buttonprop);
|
|
|
|
} else {
|
|
|
|
props.copy(b.getBinding(btn), buttonprop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var filename = id;
|
2012-08-26 20:48:53 +00:00
|
|
|
filename = string.replace(filename, " ", "-");
|
2012-08-24 21:53:21 +00:00
|
|
|
filename = string.replace(filename, ".", "");
|
|
|
|
filename = string.replace(filename, "/", "");
|
|
|
|
|
|
|
|
# Write out the file
|
|
|
|
io.write_properties(getprop("/sim/fg-home") ~ "/Input/Joysticks/" ~ filename ~ ".xml", config);
|
|
|
|
}
|
|
|
|
|
2012-12-07 22:42:18 +00:00
|
|
|
var resetConfig = func(dialog_root="/sim/gui/dialogs/joystick-config") {
|
|
|
|
writeConfig(dialog_root, 1);
|
|
|
|
}
|