Updated PointingDevice (trakball and friends)
- changed from templates back to inheritance (sorry DirtyHairy :)) - added 'tsense' commandline argument and associated UI - updated docs and screenshots for new functionality
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
* Improved emulation of Trakball controller, eliminating bias in left/
|
* Improved emulation of Trakball controller, eliminating bias in left/
|
||||||
right directions. Thanks to Thomas Jentzsch for the idea and code.
|
right directions. Thanks to Thomas Jentzsch for the idea and code.
|
||||||
|
Related to this, added 'tsense' commandline argument and associated
|
||||||
|
UI item, to allow changing sensitivity of mouse trackball emulation.
|
||||||
|
|
||||||
* Fixed an annoying bug in Linux, where Alt-Tab'ing out of a window and
|
* Fixed an annoying bug in Linux, where Alt-Tab'ing out of a window and
|
||||||
then back again would pass a 'Tab' key event to the app, which in
|
then back again would pass a 'Tab' key event to the app, which in
|
||||||
|
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 7.8 KiB |
|
@ -2703,6 +2703,7 @@
|
||||||
<tr><td>Joy deadzone size</td><td>Deadzone area for axes on joysticks/gamepads</td><td>-joydeadzone</td></tr>
|
<tr><td>Joy deadzone size</td><td>Deadzone area for axes on joysticks/gamepads</td><td>-joydeadzone</td></tr>
|
||||||
<tr><td>Digital paddle sensitivity</td><td>Sensitivity used when emulating a paddle using a digital device</td><td>-dsense</td></tr>
|
<tr><td>Digital paddle sensitivity</td><td>Sensitivity used when emulating a paddle using a digital device</td><td>-dsense</td></tr>
|
||||||
<tr><td>Mouse paddle sensitivity</td><td>Sensitivity used when emulating a paddle using a mouse</td><td>-msense</td></tr>
|
<tr><td>Mouse paddle sensitivity</td><td>Sensitivity used when emulating a paddle using a mouse</td><td>-msense</td></tr>
|
||||||
|
<tr><td>Trackball sensitivity</td><td>Sensitivity used when emulating a trackball device using a mouse</td><td>-tsense</td></tr>
|
||||||
<tr><td>Allow all 4 ...</td><td>Allow all 4 joystick directions to be pressed simultaneously</td><td>-joyallow4</td></tr>
|
<tr><td>Allow all 4 ...</td><td>Allow all 4 joystick directions to be pressed simultaneously</td><td>-joyallow4</td></tr>
|
||||||
<tr><td>Grab mouse ...</td><td>Keep mouse in window in emulation mode</td><td>-grabmouse</td></tr>
|
<tr><td>Grab mouse ...</td><td>Keep mouse in window in emulation mode</td><td>-grabmouse</td></tr>
|
||||||
<tr><td>Use Control key combos</td><td>Enable using Control key in keyboard actions</td><td>-ctrlcombo</td></tr>
|
<tr><td>Use Control key combos</td><td>Enable using Control key in keyboard actions</td><td>-ctrlcombo</td></tr>
|
||||||
|
|
|
@ -20,24 +20,31 @@
|
||||||
|
|
||||||
#include "PointingDevice.hxx"
|
#include "PointingDevice.hxx"
|
||||||
|
|
||||||
namespace {
|
class AmigaMouse : public PointingDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Create a new Amiga Mouse controller plugged into the specified jack
|
||||||
|
|
||||||
class AmigaMouseHelper {
|
@param jack The jack the controller is plugged into
|
||||||
|
@param event The event object to use for events
|
||||||
|
@param system The system using this controller
|
||||||
|
*/
|
||||||
|
AmigaMouse(Jack jack, const Event& event, const System& system)
|
||||||
|
: PointingDevice(jack, event, system, Controller::AmigaMouse,
|
||||||
|
trackballSensitivity) { }
|
||||||
|
virtual ~AmigaMouse() = default;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) {
|
uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override
|
||||||
static constexpr uInt32 ourTableH[4] = { 0x00, 0x10, 0x50, 0x40 };
|
{
|
||||||
static constexpr uInt32 ourTableV[4] = { 0x00, 0x80, 0xa0, 0x20 };
|
static constexpr uInt32 ourTableH[4] = { 0x00, 0x80, 0xa0, 0x20 };
|
||||||
|
static constexpr uInt32 ourTableV[4] = { 0x00, 0x10, 0x50, 0x40 };
|
||||||
|
|
||||||
return ourTableV[countV] | ourTableH[countH];
|
return ourTableH[countH] | ourTableV[countV];
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
static constexpr float trackballSensitivity = 0.8;
|
||||||
static constexpr Controller::Type controllerType = Controller::AmigaMouse;
|
};
|
||||||
static constexpr double trackballSensitivity = 0.8; // TODO: make configurable
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using AmigaMouse = PointingDevice<AmigaMouseHelper>;
|
|
||||||
|
|
||||||
#endif // AMIGAMOUSE_HXX
|
#endif // AMIGAMOUSE_HXX
|
||||||
|
|
|
@ -20,24 +20,31 @@
|
||||||
|
|
||||||
#include "PointingDevice.hxx"
|
#include "PointingDevice.hxx"
|
||||||
|
|
||||||
namespace {
|
class AtariMouse : public PointingDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Create a new Atari Mouse controller plugged into the specified jack
|
||||||
|
|
||||||
class AtariMouseHelper {
|
@param jack The jack the controller is plugged into
|
||||||
|
@param event The event object to use for events
|
||||||
|
@param system The system using this controller
|
||||||
|
*/
|
||||||
|
AtariMouse(Jack jack, const Event& event, const System& system)
|
||||||
|
: PointingDevice(jack, event, system, Controller::AtariMouse,
|
||||||
|
trackballSensitivity) { }
|
||||||
|
virtual ~AtariMouse() = default;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) {
|
uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override
|
||||||
static constexpr uInt32 ourTableH[4] = { 0x00, 0x80, 0xc0, 0x40 };
|
{
|
||||||
static constexpr uInt32 ourTableV[4] = { 0x00, 0x10, 0x30, 0x20 };
|
static constexpr uInt32 ourTableH[4] = { 0x00, 0x10, 0x30, 0x20 };
|
||||||
|
static constexpr uInt32 ourTableV[4] = { 0x00, 0x80, 0xc0, 0x40 };
|
||||||
|
|
||||||
return ourTableV[countV] | ourTableH[countH];
|
return ourTableH[countH] | ourTableV[countV];
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
static constexpr float trackballSensitivity = 0.8;
|
||||||
static constexpr Controller::Type controllerType = Controller::AtariMouse;
|
};
|
||||||
static constexpr double trackballSensitivity = 0.8; // TODO: make configurable
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using AtariMouse = PointingDevice<AtariMouseHelper>;
|
|
||||||
|
|
||||||
#endif // ATARIMOUSE_HXX
|
#endif // ATARIMOUSE_HXX
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
#include "Joystick.hxx"
|
#include "Joystick.hxx"
|
||||||
#include "Paddles.hxx"
|
#include "Paddles.hxx"
|
||||||
|
#include "PointingDevice.hxx"
|
||||||
#include "PropsSet.hxx"
|
#include "PropsSet.hxx"
|
||||||
#include "ListWidget.hxx"
|
#include "ListWidget.hxx"
|
||||||
#include "ScrollBarWidget.hxx"
|
#include "ScrollBarWidget.hxx"
|
||||||
|
@ -95,6 +96,7 @@ void EventHandler::initialize()
|
||||||
Joystick::setDeadZone(myOSystem.settings().getInt("joydeadzone"));
|
Joystick::setDeadZone(myOSystem.settings().getInt("joydeadzone"));
|
||||||
Paddles::setDigitalSensitivity(myOSystem.settings().getInt("dsense"));
|
Paddles::setDigitalSensitivity(myOSystem.settings().getInt("dsense"));
|
||||||
Paddles::setMouseSensitivity(myOSystem.settings().getInt("msense"));
|
Paddles::setMouseSensitivity(myOSystem.settings().getInt("msense"));
|
||||||
|
PointingDevice::setSensitivity(myOSystem.settings().getInt("tsense"));
|
||||||
|
|
||||||
// Set quick select delay when typing characters in listwidgets
|
// Set quick select delay when typing characters in listwidgets
|
||||||
ListWidget::setQuickSelectDelay(myOSystem.settings().getInt("listdelay"));
|
ListWidget::setQuickSelectDelay(myOSystem.settings().getInt("listdelay"));
|
||||||
|
|
|
@ -127,12 +127,12 @@ class Paddles : public Controller
|
||||||
|
|
||||||
// Range of values over which digital and mouse movement is scaled
|
// Range of values over which digital and mouse movement is scaled
|
||||||
// to paddle resistance
|
// to paddle resistance
|
||||||
static const int TRIGMIN = 1;
|
static constexpr int TRIGMIN = 1;
|
||||||
static const int TRIGMAX = 4096;
|
static constexpr int TRIGMAX = 4096;
|
||||||
static int TRIGRANGE; // This one is variable for the upper range
|
static int TRIGRANGE; // This one is variable for the upper range
|
||||||
|
|
||||||
static const int MAX_DIGITAL_SENSE = 20;
|
static constexpr int MAX_DIGITAL_SENSE = 20;
|
||||||
static const int MAX_MOUSE_SENSE = 20;
|
static constexpr int MAX_MOUSE_SENSE = 20;
|
||||||
static int DIGITAL_SENSITIVITY, DIGITAL_DISTANCE;
|
static int DIGITAL_SENSITIVITY, DIGITAL_DISTANCE;
|
||||||
static int MOUSE_SENSITIVITY;
|
static int MOUSE_SENSITIVITY;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include "Control.hxx"
|
||||||
|
#include "Event.hxx"
|
||||||
|
#include "System.hxx"
|
||||||
|
#include "TIA.hxx"
|
||||||
|
|
||||||
|
#include "PointingDevice.hxx"
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
PointingDevice::PointingDevice(Jack jack, const Event& event,
|
||||||
|
const System& system, Controller::Type type,
|
||||||
|
float sensitivity)
|
||||||
|
: Controller(jack, event, system, type),
|
||||||
|
mySensitivity(sensitivity),
|
||||||
|
myHCounterRemainder(0.0), myVCounterRemainder(0.0),
|
||||||
|
myTrackBallLinesH(1), myTrackBallLinesV(1),
|
||||||
|
myTrackBallLeft(false), myTrackBallDown(false),
|
||||||
|
myCountH(0), myCountV(0),
|
||||||
|
myScanCountH(0), myScanCountV(0),
|
||||||
|
myFirstScanOffsetH(0), myFirstScanOffsetV(0),
|
||||||
|
myMouseEnabled(false)
|
||||||
|
{
|
||||||
|
// The code in ::read() is set up to always return IOPortA values in
|
||||||
|
// the lower 4 bits data value
|
||||||
|
// As such, the jack type (left or right) isn't necessary here
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt8 PointingDevice::read()
|
||||||
|
{
|
||||||
|
int scanline = mySystem.tia().scanlines();
|
||||||
|
|
||||||
|
// Loop over all missed changes
|
||||||
|
while(myScanCountH < scanline)
|
||||||
|
{
|
||||||
|
if(myTrackBallLeft) myCountH--;
|
||||||
|
else myCountH++;
|
||||||
|
|
||||||
|
// Define scanline of next change
|
||||||
|
myScanCountH += myTrackBallLinesH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over all missed changes
|
||||||
|
while(myScanCountV < scanline)
|
||||||
|
{
|
||||||
|
if(myTrackBallDown) myCountV--;
|
||||||
|
else myCountV++;
|
||||||
|
|
||||||
|
// Define scanline of next change
|
||||||
|
myScanCountV += myTrackBallLinesV;
|
||||||
|
}
|
||||||
|
|
||||||
|
myCountH &= 0x03;
|
||||||
|
myCountV &= 0x03;
|
||||||
|
|
||||||
|
uInt8 portA = ioPortA(myCountH, myCountV, myTrackBallLeft, myTrackBallDown);
|
||||||
|
|
||||||
|
myDigitalPinState[One] = portA & 0x10;
|
||||||
|
myDigitalPinState[Two] = portA & 0x20;
|
||||||
|
myDigitalPinState[Three] = portA & 0x40;
|
||||||
|
myDigitalPinState[Four] = portA & 0x80;
|
||||||
|
|
||||||
|
return (portA >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PointingDevice::update()
|
||||||
|
{
|
||||||
|
if(!myMouseEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update horizontal direction
|
||||||
|
updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder,
|
||||||
|
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
|
||||||
|
|
||||||
|
// Update vertical direction
|
||||||
|
updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder,
|
||||||
|
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
|
||||||
|
|
||||||
|
// Get mouse button state
|
||||||
|
myDigitalPinState[Six] = (myEvent.get(Event::MouseButtonLeftValue) == 0) &&
|
||||||
|
(myEvent.get(Event::MouseButtonRightValue) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool PointingDevice::setMouseControl(
|
||||||
|
Controller::Type xtype, int xid, Controller::Type ytype, int yid)
|
||||||
|
{
|
||||||
|
// Currently, the various trakball controllers take full control of the
|
||||||
|
// mouse, and use both mouse buttons for the single fire button
|
||||||
|
// As well, there's no separate setting for x and y axis, so any
|
||||||
|
// combination of Controller and id is valid
|
||||||
|
myMouseEnabled = (xtype == myType || ytype == myType) &&
|
||||||
|
(xid != -1 || yid != -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PointingDevice::setSensitivity(int sensitivity)
|
||||||
|
{
|
||||||
|
BSPF::clamp(sensitivity, 1, 20, 10);
|
||||||
|
TB_SENSITIVITY = sensitivity / 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PointingDevice::updateDirection(const int& counter, float& counterRemainder,
|
||||||
|
bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset)
|
||||||
|
{
|
||||||
|
// Apply sensitivity and calculate remainder
|
||||||
|
float fTrackBallCount = counter * mySensitivity * TB_SENSITIVITY + counterRemainder;
|
||||||
|
int trackBallCount = std::lround(fTrackBallCount);
|
||||||
|
counterRemainder = fTrackBallCount - trackBallCount;
|
||||||
|
|
||||||
|
if(trackBallCount)
|
||||||
|
{
|
||||||
|
trackBallDir = (trackBallCount > 0);
|
||||||
|
trackBallCount = abs(trackBallCount);
|
||||||
|
|
||||||
|
// Calculate lines to wait between sending new horz/vert values
|
||||||
|
trackBallLines = mySystem.tia().scanlinesLastFrame() / trackBallCount;
|
||||||
|
|
||||||
|
// Set lower limit in case of (unrealistic) ultra fast mouse movements
|
||||||
|
if (trackBallLines == 0) trackBallLines = 1;
|
||||||
|
|
||||||
|
// Define scanline of first change
|
||||||
|
scanCount = (trackBallLines * firstScanOffset) >> 12;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Prevent any change
|
||||||
|
scanCount = INT_MAX;
|
||||||
|
|
||||||
|
// Define offset factor for first change, move randomly forward by up to 1/8th
|
||||||
|
firstScanOffset = (((firstScanOffset << 3) + rand() %
|
||||||
|
(1 << 12)) >> 3) & ((1 << 12) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
float PointingDevice::TB_SENSITIVITY = 1.0;
|
|
@ -18,22 +18,23 @@
|
||||||
#ifndef POINTING_DEVICE_HXX
|
#ifndef POINTING_DEVICE_HXX
|
||||||
#define POINTING_DEVICE_HXX
|
#define POINTING_DEVICE_HXX
|
||||||
|
|
||||||
|
class Controller;
|
||||||
|
class Event;
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Control.hxx"
|
|
||||||
#include "Event.hxx"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Common controller class for pointing devices (Atari Mouse, Amiga Mouse, Trak-Ball)
|
Common controller class for pointing devices (Atari Mouse, Amiga Mouse, Trak-Ball)
|
||||||
This code was heavily borrowed from z26.
|
This code was heavily borrowed from z26.
|
||||||
|
|
||||||
@author Stephen Anthony, Thomas Jentzsch & z26 team
|
@author Stephen Anthony, Thomas Jentzsch & z26 team
|
||||||
Template-ification by Christian Speckner
|
|
||||||
*/
|
*/
|
||||||
template<class T>
|
|
||||||
class PointingDevice : public Controller
|
class PointingDevice : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PointingDevice(Jack jack, const Event& event, const System& system);
|
PointingDevice(Jack jack, const Event& event,
|
||||||
|
const System& system, Controller::Type type,
|
||||||
|
float sensitivity);
|
||||||
virtual ~PointingDevice() = default;
|
virtual ~PointingDevice() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -72,13 +73,28 @@ class PointingDevice : public Controller
|
||||||
bool setMouseControl(Controller::Type xtype, int xid,
|
bool setMouseControl(Controller::Type xtype, int xid,
|
||||||
Controller::Type ytype, int yid) override;
|
Controller::Type ytype, int yid) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the sensitivity for analog emulation of trackball movement
|
||||||
|
using a mouse.
|
||||||
|
|
||||||
|
@param sensitivity Value from 1 to 20, with larger values causing
|
||||||
|
more movement (10 represents the baseline)
|
||||||
|
*/
|
||||||
|
static void setSensitivity(int sensitivity);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Each derived class must implement this, to determine how its
|
||||||
|
// IOPortA values are calculated
|
||||||
|
virtual uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateDirection(const int& counter, double& counterRemainder,
|
void updateDirection(const int& counter, float& counterRemainder,
|
||||||
bool& trackBallDir, int& trackBallLines,
|
bool& trackBallDir, int& trackBallLines,
|
||||||
int& scanCount, int& firstScanOffset);
|
int& scanCount, int& firstScanOffset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double myHCounterRemainder, myVCounterRemainder;
|
// Mouse input to sensitivity emulation
|
||||||
|
float mySensitivity, myHCounterRemainder, myVCounterRemainder;
|
||||||
|
|
||||||
// How many lines to wait between sending new horz and vert values
|
// How many lines to wait between sending new horz and vert values
|
||||||
int myTrackBallLinesH, myTrackBallLinesV;
|
int myTrackBallLinesH, myTrackBallLinesV;
|
||||||
|
@ -101,143 +117,17 @@ class PointingDevice : public Controller
|
||||||
// Whether to use the mouse to emulate this controller
|
// Whether to use the mouse to emulate this controller
|
||||||
bool myMouseEnabled;
|
bool myMouseEnabled;
|
||||||
|
|
||||||
private:
|
// User-defined sensitivity; adjustable since end-users may have different
|
||||||
|
// mouse speeds
|
||||||
|
static float TB_SENSITIVITY;
|
||||||
|
|
||||||
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
PointingDevice() = delete;
|
PointingDevice() = delete;
|
||||||
PointingDevice(const PointingDevice<T>&) = delete;
|
PointingDevice(const PointingDevice&) = delete;
|
||||||
PointingDevice(PointingDevice<T>&&) = delete;
|
PointingDevice(PointingDevice&&) = delete;
|
||||||
PointingDevice& operator=(const PointingDevice<T>&) = delete;
|
PointingDevice& operator=(const PointingDevice&) = delete;
|
||||||
PointingDevice& operator=(PointingDevice<T>&&) = delete;
|
PointingDevice& operator=(PointingDevice&&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ############################################################################
|
|
||||||
// Implementation
|
|
||||||
// ############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
template<class T>
|
|
||||||
PointingDevice<T>::PointingDevice(Jack jack, const Event& event, const System& system)
|
|
||||||
: Controller(jack, event, system, T::controllerType),
|
|
||||||
myHCounterRemainder(0.0), myVCounterRemainder(0.0),
|
|
||||||
myTrackBallLinesH(1), myTrackBallLinesV(1),
|
|
||||||
myTrackBallLeft(false), myTrackBallDown(false),
|
|
||||||
myCountH(0), myCountV(0),
|
|
||||||
myScanCountH(0), myScanCountV(0),
|
|
||||||
myFirstScanOffsetH(0), myFirstScanOffsetV(0),
|
|
||||||
myMouseEnabled(false)
|
|
||||||
{
|
|
||||||
// The code in ::read() is set up to always return IOPortA values in
|
|
||||||
// the lower 4 bits data value
|
|
||||||
// As such, the jack type (left or right) isn't necessary here
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
template<class T>
|
|
||||||
uInt8 PointingDevice<T>::read()
|
|
||||||
{
|
|
||||||
int scanline = mySystem.tia().scanlines();
|
|
||||||
|
|
||||||
// Loop over all missed changes
|
|
||||||
while(myScanCountH < scanline)
|
|
||||||
{
|
|
||||||
if(myTrackBallLeft) myCountH--;
|
|
||||||
else myCountH++;
|
|
||||||
|
|
||||||
// Define scanline of next change
|
|
||||||
myScanCountH += myTrackBallLinesH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over all missed changes
|
|
||||||
while(myScanCountV < scanline)
|
|
||||||
{
|
|
||||||
if(myTrackBallDown) myCountV--;
|
|
||||||
else myCountV++;
|
|
||||||
|
|
||||||
// Define scanline of next change
|
|
||||||
myScanCountV += myTrackBallLinesV;
|
|
||||||
}
|
|
||||||
|
|
||||||
myCountH &= 0x03;
|
|
||||||
myCountV &= 0x03;
|
|
||||||
|
|
||||||
uInt8 ioPortA = T::ioPortA(myCountV, myCountH, myTrackBallDown, myTrackBallLeft);
|
|
||||||
|
|
||||||
myDigitalPinState[One] = ioPortA & 0x10;
|
|
||||||
myDigitalPinState[Two] = ioPortA & 0x20;
|
|
||||||
myDigitalPinState[Three] = ioPortA & 0x40;
|
|
||||||
myDigitalPinState[Four] = ioPortA & 0x80;
|
|
||||||
|
|
||||||
return (ioPortA >> 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
template<class T>
|
|
||||||
void PointingDevice<T>::update()
|
|
||||||
{
|
|
||||||
if(!myMouseEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Update horizontal direction
|
|
||||||
updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder,
|
|
||||||
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
|
|
||||||
|
|
||||||
// Update vertical direction
|
|
||||||
updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder,
|
|
||||||
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
|
|
||||||
|
|
||||||
// Get mouse button state
|
|
||||||
myDigitalPinState[Six] = (myEvent.get(Event::MouseButtonLeftValue) == 0) &&
|
|
||||||
(myEvent.get(Event::MouseButtonRightValue) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
template<class T>
|
|
||||||
bool PointingDevice<T>::setMouseControl(
|
|
||||||
Controller::Type xtype, int xid, Controller::Type ytype, int yid)
|
|
||||||
{
|
|
||||||
// Currently, the various trakball controllers take full control of the
|
|
||||||
// mouse, and use both mouse buttons for the single fire button
|
|
||||||
// As well, there's no separate setting for x and y axis, so any
|
|
||||||
// combination of Controller and id is valid
|
|
||||||
myMouseEnabled = (xtype == myType || ytype == myType) &&
|
|
||||||
(xid != -1 || yid != -1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
template<class T>
|
|
||||||
void PointingDevice<T>::updateDirection(const int& counter, double& counterRemainder,
|
|
||||||
bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset)
|
|
||||||
{
|
|
||||||
// Apply sensitivity and calculate remainder
|
|
||||||
float fTrackBallCount = counter * T::trackballSensitivity + counterRemainder;
|
|
||||||
int trackBallCount = std::lround(fTrackBallCount);
|
|
||||||
counterRemainder = fTrackBallCount - trackBallCount;
|
|
||||||
|
|
||||||
if(trackBallCount)
|
|
||||||
{
|
|
||||||
trackBallDir = (trackBallCount > 0);
|
|
||||||
trackBallCount = abs(trackBallCount);
|
|
||||||
|
|
||||||
// Calculate lines to wait between sending new horz/vert values
|
|
||||||
trackBallLines = mySystem.tia().scanlinesLastFrame() / trackBallCount;
|
|
||||||
|
|
||||||
// Set lower limit in case of (unrealistic) ultra fast mouse movements
|
|
||||||
if (trackBallLines == 0) trackBallLines = 1;
|
|
||||||
|
|
||||||
// Define scanline of first change
|
|
||||||
scanCount = (trackBallLines * firstScanOffset) >> 12;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Prevent any change
|
|
||||||
scanCount = INT_MAX;
|
|
||||||
|
|
||||||
// Define offset factor for first change, move randomly forward by up to 1/8th
|
|
||||||
firstScanOffset = (((firstScanOffset << 3) + rand() %
|
|
||||||
(1 << 12)) >> 3) & ((1 << 12) - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // POINTING_DEVICE_HXX
|
#endif // POINTING_DEVICE_HXX
|
||||||
|
|
|
@ -90,6 +90,7 @@ Settings::Settings(OSystem& osystem)
|
||||||
setInternal("cursor", "2");
|
setInternal("cursor", "2");
|
||||||
setInternal("dsense", "10");
|
setInternal("dsense", "10");
|
||||||
setInternal("msense", "10");
|
setInternal("msense", "10");
|
||||||
|
setInternal("tsense", "10");
|
||||||
setInternal("saport", "lr");
|
setInternal("saport", "lr");
|
||||||
setInternal("ctrlcombo", "true");
|
setInternal("ctrlcombo", "true");
|
||||||
|
|
||||||
|
@ -309,12 +310,16 @@ void Settings::validate()
|
||||||
setInternal("cursor", "2");
|
setInternal("cursor", "2");
|
||||||
|
|
||||||
i = getInt("dsense");
|
i = getInt("dsense");
|
||||||
if(i < 1) setInternal("dsense", "1");
|
if(i < 1 || i > 20)
|
||||||
else if(i > 20) setInternal("dsense", "10");
|
setInternal("dsense", "10");
|
||||||
|
|
||||||
i = getInt("msense");
|
i = getInt("msense");
|
||||||
if(i < 1) setInternal("msense", "1");
|
if(i < 1 || i > 20)
|
||||||
else if(i > 20) setInternal("msense", "15");
|
setInternal("msense", "10");
|
||||||
|
|
||||||
|
i = getInt("tsense");
|
||||||
|
if(i < 1 || i > 20)
|
||||||
|
setInternal("tsense", "10");
|
||||||
|
|
||||||
i = getInt("ssinterval");
|
i = getInt("ssinterval");
|
||||||
if(i < 1) setInternal("ssinterval", "2");
|
if(i < 1) setInternal("ssinterval", "2");
|
||||||
|
@ -413,6 +418,7 @@ void Settings::usage() const
|
||||||
<< " -cursor <0,1,2,3> Set cursor state in UI/emulation modes\n"
|
<< " -cursor <0,1,2,3> Set cursor state in UI/emulation modes\n"
|
||||||
<< " -dsense <number> Sensitivity of digital emulated paddle movement (1-20)\n"
|
<< " -dsense <number> Sensitivity of digital emulated paddle movement (1-20)\n"
|
||||||
<< " -msense <number> Sensitivity of mouse emulated paddle movement (1-20)\n"
|
<< " -msense <number> Sensitivity of mouse emulated paddle movement (1-20)\n"
|
||||||
|
<< " -tsense <number> Sensitivity of mouse emulated trackball movement (1-20)\n"
|
||||||
<< " -saport <lr|rl> How to assign virtual ports to multiple Stelladaptor/2600-daptors\n"
|
<< " -saport <lr|rl> How to assign virtual ports to multiple Stelladaptor/2600-daptors\n"
|
||||||
<< " -ctrlcombo <1|0> Use key combos involving the Control key (Control-Q for quit may be disabled!)\n"
|
<< " -ctrlcombo <1|0> Use key combos involving the Control key (Control-Q for quit may be disabled!)\n"
|
||||||
<< " -autoslot <1|0> Automatically switch to next save slot when state saving\n"
|
<< " -autoslot <1|0> Automatically switch to next save slot when state saving\n"
|
||||||
|
|
|
@ -20,24 +20,32 @@
|
||||||
|
|
||||||
#include "PointingDevice.hxx"
|
#include "PointingDevice.hxx"
|
||||||
|
|
||||||
namespace {
|
class TrakBall : public PointingDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Create a new trakball controller plugged into the specified jack
|
||||||
|
|
||||||
class TrakBallHelper {
|
@param jack The jack the controller is plugged into
|
||||||
|
@param event The event object to use for events
|
||||||
|
@param system The system using this controller
|
||||||
|
*/
|
||||||
|
TrakBall(Jack jack, const Event& event, const System& system)
|
||||||
|
: PointingDevice(jack, event, system, Controller::TrakBall,
|
||||||
|
trackballSensitivity) { }
|
||||||
|
virtual ~TrakBall() = default;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) {
|
uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) override
|
||||||
static constexpr uInt32 ourTableH[2][2] = {{ 0x40, 0x00 }, { 0xc0, 0x80 }};
|
{
|
||||||
static constexpr uInt32 ourTableV[2][2] = {{ 0x00, 0x10 }, { 0x20, 0x30 }};
|
static constexpr uInt32 ourTableH[2][2] = {{ 0x00, 0x10 }, { 0x20, 0x30 }};
|
||||||
|
static constexpr uInt32 ourTableV[2][2] = {{ 0x40, 0x00 }, { 0xc0, 0x80 }};
|
||||||
|
|
||||||
return ourTableV[countV & 0x01][down] | ourTableH[countH & 0x01][left];
|
return ourTableH[countH & 0x01][left] | ourTableV[countV & 0x01][down];
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
// 50% of Atari and Amiga mouse
|
||||||
static constexpr Controller::Type controllerType = Controller::TrakBall;
|
static constexpr float trackballSensitivity = 0.4;
|
||||||
static constexpr double trackballSensitivity = 0.4; // 50% of Atari and Amiga mouse; TODO: make configurable
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using TrakBall = PointingDevice<TrakBallHelper>;
|
|
||||||
|
|
||||||
#endif // TRAKBALL_HXX
|
#endif // TRAKBALL_HXX
|
||||||
|
|
|
@ -67,6 +67,7 @@ MODULE_OBJS := \
|
||||||
src/emucore/MD5.o \
|
src/emucore/MD5.o \
|
||||||
src/emucore/OSystem.o \
|
src/emucore/OSystem.o \
|
||||||
src/emucore/Paddles.o \
|
src/emucore/Paddles.o \
|
||||||
|
src/emucore/PointingDevice.o \
|
||||||
src/emucore/Props.o \
|
src/emucore/Props.o \
|
||||||
src/emucore/PropsSet.o \
|
src/emucore/PropsSet.o \
|
||||||
src/emucore/SaveKey.o \
|
src/emucore/SaveKey.o \
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
#include "Joystick.hxx"
|
#include "Joystick.hxx"
|
||||||
#include "Paddles.hxx"
|
#include "Paddles.hxx"
|
||||||
|
#include "PointingDevice.hxx"
|
||||||
#include "Settings.hxx"
|
#include "Settings.hxx"
|
||||||
#include "EventMappingWidget.hxx"
|
#include "EventMappingWidget.hxx"
|
||||||
#include "EditTextWidget.hxx"
|
#include "EditTextWidget.hxx"
|
||||||
|
@ -45,7 +46,7 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent,
|
||||||
|
|
||||||
// Set real dimensions
|
// Set real dimensions
|
||||||
_w = std::min(50 * fontWidth + 10, max_w);
|
_w = std::min(50 * fontWidth + 10, max_w);
|
||||||
_h = std::min(15 * (lineHeight + 4) + 14, max_h);
|
_h = std::min(16 * (lineHeight + 4) + 14, max_h);
|
||||||
|
|
||||||
// The tab widget
|
// The tab widget
|
||||||
xpos = 2; ypos = vBorder;
|
xpos = 2; ypos = vBorder;
|
||||||
|
@ -164,8 +165,8 @@ void InputDialog::addDevicePortTab(const GUI::Font& font)
|
||||||
// Add paddle speed (digital emulation)
|
// Add paddle speed (digital emulation)
|
||||||
xpos = 5; ypos += lineHeight + 4;
|
xpos = 5; ypos += lineHeight + 4;
|
||||||
myDPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
|
myDPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
|
||||||
"Digital paddle sensitivity ",
|
"Digital paddle sensitivity ",
|
||||||
lwidth, kDPSpeedChanged);
|
lwidth, kDPSpeedChanged);
|
||||||
myDPaddleSpeed->setMinValue(1); myDPaddleSpeed->setMaxValue(20);
|
myDPaddleSpeed->setMinValue(1); myDPaddleSpeed->setMaxValue(20);
|
||||||
xpos += myDPaddleSpeed->getWidth() + 5;
|
xpos += myDPaddleSpeed->getWidth() + 5;
|
||||||
myDPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight,
|
myDPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight,
|
||||||
|
@ -176,8 +177,8 @@ void InputDialog::addDevicePortTab(const GUI::Font& font)
|
||||||
// Add paddle speed (mouse emulation)
|
// Add paddle speed (mouse emulation)
|
||||||
xpos = 5; ypos += lineHeight + 4;
|
xpos = 5; ypos += lineHeight + 4;
|
||||||
myMPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
|
myMPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
|
||||||
"Mouse paddle sensitivity ",
|
"Mouse paddle sensitivity ",
|
||||||
lwidth, kMPSpeedChanged);
|
lwidth, kMPSpeedChanged);
|
||||||
myMPaddleSpeed->setMinValue(1); myMPaddleSpeed->setMaxValue(20);
|
myMPaddleSpeed->setMinValue(1); myMPaddleSpeed->setMaxValue(20);
|
||||||
xpos += myMPaddleSpeed->getWidth() + 5;
|
xpos += myMPaddleSpeed->getWidth() + 5;
|
||||||
myMPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight,
|
myMPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight,
|
||||||
|
@ -185,6 +186,18 @@ void InputDialog::addDevicePortTab(const GUI::Font& font)
|
||||||
myMPaddleSpeed->setFlags(WIDGET_CLEARBG);
|
myMPaddleSpeed->setFlags(WIDGET_CLEARBG);
|
||||||
wid.push_back(myMPaddleSpeed);
|
wid.push_back(myMPaddleSpeed);
|
||||||
|
|
||||||
|
// Add trackball speed
|
||||||
|
xpos = 5; ypos += lineHeight + 4;
|
||||||
|
myTrackBallSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
|
||||||
|
"Trackball sensitivity ",
|
||||||
|
lwidth, kTBSpeedChanged);
|
||||||
|
myTrackBallSpeed->setMinValue(1); myTrackBallSpeed->setMaxValue(20);
|
||||||
|
xpos += myTrackBallSpeed->getWidth() + 5;
|
||||||
|
myTrackBallLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight,
|
||||||
|
"", kTextAlignLeft);
|
||||||
|
myTrackBallSpeed->setFlags(WIDGET_CLEARBG);
|
||||||
|
wid.push_back(myTrackBallSpeed);
|
||||||
|
|
||||||
// Add 'allow all 4 directions' for joystick
|
// Add 'allow all 4 directions' for joystick
|
||||||
xpos = 10; ypos += lineHeight + 12;
|
xpos = 10; ypos += lineHeight + 12;
|
||||||
myAllowAll4 = new CheckboxWidget(myTab, font, xpos, ypos,
|
myAllowAll4 = new CheckboxWidget(myTab, font, xpos, ypos,
|
||||||
|
@ -240,6 +253,10 @@ void InputDialog::loadConfig()
|
||||||
myMPaddleSpeed->setValue(instance().settings().getInt("msense"));
|
myMPaddleSpeed->setValue(instance().settings().getInt("msense"));
|
||||||
myMPaddleLabel->setLabel(instance().settings().getString("msense"));
|
myMPaddleLabel->setLabel(instance().settings().getString("msense"));
|
||||||
|
|
||||||
|
// Trackball speed
|
||||||
|
myTrackBallSpeed->setValue(instance().settings().getInt("tsense"));
|
||||||
|
myTrackBallLabel->setLabel(instance().settings().getString("tsense"));
|
||||||
|
|
||||||
// AtariVox serial port
|
// AtariVox serial port
|
||||||
myAVoxPort->setText(instance().settings().getString("avoxport"));
|
myAVoxPort->setText(instance().settings().getString("avoxport"));
|
||||||
|
|
||||||
|
@ -279,6 +296,11 @@ void InputDialog::saveConfig()
|
||||||
instance().settings().setValue("msense", sensitivity);
|
instance().settings().setValue("msense", sensitivity);
|
||||||
Paddles::setMouseSensitivity(sensitivity);
|
Paddles::setMouseSensitivity(sensitivity);
|
||||||
|
|
||||||
|
// Trackball speed
|
||||||
|
sensitivity = myTrackBallSpeed->getValue();
|
||||||
|
instance().settings().setValue("tsense", sensitivity);
|
||||||
|
PointingDevice::setSensitivity(sensitivity);
|
||||||
|
|
||||||
// AtariVox serial port
|
// AtariVox serial port
|
||||||
instance().settings().setValue("avoxport", myAVoxPort->getText());
|
instance().settings().setValue("avoxport", myAVoxPort->getText());
|
||||||
|
|
||||||
|
@ -330,6 +352,8 @@ void InputDialog::setDefaults()
|
||||||
myDPaddleLabel->setLabel("10");
|
myDPaddleLabel->setLabel("10");
|
||||||
myMPaddleSpeed->setValue(10);
|
myMPaddleSpeed->setValue(10);
|
||||||
myMPaddleLabel->setLabel("10");
|
myMPaddleLabel->setLabel("10");
|
||||||
|
myTrackBallSpeed->setValue(10);
|
||||||
|
myTrackBallLabel->setLabel("10");
|
||||||
|
|
||||||
// AtariVox serial port
|
// AtariVox serial port
|
||||||
myAVoxPort->setText("");
|
myAVoxPort->setText("");
|
||||||
|
@ -433,6 +457,10 @@ void InputDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
myMPaddleLabel->setValue(myMPaddleSpeed->getValue());
|
myMPaddleLabel->setValue(myMPaddleSpeed->getValue());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case kTBSpeedChanged:
|
||||||
|
myTrackBallLabel->setValue(myTrackBallSpeed->getValue());
|
||||||
|
break;
|
||||||
|
|
||||||
case kDBButtonPressed:
|
case kDBButtonPressed:
|
||||||
if(!myJoyDialog)
|
if(!myJoyDialog)
|
||||||
myJoyDialog = make_unique<JoystickDialog>
|
myJoyDialog = make_unique<JoystickDialog>
|
||||||
|
|
|
@ -57,6 +57,7 @@ class InputDialog : public Dialog
|
||||||
kDeadzoneChanged = 'DZch',
|
kDeadzoneChanged = 'DZch',
|
||||||
kDPSpeedChanged = 'PDch',
|
kDPSpeedChanged = 'PDch',
|
||||||
kMPSpeedChanged = 'PMch',
|
kMPSpeedChanged = 'PMch',
|
||||||
|
kTBSpeedChanged = 'TBch',
|
||||||
kDBButtonPressed = 'DBbp'
|
kDBButtonPressed = 'DBbp'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,8 +76,10 @@ class InputDialog : public Dialog
|
||||||
StaticTextWidget* myDeadzoneLabel;
|
StaticTextWidget* myDeadzoneLabel;
|
||||||
SliderWidget* myDPaddleSpeed;
|
SliderWidget* myDPaddleSpeed;
|
||||||
SliderWidget* myMPaddleSpeed;
|
SliderWidget* myMPaddleSpeed;
|
||||||
|
SliderWidget* myTrackBallSpeed;
|
||||||
StaticTextWidget* myDPaddleLabel;
|
StaticTextWidget* myDPaddleLabel;
|
||||||
StaticTextWidget* myMPaddleLabel;
|
StaticTextWidget* myMPaddleLabel;
|
||||||
|
StaticTextWidget* myTrackBallLabel;
|
||||||
CheckboxWidget* myAllowAll4;
|
CheckboxWidget* myAllowAll4;
|
||||||
CheckboxWidget* myGrabMouse;
|
CheckboxWidget* myGrabMouse;
|
||||||
CheckboxWidget* myCtrlCombo;
|
CheckboxWidget* myCtrlCombo;
|
||||||
|
|