- 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();
-
- fgRegisterKeyHandler(keyHandler);
- fgRegisterMouseClickHandler(mouseClickHandler);
- fgRegisterMouseMotionHandler(mouseMotionHandler);
-}
-
-void
-FGInput::reinit ()
-{
- init();
-}
-
-void
-FGInput::postinit ()
-{
- _postinit_joystick();
-}
-
-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;
- }
-
- 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;
- } else {
- if (k >= 1 && k <= 26) {
- if (_key_bindings[k + '@'].last_state)
- doKey(k + '@', KEYMOD_RELEASED, x, y);
- if (_key_bindings[k + '`'].last_state)
- doKey(k + '`', KEYMOD_RELEASED, x, y);
- } else if (k >= 'A' && k <= 'Z') {
- if (_key_bindings[k - '@'].last_state)
- doKey(k - '@', KEYMOD_RELEASED, x, y);
- if (_key_bindings[tolower(k)].last_state)
- doKey(tolower(k), KEYMOD_RELEASED, x, y);
- } else if (k >= 'a' && k <= 'z') {
- if (_key_bindings[k - '`'].last_state)
- doKey(k - '`', KEYMOD_RELEASED, x, y);
- if (_key_bindings[toupper(k)].last_state)
- doKey(toupper(k), KEYMOD_RELEASED, x, y);
- }
- }
- }
-}
-
-void
-FGInput::doMouseClick (int b, int updown, int x, int y)
-{
- 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 (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;
- else {
- // pui and the panel didn't want the click event so compute a
- // terrain intersection point corresponding to the mouse click
- // and be happy.
- FGScenery* scenery = globals->get_scenery();
- sgdVec3 start, dir, hit;
- if (MOUSE_BUTTON_DOWN && FGRenderer::getPickInfo(start, dir, x, y) &&
- scenery->get_cart_ground_intersection(start, dir, hit)) {
-
- Point3D geod = sgCartToGeod(Point3D(hit[0], hit[1], hit[2]));
-
- SGPropertyNode *c = fgGetNode("/sim/input/click", true);
- c->setDoubleValue("longitude-deg", geod.lon() * SGD_RADIANS_TO_DEGREES);
- c->setDoubleValue("latitude-deg", geod.lat() * SGD_RADIANS_TO_DEGREES);
- c->setDoubleValue("elevation-m", geod.elev());
- c->setDoubleValue("elevation-ft", geod.elev() * SG_METER_TO_FEET);
- }
- }
- }
-
- // 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 <= 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)
- fgWarpMouse(x, y);
- }
- m.x = x;
- m.y = y;
-}
-
-void
-FGInput::_init_keyboard ()
-{
- SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
- _module[0] = 0;
- 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");
- _key_bindings[index].last_state = 0;
- _read_bindings(keys[i], _key_bindings[index].bindings, KEYMOD_NONE);
- }
-}
-
-
-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_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;