Advanced input subsystem - Step2: Split up current input subsystem
- encapsulate code into classes - create separate subsystem for keyboard, mouse and joystick - group new subsystems into subsystemgroup "input"
This commit is contained in:
parent
aac4bec5c7
commit
aea9c750f3
17 changed files with 1972 additions and 1421 deletions
75
src/Input/FGButton.cxx
Normal file
75
src/Input/FGButton.cxx
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// FGButton.cxx -- a simple button/key wrapper class
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGButton.hxx"
|
||||||
|
|
||||||
|
FGButton::FGButton ()
|
||||||
|
: is_repeatable(false),
|
||||||
|
interval_sec(0),
|
||||||
|
last_dt(0),
|
||||||
|
last_state(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGButton::~FGButton ()
|
||||||
|
{
|
||||||
|
// bindings is a list of SGSharedPtr<SGBindings>
|
||||||
|
// no cleanup required
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FGButton::init( const SGPropertyNode * node, const string name, string & module )
|
||||||
|
{
|
||||||
|
if (node == 0) {
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name);
|
||||||
|
} else {
|
||||||
|
is_repeatable = node->getBoolValue("repeatable", is_repeatable);
|
||||||
|
// Get the bindings for the button
|
||||||
|
read_bindings( node, bindings, KEYMOD_NONE, module );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGButton::update( int modifiers, bool pressed, int x, int y)
|
||||||
|
{
|
||||||
|
if (pressed) {
|
||||||
|
// The press event may be repeated.
|
||||||
|
if (!last_state || is_repeatable) {
|
||||||
|
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
|
||||||
|
for (unsigned int k = 0; k < bindings[modifiers].size(); k++) {
|
||||||
|
bindings[modifiers][k]->fire(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The release event is never repeated.
|
||||||
|
if (last_state) {
|
||||||
|
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
|
||||||
|
for (unsigned int k = 0; k < bindings[modifiers|KEYMOD_RELEASED].size(); k++)
|
||||||
|
bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_state = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
44
src/Input/FGButton.hxx
Normal file
44
src/Input/FGButton.hxx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// FGButton.hxx -- a simple button/key wrapper class
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef FGBUTTON_H
|
||||||
|
#define FGBUTTON_H
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include <Main/fg_os.hxx>
|
||||||
|
|
||||||
|
class FGButton : public FGCommonInput {
|
||||||
|
public:
|
||||||
|
FGButton();
|
||||||
|
virtual ~FGButton();
|
||||||
|
void init( const SGPropertyNode * node, const string name, string & module );
|
||||||
|
void update( int modifiers, bool pressed, int x = -1, int y = -1);
|
||||||
|
bool is_repeatable;
|
||||||
|
float interval_sec;
|
||||||
|
float last_dt;
|
||||||
|
int last_state;
|
||||||
|
binding_list_t bindings[KEYMOD_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
71
src/Input/FGCommonInput.cxx
Normal file
71
src/Input/FGCommonInput.cxx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// FGCommonInput.cxx -- common functions for all Input subsystems
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
#include <Main/fg_os.hxx>
|
||||||
|
|
||||||
|
void FGCommonInput::read_bindings (const SGPropertyNode * node, binding_list_t * binding_list, int modifiers, string & module )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
|
||||||
|
vector<SGPropertyNode_ptr> bindings = node->getChildren("binding");
|
||||||
|
string nasal = "nasal";
|
||||||
|
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 (nasal.compare(cmd) == 0 && !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, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-shift") != 0)
|
||||||
|
read_bindings(node->getChild("mod-shift"), binding_list,
|
||||||
|
modifiers|KEYMOD_SHIFT, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-ctrl") != 0)
|
||||||
|
read_bindings(node->getChild("mod-ctrl"), binding_list,
|
||||||
|
modifiers|KEYMOD_CTRL, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-alt") != 0)
|
||||||
|
read_bindings(node->getChild("mod-alt"), binding_list,
|
||||||
|
modifiers|KEYMOD_ALT, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-meta") != 0)
|
||||||
|
read_bindings(node->getChild("mod-meta"), binding_list,
|
||||||
|
modifiers|KEYMOD_META, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-super") != 0)
|
||||||
|
read_bindings(node->getChild("mod-super"), binding_list,
|
||||||
|
modifiers|KEYMOD_SUPER, module);
|
||||||
|
|
||||||
|
if (node->getChild("mod-hyper") != 0)
|
||||||
|
read_bindings(node->getChild("mod-hyper"), binding_list,
|
||||||
|
modifiers|KEYMOD_HYPER, module);
|
||||||
|
}
|
||||||
|
|
51
src/Input/FGCommonInput.hxx
Normal file
51
src/Input/FGCommonInput.hxx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// FGCommonInput.hxx -- common functions for all Input subsystems
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef FGCOMMONINPUT_H
|
||||||
|
#define FGCOMMONINPUT_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <simgear/structure/SGBinding.hxx>
|
||||||
|
|
||||||
|
#if defined( UL_WIN32 )
|
||||||
|
#define TGT_PLATFORM "windows"
|
||||||
|
#elif defined ( UL_MAC_OSX )
|
||||||
|
#define TGT_PLATFORM "mac"
|
||||||
|
#else
|
||||||
|
#define TGT_PLATFORM "unix"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class FGCommonInput {
|
||||||
|
public:
|
||||||
|
typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
read all "binding" nodes directly under the specified base node and fill the
|
||||||
|
vector of SGBinding supplied in binding_list. Reads all the mod-xxx bindings and
|
||||||
|
add the corresponding SGBindings.
|
||||||
|
*/
|
||||||
|
static void read_bindings (const SGPropertyNode * base, binding_list_t * binding_list, int modifiers, string & module );
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
77
src/Input/FGDeviceConfigurationMap.cxx
Normal file
77
src/Input/FGDeviceConfigurationMap.cxx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// FGDeviceConfigurationMap.cxx -- a map to access xml device configuration
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGDeviceConfigurationMap.hxx"
|
||||||
|
#include <simgear/props/props_io.hxx>
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
|
||||||
|
FGDeviceConfigurationMap::FGDeviceConfigurationMap( const char * relative_path, SGPropertyNode_ptr aBase, const char * aChildname ) :
|
||||||
|
base(aBase),
|
||||||
|
childname(aChildname)
|
||||||
|
{
|
||||||
|
SGPath path(globals->get_fg_root());
|
||||||
|
path.append( relative_path );
|
||||||
|
|
||||||
|
int index = 1000;
|
||||||
|
scan_dir( path, &index);
|
||||||
|
|
||||||
|
vector<SGPropertyNode_ptr> childNodes = base->getChildren(childname);
|
||||||
|
for (int k = (int)childNodes.size() - 1; k >= 0; k--) {
|
||||||
|
SGPropertyNode *n = childNodes[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++)
|
||||||
|
(*this)[names[j]->getStringValue()] = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGDeviceConfigurationMap::~FGDeviceConfigurationMap()
|
||||||
|
{
|
||||||
|
base->removeChildren( childname );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDeviceConfigurationMap::scan_dir( SGPath & path, 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_dir(p, index);
|
||||||
|
}
|
||||||
|
ulCloseDir(dir);
|
||||||
|
|
||||||
|
} else if (path.extension() == "xml") {
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str());
|
||||||
|
SGPropertyNode_ptr n = base->getChild(childname, (*index)++, true);
|
||||||
|
readProperties(path.str(), n);
|
||||||
|
n->setStringValue("source", path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
48
src/Input/FGDeviceConfigurationMap.hxx
Normal file
48
src/Input/FGDeviceConfigurationMap.hxx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// FGDeviceConfigurationMap.hxx -- a map to access xml device configuration
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef _FGDEVICECONFIGURATIONMAP_HXX
|
||||||
|
#define _FGDEVICECONFIGURATIONMAP_HXX
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# error This library requires C++
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
using std::map;
|
||||||
|
|
||||||
|
class FGDeviceConfigurationMap : public map<string,SGPropertyNode_ptr> {
|
||||||
|
public:
|
||||||
|
FGDeviceConfigurationMap ( const char * relative_path, SGPropertyNode_ptr base, const char * childname );
|
||||||
|
virtual ~FGDeviceConfigurationMap();
|
||||||
|
private:
|
||||||
|
void scan_dir( SGPath & path, int *index);
|
||||||
|
SGPropertyNode_ptr base;
|
||||||
|
const char * childname;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
203
src/Input/FGEventInput.cxx
Normal file
203
src/Input/FGEventInput.cxx
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
// FGEventInput.cxx -- handle event driven input devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started July 2009.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
//
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGEventInput.hxx"
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <simgear/io/sg_file.hxx>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
static inline bool StartsWith( string & s, const char * cp )
|
||||||
|
{
|
||||||
|
return s.compare( 0, strlen(cp), cp ) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGInputEvent * FGInputEvent::NewObject( SGPropertyNode_ptr node )
|
||||||
|
{
|
||||||
|
string name = node->getStringValue( "name" );
|
||||||
|
if( StartsWith( name, "button-" ) )
|
||||||
|
return new FGButtonEvent( node );
|
||||||
|
|
||||||
|
if( StartsWith( name, "rel-" ) )
|
||||||
|
return new FGAxisEvent( node );
|
||||||
|
|
||||||
|
if( StartsWith( name, "abs-" ) )
|
||||||
|
return new FGAxisEvent( node );
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGInputEvent::FGInputEvent( SGPropertyNode_ptr node ) :
|
||||||
|
lastDt(0.0)
|
||||||
|
{
|
||||||
|
name = node->getStringValue( "name" );
|
||||||
|
desc = node->getStringValue( "desc" );
|
||||||
|
intervalSec = node->getDoubleValue("interval-sec",0.0);
|
||||||
|
string module = "event";
|
||||||
|
read_bindings( node, bindings, KEYMOD_NONE, module );
|
||||||
|
}
|
||||||
|
|
||||||
|
FGInputEvent::~FGInputEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGInputEvent::fire( FGEventData & eventData )
|
||||||
|
{
|
||||||
|
lastDt += eventData.dt;
|
||||||
|
if( lastDt >= intervalSec ) {
|
||||||
|
|
||||||
|
for( binding_list_t::iterator it = bindings[KEYMOD_NONE].begin(); it != bindings[KEYMOD_NONE].end(); it++ )
|
||||||
|
(*it)->fire( eventData.value, 1.0 );
|
||||||
|
|
||||||
|
lastDt -= intervalSec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGAxisEvent::FGAxisEvent( SGPropertyNode_ptr node ) :
|
||||||
|
FGInputEvent( node )
|
||||||
|
{
|
||||||
|
tolerance = node->getDoubleValue("tolerance", 0.002);
|
||||||
|
minRange = node->getDoubleValue("min-range", -1024.0);
|
||||||
|
maxRange = node->getDoubleValue("max-range", 1024.0);
|
||||||
|
center = node->getDoubleValue("center", 0.0);
|
||||||
|
deadband = node->getDoubleValue("dead-band", 0.0);
|
||||||
|
lowThreshold = node->getDoubleValue("low-threshold", -0.9);
|
||||||
|
highThreshold = node->getDoubleValue("high-threshold", 0.9);
|
||||||
|
lastValue = 9999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGAxisEvent::fire( FGEventData & eventData )
|
||||||
|
{
|
||||||
|
if (fabs( eventData.value - lastValue) < tolerance)
|
||||||
|
return;
|
||||||
|
lastValue = eventData.value;
|
||||||
|
FGInputEvent::fire( eventData );
|
||||||
|
}
|
||||||
|
|
||||||
|
FGButtonEvent::FGButtonEvent( SGPropertyNode_ptr node ) :
|
||||||
|
FGInputEvent( node )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGButtonEvent::fire( FGEventData & eventData )
|
||||||
|
{
|
||||||
|
FGInputEvent::fire( eventData );
|
||||||
|
}
|
||||||
|
|
||||||
|
FGInputDevice::~FGInputDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGInputDevice::HandleEvent( FGEventData & eventData )
|
||||||
|
{
|
||||||
|
string eventName = TranslateEventName( eventData );
|
||||||
|
cout << GetName() << " has event " << eventName << endl;
|
||||||
|
if( handledEvents.count( eventName ) > 0 ) {
|
||||||
|
handledEvents[ eventName ]->fire( eventData );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGInputDevice::SetName( string name )
|
||||||
|
{
|
||||||
|
this->name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * FGEventInput::PROPERTY_ROOT = "/input/event";
|
||||||
|
|
||||||
|
FGEventInput::FGEventInput() :
|
||||||
|
configMap( "Input/Event", fgGetNode( PROPERTY_ROOT, true ), "device-named" )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGEventInput::~FGEventInput()
|
||||||
|
{
|
||||||
|
for( map<int,FGInputDevice*>::iterator it = input_devices.begin(); it != input_devices.end(); it++ )
|
||||||
|
delete (*it).second;
|
||||||
|
input_devices.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGEventInput::init( )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing event bindings");
|
||||||
|
SGPropertyNode * base = fgGetNode("/input/event", true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGEventInput::postinit ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
||||||
|
{
|
||||||
|
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
|
||||||
|
SGPropertyNode_ptr deviceNode = NULL;
|
||||||
|
|
||||||
|
// look for configuration in the device map
|
||||||
|
if( configMap.count( inputDevice->GetName() ) > 0 ) {
|
||||||
|
// found - copy to /input/event/device[n]
|
||||||
|
|
||||||
|
// find a free index
|
||||||
|
unsigned index;
|
||||||
|
for( index = 0; index < 1000; index++ )
|
||||||
|
if( (deviceNode = baseNode->getNode( "device", index, false ) ) == NULL )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( index == 1000 ) {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "To many event devices - ignoring " << inputDevice->GetName() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create this node
|
||||||
|
deviceNode = baseNode->getNode( "device", index, true );
|
||||||
|
|
||||||
|
// and copy the properties from the configuration tree
|
||||||
|
copyProperties( configMap[ inputDevice->GetName() ], deviceNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( deviceNode == NULL ) {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "No configuration found for device " << inputDevice->GetName() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<SGPropertyNode_ptr> eventNodes = deviceNode->getChildren( "event" );
|
||||||
|
for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ ) {
|
||||||
|
FGInputEvent * p = FGInputEvent::NewObject( *it );
|
||||||
|
if( p == NULL ) {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "Unhandled event/name in " << inputDevice->GetName() );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inputDevice->AddHandledEvent( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// add nodes for the last event:
|
||||||
|
// last-event/name [string]
|
||||||
|
// last-event/value [double]
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputDevice->Open();
|
||||||
|
input_devices[ deviceNode->getIndex() ] = inputDevice;
|
||||||
|
}
|
||||||
|
catch( ... ) {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "can't open InputDevice " << inputDevice->GetName() );
|
||||||
|
}
|
||||||
|
}
|
171
src/Input/FGEventInput.hxx
Normal file
171
src/Input/FGEventInput.hxx
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
// FGEventInput.hxx -- handle event driven input devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started July 2009
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
//
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef __FGEVENTINPUT_HXX
|
||||||
|
#define __FGEVENTINPUT_HXX
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include "FGButton.hxx"
|
||||||
|
#include "FGDeviceConfigurationMap.hxx"
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A base class for event data.
|
||||||
|
*/
|
||||||
|
struct FGEventData {
|
||||||
|
FGEventData( double aValue, double aDt ) : value(aValue), dt(aDt) {}
|
||||||
|
double value;
|
||||||
|
double dt;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A wrapper class for a configured event.
|
||||||
|
*
|
||||||
|
* <event>
|
||||||
|
* <desc>Change the view pitch</desc>
|
||||||
|
* <name>rel-x-rotate</name>
|
||||||
|
* <binding>
|
||||||
|
* <command>property-adjust</command>
|
||||||
|
* <property>sim/current-view/pitch-offset-deg</property>
|
||||||
|
* <factor type="double">0.01</factor>
|
||||||
|
* <min type="double">-90.0</min>
|
||||||
|
* <max type="double">90.0</max>
|
||||||
|
* <wrap type="bool">false</wrap>
|
||||||
|
* </binding>
|
||||||
|
* </event>
|
||||||
|
*/
|
||||||
|
class FGInputEvent : public SGReferenced,FGCommonInput {
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Constructor for the class. The arg node shall point
|
||||||
|
* to the property corresponding to the <event> node
|
||||||
|
*/
|
||||||
|
FGInputEvent( SGPropertyNode_ptr node );
|
||||||
|
virtual ~FGInputEvent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dispatch the event value through all bindings
|
||||||
|
*/
|
||||||
|
virtual void fire( FGEventData & eventData );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* access for the name property
|
||||||
|
*/
|
||||||
|
string GetName() const { return name; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* access for the description property
|
||||||
|
*/
|
||||||
|
string GetDescription() const { return desc; }
|
||||||
|
|
||||||
|
static FGInputEvent * NewObject( SGPropertyNode_ptr node );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/* A more or less meaningfull description of the event */
|
||||||
|
string desc;
|
||||||
|
|
||||||
|
/* One of the predefined names of the event */
|
||||||
|
string name;
|
||||||
|
|
||||||
|
/* A list of SGBinding objects */
|
||||||
|
binding_list_t bindings[KEYMOD_MAX];
|
||||||
|
|
||||||
|
double lastDt;
|
||||||
|
double intervalSec;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FGButtonEvent : public FGInputEvent {
|
||||||
|
public:
|
||||||
|
FGButtonEvent( SGPropertyNode_ptr node );
|
||||||
|
virtual void fire( FGEventData & eventData );
|
||||||
|
};
|
||||||
|
|
||||||
|
class FGAxisEvent : public FGInputEvent {
|
||||||
|
public:
|
||||||
|
FGAxisEvent( SGPropertyNode_ptr node );
|
||||||
|
protected:
|
||||||
|
virtual void fire( FGEventData & eventData );
|
||||||
|
double tolerance;
|
||||||
|
double minRange;
|
||||||
|
double maxRange;
|
||||||
|
double center;
|
||||||
|
double deadband;
|
||||||
|
double lowThreshold;
|
||||||
|
double highThreshold;
|
||||||
|
double lastValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef class SGSharedPtr<FGInputEvent> FGInputEvent_ptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A abstract class implementing basic functionality of input devices for
|
||||||
|
* all operating systems. This is the base class for the O/S-specific
|
||||||
|
* implementation of input device handlers
|
||||||
|
*/
|
||||||
|
class FGInputDevice : public SGReferenced {
|
||||||
|
public:
|
||||||
|
FGInputDevice() {}
|
||||||
|
FGInputDevice( string aName ) : name(aName) {}
|
||||||
|
|
||||||
|
virtual ~FGInputDevice();
|
||||||
|
|
||||||
|
virtual void Open() = 0;
|
||||||
|
virtual void Close() = 0;
|
||||||
|
virtual const char * TranslateEventName( FGEventData & eventData ) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void SetName( string name );
|
||||||
|
string & GetName() { return name; }
|
||||||
|
|
||||||
|
void HandleEvent( FGEventData & eventData );
|
||||||
|
void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
|
||||||
|
if( handledEvents.count( handledEvent->GetName() ) == 0 )
|
||||||
|
handledEvents[handledEvent->GetName()] = handledEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
map<string,FGInputEvent_ptr> handledEvents;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<FGInputDevice> FGInputDevice_ptr;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Subsystem for the event input device
|
||||||
|
*/
|
||||||
|
class FGEventInput : public SGSubsystem,FGCommonInput {
|
||||||
|
public:
|
||||||
|
FGEventInput();
|
||||||
|
virtual ~FGEventInput();
|
||||||
|
virtual void init();
|
||||||
|
virtual void postinit();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static const char * PROPERTY_ROOT;
|
||||||
|
|
||||||
|
void AddDevice( FGInputDevice * inputDevice );
|
||||||
|
map<int,FGInputDevice*> input_devices;
|
||||||
|
FGDeviceConfigurationMap configMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
306
src/Input/FGJoystickInput.cxx
Normal file
306
src/Input/FGJoystickInput.cxx
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
// FGJoystickInput.cxx -- handle user input from joystick devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGJoystickInput.hxx"
|
||||||
|
#include "FGDeviceConfigurationMap.hxx"
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <Scripting/NasalSys.hxx>
|
||||||
|
|
||||||
|
FGJoystickInput::axis::axis ()
|
||||||
|
: last_value(9999999),
|
||||||
|
tolerance(0.002),
|
||||||
|
low_threshold(-0.9),
|
||||||
|
high_threshold(0.9),
|
||||||
|
interval_sec(0),
|
||||||
|
last_dt(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGJoystickInput::axis::~axis ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGJoystickInput::joystick::joystick ()
|
||||||
|
: jsnum(0),
|
||||||
|
js(0),
|
||||||
|
naxes(0),
|
||||||
|
nbuttons(0),
|
||||||
|
axes(0),
|
||||||
|
buttons(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGJoystickInput::joystick::~joystick ()
|
||||||
|
{
|
||||||
|
// delete js? why not?
|
||||||
|
// delete js;
|
||||||
|
delete[] axes;
|
||||||
|
delete[] buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FGJoystickInput::FGJoystickInput()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGJoystickInput::~FGJoystickInput()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FGJoystickInput::init()
|
||||||
|
{
|
||||||
|
jsInit();
|
||||||
|
// TODO: zero the old bindings first.
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
|
||||||
|
SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
|
||||||
|
|
||||||
|
FGDeviceConfigurationMap configMap("Input/Joysticks", js_nodes, "js-named");
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_JOYSTICKS; i++) {
|
||||||
|
jsJoystick * js = new jsJoystick(i);
|
||||||
|
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 = configMap[name])) {
|
||||||
|
string source = named->getStringValue("source", "user defined");
|
||||||
|
SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
|
||||||
|
|
||||||
|
} else if ((named = configMap["default"])) {
|
||||||
|
string source = named->getStringValue("source", "user defined");
|
||||||
|
SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
|
||||||
|
<< "\"\nUsing default: \"" << source << '"');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
js_node = js_nodes->getChild("js", i, true);
|
||||||
|
copyProperties(named, js_node);
|
||||||
|
js_node->setStringValue("id", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGJoystickInput::postinit()
|
||||||
|
{
|
||||||
|
FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
|
||||||
|
SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_JOYSTICKS; i++) {
|
||||||
|
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
|
||||||
|
jsJoystick *js = 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;
|
||||||
|
bindings[i].naxes = naxes;
|
||||||
|
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
|
||||||
|
bindings[i].axes = new axis[naxes];
|
||||||
|
bindings[i].buttons = new FGButton[nbuttons];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize nasal groups.
|
||||||
|
//
|
||||||
|
ostringstream str;
|
||||||
|
str << "__js" << i;
|
||||||
|
string 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 = 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, module );
|
||||||
|
|
||||||
|
// Initialize the virtual axis buttons.
|
||||||
|
a.low.init(axis_node->getChild("low"), "low", module );
|
||||||
|
a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
|
||||||
|
|
||||||
|
a.high.init(axis_node->getChild("high"), "high", module );
|
||||||
|
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);
|
||||||
|
bindings[i].buttons[n_but].init(button_node, buf, module );
|
||||||
|
|
||||||
|
// get interval-sec property
|
||||||
|
FGButton &b = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGJoystickInput::update( double dt )
|
||||||
|
{
|
||||||
|
float axis_values[MAX_JOYSTICK_AXES];
|
||||||
|
int modifiers = fgGetKeyModifiers();
|
||||||
|
int buttons;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_JOYSTICKS; i++) {
|
||||||
|
|
||||||
|
jsJoystick * js = bindings[i].js;
|
||||||
|
if (js == 0 || js->notWorking())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
js->read(&buttons, axis_values);
|
||||||
|
|
||||||
|
// Fire bindings for the axes.
|
||||||
|
for (int j = 0; j < bindings[i].naxes; j++) {
|
||||||
|
axis &a = 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) {
|
||||||
|
a.last_value = axis_values[j];
|
||||||
|
for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
|
||||||
|
a.bindings[KEYMOD_NONE][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())
|
||||||
|
bindings[i].axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
|
||||||
|
|
||||||
|
if (a.high.bindings[modifiers].size())
|
||||||
|
bindings[i].axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
|
||||||
|
|
||||||
|
a.last_dt -= a.interval_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire bindings for the buttons.
|
||||||
|
for (int j = 0; j < bindings[i].nbuttons; j++) {
|
||||||
|
FGButton &b = bindings[i].buttons[j];
|
||||||
|
b.last_dt += dt;
|
||||||
|
if(b.last_dt >= b.interval_sec) {
|
||||||
|
bindings[i].buttons[j].update( modifiers, (buttons & (1 << j)) > 0 );
|
||||||
|
b.last_dt -= b.interval_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
88
src/Input/FGJoystickInput.hxx
Normal file
88
src/Input/FGJoystickInput.hxx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// FGJoystickInput.hxx -- handle user input from joystick devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef _FGJOYSTICKINPUT_HXX
|
||||||
|
#define _FGJOYSTICKINPUT_HXX
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# error This library requires C++
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include "FGButton.hxx"
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
#include <plib/js.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Joystick Input Class
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
class FGJoystickInput : public SGSubsystem,FGCommonInput {
|
||||||
|
public:
|
||||||
|
FGJoystickInput();
|
||||||
|
virtual ~FGJoystickInput();
|
||||||
|
|
||||||
|
virtual void init();
|
||||||
|
virtual void postinit();
|
||||||
|
virtual void update( double dt );
|
||||||
|
|
||||||
|
static const int MAX_JOYSTICKS = 10;
|
||||||
|
static const int MAX_JOYSTICK_AXES = _JS_MAX_AXES;
|
||||||
|
static const int MAX_JOYSTICK_BUTTONS = 32;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Settings for a single joystick axis.
|
||||||
|
*/
|
||||||
|
struct axis {
|
||||||
|
axis ();
|
||||||
|
virtual ~axis ();
|
||||||
|
float last_value;
|
||||||
|
float tolerance;
|
||||||
|
binding_list_t bindings[KEYMOD_MAX];
|
||||||
|
float low_threshold;
|
||||||
|
float high_threshold;
|
||||||
|
FGButton low;
|
||||||
|
FGButton high;
|
||||||
|
float interval_sec;
|
||||||
|
double last_dt;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for a joystick.
|
||||||
|
*/
|
||||||
|
struct joystick {
|
||||||
|
joystick ();
|
||||||
|
virtual ~joystick ();
|
||||||
|
int jsnum;
|
||||||
|
jsJoystick * js;
|
||||||
|
int naxes;
|
||||||
|
int nbuttons;
|
||||||
|
axis * axes;
|
||||||
|
FGButton * buttons;
|
||||||
|
};
|
||||||
|
joystick bindings[MAX_JOYSTICKS];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
259
src/Input/FGKeyboardInput.cxx
Normal file
259
src/Input/FGKeyboardInput.cxx
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
// FGKeyboardInput.cxx -- handle user input from keyboard devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGKeyboardInput.hxx"
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <Scripting/NasalSys.hxx>
|
||||||
|
#include <plib/pu.h>
|
||||||
|
|
||||||
|
static int getModifiers ()
|
||||||
|
{
|
||||||
|
return fgGetKeyModifiers() >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModShift ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_SHIFT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModCtrl ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_CTRL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModAlt ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_ALT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModMeta ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_META) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModSuper ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_SUPER) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getModHyper ()
|
||||||
|
{
|
||||||
|
return (fgGetKeyModifiers() & KEYMOD_HYPER) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGKeyboardInput * FGKeyboardInput::keyboardInput = NULL;
|
||||||
|
|
||||||
|
FGKeyboardInput::FGKeyboardInput() :
|
||||||
|
_key_event(fgGetNode("/devices/status/keyboard/event", true))
|
||||||
|
{
|
||||||
|
if( keyboardInput == NULL )
|
||||||
|
keyboardInput = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGKeyboardInput::~FGKeyboardInput()
|
||||||
|
{
|
||||||
|
if( keyboardInput == this )
|
||||||
|
keyboardInput = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FGKeyboardInput::init()
|
||||||
|
{
|
||||||
|
fgRegisterKeyHandler(keyHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGKeyboardInput::postinit()
|
||||||
|
{
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
|
||||||
|
string module = "__kbd";
|
||||||
|
SGPropertyNode * key_nodes = fgGetNode("/input/keyboard");
|
||||||
|
if (key_nodes == NULL) {
|
||||||
|
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);
|
||||||
|
if( index >= MAX_KEYS ) {
|
||||||
|
SG_LOG(SG_INPUT, SG_WARN, "Key binding " << index << " out of range");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings[index].bindings->clear();
|
||||||
|
bindings[index].is_repeatable = keys[i]->getBoolValue("repeatable");
|
||||||
|
bindings[index].last_state = 0;
|
||||||
|
read_bindings(keys[i], bindings[index].bindings, KEYMOD_NONE, module );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGKeyboardInput::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 FGKeyboardInput::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 FGKeyboardInput::update( double dt )
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
const FGCommonInput::binding_list_t & FGKeyboardInput::_find_key_bindings (unsigned int k, int modifiers)
|
||||||
|
{
|
||||||
|
unsigned char kc = (unsigned char)k;
|
||||||
|
FGButton &b = 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGKeyboardInput::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 >> 1;
|
||||||
|
_key_pressed = (modifiers & KEYMOD_RELEASED) == 0;
|
||||||
|
_key_shift = getModShift();
|
||||||
|
_key_ctrl = getModCtrl();
|
||||||
|
_key_alt = getModAlt();
|
||||||
|
_key_meta = getModMeta();
|
||||||
|
_key_super = getModSuper();
|
||||||
|
_key_hyper = getModHyper();
|
||||||
|
_key_event->fireValueChanged();
|
||||||
|
if (_key_code < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
k = _key_code;
|
||||||
|
modifiers = _key_modifiers << 1;
|
||||||
|
if (!_key_pressed)
|
||||||
|
modifiers |= KEYMOD_RELEASED;
|
||||||
|
FGButton &b = 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 FGKeyboardInput::keyHandler(int key, int keymod, int mousex, int mousey)
|
||||||
|
{
|
||||||
|
if((keymod & KEYMOD_RELEASED) == 0)
|
||||||
|
if(puKeyboard(key, PU_DOWN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(keyboardInput)
|
||||||
|
keyboardInput->doKey(key, keymod, mousex, mousey);
|
||||||
|
}
|
71
src/Input/FGKeyboardInput.hxx
Normal file
71
src/Input/FGKeyboardInput.hxx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// FGKeyboardInput.hxx -- handle user input from keyboard devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#ifndef _FGKEYBOARDINPUT_HXX
|
||||||
|
#define _FGKEYBOARDINPUT_HXX
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# error This library requires C++
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include "FGButton.hxx"
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Keyboard Input Class
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
class FGKeyboardInput : public SGSubsystem,FGCommonInput {
|
||||||
|
public:
|
||||||
|
FGKeyboardInput();
|
||||||
|
virtual ~FGKeyboardInput();
|
||||||
|
|
||||||
|
virtual void init();
|
||||||
|
virtual void postinit();
|
||||||
|
virtual void bind();
|
||||||
|
virtual void unbind();
|
||||||
|
virtual void update( double dt );
|
||||||
|
|
||||||
|
static const int MAX_KEYS = 1024;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const binding_list_t& _find_key_bindings (unsigned int k, int modifiers);
|
||||||
|
void doKey (int k, int modifiers, int x, int y);
|
||||||
|
|
||||||
|
static void keyHandler(int key, int keymod, int mousex, int mousey);
|
||||||
|
static FGKeyboardInput * keyboardInput;
|
||||||
|
FGButton bindings[MAX_KEYS];
|
||||||
|
SGPropertyNode_ptr _key_event;
|
||||||
|
int _key_code;
|
||||||
|
int _key_modifiers;
|
||||||
|
bool _key_pressed;
|
||||||
|
bool _key_shift;
|
||||||
|
bool _key_ctrl;
|
||||||
|
bool _key_alt;
|
||||||
|
bool _key_meta;
|
||||||
|
bool _key_super;
|
||||||
|
bool _key_hyper;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
363
src/Input/FGMouseInput.cxx
Normal file
363
src/Input/FGMouseInput.cxx
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
// FGMouseInput.cxx -- handle user input from mouse devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
#include "FGMouseInput.hxx"
|
||||||
|
|
||||||
|
|
||||||
|
void ActivePickCallbacks::init( int b, const osgGA::GUIEventAdapter* ea )
|
||||||
|
{
|
||||||
|
// 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(pickList, ea)) {
|
||||||
|
std::vector<SGSceneryPick>::const_iterator i;
|
||||||
|
for (i = pickList.begin(); i != pickList.end(); ++i) {
|
||||||
|
if (i->callback->buttonPressed(b, i->info)) {
|
||||||
|
(*this)[b].push_back(i->callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivePickCallbacks::update( double dt )
|
||||||
|
{
|
||||||
|
// handle repeatable mouse press events
|
||||||
|
for( iterator mi = begin(); mi != end(); ++mi ) {
|
||||||
|
std::list<SGSharedPtr<SGPickCallback> >::iterator li;
|
||||||
|
for (li = mi->second.begin(); li != mi->second.end(); ++li) {
|
||||||
|
(*li)->update(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <plib/pu.h>
|
||||||
|
#include <Model/panelnode.hxx>
|
||||||
|
#include <Cockpit/panel.hxx>
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Mouse Input Implementation
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const FGMouseInput::MouseCursorMap FGMouseInput::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 }
|
||||||
|
};
|
||||||
|
|
||||||
|
FGMouseInput * FGMouseInput::mouseInput = NULL;
|
||||||
|
|
||||||
|
FGMouseInput::FGMouseInput()
|
||||||
|
{
|
||||||
|
if( mouseInput == NULL )
|
||||||
|
mouseInput = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGMouseInput::~FGMouseInput()
|
||||||
|
{
|
||||||
|
if( mouseInput == this )
|
||||||
|
mouseInput = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGMouseInput::init()
|
||||||
|
{
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
|
||||||
|
string 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 = 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 FGButton[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);
|
||||||
|
m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf, module );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the axis bindings for this mode
|
||||||
|
read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
|
||||||
|
read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fgRegisterMouseClickHandler(mouseClickHandler);
|
||||||
|
fgRegisterMouseMotionHandler(mouseMotionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGMouseInput::update ( double dt )
|
||||||
|
{
|
||||||
|
mouse &m = 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 );
|
||||||
|
if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
activePickCallbacks.update( dt );
|
||||||
|
}
|
||||||
|
|
||||||
|
FGMouseInput::mouse::mouse ()
|
||||||
|
: x(-1),
|
||||||
|
y(-1),
|
||||||
|
save_x(-1),
|
||||||
|
save_y(-1),
|
||||||
|
nModes(1),
|
||||||
|
current_mode(0),
|
||||||
|
modes(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGMouseInput::mouse::~mouse ()
|
||||||
|
{
|
||||||
|
delete [] modes;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGMouseInput::mouse_mode::mouse_mode ()
|
||||||
|
: cursor(MOUSE_CURSOR_POINTER),
|
||||||
|
constrained(false),
|
||||||
|
pass_through(false),
|
||||||
|
buttons(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGMouseInput::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
|
||||||
|
{
|
||||||
|
int modifiers = fgGetKeyModifiers();
|
||||||
|
|
||||||
|
mouse &m = 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) {
|
||||||
|
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) {
|
||||||
|
activePickCallbacks.init( b, ea );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGMouseInput::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 = 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 FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
|
||||||
|
{
|
||||||
|
if(mouseInput)
|
||||||
|
mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGMouseInput::mouseMotionHandler(int x, int y)
|
||||||
|
{
|
||||||
|
if (mouseInput != 0)
|
||||||
|
mouseInput->doMouseMotion(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
123
src/Input/FGMouseInput.hxx
Normal file
123
src/Input/FGMouseInput.hxx
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// FGMouseInput.hxx -- handle user input from mouse devices
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started August 2009
|
||||||
|
// Based on work from David Megginson, started May 2001.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
|
// 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$
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FGMOUSEINPUT_HXX
|
||||||
|
#define _FGMOUSEINPUT_HXX
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# error This library requires C++
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "FGCommonInput.hxx"
|
||||||
|
#include "FGButton.hxx"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
#include <simgear/scene/util/SGPickCallback.hxx>
|
||||||
|
#include <Main/renderer.hxx>
|
||||||
|
/**
|
||||||
|
* List of currently pressed mouse button events
|
||||||
|
*/
|
||||||
|
class ActivePickCallbacks : public std::map<int, std::list<SGSharedPtr<SGPickCallback> > > {
|
||||||
|
public:
|
||||||
|
void update( double dt );
|
||||||
|
void init( int b, const osgGA::GUIEventAdapter* ea );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Mouse Input Class
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
class FGMouseInput : public SGSubsystem,FGCommonInput {
|
||||||
|
public:
|
||||||
|
FGMouseInput();
|
||||||
|
virtual ~FGMouseInput();
|
||||||
|
|
||||||
|
virtual void init();
|
||||||
|
virtual void update( double dt );
|
||||||
|
|
||||||
|
static const int MAX_MICE = 1;
|
||||||
|
static const int MAX_MOUSE_BUTTONS = 8;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea);
|
||||||
|
void doMouseMotion (int x, int y);
|
||||||
|
static FGMouseInput * mouseInput;
|
||||||
|
static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
|
||||||
|
static void mouseMotionHandler(int x, int y);
|
||||||
|
|
||||||
|
ActivePickCallbacks activePickCallbacks;
|
||||||
|
/**
|
||||||
|
* Settings for a mouse mode.
|
||||||
|
*/
|
||||||
|
struct mouse_mode {
|
||||||
|
mouse_mode ();
|
||||||
|
virtual ~mouse_mode ();
|
||||||
|
int cursor;
|
||||||
|
bool constrained;
|
||||||
|
bool pass_through;
|
||||||
|
FGButton * buttons;
|
||||||
|
binding_list_t x_bindings[KEYMOD_MAX];
|
||||||
|
binding_list_t y_bindings[KEYMOD_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for a mouse.
|
||||||
|
*/
|
||||||
|
struct mouse {
|
||||||
|
mouse ();
|
||||||
|
virtual ~mouse ();
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int save_x;
|
||||||
|
int save_y;
|
||||||
|
SGPropertyNode_ptr mode_node;
|
||||||
|
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
|
||||||
|
int nModes;
|
||||||
|
int current_mode;
|
||||||
|
double timeout;
|
||||||
|
mouse_mode * modes;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
const static struct MouseCursorMap {
|
||||||
|
const char * name;
|
||||||
|
int cursor;
|
||||||
|
} mouse_cursor_map[];
|
||||||
|
|
||||||
|
mouse bindings[MAX_MICE];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,7 +2,12 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\"
|
||||||
|
|
||||||
noinst_LIBRARIES = libInput.a
|
noinst_LIBRARIES = libInput.a
|
||||||
|
|
||||||
libInput_a_SOURCES = input.cxx input.hxx
|
libInput_a_SOURCES = input.cxx input.hxx FGCommonInput.cxx FGCommonInput.hxx \
|
||||||
|
FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \
|
||||||
|
FGButton.cxx FGButton.hxx \
|
||||||
|
FGMouseInput.cxx FGMouseInput.hxx \
|
||||||
|
FGKeyboardInput.cxx FGKeyboardInput.hxx \
|
||||||
|
FGJoystickInput.cxx FGJoystickInput.hxx
|
||||||
|
|
||||||
bin_PROGRAMS = js_demo fgjs
|
bin_PROGRAMS = js_demo fgjs
|
||||||
|
|
||||||
|
|
1149
src/Input/input.cxx
1149
src/Input/input.cxx
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,10 @@
|
||||||
// input.hxx -- handle user input from various sources.
|
// input.hxx -- handle user input from various sources.
|
||||||
//
|
//
|
||||||
// Written by David Megginson, started May 2001.
|
// Written by David Megginson, started May 2001.
|
||||||
|
// Major redesign by Torsten Dreyer, started August 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2001 David Megginson, david@megginson.com
|
// Copyright (C) 2001 David Megginson, david@megginson.com
|
||||||
|
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License as
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
@ -28,40 +30,10 @@
|
||||||
# error This library requires C++
|
# error This library requires C++
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <plib/js.h>
|
|
||||||
#include <plib/ul.h>
|
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
#include <simgear/structure/SGBinding.hxx>
|
|
||||||
#include <simgear/props/condition.hxx>
|
|
||||||
#include <simgear/props/props.hxx>
|
|
||||||
#include <simgear/scene/util/SGSceneUserData.hxx>
|
|
||||||
|
|
||||||
#include <Main/fg_os.hxx>
|
|
||||||
#include <Main/fg_props.hxx>
|
|
||||||
#include <Main/globals.hxx>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using std::map;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined( UL_WIN32 )
|
|
||||||
#define TGT_PLATFORM "windows"
|
|
||||||
#elif defined ( UL_MAC_OSX )
|
|
||||||
#define TGT_PLATFORM "mac"
|
|
||||||
#else
|
|
||||||
#define TGT_PLATFORM "unix"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -76,7 +48,7 @@ using std::vector;
|
||||||
* keyboard, joystick, mouse, or even panel switches -- in a consistent
|
* keyboard, joystick, mouse, or even panel switches -- in a consistent
|
||||||
* way, and to allow users to rebind any of the actions at runtime.</p>
|
* way, and to allow users to rebind any of the actions at runtime.</p>
|
||||||
*/
|
*/
|
||||||
class FGInput : public SGSubsystem
|
class FGInput : public SGSubsystemGroup
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -89,259 +61,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual ~FGInput();
|
virtual ~FGInput();
|
||||||
|
|
||||||
//
|
|
||||||
// Implementation of SGSubsystem.
|
|
||||||
//
|
|
||||||
virtual void init ();
|
|
||||||
virtual void reinit ();
|
|
||||||
virtual void postinit ();
|
|
||||||
virtual void bind ();
|
|
||||||
virtual void unbind ();
|
|
||||||
virtual void update (double dt);
|
|
||||||
virtual void suspend ();
|
|
||||||
virtual void resume ();
|
|
||||||
virtual bool is_suspended () const;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control whether this is the default module to receive events.
|
|
||||||
*
|
|
||||||
* The first input module created will set itself as the default
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* @param status true if this should be the default module for
|
|
||||||
* events, false otherwise.
|
|
||||||
*/
|
|
||||||
virtual void makeDefault (bool status = true);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a single keystroke.
|
|
||||||
*
|
|
||||||
* @param k The integer key code, see Main/fg_os.hxx
|
|
||||||
* @param modifiers Modifier keys pressed (bitfield).
|
|
||||||
* @param x The mouse x position at the time of keypress.
|
|
||||||
* @param y The mouse y position at the time of keypress.
|
|
||||||
*/
|
|
||||||
virtual void doKey (int k, int modifiers, int x, int y);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a mouse click event.
|
|
||||||
*
|
|
||||||
* @param button The mouse button selected.
|
|
||||||
* @param updown Button status.
|
|
||||||
* @param x The X position of the mouse event, in screen coordinates.
|
|
||||||
* @param y The Y position of the mouse event, in screen coordinates.
|
|
||||||
*/
|
|
||||||
virtual void doMouseClick (int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle mouse motion.
|
|
||||||
*
|
|
||||||
* @param x The new mouse x position, in screen coordinates.
|
|
||||||
* @param y The new mouse y position, in screen coordinates.
|
|
||||||
*/
|
|
||||||
virtual void doMouseMotion (int x, int y);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Constants
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
MAX_KEYS = 1024,
|
|
||||||
MAX_JOYSTICKS = 10,
|
|
||||||
MAX_JOYSTICK_AXES = _JS_MAX_AXES,
|
|
||||||
MAX_JOYSTICK_BUTTONS = 32,
|
|
||||||
|
|
||||||
MAX_MICE = 1,
|
|
||||||
MAX_MOUSE_BUTTONS = 8
|
|
||||||
};
|
|
||||||
struct mouse;
|
|
||||||
friend struct mouse;
|
|
||||||
|
|
||||||
typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings for a key or button.
|
|
||||||
*/
|
|
||||||
struct button {
|
|
||||||
button ();
|
|
||||||
virtual ~button ();
|
|
||||||
bool is_repeatable;
|
|
||||||
float interval_sec;
|
|
||||||
float last_dt;
|
|
||||||
int last_state;
|
|
||||||
binding_list_t bindings[KEYMOD_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings for a single joystick axis.
|
|
||||||
*/
|
|
||||||
struct axis {
|
|
||||||
axis ();
|
|
||||||
virtual ~axis ();
|
|
||||||
float last_value;
|
|
||||||
float tolerance;
|
|
||||||
binding_list_t bindings[KEYMOD_MAX];
|
|
||||||
float low_threshold;
|
|
||||||
float high_threshold;
|
|
||||||
struct button low;
|
|
||||||
struct button high;
|
|
||||||
float interval_sec;
|
|
||||||
double last_dt;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings for a joystick.
|
|
||||||
*/
|
|
||||||
struct joystick {
|
|
||||||
joystick ();
|
|
||||||
virtual ~joystick ();
|
|
||||||
int jsnum;
|
|
||||||
jsJoystick * js;
|
|
||||||
int naxes;
|
|
||||||
int nbuttons;
|
|
||||||
axis * axes;
|
|
||||||
button * buttons;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings for a mouse mode.
|
|
||||||
*/
|
|
||||||
struct mouse_mode {
|
|
||||||
mouse_mode ();
|
|
||||||
virtual ~mouse_mode ();
|
|
||||||
int cursor;
|
|
||||||
bool constrained;
|
|
||||||
bool pass_through;
|
|
||||||
button * buttons;
|
|
||||||
binding_list_t x_bindings[KEYMOD_MAX];
|
|
||||||
binding_list_t y_bindings[KEYMOD_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings for a mouse.
|
|
||||||
*/
|
|
||||||
struct mouse {
|
|
||||||
mouse ();
|
|
||||||
virtual ~mouse ();
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int save_x;
|
|
||||||
int save_y;
|
|
||||||
SGPropertyNode_ptr mode_node;
|
|
||||||
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
|
|
||||||
int nModes;
|
|
||||||
int current_mode;
|
|
||||||
double timeout;
|
|
||||||
mouse_mode * modes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize joystick bindings.
|
|
||||||
*/
|
|
||||||
void _init_joystick ();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scan directory recursively for "named joystick" configuration files,
|
|
||||||
* and read them into /input/joysticks/js-named[index]++.
|
|
||||||
*/
|
|
||||||
void _scan_joystick_dir (SGPath *path, SGPropertyNode* node, int *index);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize mouse bindings.
|
|
||||||
*/
|
|
||||||
void _init_mouse ();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a single button.
|
|
||||||
*/
|
|
||||||
inline void _init_button (const SGPropertyNode * node,
|
|
||||||
button &b,
|
|
||||||
const string name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize key bindings, as well as those joystick parts that
|
|
||||||
* depend on a working Nasal subsystem.
|
|
||||||
*/
|
|
||||||
void _postinit_keyboard ();
|
|
||||||
void _postinit_joystick ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the keyboard.
|
|
||||||
*/
|
|
||||||
void _update_keyboard ();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the joystick.
|
|
||||||
*/
|
|
||||||
void _update_joystick (double dt);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the mouse.
|
|
||||||
*/
|
|
||||||
void _update_mouse (double dt);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a single button.
|
|
||||||
*/
|
|
||||||
inline void _update_button (button &b, int modifiers, bool pressed,
|
|
||||||
int x, int y);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read bindings and modifiers.
|
|
||||||
*/
|
|
||||||
void _read_bindings (const SGPropertyNode * node,
|
|
||||||
binding_list_t * binding_list,
|
|
||||||
int modifiers);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up the bindings for a key code.
|
|
||||||
*/
|
|
||||||
const binding_list_t& _find_key_bindings (unsigned int k,
|
|
||||||
int modifiers);
|
|
||||||
|
|
||||||
button _key_bindings[MAX_KEYS];
|
|
||||||
joystick _joystick_bindings[MAX_JOYSTICKS];
|
|
||||||
mouse _mouse_bindings[MAX_MICE];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Nasal module name/namespace.
|
|
||||||
*/
|
|
||||||
string _module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of currently pressed mouse button events
|
|
||||||
*/
|
|
||||||
std::map<int, std::list<SGSharedPtr<SGPickCallback> > > _activePickCallbacks;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Key listener interface.
|
|
||||||
*/
|
|
||||||
SGPropertyNode_ptr _key_event;
|
|
||||||
int _key_code;
|
|
||||||
int _key_modifiers;
|
|
||||||
bool _key_pressed;
|
|
||||||
bool _key_shift;
|
|
||||||
bool _key_ctrl;
|
|
||||||
bool _key_alt;
|
|
||||||
bool _key_meta;
|
|
||||||
bool _key_super;
|
|
||||||
bool _key_hyper;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _INPUT_HXX
|
#endif // _INPUT_HXX
|
||||||
|
|
Loading…
Reference in a new issue