1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "Control.hxx"
19#include "Event.hxx"
20#include "System.hxx"
21#include "TIA.hxx"
22
23#include "PointingDevice.hxx"
24
25// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26PointingDevice::PointingDevice(Jack jack, const Event& event,
27 const System& system, Controller::Type type,
28 float sensitivity)
29 : Controller(jack, event, system, type),
30 mySensitivity(sensitivity),
31 myHCounterRemainder(0.0), myVCounterRemainder(0.0),
32 myTrackBallLinesH(1), myTrackBallLinesV(1),
33 myTrackBallLeft(false), myTrackBallDown(false),
34 myCountH(0), myCountV(0),
35 myScanCountH(0), myScanCountV(0),
36 myFirstScanOffsetH(0), myFirstScanOffsetV(0),
37 myMouseEnabled(false)
38{
39 // The code in ::read() is set up to always return IOPortA values in
40 // the lower 4 bits data value
41 // As such, the jack type (left or right) isn't necessary here
42}
43
44// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45uInt8 PointingDevice::read()
46{
47 int scanline = mySystem.tia().scanlines();
48
49 // Loop over all missed changes
50 while(myScanCountH < scanline)
51 {
52 if(myTrackBallLeft) --myCountH;
53 else ++myCountH;
54
55 // Define scanline of next change
56 myScanCountH += myTrackBallLinesH;
57 }
58
59 // Loop over all missed changes
60 while(myScanCountV < scanline)
61 {
62 if(myTrackBallDown) ++myCountV;
63 else --myCountV;
64
65 // Define scanline of next change
66 myScanCountV += myTrackBallLinesV;
67 }
68
69 myCountH &= 0b11;
70 myCountV &= 0b11;
71
72 uInt8 portA = ioPortA(myCountH, myCountV, myTrackBallLeft, myTrackBallDown);
73
74 setPin(DigitalPin::One, portA & 0b0001);
75 setPin(DigitalPin::Two, portA & 0b0010);
76 setPin(DigitalPin::Three, portA & 0b0100);
77 setPin(DigitalPin::Four, portA & 0b1000);
78
79 return portA;
80}
81
82// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
83void PointingDevice::update()
84{
85 if(!myMouseEnabled)
86 return;
87
88 // Update horizontal direction
89 updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder,
90 myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
91
92 // Update vertical direction
93 updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder,
94 myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
95
96 // Get mouse button state
97 setPin(DigitalPin::Six, (myEvent.get(Event::MouseButtonLeftValue) == 0) &&
98 (myEvent.get(Event::MouseButtonRightValue) == 0));
99}
100
101// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102bool PointingDevice::setMouseControl(
103 Controller::Type xtype, int xid, Controller::Type ytype, int yid)
104{
105 // Currently, the various trakball controllers take full control of the
106 // mouse, and use both mouse buttons for the single fire button
107 // As well, there's no separate setting for x and y axis, so any
108 // combination of Controller and id is valid
109 myMouseEnabled = (xtype == myType || ytype == myType) &&
110 (xid != -1 || yid != -1);
111 return true;
112}
113
114// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
115void PointingDevice::setSensitivity(int sensitivity)
116{
117 BSPF::clamp(sensitivity, 1, 20, 10);
118 TB_SENSITIVITY = sensitivity / 10.0f;
119}
120
121// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
122void PointingDevice::updateDirection(int counter, float& counterRemainder,
123 bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset)
124{
125 // Apply sensitivity and calculate remainder
126 float fTrackBallCount = counter * mySensitivity * TB_SENSITIVITY + counterRemainder;
127 int trackBallCount = int(std::lround(fTrackBallCount));
128 counterRemainder = fTrackBallCount - trackBallCount;
129
130 if(trackBallCount)
131 {
132 trackBallDir = (trackBallCount > 0);
133 trackBallCount = abs(trackBallCount);
134
135 // Calculate lines to wait between sending new horz/vert values
136 trackBallLines = mySystem.tia().scanlinesLastFrame() / trackBallCount;
137
138 // Set lower limit in case of (unrealistic) ultra fast mouse movements
139 if (trackBallLines == 0) trackBallLines = 1;
140
141 // Define scanline of first change
142 scanCount = (trackBallLines * firstScanOffset) >> 12;
143 }
144 else
145 {
146 // Prevent any change
147 scanCount = INT_MAX;
148
149 // Define offset factor for first change, move randomly forward by up to 1/8th
150 firstScanOffset = (((firstScanOffset << 3) + mySystem.randGenerator().next() %
151 (1 << 12)) >> 3) & ((1 << 12) - 1);
152 }
153}
154
155// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156float PointingDevice::TB_SENSITIVITY = 1.0;
157