-
Notifications
You must be signed in to change notification settings - Fork 2
/
ovrldbtn.lua
305 lines (249 loc) · 12.7 KB
/
ovrldbtn.lua
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
--[[
ovrldbtn - event-based manager of an overloaded button for FSUIPC
Version 20210620-0-4b40cac
The MIT License (MIT)
Copyright © 2021 Blake Buhlig
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
“Software”), to deal in the Software without restriction, including without
limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Description:
This module implements the "overloading" of a joystick button to control
multiple actions by mapping various patterns of clicking that button to those
different actions. The basic supported pattern is a train of consecutive button
clicks, and a variant is the train of clicks except with the final click
continuing to be held down. Different actions are signaled by changing the
length of consecutive clicks or click-and-holds. For example, the actions on
the overloaded button as noted below left, could trigger the corresponding
actions below right.
- single click and release : reset view
- single click and hold : transmit voice while held
- double click and release : toggle view zoom
- double click and hold : enable 2nd use of other buttons while held
- triple click and release : toggle internal/external view
- triple click and hold : enable 3rd use of other buttons while held
Each click and release sequence toggles a different virtual button, to which
you can assign in the INI file like any physical button. Similarly each click
and hold sequence will press a virtual button during the hold and will
release the virtual button once the overloaded joystick button is released.
Upon detecting a particular sequence, the module will play a short audio
WAV file as feedback to you about what it detected.
The module implements a similar concept as TripleUse.lua but can handle an
arbitrarily long click sequence, manages virtual buttons instead of directly
using lua calls to trigger the associated actions, is event-based for better
efficiency compared to polling, and manages the sound feedback.
Presently the module supports managing a single overloaded button. To manage
multiple buttons, you could make copies of this script under different names
so that an additional lua thread is launched for each additional button.
See the below CONFIGURATION section for parameters to configure as well as
more detailed documentation. A first time user might have a hard time
understanding some of the details, because tradeoffs in some of the settings
are subtle. Thus I recommend starting out with the defaults in the more complex
parameters until you have had a chance to experiment, after which point some of
the subtle points should become clearer.
One piece of mandatory "configuration" you will need to deal with is the
installation of the audio WAV files, so that part is discussed now.
AUDIO FEEDBACK
The framework gives audible feedback on the detected click and clkNhold actions
by playing WAV files.
As an example, in my setup I generated generate morse code WAV files from
https://www.meridianoutpost.com/resources/etools/calculators/calculator-morse-code.php
per the below table, so that the dit's and dah's correspond to the
sequence of clicks and holds.
Letter Morse Code Representing
=== ===== ======================
E . single click
T - single click-and-hold
I .. double click
A .- double click-and-hold
S ... triple click
U ..- triple click-and-hold
H .... 4x click
V ...- 4x click-and-hold
5 ..... 5x click
4 ....- 5x click-and-hold
In my setup, I generated the fastest morse code that the WAV generator would
provide, and used a different pitch for clicks versus the click-and-holds.
The generated WAV files need to be installed in the FSUIPC directory, and need
to be named according to a convention that makes it easy for the module to
pick the right file based on what it detected. For all the click-and-release
sequences (e.g. single click, double click, triple click, ...), that
convention is:
- [Joystick Code]_[Button Code]c[Number of clicks].wav
For the click-and-holds (e.g. single click-and-hold, double click-and hold...)
the convention is:
- [Joystick Code]_[Button Code]h[Number of clicks].wav
For example, a double-click on joystick "A" button "0" will play "A_0c2.wav".
A 5x click-and-hold on joystick "2" button "13" would play "2_13h5.wav".
As you'll learn more about in the CONFIGURATION section, 'n_clicks_max' defines
the maximum sequence of click-and-releases and click-and-holds that will be
detected by the module, so you need only generate these files for
[Number of clicks] ranging from 1 to 'n_clicks_max'.
Then, one final sound: the WAV file "ovrldbtn.wav" is played on startup of the
framework, as an indicator the framework is ready and as a test of the sound
system. For my 'ovrldbtn.wav', I used the aforementioned website to generate a
morse code WAV file that plays the word "ready".
--]]
-- CONFIGURATION
-- Joycode number/joyletter string for the joystick w/ the button to overload
joy = 'A'
-- Button id for the button to overload
btn = 0
--[[
n_click_threshold_ms:
Maximum time (in milliseconds) between the release of a button click and the
press of another click, for the later click to be considered an n_click.
For example, upon releasing the button, click it again within this time to
consider the action an n_click=2, then release it and click it again within
another period of this duration to consider it an n_click=3 -... and so on
until n_clicks_max is reached. The action associated with an n_click series
will only be triggered after this amount of time passes after the release of
the last click. The larger the value, the more leisurely your finger twitches
from release-to-next-press can be to have them still registered as an n_click.
But this time also represents a delay that you must wait for after the release
of the last click before the associated action is generated, or that you start
clicking again to trigger a new n_click sequence. Note this value doesn't
affect the period between pressing a button and releasing it; for that
consideration see button_hold_threshold_ms. n_click_threshold_ms only affects
how quickly the button must be pressed after being released, to be treated as a
continuation of the active n_click series.
--]]
n_click_threshold_ms = 300
--[[
n_clicks_max: if the button is successively clicked more than this number of
times within n_click_threshold_ms of prior button releases, the number of extra
clicks are ignored and the generated action will reflect only n_clicks=
n_clicks_max. However the n_clicks=n_clicks_max action will not be generated
until n_click_threshold_ms after the release of the last physical click.
For example if this were set to 2, a click,click,click-series would generate
the same action as if you had done a click,click, though will wait to do so
until after the release of the 3rd click. Larger numbers allow you to bind more
actions to the overloaded button which might be useful mainly when
n_click_threshold_ms is larger, to allow your brain the time to reliably count
how many clicks in a row you've pressed. Smaller numbers can let you more
easily generate the n_clicks=n_clicks_max action because you won't need to as
precisely count a series of rapid finger twitches, so long as you can be sure
you clicked at least n_clicks_max number of times.
--]]
n_clicks_max = 3
--[[
vbtn_base_clicks:
The framework will toggle a virtual button "N" when the button is N-clicked,
whose ID is vbtn_base_clicks + N - 1. Use the provided function VirtualButton
to calculate vbtn_base_clicks for the N=1 virtual button, and additional
buttons will be managed up through n_clicks_max. For example if n_clicks_max=4
and vbtn_base_clicks=VirtualButton(64,8), then button 64,8 will be toggled on
every single-click, 64,9 will be toggled on every double-click, 64,10 will be
toggled on every triple-click, and 64,11 will be toggled every 4-click ...
through vbtn_base_clicks + n_clicks_max.
--]]
function VirtualButton(joy,btn)
if joy < 64 or joy > 72 then
error("Bad joy code, must be 64-72, was " + joy)
elseif btn < 0 or btn > 31 then
error("Bad btn code, must be 0-31, was " + btn)
end
return (joy-64)*32+btn
end
vbtn_base_clicks = VirtualButton(65,1)
--[[
btn_hold_threshold_ms: If after a button click, a button release is not
detected prior to this amount of time (in milliseconds), the button will be
considered held and the clickNhold action for the current value of n_clicks
will be generated. Contrasted with n_click_threshold_ms which constrains how
long you can wait between a button release and the next button press before an
n_click action is generated, this parameter constrains how long you can wait
between a button press and its release before a clickNhold action is generated.
Longer values let your finger move more slowly to release the button when
clicking through an n_click series, but will lengthen how long you have to keep
the button held to generate a clickNhold action. If you only care about n_click
actions and not about any clickNHold actions, then you could set this value to
something impossibly large. Of course actually holding the button down for any
amount of time in an n_click series will lengthen how long it takes before the
n_click action is triggered.
--]]
btn_hold_threshold_ms = 200
--[[
vbtn_base_clkNhold:
The framework will set a virtual button "N" if and only if the button is being
N-click-and held, where N is vbtn_base_clkNhold + N - 1. Use the defined
function VirtualButton(joy,btn) to calculate vbtn_base_clicks for the N=1
virtual button, and additional buttons will be managed up through n_clicks_max.
Only one vButton within the range will be set at a given time, and it will be
cleared when the held button is released. For example if n_clicks_max=4 and
vbtn_base_clicks=VirtualButton(66,1), then button 66,1 will be set on every
single-click-and-hold, 66,2 will be set on every double-click-and-hold, 66,3
will be set on every triple-click-and-hold, 66,4 will be set every
4x-click-and-hold... through vbtn_base_clkNhold + n_clicks_max.
--]]
vbtn_base_clkNhold = VirtualButton(66,1)
--
-- IMPLEMENTATION
--
_sound_pfx_base=joy .. "_" .. btn
_sound_pfx_click=_sound_pfx_base .. "c"
_sound_pfx_clkNhold=_sound_pfx_base .. "h"
-- state variables
_last_btn_dn_time_ms = 0
_n_clicks = 0
_pending_button_rel_events = 0
_n_clkNhold = 0
function _process_button_release(button_currently_released)
event.cancel("_ev_button_held")
if (_n_clkNhold > 0) then
ipc.btnRelease(vbtn_base_clkNhold+_n_clkNhold-1)
_n_clkNhold = 0
else
if (button_currently_released) then
event.timer(n_click_threshold_ms, '_ev_click')
end
end
end
function _button_pressed(j, b, du)
local cur_btn_dn_time_ms=ipc.elapsedtime()
event.cancel("_ev_click")
if (_pending_button_rel_events > 0) then
_process_button_release(false)
end
_pending_button_rel_events = _pending_button_rel_events + 1
if (cur_btn_dn_time_ms - _last_btn_dn_time_ms > n_click_threshold_ms) then
_n_clicks = 1
else
if (_n_clicks < n_clicks_max) then
_n_clicks = _n_clicks + 1
end
end
_last_btn_dn_time_ms = cur_btn_dn_time_ms
event.timer(btn_hold_threshold_ms,"_ev_button_held")
end
function _ev_button_held(elapsed_time_ms)
sound.play(_sound_pfx_clkNhold .. _n_clicks)
event.cancel("_ev_button_held")
ipc.btnPress(vbtn_base_clkNhold+_n_clicks-1)
_n_clkNhold = _n_clicks
end
function _ev_click(elapsed_time_ms)
sound.play(_sound_pfx_click .. _n_clicks)
event.cancel("_ev_click")
ipc.btnToggle(vbtn_base_clicks+_n_clicks-1)
end
function _button_released(j, b, du)
if (_pending_button_rel_events == 1) then
_process_button_release(true)
end
_pending_button_rel_events = _pending_button_rel_events - 1
end
event.button(joy, btn, 1, "_button_pressed")
event.button(joy, btn, 2, "_button_released")
sound.play("ovrldbtn") -- Alert readiness