1
0
Fork 0
flightgear/src/Input/input.cxx
andy d14bba8458 Fix for a premature deletion bug. The _arg SGPropertyNode* is passed
to handlers which might want to assign it to a SGPropertyNode_ptr for
reference counting (Nasal does, for instance, to prevent garbage
collector interactions).  If that smart pointer is then destroyed,
that will free this object while it is still live.

Simply use a SGPropertyNode_ptr here; the code ends up smaller as a
bonus, since FGBinding no longer has to deallocation for _arg.
2003-12-22 19:18:22 +00:00

1061 lines
29 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., 675 Mass Ave, Cambridge, MA 02139, 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 STL_FSTREAM
#include STL_STRING
#include <vector>
#include FG_GLUT_H
#include <plib/pu.h>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props.hxx>
#include <Aircraft/aircraft.hxx>
#include <Autopilot/auto_gui.hxx>
#include <Autopilot/newauto.hxx>
#include <Cockpit/hud.hxx>
#include <Cockpit/panel.hxx>
#include <Cockpit/panel_io.hxx>
#include <GUI/gui.h>
#include <Model/panelnode.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include "input.hxx"
SG_USING_STD(ifstream);
SG_USING_STD(string);
SG_USING_STD(vector);
////////////////////////////////////////////////////////////////////////
// Local variables.
////////////////////////////////////////////////////////////////////////
static FGInput * default_input = 0;
////////////////////////////////////////////////////////////////////////
// Implementation of FGBinding.
////////////////////////////////////////////////////////////////////////
FGBinding::FGBinding ()
: _command(0),
_arg(new SGPropertyNode),
_setting(0)
{
}
FGBinding::FGBinding (const SGPropertyNode * node)
: _command(0),
_arg(0),
_setting(0)
{
read(node);
}
void
FGBinding::read (const SGPropertyNode * node)
{
const SGPropertyNode * conditionNode = node->getChild("condition");
if (conditionNode != 0)
setCondition(sgReadCondition(globals->get_props(), conditionNode));
_command_name = node->getStringValue("command", "");
if (_command_name.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "No command supplied for binding.");
_command = 0;
return;
}
_arg = new SGPropertyNode;
_setting = 0;
copyProperties(node, _arg); // FIXME: don't use whole node!!!
}
void
FGBinding::fire () const
{
if (test()) {
if (_command == 0)
_command = globals->get_commands()->getCommand(_command_name);
if (_command == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No command attached to binding");
} else if (!(*_command)(_arg)) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command "
<< _command_name);
}
}
}
void
FGBinding::fire (double offset, double max) const
{
if (test()) {
_arg->setDoubleValue("offset", offset/max);
fire();
}
}
void
FGBinding::fire (double setting) const
{
if (test()) {
// A value is automatically added to
// the args
if (_setting == 0) // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true);
_setting->setDoubleValue(setting);
fire();
}
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput.
////////////////////////////////////////////////////////////////////////
FGInput::FGInput ()
{
if (default_input == 0)
default_input = this;
}
FGInput::~FGInput ()
{
if (default_input == this)
default_input = 0;
}
void
FGInput::init ()
{
_init_keyboard();
_init_joystick();
_init_mouse();
glutKeyboardFunc(GLUTkey);
glutKeyboardUpFunc(GLUTkeyup);
glutSpecialFunc(GLUTspecialkey);
glutSpecialUpFunc(GLUTspecialkeyup);
glutMouseFunc (GLUTmouse);
glutMotionFunc (GLUTmotion);
glutPassiveMotionFunc (GLUTmotion);
}
void
FGInput::update (double dt)
{
_update_keyboard();
_update_joystick(dt);
_update_mouse();
}
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;
}
button &b = _key_bindings[k];
// Key pressed.
if (modifiers&FG_MOD_UP == 0) {
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);
int max = bindings.size();
if (max > 0) {
for (int i = 0; i < max; i++)
bindings[i]->fire();
return;
}
}
}
// 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);
int max = bindings.size();
if (max > 0) {
for (int i = 0; i < max; i++)
bindings[i]->fire();
return;
}
}
}
// Use the old, default actions.
SG_LOG( SG_INPUT, SG_DEBUG, "(No user binding.)" );
if (modifiers&FG_MOD_UP)
return;
// everything after here will be removed sooner or later...
if (modifiers & FG_MOD_SHIFT) {
switch (k) {
case 72: // H key
HUD_brightkey( true );
return;
case 73: // I key
// Minimal Hud
fgHUDInit2(&current_aircraft);
return;
}
} else {
SG_LOG( SG_INPUT, SG_DEBUG, "" );
switch (k) {
case 104: // h key
HUD_masterswitch( true );
return;
case 105: // i key
fgHUDInit(&current_aircraft); // normal HUD
return;
// START SPECIALS
case 256+GLUT_KEY_F6: // F6 toggles Autopilot target location
if ( globals->get_autopilot()->get_HeadingMode() !=
FGAutopilot::FG_HEADING_WAYPOINT ) {
globals->get_autopilot()->set_HeadingMode(
FGAutopilot::FG_HEADING_WAYPOINT );
globals->get_autopilot()->set_HeadingEnabled( true );
} else {
globals->get_autopilot()->set_HeadingMode(
FGAutopilot::FG_TC_HEADING_LOCK );
}
return;
}
// END SPECIALS
}
}
void
FGInput::doMouseClick (int b, int updown, int x, int y)
{
int modifiers = FG_MOD_NONE; // FIXME: any way to get the real ones?
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 == GLUT_DOWN);
// Pass on to PUI and the panel if
// requested, and return if one of
// them consumes the event.
if (mode.pass_through) {
if (puMouse(b, updown, x, y))
return;
else if ((globals->get_current_panel() != 0) &&
globals->get_current_panel()->getVisibility() &&
globals->get_current_panel()->doMouseAction(b, updown, x, y))
return;
else if (fgHandle3DPanelMouseEvent(b, updown, x, y))
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)
{
int modifiers = FG_MOD_NONE; // FIXME: any way to get the real ones?
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)
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))
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 <= 0) {
x = xsize - 2;
need_warp = true;
} else if (x >= (xsize-1)) {
x = 1;
need_warp = true;
}
if (y <= 0) {
y = ysize - 2;
need_warp = true;
} else if (y >= (ysize-1)) {
y = 1;
need_warp = true;
}
if (need_warp)
glutWarpPointer(x, y);
}
m.x = x;
m.y = y;
}
void
FGInput::_init_keyboard ()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
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);
}
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");
_read_bindings(keys[i], _key_bindings[index].bindings, FG_MOD_NONE);
}
}
void
FGInput::_init_joystick ()
{
// TODO: zero the old bindings first.
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
SGPropertyNode * js_nodes = fgGetNode("/input/joysticks");
if (js_nodes == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No joystick bindings (/input/joysticks)!!");
js_nodes = fgGetNode("/input/joysticks", true);
}
for (int i = 0; i < MAX_JOYSTICKS; i++) {
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
if (js_node == 0) {
SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for joystick " << i);
js_node = js_nodes->getChild("js", i, true);
}
jsJoystick * js = new jsJoystick(i);
_joystick_bindings[i].js = js;
if (js->notWorking()) {
SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
continue;
} else {
bool found_js = false;
const char * name = js->getName();
SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \""
<< name << '"');
vector<SGPropertyNode_ptr> nodes = js_nodes->getChildren("js-named");
for (unsigned int i = 0; i < nodes.size(); i++) {
SGPropertyNode_ptr node = nodes[i];
vector<SGPropertyNode_ptr> name_nodes = node->getChildren("name");
for (unsigned int j = 0; j < name_nodes.size(); j++) {
const char * js_name = name_nodes[j]->getStringValue();
SG_LOG(SG_INPUT, SG_INFO, " Trying \"" << js_name << '"');
if (!strcmp(js_name, name)) {
SG_LOG(SG_INPUT, SG_INFO, " Found bindings");
js_node = node;
found_js = true;
break;
}
}
if (found_js)
break;
}
}
#ifdef WIN32
JOYCAPS jsCaps ;
joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
int nbuttons = jsCaps.wNumButtons;
if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
#else
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 the axes.
//
int j;
for (j = 0; j < naxes; j++) {
const SGPropertyNode * axis_node = js_node->getChild("axis", j);
if (axis_node == 0) {
SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for axis " << j);
axis_node = js_node->getChild("axis", j, true);
}
axis &a = _joystick_bindings[i].axes[j];
js->setDeadBand(j, axis_node->getDoubleValue("dead-band", 0.0));
a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
minRange[j] = axis_node->getDoubleValue("min-range", minRange[j]);
maxRange[j] = axis_node->getDoubleValue("max-range", maxRange[j]);
center[j] = axis_node->getDoubleValue("center", center[j]);
_read_bindings(axis_node, a.bindings, FG_MOD_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.
//
char buf[32];
for (j = 0; j < nbuttons; j++) {
sprintf(buf, "%d", j);
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << j);
_init_button(js_node->getChild("button", j),
_joystick_bindings[i].buttons[j],
buf);
// get interval-sec property
button &b = _joystick_bindings[i].buttons[j];
const SGPropertyNode * button_node = js_node->getChild("button", j);
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 GLUT cursor names
//
struct {
const char * name;
int cursor;
} mouse_cursor_map[] = {
{ "right-arrow", GLUT_CURSOR_RIGHT_ARROW },
{ "left-arrow", GLUT_CURSOR_LEFT_ARROW },
{ "info", GLUT_CURSOR_INFO },
{ "destroy", GLUT_CURSOR_DESTROY },
{ "help", GLUT_CURSOR_HELP },
{ "cycle", GLUT_CURSOR_CYCLE },
{ "spray", GLUT_CURSOR_SPRAY },
{ "wait", GLUT_CURSOR_WAIT },
{ "text", GLUT_CURSOR_TEXT },
{ "crosshair", GLUT_CURSOR_CROSSHAIR },
{ "up-down", GLUT_CURSOR_UP_DOWN },
{ "left-right", GLUT_CURSOR_LEFT_RIGHT },
{ "top-side", GLUT_CURSOR_TOP_SIDE },
{ "bottom-side", GLUT_CURSOR_BOTTOM_SIDE },
{ "left-side", GLUT_CURSOR_LEFT_SIDE },
{ "right-side", GLUT_CURSOR_RIGHT_SIDE },
{ "top-left-corner", GLUT_CURSOR_TOP_LEFT_CORNER },
{ "top-right-corner", GLUT_CURSOR_TOP_RIGHT_CORNER },
{ "bottom-right-corner", GLUT_CURSOR_BOTTOM_RIGHT_CORNER },
{ "bottom-left-corner", GLUT_CURSOR_BOTTOM_LEFT_CORNER },
{ "inherit", GLUT_CURSOR_INHERIT },
{ "none", GLUT_CURSOR_NONE },
{ "full-crosshair", GLUT_CURSOR_FULL_CROSSHAIR },
{ 0, 0 }
};
void
FGInput::_init_mouse ()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
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, 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 = GLUT_CURSOR_INHERIT;
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,
FG_MOD_NONE);
_read_bindings(mode_node->getChild("y-axis", 0, true),
m.modes[j].y_bindings,
FG_MOD_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, FG_MOD_NONE);
}
}
void
FGInput::_update_keyboard ()
{
// no-op
}
void
FGInput::_update_joystick (double dt)
{
int modifiers = FG_MOD_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");
SGPropertyNode node;
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 ()
{
mouse &m = _mouse_bindings[0];
int mode = m.mode_node->getIntValue();
if (mode != m.current_mode) {
m.current_mode = mode;
if (mode >= 0 && mode < m.nModes) {
glutSetCursor(m.modes[mode].cursor);
m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
glutWarpPointer(m.x, m.y);
} else {
SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
glutSetCursor(GLUT_CURSOR_INHERIT);
}
}
}
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|FG_MOD_UP].size(); k++)
b.bindings[modifiers|FG_MOD_UP][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++) {
SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding "
<< bindings[i]->getStringValue("command"));
binding_list[modifiers].push_back(new FGBinding(bindings[i]));
}
// Read nested bindings for modifiers
if (node->getChild("mod-up") != 0)
_read_bindings(node->getChild("mod-up"), binding_list,
modifiers|FG_MOD_UP);
if (node->getChild("mod-shift") != 0)
_read_bindings(node->getChild("mod-shift"), binding_list,
modifiers|FG_MOD_SHIFT);
if (node->getChild("mod-ctrl") != 0)
_read_bindings(node->getChild("mod-ctrl"), binding_list,
modifiers|FG_MOD_CTRL);
if (node->getChild("mod-alt") != 0)
_read_bindings(node->getChild("mod-alt"), binding_list,
modifiers|FG_MOD_ALT);
}
const vector<FGBinding *> &
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&(FG_MOD_CTRL|FG_MOD_ALT))
return _find_key_bindings(k, modifiers&~(FG_MOD_CTRL|FG_MOD_ALT));
// Try removing the control modifier
// for control keys.
else if ((modifiers&FG_MOD_CTRL) && iscntrl(kc))
return _find_key_bindings(k, modifiers&~FG_MOD_CTRL);
// Try removing shift modifier
// for upper case or any punctuation
// (since different keyboards will
// shift different punctuation types)
else if ((modifiers&FG_MOD_SHIFT) && (isupper(kc) || ispunct(kc)))
return _find_key_bindings(k, modifiers&~FG_MOD_SHIFT);
// Try removing alt modifier for
// high-bit characters.
else if ((modifiers&FG_MOD_ALT) && k >= 128 && k < 256)
return _find_key_bindings(k, modifiers&~FG_MOD_ALT);
// Give up and return the empty vector.
else
return b.bindings[modifiers];
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::button.
////////////////////////////////////////////////////////////////////////
FGInput::button::button ()
: is_repeatable(false),
last_state(-1)
{
}
FGInput::button::~button ()
{
// FIXME: memory leak
// for (int i = 0; i < FG_MOD_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)
{
}
FGInput::axis::~axis ()
{
// for (int i = 0; i < FG_MOD_MAX; i++)
// for (int j = 0; i < bindings[i].size(); j++)
// delete bindings[i][j];
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::joystick.
////////////////////////////////////////////////////////////////////////
FGInput::joystick::joystick ()
{
}
FGInput::joystick::~joystick ()
{
// delete js;
delete[] axes;
delete[] buttons;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput::mouse_mode
////////////////////////////////////////////////////////////////////////
FGInput::mouse_mode::mouse_mode ()
: cursor(GLUT_CURSOR_INHERIT),
constrained(false),
pass_through(false),
buttons(0)
{
}
FGInput::mouse_mode::~mouse_mode ()
{
// FIXME: memory leak
// for (int i = 0; i < FG_MOD_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 GLUT callbacks.
////////////////////////////////////////////////////////////////////////
/**
* Construct the modifiers.
*/
static inline int get_mods ()
{
int glut_modifiers = glutGetModifiers();
int modifiers = 0;
if (glut_modifiers & GLUT_ACTIVE_SHIFT)
modifiers |= FGInput::FG_MOD_SHIFT;
if (glut_modifiers & GLUT_ACTIVE_CTRL)
modifiers |= FGInput::FG_MOD_CTRL;
if (glut_modifiers & GLUT_ACTIVE_ALT)
modifiers |= FGInput::FG_MOD_ALT;
return modifiers;
}
////////////////////////////////////////////////////////////////////////
// GLUT C callbacks.
////////////////////////////////////////////////////////////////////////
void
GLUTkey(unsigned char k, int x, int y)
{
// Give PUI a chance to grab it first.
if (!puKeyboard(k, PU_DOWN)) {
if (default_input != 0)
default_input->doKey(k, get_mods(), x, y);
}
}
void
GLUTkeyup(unsigned char k, int x, int y)
{
if (default_input != 0)
default_input->doKey(k, get_mods()|FGInput::FG_MOD_UP, x, y);
}
void
GLUTspecialkey(int k, int x, int y)
{
// Give PUI a chance to grab it first.
if (!puKeyboard(k + PU_KEY_GLUT_SPECIAL_OFFSET, PU_DOWN)) {
if (default_input != 0)
default_input->doKey(k + 256, get_mods(), x, y);
}
}
void
GLUTspecialkeyup(int k, int x, int y)
{
if (default_input != 0)
default_input->doKey(k + 256, get_mods()|FGInput::FG_MOD_UP, x, y);
}
void
GLUTmouse (int button, int updown, int x, int y)
{
if (default_input != 0)
default_input->doMouseClick(button, updown, x, y);
}
void
GLUTmotion (int x, int y)
{
if (default_input != 0)
default_input->doMouseMotion(x, y);
}
// end of input.cxx