1
0
Fork 0

MouseInput changes to support hover.

This adds the framework for platform cursor implementations; Windows and X11 to be done.
It also extend the mouse-input code to generate hover events suitable for driving tooltips.

Note there should be no visible functionality change from this commit, since everything
is inactive until fgdata changes are made.
This commit is contained in:
James Turner 2013-02-09 16:05:54 +00:00
parent 22a1c9b2af
commit f2c267f5d7
17 changed files with 712 additions and 255 deletions

View file

@ -20,6 +20,7 @@ set(SOURCES
FGColor.cxx
FileDialog.cxx
PUIFileDialog.cxx
MouseCursor.cxx
)
set(HEADERS
@ -39,11 +40,12 @@ set(HEADERS
FGColor.hxx
FileDialog.hxx
PUIFileDialog.hxx
MouseCursor.hxx
)
if (APPLE)
list(APPEND HEADERS FGCocoaMenuBar.hxx CocoaFileDialog.hxx)
list(APPEND SOURCES FGCocoaMenuBar.mm CocoaFileDialog.mm)
list(APPEND HEADERS FGCocoaMenuBar.hxx CocoaFileDialog.hxx CocoaMouseCursor.hxx)
list(APPEND SOURCES FGCocoaMenuBar.mm CocoaFileDialog.mm CocoaMouseCursor.mm)
endif()
flightgear_component(GUI "${SOURCES}" "${HEADERS}")

View file

@ -0,0 +1,28 @@
#ifndef FG_GUI_COCOA_MOUSE_CURSOR_HXX
#define FG_GUI_COCOA_MOUSE_CURSOR_HXX
#include <memory> // for auto_ptr
#include "MouseCursor.hxx"
class CocoaMouseCursor : public FGMouseCursor
{
public:
CocoaMouseCursor();
virtual ~CocoaMouseCursor();
virtual void setCursor(Cursor aCursor);
virtual void setCursorVisible(bool aVis);
virtual void hideCursorUntilMouseMove();
virtual void mouseMoved();
private:
class CocoaMouseCursorPrivate;
std::auto_ptr<CocoaMouseCursorPrivate> d;
};
#endif

View file

@ -0,0 +1,93 @@
#include "CocoaMouseCursor.hxx"
#include <Cocoa/Cocoa.h>
#include <map>
#include <Main/globals.hxx>
class CocoaMouseCursor::CocoaMouseCursorPrivate
{
public:
Cursor activeCursorKey;
typedef std::map<Cursor, NSCursor*> CursorMap;
CursorMap cursors;
};
NSCursor* cocoaCursorForKey(FGMouseCursor::Cursor aKey)
{
NSImage* img = nil;
NSString* path = [NSString stringWithCString:globals->get_fg_root().c_str()
encoding:NSUTF8StringEncoding];
path = [path stringByAppendingPathComponent:@"gui"];
switch (aKey) {
case FGMouseCursor::CURSOR_HAND: return [NSCursor pointingHandCursor];
case FGMouseCursor::CURSOR_CROSSHAIR: return [NSCursor crosshairCursor];
case FGMouseCursor::CURSOR_IBEAM: return [NSCursor IBeamCursor];
// FIXME - use a proper left-right cursor here.
case FGMouseCursor::CURSOR_LEFT_RIGHT: return [NSCursor resizeLeftRightCursor];
case FGMouseCursor::CURSOR_SPIN_CW:
path = [path stringByAppendingPathComponent:@"cursor-spin-cw.png"];
img = [[NSImage alloc] initWithContentsOfFile:path];
return [[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(16,16)];
case FGMouseCursor::CURSOR_SPIN_CCW:
path = [path stringByAppendingPathComponent:@"cursor-spin-cw.png"];
img = [[NSImage alloc] initWithContentsOfFile:path];
return [[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(16,16)];
default: return [NSCursor arrowCursor];
}
}
CocoaMouseCursor::CocoaMouseCursor() :
d(new CocoaMouseCursorPrivate)
{
}
CocoaMouseCursor::~CocoaMouseCursor()
{
}
void CocoaMouseCursor::setCursor(Cursor aCursor)
{
if (aCursor == d->activeCursorKey) {
return;
}
d->activeCursorKey = aCursor;
if (d->cursors.find(aCursor) == d->cursors.end()) {
d->cursors[aCursor] = cocoaCursorForKey(aCursor);
[d->cursors[aCursor] retain];
}
[d->cursors[aCursor] set];
}
void CocoaMouseCursor::setCursorVisible(bool aVis)
{
if (aVis) {
[NSCursor unhide];
} else {
[NSCursor hide];
}
}
void CocoaMouseCursor::hideCursorUntilMouseMove()
{
[NSCursor setHiddenUntilMouseMoves:YES];
}
void CocoaMouseCursor::mouseMoved()
{
// no-op
}

203
src/GUI/MouseCursor.cxx Normal file
View file

@ -0,0 +1,203 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "MouseCursor.hxx"
#include <cstring>
#include <boost/foreach.hpp>
#include <osgViewer/GraphicsWindow>
#include <osgViewer/Viewer>
#include <simgear/debug/logstream.hxx>
#include <simgear/simgear_config.h>
#include <simgear/structure/commands.hxx>
#ifdef SG_MAC
#include "CocoaMouseCursor.hxx"
#endif
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Viewer/renderer.hxx>
#include <Main/fg_os.hxx> // for fgWarpMouse
namespace
{
/**
* @brief when no native cursor implementation is available, use the osgViewer support. This
* has several limitations but is better than nothing
*/
class StockOSGCursor : public FGMouseCursor
{
public:
StockOSGCursor() :
mCursorObscured(false),
mCursorVisible(true),
mCursor(osgViewer::GraphicsWindow::InheritCursor)
{
mActualCursor = mCursor;
globals->get_renderer()->getViewer()->getWindows(mWindows);
}
virtual void setCursor(Cursor aCursor)
{
mCursor = translateCursor(aCursor);
updateCursor();
}
virtual void setCursorVisible(bool aVis)
{
if (mCursorObscured == aVis) {
return;
}
mCursorVisible = aVis;
updateCursor();
}
virtual void hideCursorUntilMouseMove()
{
if (mCursorObscured) {
return;
}
mCursorObscured = true;
updateCursor();
}
virtual void mouseMoved()
{
if (mCursorObscured) {
mCursorObscured = false;
updateCursor();
}
}
private:
osgViewer::GraphicsWindow::MouseCursor translateCursor(Cursor aCursor)
{
switch (aCursor) {
case CURSOR_HAND: return osgViewer::GraphicsWindow::HandCursor;
case CURSOR_CROSSHAIR: return osgViewer::GraphicsWindow::CrosshairCursor;
case CURSOR_IBEAM: return osgViewer::GraphicsWindow::TextCursor;
case CURSOR_LEFT_RIGHT: return osgViewer::GraphicsWindow::LeftRightCursor;
default: return osgViewer::GraphicsWindow::InheritCursor;
}
}
void updateCursor()
{
osgViewer::GraphicsWindow::MouseCursor cur = osgViewer::GraphicsWindow::InheritCursor;
if (mCursorObscured || !mCursorVisible) {
cur = osgViewer::GraphicsWindow::NoCursor;
} else {
cur = mCursor;
}
if (cur == mActualCursor) {
return;
}
std::cout << "actually setting cursor" << std::endl;
BOOST_FOREACH(osgViewer::GraphicsWindow* gw, mWindows) {
gw->setCursor(cur);
}
mActualCursor = cur;
}
bool mCursorObscured;
bool mCursorVisible;
osgViewer::GraphicsWindow::MouseCursor mCursor, mActualCursor;
std::vector<osgViewer::GraphicsWindow*> mWindows;
};
} // of anonymous namespace
static FGMouseCursor* static_instance = NULL;
FGMouseCursor::FGMouseCursor() :
mAutoHideTimeMsec(10000)
{
}
FGMouseCursor* FGMouseCursor::instance()
{
if (static_instance == NULL) {
#ifdef SG_MAC
if (true) {
static_instance = new CocoaMouseCursor;
}
#endif
// windows
// X11
if (static_instance == NULL) {
static_instance = new StockOSGCursor;
}
// initialise mouse-hide delay from global properties
globals->get_commands()->addCommand("set-cursor", static_instance, &FGMouseCursor::setCursorCommand);
}
return static_instance;
}
void FGMouseCursor::setAutoHideTimeMsec(unsigned int aMsec)
{
mAutoHideTimeMsec = aMsec;
}
bool FGMouseCursor::setCursorCommand(const SGPropertyNode* arg)
{
// JMT 2013 - I would prefer this was a seperate 'warp' command, but
// historically set-cursor has done both.
if (arg->hasValue("x") || arg->hasValue("y")) {
SGPropertyNode *mx = fgGetNode("/devices/status/mice/mouse/x", true);
SGPropertyNode *my = fgGetNode("/devices/status/mice/mouse/y", true);
int x = arg->getIntValue("x", mx->getIntValue());
int y = arg->getIntValue("y", my->getIntValue());
fgWarpMouse(x, y);
mx->setIntValue(x);
my->setIntValue(y);
}
Cursor c = cursorFromString(arg->getStringValue("cursor"));
setCursor(c);
return true;
}
typedef struct {
const char * name;
FGMouseCursor::Cursor cursor;
} MouseCursorMap;
const MouseCursorMap mouse_cursor_map[] = {
{ "inherit", FGMouseCursor::CURSOR_ARROW },
{ "crosshair", FGMouseCursor::CURSOR_CROSSHAIR },
{ "left-right", FGMouseCursor::CURSOR_LEFT_RIGHT },
{ "hand", FGMouseCursor::CURSOR_HAND },
{ "text", FGMouseCursor::CURSOR_IBEAM },
{ 0, FGMouseCursor::CURSOR_ARROW }
};
FGMouseCursor::Cursor FGMouseCursor::cursorFromString(const char* cursor_name)
{
for (unsigned int k = 0; mouse_cursor_map[k].name != 0; k++) {
if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
return mouse_cursor_map[k].cursor;
}
}
SG_LOG(SG_GENERAL, SG_WARN, "unknown cursor:" << cursor_name);
return CURSOR_ARROW;
}

47
src/GUI/MouseCursor.hxx Normal file
View file

@ -0,0 +1,47 @@
// MouseCursor.hxx - abstract inteface for mouse cursor control
#ifndef FG_GUI_MOUSE_CURSOR_HXX
#define FG_GUI_MOUSE_CURSOR_HXX 1
class SGPropertyNode;
class FGMouseCursor
{
public:
static FGMouseCursor* instance();
virtual void setAutoHideTimeMsec(unsigned int aMsec);
enum Cursor
{
CURSOR_ARROW,
CURSOR_HAND,
CURSOR_CROSSHAIR,
CURSOR_IBEAM, ///< for editing text
CURSOR_IN_OUT, ///< arrow pointing into / out of the screen
CURSOR_LEFT_RIGHT,
CURSOR_UP_DOWN,
CURSOR_SPIN_CW,
CURSOR_SPIN_CCW
};
virtual void setCursor(Cursor aCursor) = 0;
virtual void setCursorVisible(bool aVis) = 0;
virtual void hideCursorUntilMouseMove() = 0;
virtual void mouseMoved() = 0;
static Cursor cursorFromString(const char* str);
protected:
FGMouseCursor();
bool setCursorCommand(const SGPropertyNode* arg);
unsigned int mAutoHideTimeMsec;
};
#endif // FG_GUI_MOUSE_CURSOR_HXX

View file

@ -28,20 +28,50 @@
#include "FGMouseInput.hxx"
#include <boost/foreach.hpp>
#include <osgGA/GUIEventAdapter>
#include <simgear/scene/util/SGPickCallback.hxx>
#include <simgear/timing/timestamp.hxx>
#include "FGButton.hxx"
#include "Main/globals.hxx"
#include <Viewer/renderer.hxx>
#include <plib/pu.h>
#include <Model/panelnode.hxx>
#include <Cockpit/panel.hxx>
#include <Viewer/FGEventHandler.hxx>
#include <GUI/MouseCursor.hxx>
using std::ios_base;
const int MAX_MICE = 1;
const int MAX_MOUSE_BUTTONS = 8;
////////////////////////////////////////////////////////////////////////
/**
* 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 button, const osgGA::GUIEventAdapter* ea );
};
void ActivePickCallbacks::init( int button, const osgGA::GUIEventAdapter* ea )
{
osg::Vec2d windowPos;
flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
// 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 (!globals->get_renderer()->pick(pickList, ea)) {
if (!globals->get_renderer()->pick(pickList, windowPos)) {
return;
}
@ -65,42 +95,155 @@ void ActivePickCallbacks::update( double dt )
}
}
////////////////////////////////////////////////////////////////////////
/**
* Settings for a mouse mode.
*/
struct mouse_mode {
mouse_mode ();
virtual ~mouse_mode ();
FGMouseCursor::Cursor cursor;
bool constrained;
bool pass_through;
FGButton * buttons;
SGBindingList x_bindings[KEYMOD_MAX];
SGBindingList y_bindings[KEYMOD_MAX];
};
/**
* Settings for a mouse.
*/
struct mouse {
mouse ();
virtual ~mouse ();
int x, y;
SGPropertyNode_ptr mode_node;
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
int nModes;
int current_mode;
SGTimeStamp timeSinceLastMove;
mouse_mode * modes;
};
////////////////////////////////////////////////////////////////////////
class FGMouseInput::FGMouseInputPrivate
{
public:
FGMouseInputPrivate() :
haveWarped(false),
xSizeNode(fgGetNode("/sim/startup/xsize", false ) ),
ySizeNode(fgGetNode("/sim/startup/ysize", false ) ),
xAccelNode(fgGetNode("/devices/status/mice/mouse/accel-x", true ) ),
yAccelNode(fgGetNode("/devices/status/mice/mouse/accel-y", true ) ),
hideCursorNode(fgGetNode("/sim/mouse/hide-cursor", true ) ),
cursorTimeoutNode(fgGetNode("/sim/mouse/cursor-timeout-sec", true ) ),
rightButtonModeCycleNode(fgGetNode("/sim/mouse/right-button-mode-cycle-enabled", true)),
tooltipShowDelayNode( fgGetNode("/sim/mouse/tooltip-delay-msec", true) ),
clickTriggersTooltipNode( fgGetNode("/sim/mouse/click-shows-tooltip", true) )
{
tooltipTimeoutDone = false;
}
void centerMouseCursor(mouse& m)
{
// center the cursor
m.x = (xSizeNode ? xSizeNode->getIntValue() : 800) / 2;
m.y = (ySizeNode ? ySizeNode->getIntValue() : 600) / 2;
fgWarpMouse(m.x, m.y);
haveWarped = true;
}
void doHoverPick(const osg::Vec2d& windowPos)
{
std::vector<SGSceneryPick> pickList;
SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
if (globals->get_renderer()->pick(pickList, windowPos)) {
std::vector<SGSceneryPick>::const_iterator i;
for (i = pickList.begin(); i != pickList.end(); ++i) {
if (i->callback->hover(windowPos, i->info)) {
return;
}
// if the callback is of higher prioirty (lower enum index),
// record that.
if (i->callback->getPriority() < priority) {
priority = i->callback->getPriority();
}
}
} // of have valid pick
if (priority < SGPickCallback::PriorityScenery) {
FGMouseCursor::instance()->setCursor(FGMouseCursor::CURSOR_HAND);
} else {
// restore normal cursor
FGMouseCursor::instance()->setCursor(FGMouseCursor::CURSOR_ARROW);
}
updateHover();
}
void updateHover()
{
SGPropertyNode_ptr args(new SGPropertyNode);
globals->get_commands()->execute("update-hover", args);
}
ActivePickCallbacks activePickCallbacks;
mouse mice[MAX_MICE];
bool haveWarped;
bool tooltipTimeoutDone;
SGPropertyNode_ptr xSizeNode;
SGPropertyNode_ptr ySizeNode;
SGPropertyNode_ptr xAccelNode;
SGPropertyNode_ptr yAccelNode;
SGPropertyNode_ptr hideCursorNode;
SGPropertyNode_ptr cursorTimeoutNode;
SGPropertyNode_ptr rightButtonModeCycleNode;
SGPropertyNode_ptr tooltipShowDelayNode;
SGPropertyNode_ptr clickTriggersTooltipNode;
};
#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 }
};
static FGMouseInput* global_mouseInput = NULL;
static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
if(global_mouseInput)
global_mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
}
static void mouseMotionHandler(int x, int y, const osgGA::GUIEventAdapter* ea)
{
if (global_mouseInput != 0)
global_mouseInput->doMouseMotion(x, y, ea);
}
FGMouseInput * FGMouseInput::mouseInput = NULL;
FGMouseInput::FGMouseInput() :
haveWarped(false),
xSizeNode(fgGetNode("/sim/startup/xsize", false ) ),
ySizeNode(fgGetNode("/sim/startup/ysize", false ) ),
xAccelNode(fgGetNode("/devices/status/mice/mouse/accel-x", true ) ),
yAccelNode(fgGetNode("/devices/status/mice/mouse/accel-y", true ) ),
hideCursorNode(fgGetNode("/sim/mouse/hide-cursor", true ) ),
cursorTimeoutNode(fgGetNode("/sim/mouse/cursor-timeout-sec", true ) )
d(new FGMouseInputPrivate)
{
if( mouseInput == NULL )
mouseInput = this;
global_mouseInput = this;
}
FGMouseInput::~FGMouseInput()
{
if( mouseInput == this )
mouseInput = NULL;
global_mouseInput = NULL;
}
void FGMouseInput::init()
@ -117,7 +260,7 @@ void FGMouseInput::init()
int j;
for (int i = 0; i < MAX_MICE; i++) {
SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
mouse &m = bindings[i];
mouse &m = d->mice[i];
// Grab node pointers
std::ostringstream buf;
@ -140,19 +283,11 @@ void FGMouseInput::init()
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 the mouse cursor for this mode
m.modes[j].cursor = FGMouseCursor::cursorFromString(mode_node->getStringValue("cursor", "inherit"));
// 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);
@ -163,7 +298,6 @@ void FGMouseInput::init()
for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
buf.seekp(ios_base::beg);
buf << "mouse button " << k;
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf.str(), module );
}
@ -179,70 +313,68 @@ void FGMouseInput::init()
void FGMouseInput::update ( double dt )
{
double cursorTimeout = cursorTimeoutNode ? cursorTimeoutNode->getDoubleValue() : 10.0;
mouse &m = bindings[0];
int cursorTimeoutMsec = d->cursorTimeoutNode->getDoubleValue() * 1000;
int tooltipDelayMsec = d->tooltipShowDelayNode->getIntValue();
mouse &m = d->mice[0];
int mode = m.mode_node->getIntValue();
if (mode != m.current_mode) {
// current mode has changed
m.current_mode = mode;
m.timeout = cursorTimeout;
m.timeSinceLastMove.stamp();
if (mode >= 0 && mode < m.nModes) {
fgSetMouseCursor(m.modes[mode].cursor);
m.x = (xSizeNode ? xSizeNode->getIntValue() : 800) / 2;
m.y = (ySizeNode ? ySizeNode->getIntValue() : 600) / 2;
fgWarpMouse(m.x, m.y);
haveWarped = true;
FGMouseCursor::instance()->setCursor(m.modes[mode].cursor);
d->centerMouseCursor(m);
} else {
SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
fgSetMouseCursor(MOUSE_CURSOR_POINTER);
SG_LOG(SG_INPUT, SG_WARN, "Mouse mode " << mode << " out of range");
FGMouseCursor::instance()->setCursor(FGMouseCursor::CURSOR_ARROW);
}
}
if ( hideCursorNode ==NULL || hideCursorNode->getBoolValue() ) {
if ( m.x != m.save_x || m.y != m.save_y ) {
m.timeout = cursorTimeout;
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;
}
if ( d->hideCursorNode == NULL || d->hideCursorNode->getBoolValue() ) {
// if delay is <= 0, disable tooltips
if ( !d->tooltipTimeoutDone &&
(tooltipDelayMsec > 0) &&
(m.timeSinceLastMove.elapsedMSec() > tooltipDelayMsec))
{
d->tooltipTimeoutDone = true;
SGPropertyNode_ptr arg(new SGPropertyNode);
globals->get_commands()->execute("tooltip-timeout", arg);
}
if ( m.timeSinceLastMove.elapsedMSec() > cursorTimeoutMsec) {
FGMouseCursor::instance()->hideCursorUntilMouseMove();
m.timeSinceLastMove.stamp();
}
m.save_x = m.x;
m.save_y = m.y;
}
activePickCallbacks.update( dt );
d->activePickCallbacks.update( dt );
}
FGMouseInput::mouse::mouse ()
mouse::mouse ()
: x(-1),
y(-1),
save_x(-1),
save_y(-1),
nModes(1),
current_mode(0),
timeout(0),
modes(NULL)
{
}
FGMouseInput::mouse::~mouse ()
mouse::~mouse ()
{
delete [] modes;
}
FGMouseInput::mouse_mode::mouse_mode ()
: cursor(MOUSE_CURSOR_POINTER),
mouse_mode::mouse_mode ()
: cursor(FGMouseCursor::CURSOR_ARROW),
constrained(false),
pass_through(false),
buttons(NULL)
{
}
FGMouseInput::mouse_mode::~mouse_mode ()
mouse_mode::~mouse_mode ()
{
// FIXME: memory leak
// for (int i = 0; i < KEYMOD_MAX; i++) {
@ -252,30 +384,36 @@ FGMouseInput::mouse_mode::~mouse_mode ()
// for (j = 0; j < y_bindings[i].size(); j++)
// delete bindings[i][j];
// }
delete [] buttons;
if (buttons) {
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 &m = d->mice[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 (!d->rightButtonModeCycleNode->getBoolValue() && (b == 2)) {
// in spring-loaded look mode, ignore right clicks entirely here
return;
}
// 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();
while (!d->activePickCallbacks[b].empty()) {
d->activePickCallbacks[b].front()->buttonReleased();
d->activePickCallbacks[b].pop_front();
}
}
@ -287,7 +425,7 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
// 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 );
d->activePickCallbacks.init( b, ea );
}
}
}
@ -300,54 +438,91 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
}
m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
if (d->clickTriggersTooltipNode->getBoolValue()) {
SGPropertyNode_ptr args(new SGPropertyNode);
args->setStringValue("reason", "click");
globals->get_commands()->execute("tooltip-timeout", args);
d->tooltipTimeoutDone = true;
}
}
void FGMouseInput::doMouseMotion (int x, int y)
void FGMouseInput::doMouseMotion (int x, int y, const osgGA::GUIEventAdapter* ea)
{
// 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 modifiers = fgGetKeyModifiers();
int xsize = xSizeNode ? xSizeNode->getIntValue() : 800;
int ysize = ySizeNode ? ySizeNode->getIntValue() : 600;
int xsize = d->xSizeNode ? d->xSizeNode->getIntValue() : 800;
int ysize = d->ySizeNode ? d->ySizeNode->getIntValue() : 600;
mouse &m = bindings[0];
mouse &m = d->mice[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 (!d->activePickCallbacks[0].empty()) {
SG_LOG(SG_GENERAL, SG_INFO, "mouse-motion, have active pick callback");
BOOST_FOREACH(SGPickCallback* cb, d->activePickCallbacks[0]) {
cb->mouseMoved(ea);
}
m.x = x;
m.y = y;
return;
}
m.timeSinceLastMove.stamp();
FGMouseCursor::instance()->mouseMoved();
int modeIndex = m.current_mode;
// are we in spring-loaded look mode?
if (!d->rightButtonModeCycleNode->getBoolValue()) {
if (m.mouse_button_nodes[2]->getBoolValue()) {
// right mouse is down, force look mode
modeIndex = 3;
}
}
if (modeIndex == 0) {
osg::Vec2d windowPos;
flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
d->doHoverPick(windowPos);
// mouse has moved, so we may need to issue tooltip-timeout command
// again
d->tooltipTimeoutDone = false;
}
mouse_mode &mode = m.modes[modeIndex];
// 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;
}
if (haveWarped)
if (d->haveWarped)
{
// don't fire mouse-movement events at the first update after warping the mouse,
// just remember the new mouse position
haveWarped = false;
d->haveWarped = false;
}
else
{
// OK, PUI didn't want the event,
// so we can play with it.
// OK, PUI didn't want the event,
// so we can play with it.
if (x != m.x) {
int delta = x - m.x;
xAccelNode->setIntValue( delta );
d->xAccelNode->setIntValue( delta );
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;
yAccelNode->setIntValue( -delta );
d->yAccelNode->setIntValue( -delta );
for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
}
@ -370,8 +545,7 @@ void FGMouseInput::doMouseMotion (int x, int y)
if (need_warp)
{
fgWarpMouse(new_x, new_y);
haveWarped = true;
SG_LOG(SG_INPUT, SG_DEBUG, "Mouse warp: " << x << ", " << y << " => " << new_x << ", " << new_y);
d->haveWarped = true;
}
}
@ -382,16 +556,5 @@ void FGMouseInput::doMouseMotion (int x, int 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);
}

View file

@ -26,33 +26,19 @@
#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 <memory>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/scene/util/SGPickCallback.hxx>
#include <Viewer/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 button, const osgGA::GUIEventAdapter* ea );
};
// forward decls
namespace osgGA { class GUIEventAdapter; }
////////////////////////////////////////////////////////////////////////
// The Mouse Input Class
////////////////////////////////////////////////////////////////////////
class FGMouseInput : public SGSubsystem,FGCommonInput {
class FGMouseInput : public SGSubsystem, FGCommonInput {
public:
FGMouseInput();
virtual ~FGMouseInput();
@ -60,74 +46,12 @@ public:
virtual void init();
virtual void update( double dt );
static const int MAX_MICE = 1;
static const int MAX_MOUSE_BUTTONS = 8;
void doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea);
void doMouseMotion (int x, int y, const osgGA::GUIEventAdapter*);
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];
class FGMouseInputPrivate;
std::auto_ptr<FGMouseInputPrivate> d;
bool haveWarped;
SGPropertyNode_ptr xSizeNode;
SGPropertyNode_ptr ySizeNode;
SGPropertyNode_ptr xAccelNode;
SGPropertyNode_ptr yAccelNode;
SGPropertyNode_ptr hideCursorNode;
SGPropertyNode_ptr cursorTimeoutNode;
};
#endif

View file

@ -1108,32 +1108,6 @@ do_add_model (const SGPropertyNode * arg)
return true;
}
/**
* Set mouse cursor coordinates and cursor shape.
*/
static bool
do_set_cursor (const SGPropertyNode * arg)
{
if (arg->hasValue("x") || arg->hasValue("y")) {
SGPropertyNode *mx = fgGetNode("/devices/status/mice/mouse/x", true);
SGPropertyNode *my = fgGetNode("/devices/status/mice/mouse/y", true);
int x = arg->getIntValue("x", mx->getIntValue());
int y = arg->getIntValue("y", my->getIntValue());
fgWarpMouse(x, y);
mx->setIntValue(x);
my->setIntValue(y);
}
SGPropertyNode *cursor = const_cast<SGPropertyNode *>(arg)->getNode("cursor", true);
if (cursor->getType() != simgear::props::NONE)
fgSetMouseCursor(cursor->getIntValue());
cursor->setIntValue(fgGetMouseCursor());
return true;
}
/**
* Built-in command: play an audio message (i.e. a wav file) This is
* fire and forget. Call this once per message and it will get dumped
@ -1612,7 +1586,6 @@ static struct {
{ "open-browser", do_open_browser },
{ "gui-redraw", do_gui_redraw },
{ "add-model", do_add_model },
{ "set-cursor", do_set_cursor },
{ "play-audio-sample", do_play_audio_sample },
{ "presets-commit", do_presets_commit },
{ "log-level", do_log_level },

View file

@ -82,7 +82,7 @@ typedef void (*fgWindowResizeHandler)(int w, int h);
typedef void (*fgKeyHandler)(int key, int keymod, int mousex, int mousey);
typedef void (*fgMouseClickHandler)(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
typedef void (*fgMouseMotionHandler)(int x, int y);
typedef void (*fgMouseMotionHandler)(int x, int y, const osgGA::GUIEventAdapter*);
void fgRegisterIdleHandler(fgIdleHandler func);
void fgRegisterDrawHandler(fgDrawHandler func);

View file

@ -47,9 +47,11 @@
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
#include <simgear/bvh/BVHNode.hxx>
#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
#include <simgear/structure/commands.hxx>
#include <Viewer/renderer.hxx>
#include <Main/fg_props.hxx>
#include <GUI/MouseCursor.hxx>
#include "tilemgr.hxx"
#include "scenery.hxx"
@ -59,6 +61,9 @@ using namespace simgear;
class FGGroundPickCallback : public SGPickCallback {
public:
FGGroundPickCallback() : SGPickCallback(PriorityScenery)
{ }
virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter*, const Info& info)
{
// only on left mouse button

View file

@ -28,6 +28,7 @@
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/debug/BufferedLogCallback.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include "NasalSys.hxx"
#include "NasalSys_private.hxx"
@ -502,7 +503,7 @@ public:
{
_sys->setCmdArg(const_cast<SGPropertyNode*>(aNode));
naRef args[1];
args[0] = _sys->cmdArgGhost();
args[0] = _sys->wrappedPropsNode(const_cast<SGPropertyNode*>(aNode));
_sys->callMethod(_func, naNil(), 1, args, naNil() /* locals */);
@ -697,6 +698,20 @@ void FGNasalSys::init()
postinitNasalGUI(_globals, _context);
}
naRef FGNasalSys::wrappedPropsNode(SGPropertyNode* aProps)
{
static naRef wrapNodeFunc = naNil();
if (naIsNil(wrapNodeFunc)) {
nasal::Hash g(_globals, _context);
nasal::Hash props = g.get<nasal::Hash>("props");
wrapNodeFunc = props.get("wrapNode");
}
naRef args[1];
args[0] = propNodeGhost(aProps);
return naCall(_context, wrapNodeFunc, 1, args, naNil(), naNil());
}
void FGNasalSys::update(double)
{
if( NasalClipboard::getInstance() )

View file

@ -64,6 +64,12 @@ public:
void setCmdArg(SGPropertyNode* aNode);
/**
* create Nasal props.Node for an SGPropertyNode*
* This is the actual ghost, wrapped in a Nasal sugar class.
*/
naRef wrappedPropsNode(SGPropertyNode* aProps);
// Callbacks for command and timer bindings
virtual bool handleCommand( const char* moduleName,
const char* fileName,

View file

@ -27,6 +27,8 @@
#include "FGEventHandler.hxx"
#include "WindowBuilder.hxx"
#include "WindowSystemAdapter.hxx"
#include <simgear/math/SGRect.hxx>
#include <simgear/props/props.hxx>
#include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/OSGVersion.hxx>
@ -1142,34 +1144,34 @@ Camera* getGUICamera(CameraGroup* cgroup)
return info->getCamera(MAIN_CAMERA);
}
static bool computeCameraIntersection(const CameraInfo* cinfo,
const osgGA::GUIEventAdapter* ea,
static bool computeCameraIntersection(const CameraInfo* cinfo, const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections)
{
using osgUtil::Intersector;
using osgUtil::LineSegmentIntersector;
double x, y;
eventToWindowCoords(ea, x, y);
if (!(cinfo->flags & CameraGroup::DO_INTERSECTION_TEST))
return false;
const Camera* camera = cinfo->getCamera(MAIN_CAMERA);
if ( !camera )
camera = cinfo->getCamera( GEOMETRY_CAMERA );
if (camera->getGraphicsContext() != ea->getGraphicsContext())
return false;
// if (camera->getGraphicsContext() != ea->getGraphicsContext())
// return false;
const Viewport* viewport = camera->getViewport();
SGRect<double> viewportRect(viewport->x(), viewport->y(),
viewport->x() + viewport->width() - 1.0,
viewport->y() + viewport->height()- 1.0);
double epsilon = 0.5;
if (!(x >= viewport->x() - epsilon
&& x < viewport->x() + viewport->width() -1.0 + epsilon
&& y >= viewport->y() - epsilon
&& y < viewport->y() + viewport->height() -1.0 + epsilon))
if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon))
return false;
Vec4d start(x, y, 0.0, 1.0);
Vec4d end(x, y, 1.0, 1.0);
Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0);
Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0);
Matrix windowMat = viewport->computeWindowMatrix();
Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix()
* windowMat);
@ -1200,12 +1202,12 @@ static bool computeCameraIntersection(const CameraInfo* cinfo,
}
bool computeIntersections(const CameraGroup* cgroup,
const osgGA::GUIEventAdapter* ea,
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections)
{
// test the GUI first
const CameraInfo* guiCamera = cgroup->getGUICamera();
if (guiCamera && computeCameraIntersection(guiCamera, ea, intersections))
if (guiCamera && computeCameraIntersection(guiCamera, windowPos, intersections))
return true;
// Find camera that contains event
@ -1217,7 +1219,7 @@ bool computeIntersections(const CameraGroup* cgroup,
if (cinfo == guiCamera)
continue;
if (computeCameraIntersection(cinfo, ea, intersections))
if (computeCameraIntersection(cinfo, windowPos, intersections))
return true;
}

View file

@ -292,11 +292,6 @@ protected:
}
namespace osgGA
{
class GUIEventAdapter;
}
namespace flightgear
{
/** Get the osg::Camera that draws the GUI, if any, from a camera
@ -315,7 +310,7 @@ osg::Camera* getGUICamera(CameraGroup* cgroup);
* @return true if any intersections are found
*/
bool computeIntersections(const CameraGroup* cgroup,
const osgGA::GUIEventAdapter* ea,
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections&
intersections);
/** Warp the pointer to coordinates in the GUI camera of a camera group.

View file

@ -261,7 +261,7 @@ bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
if (mouseWarped)
return true;
if (eventToViewport(ea, us, x, y) && mouseMotionHandler)
(*mouseMotionHandler)(x, y);
(*mouseMotionHandler)(x, y, &ea);
return true;
case osgGA::GUIEventAdapter::RESIZE:
SG_LOG(SG_VIEW, SG_DEBUG, "FGEventHandler::handle: RESIZE event " << ea.getWindowHeight() << " x " << ea.getWindowWidth() << ", resizable: " << resizable);
@ -417,6 +417,7 @@ void eventToWindowCoords(const osgGA::GUIEventAdapter* ea,
y = (double)traits->height - y;
}
#if 0
void eventToWindowCoordsYDown(const osgGA::GUIEventAdapter* ea,
double& x, double& y)
{
@ -431,4 +432,6 @@ void eventToWindowCoordsYDown(const osgGA::GUIEventAdapter* ea,
if (ea->getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS)
y = (double)traits->height - y;
}
#endif
}

View file

@ -1724,15 +1724,14 @@ FGRenderer::resize( int width, int height )
}
bool
FGRenderer::pick(std::vector<SGSceneryPick>& pickList,
const osgGA::GUIEventAdapter* ea)
FGRenderer::pick(std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPos)
{
// wipe out the return ...
pickList.clear();
typedef osgUtil::LineSegmentIntersector::Intersections Intersections;
Intersections intersections;
if (!computeIntersections(CameraGroup::getDefault(), ea, intersections))
if (!computeIntersections(CameraGroup::getDefault(), windowPos, intersections))
return false;
for (Intersections::iterator hit = intersections.begin(),
e = intersections.end();

View file

@ -61,8 +61,7 @@ public:
/** Just pick into the scene and return the pick callbacks on the way ...
*/
bool pick( std::vector<SGSceneryPick>& pickList,
const osgGA::GUIEventAdapter* ea );
bool pick( std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPos);
/** Get and set the OSG Viewer object, if any.
*/