# 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 <fstream>
+#include <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 <Scenery/scenery.hxx>
#include <Main/renderer.hxx>
-#include <plib/ssg.h>
-#include <simgear/math/sg_geodesy.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);
+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);
static FGInput * default_input = 0;
-
\f
////////////////////////////////////////////////////////////////////////
-// Implementation of FGBinding.
+// Local functions.
////////////////////////////////////////////////////////////////////////
-FGBinding::FGBinding ()
- : _command(0),
- _arg(new SGPropertyNode),
- _setting(0)
+static int
+getModifiers ()
{
+ return fgGetKeyModifiers() >> 1;
}
-FGBinding::FGBinding (const SGPropertyNode * node)
- : _command(0),
- _arg(0),
- _setting(0)
+static bool
+getModShift ()
{
- read(node);
+ return bool(fgGetKeyModifiers() & KEYMOD_SHIFT);
}
-FGBinding::~FGBinding ()
+static bool
+getModCtrl ()
{
- _arg->getParent()->removeChild(_arg->getName(), _arg->getIndex(), false);
+ return bool(fgGetKeyModifiers() & KEYMOD_CTRL);
}
-void
-FGBinding::read (const SGPropertyNode * node)
+static bool
+getModAlt ()
{
- 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 = (SGPropertyNode *)node;
- _setting = 0;
+ return bool(fgGetKeyModifiers() & KEYMOD_ALT);
}
-void
-FGBinding::fire () const
+static bool
+getModMeta ()
{
- 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);
- }
- }
+ return bool(fgGetKeyModifiers() & KEYMOD_META);
}
-void
-FGBinding::fire (double offset, double max) const
+static bool
+getModSuper ()
{
- if (test()) {
- _arg->setDoubleValue("offset", offset/max);
- fire();
- }
+ return bool(fgGetKeyModifiers() & KEYMOD_SUPER);
}
-void
-FGBinding::fire (double setting) const
+static bool
+getModHyper ()
{
- 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();
- }
+ return bool(fgGetKeyModifiers() & KEYMOD_HYPER);
}
-
\f
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput.
////////////////////////////////////////////////////////////////////////
-FGInput::FGInput ()
+FGInput::FGInput () :
+ _key_event(fgGetNode("/devices/status/keyboard/event", true))
{
if (default_input == 0)
default_input = this;
void
FGInput::init ()
{
- _init_keyboard();
_init_joystick();
_init_mouse();
FGInput::postinit ()
{
_postinit_joystick();
+ _postinit_keyboard();
+}
+
+void
+FGInput::bind ()
+{
+ fgTie("/devices/status/keyboard", getModifiers);
+ 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);
+ fgTie("/devices/status/keyboard/hyper", getModHyper);
+
+ _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));
+ _key_event->tie("modifier/hyper", SGRawValuePointer<bool>(&_key_hyper));
+}
+
+void
+FGInput::unbind ()
+{
+ fgUntie("/devices/status/keyboard");
+ 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");
+ fgUntie("/devices/status/keyboard/hyper");
+
+ _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");
+ _key_event->untie("modifier/hyper");
}
void
return;
}
+ _key_code = k;
+ _key_modifiers = modifiers >> 1;
+ _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_hyper = bool(modifiers & KEYMOD_HYPER);
+ _key_event->fireValueChanged();
+ if (_key_code < 0)
+ return;
+
+ k = _key_code;
+ modifiers = _key_modifiers << 1;
+ if (!_key_pressed)
+ modifiers |= KEYMOD_RELEASED;
button &b = _key_bindings[k];
// Key pressed.
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)
+FGInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
int modifiers = fgGetKeyModifiers();
// 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) {
- if (puMouse(b, updown, x, y))
+ if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
return;
- else if ((globals->get_current_panel() != 0) &&
+ 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 (fgHandle3DPanelMouseEvent(b, updown, x, y))
+ else if (0 <= x && 0 <= y && 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 (updown == 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);
+ // 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
+ // 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");
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);
+ if (m.x != x)
+ fgSetInt("/devices/status/mice/mouse/x", m.x = x);
- _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);
- }
+ if (m.y != y)
+ fgSetInt("/devices/status/mice/mouse/y", m.y = y);
}
}
+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()
{
//
// Initialize nasal groups.
//
- string init;
- init = "this=\"" + string(js_node->getPath()) + "\"";
- sprintf(_module, "__js%d", i);
- nasalsys->createModule(_module, _module, init.c_str(), init.size());
+ 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);
+ nasal[j]->setStringValue("module", _module.c_str());
nasalsys->handleCommand(nasal[j]);
}
FGInput::_init_mouse ()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
- _module[0] = 0;
+ _module = "";
SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
if (mouse_nodes == 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
// 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++)
+ 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.
const char *cmd = bindings[i]->getStringValue("command");
SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd);
- if (!strcmp(cmd, "nasal") && _module[0])
- bindings[i]->setStringValue("module", _module);
- binding_list[modifiers].push_back(new FGBinding(bindings[i]));
+ 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-alt") != 0)
_read_bindings(node->getChild("mod-alt"), binding_list,
modifiers|KEYMOD_ALT);
+
+ if (node->getChild("mod-meta") != 0)
+ _read_bindings(node->getChild("mod-meta"), binding_list,
+ modifiers|KEYMOD_META);
+
+ if (node->getChild("mod-super") != 0)
+ _read_bindings(node->getChild("mod-super"), binding_list,
+ modifiers|KEYMOD_SUPER);
+
+ if (node->getChild("mod-hyper") != 0)
+ _read_bindings(node->getChild("mod-hyper"), binding_list,
+ modifiers|KEYMOD_HYPER);
}
-const vector<FGBinding *> &
+const FGInput::binding_list_t&
FGInput::_find_key_bindings (unsigned int k, int modifiers)
{
unsigned char kc = (unsigned char)k;
FGInput::mouse::mouse ()
: x(-1),
y(-1),
+ save_x(-1),
+ save_y(-1),
nModes(1),
current_mode(0),
modes(0)
default_input->doKey(key, keymod, mousex, mousey);
}
-void mouseClickHandler(int button, int updown, int x, int y)
+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);
+ default_input->doMouseClick(button, updown, x, y, mainWindow, ea);
}
void mouseMotionHandler(int x, int y)