I wrote:
> The new code does everything the old does except for the warping of
> the mouse cursor at mode change (no API for that from either Nasal
> or the property tree) and mouse cursor changing (I was too lazy to
> put the list of Glut cursor names into the Nasal script).

That's one of those limitations that sounds kinda dumb when you see it
in email. :) So here's an updated mouse.nas file that does (I think)
literally everything that the existing mouse system does; and it fixes
a few bugs with property names and directions, too.

Again, the purpose here isn't to rewrite the mouse system, but to
demonstrate how easy and clean it can be to write non-trivial
functionality in Nasal.

You'll need this hacked version of _update_mouse in your input.cxx to
make things work:

void
FGInput::_update_mouse ()
{
  //
  // Hacked method to support Andy's mouse.nas script.  Not to be
  // checked in.
  //
  mouse &m = _mouse_bindings[0];
  m.modes[0].constrained = m.mode_node->getBoolValue("constrained", false);
  m.modes[0].pass_through = m.mode_node->getBoolValue("pass-through",
false);

  int oldCursor = m.modes[0].cursor;
  m.modes[0].cursor = m.mode_node->getIntValue("cursor",
GLUT_CURSOR_INHERIT);
  if(oldCursor != m.modes[0].cursor) {
      glutSetCursor(m.modes[0].cursor);
      m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
      m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
      glutWarpPointer(m.x, m.y);
  }
}

print("Loading mouse.nas");

# Cursor constants lifted from glut.h
# Propertly, these should be synchronized with a FlightGear
# definition, and not depend silently on a 3rd party include file.
Cursors = { RIGHT_ARROW : 0, LEFT_ARROW : 1, INFO : 2, DESTROY : 3,
            HELP : 4, CYCLE : 5, SPRAY : 6, WAIT : 7, TEXT : 8,
            CROSSHAIR : 9, UP_DOWN : 10, LEFT_RIGHT : 11, TOP_SIDE : 12,
            BOTTOM_SIDE : 13, LEFT_SIDE : 14, RIGHT_SIDE : 15,
            TOP_LEFT_CORNER : 16, TOP_RIGHT_CORNER : 17,
            BOTTOM_RIGHT_CORNER : 18, BOTTOM_LEFT_CORNER : 19,
            INHERIT : 100, NONE : 101, FULL_CROSSHAIR : 102 };

# General utilities.  This should probably live in a separate package,
# or be implemented as extension functions.

wrap = func {
    min = arg[0]; max = arg[1]; val = arg[2];
    while(val < min) { val = val + (max-min); }
    while(val > max) { val = val - (max-min); }
    return val;
}

clamp = func {
    min = arg[0]; max = arg[1]; val = arg[2];
    return if(val>max){max}elsif(val<min){min}else{val};
}

adjprop = func {
    min = arg[0]; max = arg[1]; prop = arg[2]; val = arg[3];
    v0 = getprop(prop);
    if(v0 == nil) { v0 = 0; }
    val = clamp(min, max, v0 + val);
    setprop(prop, val);
}

# Property tree root of the mouse properties.
MouseRoot = "/devices/status/mice/mouse";

# Returns 0: no buttons pressed, 1: left, 2: middle, 3: both
buttonState = func {
    result = 0;
    if(getprop(MouseRoot, "button[0]")) { result = result + 1; }
    if(getprop(MouseRoot, "button[1]")) { result = result + 2; }
    return result;
}

########################################################################
# Mode object methods
########################################################################

Modes = []; # Forward reference.  Will redefine later
CurrMode = 0;

# Cycle to the next mode.
# Making the mode properties work requires changes to input.cxx
nextMode = func {
    n = 1 + CurrMode;
    if(n >= size(Modes)) { n = 0; }
    CurrMode = n;
    print("nextMode: ", CurrMode);
    setprop(MouseRoot, "mode", "cursor", Modes[n].cursor);
    setprop(MouseRoot, "mode", "constrained", Modes[n].constrained);
    setprop(MouseRoot, "mode", "passThrough", Modes[n].passThrough);
}

# Center the view
centerView = func {
    print("centerView()");
    v = getprop("/sim/view/config/front-direction-deg");
    setprop("/sim/current-view/heading-offset-deg", v);
    v = getprop("/sim/current-view/config/pitch-offset-deg");
    setprop("/sim/current-view/goal-pitch-offset-deg", v);
}

# Pan the view horizontally
lookX = func {
    if(buttonState() != 0) { return };
    val = arg[0] * -360 + getprop("/sim/current-view/heading-offset-deg");
    setprop("/sim/current-view/heading-offset-deg", wrap(0, 360, val));
}

# Pan the view vertically
lookY = func {
    if(buttonState() != 0) { return };
    val = arg[0] * -180 + getprop("/sim/current-view/pitch-offset-deg");
    setprop("/sim/current-view/pitch-offset-deg", clamp(-90, 90, val));
}

# Handle horizontal mouse motion in control mode
controlX = func {
    s = buttonState();
    if   (s == 0) { prop = "/controls/flight/aileron"; }
    elsif(s == 1) { prop = "/controls/flight/rudder"; }
    else { return; }
    adjprop(-1, 1, prop, 4 * arg[0]);
}

# Handle vertical mouse motion in control mode
controlY = func {
    s = buttonState();
    min = -1;
    if   (s == 0) { prop = "/controls/flight/elevator"; }
    elsif(s == 2) {
        prop = "/controls/engines/engine[0]/throttle";
        min = 0;
    } else { return; }
    adjprop(min, 1, prop, -4 * arg[0]);
}

########################################################################
# Mode definitions.  These define "objects" to implement each mode.
# Note the use of Nasal inheritence via the parents array.
########################################################################

PointerMode = {
    cursor : Cursors.INHERIT, constrained : 0, passThrough : 1,

    button0 : func {},
    button1 : func {},
    button2 : nextMode,
    button3 : func { adjprop("/controls/flight/elevator-trim",  0.002) },
    button4 : func { adjprop("/controls/flight/elevator-trim", -0.002) },
    xAxis   : func {},
    yAxis   : func {}
};

ControlMode = {
    parents : [PointerMode],
    cursor : Cursors.CROSSHAIR, constrained : 1, passThrough : 0,

    xAxis : controlX,
    yAxis : controlY,
};

LookMode = {
    parents : [PointerMode],
    cursor : Cursors.LEFT_RIGHT, constrained : 1, passThrough : 0,

    button0 : centerView,
    xAxis : lookX,
    yAxis : lookY,
};

Modes = [PointerMode, ControlMode, LookMode];

########################################################################
# Handlers for stuff in the XML file.  Translate FG commands into
# method calls on the current mode.
########################################################################

handleButton0 = func { Modes[CurrMode].button0() }
handleButton1 = func { Modes[CurrMode].button1() }
handleButton2 = func { Modes[CurrMode].button2() }
handleButton3 = func { Modes[CurrMode].button3() }
handleButton4 = func { Modes[CurrMode].button4() }
handleXAxis   = func { Modes[CurrMode].xAxis(arg[0]); }
handleYAxis   = func { Modes[CurrMode].yAxis(arg[0]); }

print("mouse.nas initialization finished");
_______________________________________________
Flightgear-devel mailing list
[EMAIL PROTECTED]
http://mail.flightgear.org/mailman/listinfo/flightgear-devel

Reply via email to