warthog: first stab at throttle hardware config (backlight + LEDs)
This works under Linux only, which is certainly suboptimal. But there's no reason why support for other OSes couldn't be added later, and there's no reason to not support Linux now, either. The code can set the throttle's backlight brightness and the five programmable LEDs. As a demonstration, the latter is currently used for a gear-down warning. Support for setting the stick's hardware deadzone is planned. (It's on by default.) The mechanism writes directly to Linux' hidraw devices, which requires some simple configuration. This is described in the README. It doesn't depend on any external utility.
This commit is contained in:
parent
45766cf824
commit
7d9ea536f7
5 changed files with 170 additions and 13 deletions
|
@ -33,3 +33,75 @@ axis definition uses the same index as the master file. The n-th
|
||||||
uses <number><unix> etc. Nasal blocks are properly executed in the
|
uses <number><unix> etc. Nasal blocks are properly executed in the
|
||||||
namespace of the master file, so you can access all its functions
|
namespace of the master file, so you can access all its functions
|
||||||
and variables.
|
and variables.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Joystick configuration from within FlightGear under Linux:
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
This allows to set backlight brightness and LEDs from within FlightGear
|
||||||
|
without requiring any config application, by directly writing to the devices
|
||||||
|
via raw HID support.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(1) Make sure your kernel has hidraw support compiled in: check if there is at
|
||||||
|
least one file listed with
|
||||||
|
|
||||||
|
$ ls /dev/hidraw*
|
||||||
|
|
||||||
|
or if your kernel .config contains:
|
||||||
|
|
||||||
|
CONFIG_HIDRAW=y
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(2) To get a reliable, persistent file name for your Warthog devices, create
|
||||||
|
a file /etc/udev/rules.d/00-local.rules and add these lines:
|
||||||
|
|
||||||
|
SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}!="?*", IMPORT{program}="input_id %p"
|
||||||
|
SUBSYSTEM=="hidraw", ENV{ID_SERIAL}!="?*", IMPORT{program}="usb_id --export %p"
|
||||||
|
SUBSYSTEM=="hidraw", SYMLINK+="input/hidraw/%E{ID_SERIAL}"
|
||||||
|
SUBSYSTEM=="hidraw", ENV{ID_INPUT_JOYSTICK}!="0", GROUP:="js"
|
||||||
|
|
||||||
|
If you don't have a user group "js" (for joystick hardware access) either
|
||||||
|
create one or, in the fourth line, use a group instead where all joystick
|
||||||
|
users are member, e.g. GROUP:="users". Then plug your Warthog devices out
|
||||||
|
and in again and check if this created two device files under /dev/hid/:
|
||||||
|
|
||||||
|
$ ls -l /dev/input/hidraw/*
|
||||||
|
lrwxrwxrwx 1 root root 10 Oct 2 14:23 Thrustmaster_Throttle_-_HOTAS_Warthog -> ../hidraw1
|
||||||
|
lrwxrwxrwx 1 root root 10 Oct 2 14:23 Thustmaster_Joystick_-_HOTAS_Warthog -> ../hidraw0
|
||||||
|
|
||||||
|
Also check if the linked-to hidraw devices have proper permissions (rw for "js"):
|
||||||
|
|
||||||
|
$ ls -l /dev/hidraw*
|
||||||
|
crw-rw---- 1 root js 251, 0 Oct 2 09:11 /dev/hidraw0
|
||||||
|
crw-rw---- 1 root js 251, 1 Oct 2 14:23 /dev/hidraw1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(3) Allow FlightGear to write to the two devices. The best way to do this is
|
||||||
|
to edit a local copy of the global IOrules. Just copy the global file
|
||||||
|
to your FG_HOME directory:
|
||||||
|
|
||||||
|
$ mkdir -p ~/.fgfs/Nasal
|
||||||
|
$ cp $FG_ROOT/Nasal/IOrules ~/.fgfs/Nasal
|
||||||
|
|
||||||
|
Now add these two lines there:
|
||||||
|
|
||||||
|
WRITE ALLOW /dev/input/hidraw/Thustmaster_Joystick_-_HOTAS_Warthog
|
||||||
|
WRITE ALLOW /dev/input/hidraw/Thrustmaster_Throttle_-_HOTAS_Warthog
|
||||||
|
|
||||||
|
CAVE: Don't fix the bad spelling of "Thrustmaster", unless your stick
|
||||||
|
really uses that!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DISCLAIMER: Of course, you take all responsibility for any possible
|
||||||
|
damages to your hardware if you make these changes. Neither the
|
||||||
|
FlightGear project nor any of its developers and contributors are in
|
||||||
|
any way liable.
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
<PropertyList>
|
<PropertyList>
|
||||||
<nasal>
|
<nasal>
|
||||||
<script>
|
<script>
|
||||||
if (init)
|
jslistener("/controls/flight/speedbrake", func(n) {
|
||||||
setlistener("/controls/flight/speedbrake", func(n) {
|
print(n.getValue());
|
||||||
setprop("/controls/flight/speedbrake-lever", n.getValue());
|
setprop("/controls/flight/speedbrake-lever", n.getValue());
|
||||||
seahawk.adjustFlaps();
|
seahawk.adjustFlaps();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</nasal>
|
</nasal>
|
||||||
</PropertyList>
|
</PropertyList>
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
<script>
|
<script>
|
||||||
var this = cmdarg().getParent();
|
var this = cmdarg().getParent();
|
||||||
var init = !contains(caller(0)[0], "init");
|
var init = !contains(caller(0)[0], "init");
|
||||||
|
if (!contains(globals, "warthog"))
|
||||||
|
io.load_nasal(getprop("/sim/fg-root") ~ "/Input/Joysticks/ThrustMaster/Warthog/warthog.nas");
|
||||||
|
|
||||||
|
var jslistener = func init and call(setlistener, arg);
|
||||||
var popup = func gui.popupTip(call(sprintf, arg));
|
var popup = func gui.popupTip(call(sprintf, arg));
|
||||||
var is_helicopter = (func {(var n = props.globals.getNode("rotors", 0)) != nil and n.getAttribute("children")})();
|
var is_helicopter = (func {(var n = props.globals.getNode("rotors", 0)) != nil and n.getAttribute("children")})();
|
||||||
var aircraft_type = getprop("/sim/type");
|
var aircraft_type = getprop("/sim/type");
|
||||||
|
@ -43,10 +47,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var mod = 0;
|
var mod = 0;
|
||||||
if (init) {
|
var m = props.globals.initNode("/devices/status/joysticks/warthog/modifier", mod, "INT");
|
||||||
var m = props.globals.initNode("/devices/status/joysticks/warthog/modifier", mod, "INT");
|
jslistener(m, func(n) mod = n.getValue());
|
||||||
setlistener(m, func(n) mod = n.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
var trimstep = 0.75;
|
var trimstep = 0.75;
|
||||||
var viewstep = 0.5;
|
var viewstep = 0.5;
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
<script>
|
<script>
|
||||||
var this = cmdarg().getParent();
|
var this = cmdarg().getParent();
|
||||||
var init = !contains(caller(0)[0], "init");
|
var init = !contains(caller(0)[0], "init");
|
||||||
|
if (!contains(globals, "warthog"))
|
||||||
|
io.load_nasal(getprop("/sim/fg-root") ~ "/Input/Joysticks/ThrustMaster/Warthog/warthog.nas");
|
||||||
|
|
||||||
|
var jslistener = func init and call(setlistener, arg);
|
||||||
var popup = func gui.popupTip(call(sprintf, arg));
|
var popup = func gui.popupTip(call(sprintf, arg));
|
||||||
var is_helicopter = (func {(var n = props.globals.getNode("rotors", 0)) != nil and n.getAttribute("children")})();
|
var is_helicopter = (func {(var n = props.globals.getNode("rotors", 0)) != nil and n.getAttribute("children")})();
|
||||||
var aircraft_type = getprop("/sim/type");
|
var aircraft_type = getprop("/sim/type");
|
||||||
|
@ -37,11 +41,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gearcheck = func {
|
||||||
|
var agl = getprop("/position/altitude-agl-ft");
|
||||||
|
var geardown = getprop("/controls/gear/gear-down");
|
||||||
|
if (agl < 500 and !geardown)
|
||||||
|
warthog.throttle.toggle_leds(1, 2, 3, 4, 5);
|
||||||
|
else
|
||||||
|
warthog.throttle.set_leds(0, 1, 2, 3, 4, 5);
|
||||||
|
settimer(gearcheck, 0.5);
|
||||||
|
};
|
||||||
|
init and gearcheck();
|
||||||
|
|
||||||
var mod = 0;
|
var mod = 0;
|
||||||
if (init) {
|
var m = props.globals.initNode("/devices/status/joysticks/warthog/modifier", mod, "INT");
|
||||||
var m = props.globals.initNode("/devices/status/joysticks/warthog/modifier", mod, "INT");
|
jslistener(m, func(n) mod = n.getValue());
|
||||||
setlistener(m, func(n) mod = n.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
var left_engines = [0, 2, 4, 6, 8, 10];
|
var left_engines = [0, 2, 4, 6, 8, 10];
|
||||||
var right_engines = [1, 3, 5, 7, 9, 11];
|
var right_engines = [1, 3, 5, 7, 9, 11];
|
||||||
|
@ -231,10 +244,26 @@
|
||||||
|
|
||||||
<button n="10">
|
<button n="10">
|
||||||
<name>China Hat Forward</name>
|
<name>China Hat Forward</name>
|
||||||
|
<desc>+mod: increase LED brightness (see README)</desc>
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>
|
||||||
|
if (mod)
|
||||||
|
warthog.throttle.brighter();
|
||||||
|
</script>
|
||||||
|
</binding>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button n="11">
|
<button n="11">
|
||||||
<name>China Hat Aft</name>
|
<name>China Hat Aft</name>
|
||||||
|
<desc>+mod: decrease LED brightness (see README)</desc>
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>
|
||||||
|
if (mod)
|
||||||
|
warthog.throttle.darker();
|
||||||
|
</script>
|
||||||
|
</binding>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button n="12">
|
<button n="12">
|
||||||
|
|
54
Input/Joysticks/ThrustMaster/Warthog/warthog.nas
Normal file
54
Input/Joysticks/ThrustMaster/Warthog/warthog.nas
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# HIDRAW Interface (currently Linux-only; see README)
|
||||||
|
|
||||||
|
var device = {
|
||||||
|
new: func(path, bufsize) {
|
||||||
|
var m = { parents: [device] };
|
||||||
|
m.path = path;
|
||||||
|
m.bufsize = bufsize;
|
||||||
|
m.leds = 0;
|
||||||
|
m.bright = 0;
|
||||||
|
var stat = io.stat(m.path);
|
||||||
|
if (stat == nil or stat[11] != "chr")
|
||||||
|
m.send = func { nil };
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
send: func {
|
||||||
|
var buf = bits.buf(me.bufsize);
|
||||||
|
buf[0] = 1;
|
||||||
|
forindex (var i; arg)
|
||||||
|
buf[i + 1] = arg[i];
|
||||||
|
var file = io.open(me.path, "wb");
|
||||||
|
io.write(file, buf);
|
||||||
|
io.close(file);
|
||||||
|
},
|
||||||
|
set_leds: func(on, which...) { # on/off, list of leds (0: background, 1-5)
|
||||||
|
foreach (var w; which)
|
||||||
|
me.leds = bits.switch(me.leds, me._ledmap[w], on);
|
||||||
|
me.send(6, me.leds, me.bright);
|
||||||
|
},
|
||||||
|
toggle_leds: func(which...) {
|
||||||
|
foreach (var w; which)
|
||||||
|
me.leds = bits.toggle(me.leds, me._ledmap[w]);
|
||||||
|
me.send(6, me.leds, me.bright);
|
||||||
|
},
|
||||||
|
set_brightness: func(v) {
|
||||||
|
me.send(6, me.leds, me.bright = v < 0 ? 0 : v > 5 ? 5 : v);
|
||||||
|
},
|
||||||
|
brighter: func {
|
||||||
|
me.leds = bits.set(me.leds, me._ledmap[0]);
|
||||||
|
me.set_brightness(me.bright + 1);
|
||||||
|
},
|
||||||
|
darker: func {
|
||||||
|
me.leds = bits.set(me.leds, me._ledmap[0]);
|
||||||
|
me.set_brightness(me.bright - 1);
|
||||||
|
},
|
||||||
|
_ledmap: {0: 3, 1: 2, 2: 1, 3: 4, 4: 0, 5: 6},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var joystick = device.new("/dev/input/hidraw/Thustmaster_Joystick_-_HOTAS_Warthog", 12);
|
||||||
|
var throttle = device.new("/dev/input/hidraw/Thrustmaster_Throttle_-_HOTAS_Warthog", 36);
|
||||||
|
|
||||||
|
throttle.set_brightness(1);
|
||||||
|
throttle.set_leds(1, 0); # backlight on
|
||||||
|
throttle.set_leds(0, 1, 2, 3, 4, 5); # other LEDs off
|
Loading…
Reference in a new issue