-
Notifications
You must be signed in to change notification settings - Fork 4
/
forcefeedback.go
347 lines (301 loc) · 8.85 KB
/
forcefeedback.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.
package evdev
import "unsafe"
// Values describing the status of a force-feedback effect
const (
FFStatusStopped = 0x00
FFStatusPlaying = 0x01
FFStatusMax = 0x01
)
// Force feedback effect types.
const (
FFRumble = 0x50
FFPeriodic = 0x51
FFConstant = 0x52
FFSpring = 0x53
FFFriction = 0x54
FFDamper = 0x55
FFInertia = 0x56
FFRamp = 0x57
FFEffectMin = FFRumble
FFEffectMax = FFRamp
)
// Force feedback periodic effect types
const (
FFSquare = 0x58
FFTriangle = 0x59
FFSine = 0x5a
FFSawUp = 0x5b
FFSawDown = 0x5c
FFCustom = 0x5d
FFWaveformMin = FFSquare
FFWaveformMax = FFCustom
)
// Set Force feedback device properties
const (
FFGain = 0x60
FFAutoCenter = 0x61
FFMax = 0x7f
FFCount = FFMax + 1
)
// Directions encoded in Effect.Direction
const (
DirDown = 0x0000 // 0 degrees
DirLeft = 0x4000 // 90 degrees
DirUp = 0x8000 // 180 degrees
DirRight = 0xc000 // 270 degrees
)
/*
Effect describes any of the supported Force Feedback effects.
Supported effects are as follows:
- FFConstant Renders constant force effects
- FFPeriodic Renders periodic effects with the following waveforms:
- FFSquare Square waveform
- FFTriangle Triangle waveform
- FFSine Sine waveform
- FFSawUp Sawtooth up waveform
- FFSawDown Sawtooth down waveform
- FFCustom Custom waveform
- FFRamp Renders ramp effects
- FFSpring Simulates the presence of a spring
- FFFriction Simulates friction
- FFDamper Simulates damper effects
- FFRumble Rumble effects
- FFInertia Simulates inertia
- FFGain Gain is adjustable
- FFAutoCenter Autocenter is adjustable
Note: In most cases you should use FFPeriodic instead of FFRumble. All
devices that support FFRumble support FFPeriodic (square, triangle,
sine) and the other way around.
Note: The exact layout of FFCustom waveforms is undefined for the
time being as no driver supports it yet.
Note: All duration values are expressed in milliseconds.
Values above 32767 ms (0x7fff) should not be used and have unspecified results.
*/
type Effect struct {
Type uint16
Id int16
Direction uint16
Trigger Trigger
Replay Replay
data unsafe.Pointer
}
// Data returns the event data structure as a concrete type.
// Its type depends on the value of Effect.Type and can be any of:
//
// FFConstant -> ConstantEffect
// FFPeriodic -> PeriodicEffect
// FFRamp -> RampEffect
// FFRumble -> RumbleEffect
// FFSpring -> [2]ConditionEffect
// FFDamper -> [2]ConditionEffect
//
// This returns nil if the type was not recognized.
func (e *Effect) Data() interface{} {
// FIXME(jimt): Deal with: FFFriction, FFInertia:
// Unsure what they should return.
if e.data == nil {
return nil
}
switch e.Type {
case FFConstant:
return *(*ConstantEffect)(e.data)
case FFPeriodic:
return *(*PeriodicEffect)(e.data)
case FFRamp:
return *(*RampEffect)(e.data)
case FFRumble:
return *(*RumbleEffect)(e.data)
case FFSpring, FFDamper:
return *(*[2]ConditionEffect)(e.data)
}
return nil
}
// SetData sets the event data structure.
func (e *Effect) SetData(v interface{}) {
if v != nil {
e.data = unsafe.Pointer(&v)
}
}
type Replay struct {
Length uint16
Delay uint16
}
type Trigger struct {
Button uint16
Interval uint16
}
type Envelope struct {
AttackLength uint16
AttackLevel uint16
FadeLength uint16
FadeLevel uint16
}
// ConstantEffect renders constant force-feedback effects.
type ConstantEffect struct {
Level int16
Envelope Envelope
}
// RampEffect renders ramp force-feedback effects.
type RampEffect struct {
StartLevel int16
EndLevel int16
Envelope Envelope
}
// ConditionEffect represents a confitional force feedback effect.
type ConditionEffect struct {
RightSaturation uint16
LeftSaturation uint16
RightCoeff int16
LeftCoeff int16
Deadband uint16
Center int16
}
// PeriodicEffect renders periodic force-feedback effects with
// the following waveforms: Square, Triangle, Sine, Sawtooth
// or a custom waveform.
type PeriodicEffect struct {
Waveform uint16
Period uint16
Magnitude int16
Offset int16
Phase uint16
Envelope Envelope
custom_len uint32
custom_data unsafe.Pointer // *int16
}
// Data returns custom waveform information.
// This comes in the form of a signed 16-bit slice.
//
// The exact layout of a custom waveform is undefined for the
// time being as no driver supports it yet.
func (e *PeriodicEffect) Data() []int16 {
if e.custom_data == nil {
return nil
}
return (*(*[1<<27 - 1]int16)(e.custom_data))[:e.custom_len]
}
// SetData sets custom waveform information.
//
// The exact layout of a custom waveform is undefined for the
// time being as no driver supports it yet.
func (e *PeriodicEffect) SetData(v []int16) {
e.custom_len = uint32(len(v))
e.custom_data = unsafe.Pointer(nil)
if e.custom_len > 0 {
e.custom_data = unsafe.Pointer(&v[0])
}
}
// The rumble effect is the most basic effect, it lets the
// device vibrate. The API contains support for two motors,
// a strong one and a weak one, which can be controlled independently.
type RumbleEffect struct {
StrongMagnitude uint16
WeakMagnitude uint16
}
// ForceFeedbackCaps returns a bitset which specified the kind of Force Feedback
// effects supported by this device. The bits can be compared against
// the FFXXX constants. Additionally, it returns the number of effects
// this device can handle simultaneously.
//
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) ForceFeedbackCaps() (int, Bitset) {
bs := NewBitset(24)
buf := bs.Bytes()
ioctl(d.fd.Fd(), _EVIOCGBIT(EvForceFeedback, len(buf)), unsafe.Pointer(&buf[0]))
var count int32
ioctl(d.fd.Fd(), _EVIOCGEFFECTS, unsafe.Pointer(&count))
return int(count), bs
}
// SetEffects sends the given list of Force Feedback effects
// to the device. The length of the list should not exceed the
// count returned from `Device.ForceFeedbackCaps()`.
//
// After this call completes, the effect.Id field will contain
// the effect's id which must be used when playing or stopping the effect.
// It is also possible to reupload the same effect with the same
// id later on with new parameters. This allows us to update a
// running effect, without first stopping it.
//
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) SetEffects(list ...*Effect) bool {
for _, effect := range list {
err := ioctl(d.fd.Fd(), _EVIOCSFF, unsafe.Pointer(effect))
if err != nil {
return false
}
}
return true
}
// UnsetEffects deletes the given effects from the device.
// This makes room for new effects in the device's memory.
// Note that this also stops the effect if it was playing.
//
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) UnsetEffects(list ...*Effect) bool {
for _, effect := range list {
err := ioctl(d.fd.Fd(), _EVIOCRMFF, int(effect.Id))
if err != nil {
return false
}
}
return true
}
// SetEffectGain changes the force feedback gain.
//
// Not all devices have the same effect strength. Therefore,
// users should set a gain factor depending on how strong they
// want effects to be. This setting is persistent across
// access to the driver.
//
// The specified gain should be in the range 0-100.
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) SetEffectGain(gain int) {
d.setEffectFactor(gain, FFGain)
}
// SetEffectAutoCenter changes the force feedback autocenter factor.
// The specified factor should be in the range 0-100.
// A value of 0 means: no autocenter.
//
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) SetEffectAutoCenter(factor int) {
d.setEffectFactor(factor, FFAutoCenter)
}
// setEffectFactor changes the given effect factor.
// E.g.: FFAutoCenter or FFGain.
//
// This is only applicable to devices with EvForceFeedback event support.
func (d *Device) setEffectFactor(factor int, code uint16) {
if factor < 0 {
factor = 0
}
if factor > 100 {
factor = 100
}
var e Event
e.Type = EvForceFeedback
e.Code = code
e.Value = 0xffff * int32(factor) / 100
d.Outbox <- e
}
// PlayEffect plays a previously uploaded effect.
func (d *Device) PlayEffect(id int16) {
d.toggleEffect(id, true)
}
// StopEffect stops a previously uploaded effect from playing.
func (d *Device) StopEffect(id int16) {
d.toggleEffect(id, false)
}
// ToggleEffect plays or stops a previously uploaded effect with the given id.
func (d *Device) toggleEffect(id int16, play bool) {
var e Event
e.Type = EvForceFeedback
e.Code = uint16(id)
if play {
e.Value = 1
} else {
e.Value = 0
}
d.Outbox <- e
}