1
0
Fork 0
flightgear/src/Input/input.cxx
mfranz 9fa86acc65 make the state of the Meta and Super modifier keys available. These keys
are not available out-of-the-box on all systems and keyboards, and should
therefore not be used in files committed to CVS. This makes them well
suited for local key bindings, as they aren't likely to get overwritten
with later releases. SDL supports Meta and Super, OSG supports only Meta,
and GLUT supports neither.
2007-12-01 13:09:11 +00:00

1152 lines
34 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// input.cxx -- handle user input from various sources.
//
// Written by David Megginson, started May 2001.
//
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <simgear/compiler.h>
#include <math.h>
#include <ctype.h>
#include <sstream>
#include STL_FSTREAM
#include STL_STRING
#include <vector>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <Aircraft/aircraft.hxx>
#include <Autopilot/xmlauto.hxx>
#include <Cockpit/hud.hxx>
#include <Cockpit/panel.hxx>
#include <Cockpit/panel_io.hxx>
#include <GUI/gui.h>
#include <Model/panelnode.hxx>
#include <Scripting/NasalSys.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include "input.hxx"
#include <Scenery/scenery.hxx>
#include <Main/renderer.hxx>
SG_USING_STD(ifstream);
SG_USING_STD(ostringstream);
SG_USING_STD(string);
SG_USING_STD(vector);
void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
void mouseMotionHandler(int x, int y);
void keyHandler(int key, int keymod, int mousex, int mousey);
////////////////////////////////////////////////////////////////////////
// Local variables.
////////////////////////////////////////////////////////////////////////
static FGInput * default_input = 0;
////////////////////////////////////////////////////////////////////////
// Local functions.
////////////////////////////////////////////////////////////////////////
static bool
getModShift ()
{
return bool(fgGetKeyModifiers() & KEYMOD_SHIFT);
}
static bool
getModCtrl ()
{
return bool(fgGetKeyModifiers() & KEYMOD_CTRL);
}
static bool
getModAlt ()
{
return bool(fgGetKeyModifiers() & KEYMOD_ALT);
}
static bool
getModMeta ()
{
return bool(fgGetKeyModifiers() & KEYMOD_META);
}
static bool
getModSuper ()
{
return bool(fgGetKeyModifiers() & KEYMOD_SUPER);
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput.
////////////////////////////////////////////////////////////////////////
FGInput::FGInput () :
_key_event(fgGetNode("/devices/status/keyboard/event", true))
{
if (default_input == 0)
default_input = this;
}
FGInput::~FGInput ()
{
if (default_input == this)
default_input = 0;
}
void
FGInput::init ()
{
_init_joystick();
_init_mouse();
fgRegisterKeyHandler(keyHandler);
fgRegisterMouseClickHandler(mouseClickHandler);
fgRegisterMouseMotionHandler(mouseMotionHandler);
}
void
FGInput::reinit ()
{
init();
}
void
FGInput::postinit ()
{
_postinit_joystick();
_postinit_keyboard();
}
void
FGInput::bind ()
{
fgTie("/devices/status/keyboard/shift", getModShift);
fgTie("/devices/status/keyboard/ctrl", getModCtrl);
fgTie("/devices/status/keyboard/alt", getModAlt);
fgTie("/devices/status/keyboard/meta", getModMeta);
fgTie("/devices/status/keyboard/super", getModSuper);
_key_event->tie("key", SGRawValuePointer<int>(&_key_code));
_key_event->tie("pressed", SGRawValuePointer<bool>(&_key_pressed));
_key_event->tie("modifier", SGRawValuePointer<int>(&_key_modifiers));
_key_event->tie("modifier/shift", SGRawValuePointer<bool>(&_key_shift));
_key_event->tie("modifier/ctrl", SGRawValuePointer<bool>(&_key_ctrl));
_key_event->tie("modifier/alt", SGRawValuePointer<bool>(&_key_alt));
_key_event->tie("modifier/meta", SGRawValuePointer<bool>(&_key_meta));
_key_event->tie("modifier/super", SGRawValuePointer<bool>(&_key_super));
}
void
FGInput::unbind ()
{
fgUntie("/devices/status/keyboard/shift");
fgUntie("/devices/status/keyboard/ctrl");
fgUntie("/devices/status/keyboard/alt");
fgUntie("/devices/status/keyboard/meta");
fgUntie("/devices/status/keyboard/super");
_key_event->untie("key");
_key_event->untie("pressed");
_key_event->untie("modifier");
_key_event->untie("modifier/shift");
_key_event->untie("modifier/ctrl");
_key_event->untie("modifier/alt");
_key_event->untie("modifier/meta");
_key_event->untie("modifier/super");
}
void
FGInput::update (double dt)
{
_update_keyboard();
_update_joystick(dt);
_update_mouse(dt);
}
void
FGInput::suspend ()
{
// NO-OP
}
void
FGInput::resume ()
{
// NO-OP
}
bool
FGInput::is_suspended () const
{
return false;
}
void
FGInput::makeDefault (bool status)
{
if (status)
default_input = this;
else if (default_input == this)
default_input = 0;
}
void
FGInput::doKey (int k, int modifiers, int x, int y)
{
// Sanity check.
if (k < 0 || k >= MAX_KEYS) {
SG_LOG(SG_INPUT, SG_WARN, "Key value " << k << " out of range");
return;
}
_key_code = k;
_key_modifiers = modifiers & ~KEYMOD_RELEASED;
_key_pressed = !bool(modifiers & KEYMOD_RELEASED);
_key_shift = bool(modifiers & KEYMOD_SHIFT);
_key_ctrl = bool(modifiers & KEYMOD_CTRL);
_key_alt = bool(modifiers & KEYMOD_ALT);
_key_meta = bool(modifiers & KEYMOD_META);
_key_super = bool(modifiers & KEYMOD_SUPER);
_key_event->fireValueChanged();
if (!_key_code)
return;
button &b = _key_bindings[k];
// Key pressed.
if (!(modifiers & KEYMOD_RELEASED)) {
SG_LOG( SG_INPUT, SG_DEBUG, "User pressed key " << k
<< " with modifiers " << modifiers );
if (!b.last_state || b.is_repeatable) {
const binding_list_t &bindings = _find_key_bindings(k, modifiers);
for (unsigned int i = 0; i < bindings.size(); i++)
bindings[i]->fire();
b.last_state = 1;
}
}
// Key released.
else {
SG_LOG(SG_INPUT, SG_DEBUG, "User released key " << k
<< " with modifiers " << modifiers);
if (b.last_state) {
const binding_list_t &bindings = _find_key_bindings(k, modifiers);
for (unsigned int i = 0; i < bindings.size(); i++)
bindings[i]->fire();
b.last_state = 0;
}
}
}
void
FGInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
int modifiers = fgGetKeyModifiers();
mouse &m = _mouse_bindings[0];
mouse_mode &mode = m.modes[m.current_mode];
// Let the property manager know.
if (b >= 0 && b < MAX_MOUSE_BUTTONS)
m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
// Pass on to PUI and the panel if
// requested, and return if one of
// them consumes the event.
if (updown != MOUSE_BUTTON_DOWN) {
// Execute the mouse up event in any case, may be we should
// stop processing here?
while (!_activePickCallbacks[b].empty()) {
_activePickCallbacks[b].front()->buttonReleased();
_activePickCallbacks[b].pop_front();
}
}
if (mode.pass_through) {
// The pu stuff seems to need that. May be it does opengl picking ...
fgMakeCurrent();
if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
return;
else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
globals->get_current_panel()->getVisibility() &&
globals->get_current_panel()->doMouseAction(b, updown, x, y))
return;
else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
return;
else {
// pui didn't want the click event so compute a
// scenegraph intersection point corresponding to the mouse click
if (updown == MOUSE_BUTTON_DOWN) {
// Get the list of hit callbacks. Take the first callback that
// accepts the mouse button press and ignore the rest of them
// That is they get sorted by distance and by scenegraph depth.
// The nearest one is the first one and the deepest
// (the most specialized one in the scenegraph) is the first.
std::vector<SGSceneryPick> pickList;
if (FGRenderer::pick(x, y, pickList, ea)) {
std::vector<SGSceneryPick>::const_iterator i;
for (i = pickList.begin(); i != pickList.end(); ++i) {
if (i->callback->buttonPressed(b, i->info)) {
_activePickCallbacks[b].push_back(i->callback);
return;
}
}
}
}
}
}
// OK, PUI and the panel didn't want the click
if (b >= MAX_MOUSE_BUTTONS) {
SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
<< " where only " << MAX_MOUSE_BUTTONS << " expected");
return;
}
_update_button(m.modes[m.current_mode].buttons[b], modifiers, 0 != updown, x, y);
}
void
FGInput::doMouseMotion (int x, int y)
{
// Don't call fgGetKeyModifiers() here, until we are using a
// toolkit that supports getting the mods from outside a key
// callback. Glut doesn't.
int modifiers = KEYMOD_NONE;
int xsize = fgGetInt("/sim/startup/xsize", 800);
int ysize = fgGetInt("/sim/startup/ysize", 600);
mouse &m = _mouse_bindings[0];
if (m.current_mode < 0 || m.current_mode >= m.nModes) {
m.x = x;
m.y = y;
return;
}
mouse_mode &mode = m.modes[m.current_mode];
// Pass on to PUI if requested, and return
// if PUI consumed the event.
if (mode.pass_through && puMouse(x, y)) {
m.x = x;
m.y = y;
return;
}
// OK, PUI didn't want the event,
// so we can play with it.
if (x != m.x) {
int delta = x - m.x;
for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
}
if (y != m.y) {
int delta = y - m.y;
for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
}
// Constrain the mouse if requested
if (mode.constrained) {
bool need_warp = false;
if (x <= (xsize * .25) || x >= (xsize * .75)) {
x = int(xsize * .5);
need_warp = true;
}
if (y <= (ysize * .25) || y >= (ysize * .75)) {
y = int(ysize * .5);
need_warp = true;
}
if (need_warp)
fgWarpMouse(x, y);
}
if (m.x != x)
fgSetInt("/devices/status/mice/mouse/x", m.x = x);
if (m.y != y)
fgSetInt("/devices/status/mice/mouse/y", m.y = y);
}
void
FGInput::_scan_joystick_dir(SGPath *path, SGPropertyNode* node, int *index)
{
ulDir *dir = ulOpenDir(path->c_str());
if (dir) {
ulDirEnt* dent;
while ((dent = ulReadDir(dir)) != 0) {
if (dent->d_name[0] == '.')
continue;
SGPath p(path->str());
p.append(dent->d_name);
_scan_joystick_dir(&p, node, index);
}
ulCloseDir(dir);
} else if (path->extension() == "xml") {
SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path->str());
SGPropertyNode *n = node->getChild("js-named", (*index)++, true);
readProperties(path->str(), n);
n->setStringValue("source", path->c_str());
}
}
void
FGInput::_init_joystick ()
{
jsInit();
// TODO: zero the old bindings first.
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
// read all joystick xml files into /input/joysticks/js_named[1000++]
SGPath path(globals->get_fg_root());
path.append("Input/Joysticks");
int js_named_index = 1000;
_scan_joystick_dir(&path, js_nodes, &js_named_index);
// build name->node map with each <name> (reverse order)
map<string, SGPropertyNode_ptr> jsmap;
vector<SGPropertyNode_ptr> js_named = js_nodes->getChildren("js-named");
for (int k = (int)js_named.size() - 1; k >= 0; k--) {
SGPropertyNode *n = js_named[k];
vector<SGPropertyNode_ptr> names = n->getChildren("name");
if (names.size() && (n->getChildren("axis").size() || n->getChildren("button").size()))
for (unsigned int j = 0; j < names.size(); j++)
jsmap[names[j]->getStringValue()] = n;
}
// set up js[] nodes
for (int i = 0; i < MAX_JOYSTICKS; i++) {
jsJoystick * js = new jsJoystick(i);
_joystick_bindings[i].js = js;
if (js->notWorking()) {
SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
continue;
}
const char * name = js->getName();
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
if (js_node) {
SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
} else {
SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
SGPropertyNode_ptr named;
if ((named = jsmap[name])) {
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
} else if ((named = jsmap["default"])) {
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
<< "\"\nUsing default: \"" << source << '"');
} else {
throw sg_throwable(string("No joystick configuration file with "
"<name>default</name> entry found!"));
}
js_node = js_nodes->getChild("js", i, true);
copyProperties(named, js_node);
js_node->setStringValue("id", name);
}
}
// get rid of unused config nodes
js_nodes->removeChildren("js-named", false);
}
void
FGInput::_postinit_keyboard()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
_module = "__kbd";
SGPropertyNode * key_nodes = fgGetNode("/input/keyboard");
if (key_nodes == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No key bindings (/input/keyboard)!!");
key_nodes = fgGetNode("/input/keyboard", true);
}
FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
vector<SGPropertyNode_ptr> nasal = key_nodes->getChildren("nasal");
for (unsigned int j = 0; j < nasal.size(); j++) {
nasal[j]->setStringValue("module", _module.c_str());
nasalsys->handleCommand(nasal[j]);
}
vector<SGPropertyNode_ptr> keys = key_nodes->getChildren("key");
for (unsigned int i = 0; i < keys.size(); i++) {
int index = keys[i]->getIndex();
SG_LOG(SG_INPUT, SG_DEBUG, "Binding key " << index);
_key_bindings[index].bindings->clear();
_key_bindings[index].is_repeatable = keys[i]->getBoolValue("repeatable");
_key_bindings[index].last_state = 0;
_read_bindings(keys[i], _key_bindings[index].bindings, KEYMOD_NONE);
}
}
void
FGInput::_postinit_joystick()
{
FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
SGPropertyNode *js_nodes = fgGetNode("/input/joysticks");
for (int i = 0; i < MAX_JOYSTICKS; i++) {
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
jsJoystick *js = _joystick_bindings[i].js;
if (!js_node || js->notWorking())
continue;
#ifdef WIN32
JOYCAPS jsCaps ;
joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
unsigned int nbuttons = jsCaps.wNumButtons;
if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
#else
unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
#endif
int naxes = js->getNumAxes();
if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
_joystick_bindings[i].naxes = naxes;
_joystick_bindings[i].nbuttons = nbuttons;
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
// Set up range arrays
float minRange[MAX_JOYSTICK_AXES];
float maxRange[MAX_JOYSTICK_AXES];
float center[MAX_JOYSTICK_AXES];
// Initialize with default values
js->getMinRange(minRange);
js->getMaxRange(maxRange);
js->getCenter(center);
// Allocate axes and buttons
_joystick_bindings[i].axes = new axis[naxes];
_joystick_bindings[i].buttons = new button[nbuttons];
//
// Initialize nasal groups.
//
ostringstream str;
str << "__js" << i;
_module = str.str();
nasalsys->createModule(_module.c_str(), _module.c_str(), "", 0);
vector<SGPropertyNode_ptr> nasal = js_node->getChildren("nasal");
unsigned int j;
for (j = 0; j < nasal.size(); j++) {
nasal[j]->setStringValue("module", _module.c_str());
nasalsys->handleCommand(nasal[j]);
}
//
// Initialize the axes.
//
vector<SGPropertyNode_ptr> axes = js_node->getChildren("axis");
size_t nb_axes = axes.size();
for (j = 0; j < nb_axes; j++ ) {
const SGPropertyNode * axis_node = axes[j];
const SGPropertyNode * num_node = axis_node->getChild("number");
int n_axis = axis_node->getIndex();
if (num_node != 0) {
n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
// Silently ignore platforms that are not specified within the
// <number></number> section
if (n_axis < 0)
continue;
}
if (n_axis >= naxes) {
SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
continue;
}
axis &a = _joystick_bindings[i].axes[n_axis];
js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
_read_bindings(axis_node, a.bindings, KEYMOD_NONE);
// Initialize the virtual axis buttons.
_init_button(axis_node->getChild("low"), a.low, "low");
a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
_init_button(axis_node->getChild("high"), a.high, "high");
a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
a.last_dt = 0.0;
}
//
// Initialize the buttons.
//
vector<SGPropertyNode_ptr> buttons = js_node->getChildren("button");
char buf[32];
for (j = 0; j < buttons.size() && j < nbuttons; j++) {
const SGPropertyNode * button_node = buttons[j];
const SGPropertyNode * num_node = button_node->getChild("number");
size_t n_but = button_node->getIndex();
if (num_node != 0) {
n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
}
if (n_but >= nbuttons) {
SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
continue;
}
sprintf(buf, "%d", n_but);
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but);
_init_button(button_node,
_joystick_bindings[i].buttons[n_but],
buf);
// get interval-sec property
button &b = _joystick_bindings[i].buttons[n_but];
if (button_node != 0) {
b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
b.last_dt = 0.0;
}
}
js->setMinRange(minRange);
js->setMaxRange(maxRange);
js->setCenter(center);
}
}
//
// Map of all known cursor names
// This used to contain all the Glut cursors, but those are
// not defined by other toolkits. It now supports only the cursor
// images we actually use, in the interest of portability. Someday,
// it would be cool to write an OpenGL cursor renderer, with the
// cursors defined as textures referenced in the property tree. This
// list could then be eliminated. -Andy
//
static struct {
const char * name;
int cursor;
} mouse_cursor_map[] = {
{ "none", MOUSE_CURSOR_NONE },
{ "inherit", MOUSE_CURSOR_POINTER },
{ "wait", MOUSE_CURSOR_WAIT },
{ "crosshair", MOUSE_CURSOR_CROSSHAIR },
{ "left-right", MOUSE_CURSOR_LEFTRIGHT },
{ 0, 0 }
};
void
FGInput::_init_mouse ()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
_module = "";
SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
if (mouse_nodes == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
mouse_nodes = fgGetNode("/input/mice", true);
}
int j;
for (int i = 0; i < MAX_MICE; i++) {
SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
mouse &m = _mouse_bindings[i];
// Grab node pointers
char buf[64];
sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i);
m.mode_node = fgGetNode(buf);
if (m.mode_node == NULL) {
m.mode_node = fgGetNode(buf, true);
m.mode_node->setIntValue(0);
}
for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j);
m.mouse_button_nodes[j] = fgGetNode(buf, true);
m.mouse_button_nodes[j]->setBoolValue(false);
}
// Read all the modes
m.nModes = mouse_node->getIntValue("mode-count", 1);
m.modes = new mouse_mode[m.nModes];
for (int j = 0; j < m.nModes; j++) {
int k;
// Read the mouse cursor for this mode
SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
const char * cursor_name =
mode_node->getStringValue("cursor", "inherit");
m.modes[j].cursor = MOUSE_CURSOR_POINTER;
for (k = 0; mouse_cursor_map[k].name != 0; k++) {
if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
m.modes[j].cursor = mouse_cursor_map[k].cursor;
break;
}
}
// Read other properties for this mode
m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
// Read the button bindings for this mode
m.modes[j].buttons = new button[MAX_MOUSE_BUTTONS];
char buf[32];
for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
sprintf(buf, "mouse button %d", k);
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
_init_button(mode_node->getChild("button", k),
m.modes[j].buttons[k],
buf);
}
// Read the axis bindings for this mode
_read_bindings(mode_node->getChild("x-axis", 0, true),
m.modes[j].x_bindings,
KEYMOD_NONE);
_read_bindings(mode_node->getChild("y-axis", 0, true),
m.modes[j].y_bindings,
KEYMOD_NONE);
}
}
}
void
FGInput::_init_button (const SGPropertyNode * node,
button &b,
const string name)
{
if (node == 0) {
SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name);
} else {
b.is_repeatable = node->getBoolValue("repeatable", b.is_repeatable);
// Get the bindings for the button
_read_bindings(node, b.bindings, KEYMOD_NONE);
}
}
void
FGInput::_update_keyboard ()
{
// no-op
}
void
FGInput::_update_joystick (double dt)
{
int modifiers = KEYMOD_NONE; // FIXME: any way to get the real ones?
int buttons;
// float js_val, diff;
float axis_values[MAX_JOYSTICK_AXES];
int i;
int j;
for ( i = 0; i < MAX_JOYSTICKS; i++) {
jsJoystick * js = _joystick_bindings[i].js;
if (js == 0 || js->notWorking())
continue;
js->read(&buttons, axis_values);
// Fire bindings for the axes.
for ( j = 0; j < _joystick_bindings[i].naxes; j++) {
axis &a = _joystick_bindings[i].axes[j];
// Do nothing if the axis position
// is unchanged; only a change in
// position fires the bindings.
if (fabs(axis_values[j] - a.last_value) > a.tolerance) {
// SG_LOG(SG_INPUT, SG_DEBUG, "Axis " << j << " has moved");
a.last_value = axis_values[j];
// SG_LOG(SG_INPUT, SG_DEBUG, "There are "
// << a.bindings[modifiers].size() << " bindings");
for (unsigned int k = 0; k < a.bindings[modifiers].size(); k++)
a.bindings[modifiers][k]->fire(axis_values[j]);
}
// do we have to emulate axis buttons?
a.last_dt += dt;
if(a.last_dt >= a.interval_sec) {
if (a.low.bindings[modifiers].size())
_update_button(_joystick_bindings[i].axes[j].low,
modifiers,
axis_values[j] < a.low_threshold,
-1, -1);
if (a.high.bindings[modifiers].size())
_update_button(_joystick_bindings[i].axes[j].high,
modifiers,
axis_values[j] > a.high_threshold,
-1, -1);
a.last_dt -= a.interval_sec;
}
}
// Fire bindings for the buttons.
for (j = 0; j < _joystick_bindings[i].nbuttons; j++) {
button &b = _joystick_bindings[i].buttons[j];
b.last_dt += dt;
if(b.last_dt >= b.interval_sec) {
_update_button(_joystick_bindings[i].buttons[j],
modifiers,
(buttons & (1 << j)) > 0,
-1, -1);
b.last_dt -= b.interval_sec;
}
}
}
}
void
FGInput::_update_mouse ( double dt )
{
mouse &m = _mouse_bindings[0];
int mode = m.mode_node->getIntValue();
if (mode != m.current_mode) {
m.current_mode = mode;
m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
if (mode >= 0 && mode < m.nModes) {
fgSetMouseCursor(m.modes[mode].cursor);
m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
fgWarpMouse(m.x, m.y);
} else {
SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
fgSetMouseCursor(MOUSE_CURSOR_POINTER);
}
}
if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) {
if ( m.x != m.save_x || m.y != m.save_y ) {
m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
fgSetMouseCursor(m.modes[mode].cursor);
} else {
m.timeout -= dt;
if ( m.timeout <= 0.0 ) {
fgSetMouseCursor(MOUSE_CURSOR_NONE);
m.timeout = 0.0;
}
}
m.save_x = m.x;
m.save_y = m.y;
}
// handle repeatable mouse press events
std::map<int, std::list<SGSharedPtr<SGPickCallback> > >::iterator mi;
for (mi = _activePickCallbacks.begin();
mi != _activePickCallbacks.end(); ++mi) {
std::list<SGSharedPtr<SGPickCallback> >::iterator li;
for (li = mi->second.begin(); li != mi->second.end(); ++li) {
(*li)->update(dt);
}
}
}
void
FGInput::_update_button (button &b, int modifiers, bool pressed,
int x, int y)
{
if (pressed) {
// The press event may be repeated.
if (!b.last_state || b.is_repeatable) {
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
for (unsigned int k = 0; k < b.bindings[modifiers].size(); k++) {
b.bindings[modifiers][k]->fire(x, y);
}
}
} else {
// The release event is never repeated.
if (b.last_state) {
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
for (unsigned int k = 0; k < b.bindings[modifiers|KEYMOD_RELEASED].size(); k++)
b.bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y);
}
}
b.last_state = pressed;
}
void
FGInput::_read_bindings (const SGPropertyNode * node,
binding_list_t * binding_list,
int modifiers)
{
SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
vector<SGPropertyNode_ptr> bindings = node->getChildren("binding");
for (unsigned int i = 0; i < bindings.size(); i++) {
const char *cmd = bindings[i]->getStringValue("command");
SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd);
if (!strcmp(cmd, "nasal") && !_module.empty())
bindings[i]->setStringValue("module", _module.c_str());
binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props()));
}
// Read nested bindings for modifiers
if (node->getChild("mod-up") != 0)
_read_bindings(node->getChild("mod-up"), binding_list,
modifiers|KEYMOD_RELEASED);
if (node->getChild("mod-shift") != 0)
_read_bindings(node->getChild("mod-shift"), binding_list,
modifiers|KEYMOD_SHIFT);
if (node->getChild("mod-ctrl") != 0)
_read_bindings(node->getChild("mod-ctrl"), binding_list,
modifiers|KEYMOD_CTRL);
if (node->getChild("mod-alt") != 0)
_read_bindings(node->getChild("mod-alt"), binding_list,
modifiers|KEYMOD_ALT);
}
const FGInput::binding_list_t&
FGInput::_find_key_bindings (unsigned int k, int modifiers)
{
unsigned char kc = (unsigned char)k;
button &b = _key_bindings[k];
// Try it straight, first.
if (b.bindings[modifiers].size() > 0)
return b.bindings[modifiers];
// Alt-Gr is CTRL+ALT
else if (modifiers&(KEYMOD_CTRL|KEYMOD_ALT))
return _find_key_bindings(k, modifiers&~(KEYMOD_CTRL|KEYMOD_ALT));
// Try removing the control modifier
// for control keys.
else if ((modifiers&KEYMOD_CTRL) && iscntrl(kc))
return _find_key_bindings(k, modifiers&~KEYMOD_CTRL);
// Try removing shift modifier
// for upper case or any punctuation
// (since different keyboards will
// shift different punctuation types)
else if ((modifiers&KEYMOD_SHIFT) && (isupper(kc) || ispunct(kc)))
return _find_key_bindings(k, modifiers&~KEYMOD_SHIFT);
// Try removing alt modifier for
// high-bit characters.
else if ((modifiers&KEYMOD_ALT) && k >= 128 && k < 256)
return _find_key_bindings(k, modifiers&~KEYMOD_ALT);
// Give up and return the empty vector.
else
return b.bindings[modifiers];
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::button.
////////////////////////////////////////////////////////////////////////
FGInput::button::button ()
: is_repeatable(false),
interval_sec(0),
last_dt(0),
last_state(0)
{
}
FGInput::button::~button ()
{
// FIXME: memory leak
// for (int i = 0; i < KEYMOD_MAX; i++)
// for (int j = 0; i < bindings[i].size(); j++)
// delete bindings[i][j];
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::axis.
////////////////////////////////////////////////////////////////////////
FGInput::axis::axis ()
: last_value(9999999),
tolerance(0.002),
low_threshold(-0.9),
high_threshold(0.9),
interval_sec(0),
last_dt(0)
{
}
FGInput::axis::~axis ()
{
// for (int i = 0; i < KEYMOD_MAX; i++)
// for (int j = 0; i < bindings[i].size(); j++)
// delete bindings[i][j];
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::joystick.
////////////////////////////////////////////////////////////////////////
FGInput::joystick::joystick ()
: jsnum(0),
js(0),
naxes(0),
nbuttons(0),
axes(0),
buttons(0)
{
}
FGInput::joystick::~joystick ()
{
// delete js;
delete[] axes;
delete[] buttons;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::mouse_mode
////////////////////////////////////////////////////////////////////////
FGInput::mouse_mode::mouse_mode ()
: cursor(MOUSE_CURSOR_POINTER),
constrained(false),
pass_through(false),
buttons(0)
{
}
FGInput::mouse_mode::~mouse_mode ()
{
// FIXME: memory leak
// for (int i = 0; i < KEYMOD_MAX; i++) {
// int j;
// for (j = 0; i < x_bindings[i].size(); j++)
// delete bindings[i][j];
// for (j = 0; j < y_bindings[i].size(); j++)
// delete bindings[i][j];
// }
delete [] buttons;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::mouse
////////////////////////////////////////////////////////////////////////
FGInput::mouse::mouse ()
: x(-1),
y(-1),
nModes(1),
current_mode(0),
modes(0)
{
}
FGInput::mouse::~mouse ()
{
delete [] modes;
}
////////////////////////////////////////////////////////////////////////
// Implementation of OS callbacks.
////////////////////////////////////////////////////////////////////////
void keyHandler(int key, int keymod, int mousex, int mousey)
{
if((keymod & KEYMOD_RELEASED) == 0)
if(puKeyboard(key, PU_DOWN))
return;
if(default_input)
default_input->doKey(key, keymod, mousex, mousey);
}
void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
if(default_input)
default_input->doMouseClick(button, updown, x, y, mainWindow, ea);
}
void mouseMotionHandler(int x, int y)
{
if (default_input != 0)
default_input->doMouseMotion(x, y);
}