1-- This file is a part of Avoision.
2--
3-- Copyright (C) 2012 Jared Krinke.
4--
5-- Avoision is free software; you can redistribute it and/or
6-- modify it under the terms of the GNU General Public License
7-- as published by the Free Software Foundation; either version 2
8-- of the License, or (at your option) any later version.
9--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13-- GNU General Public License for more details.
14--
15-- You should have received a copy of the GNU General Public License
16-- along with this program; if not, write to the Free Software
17-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19-- Raw events are string representations of unique raw events (e.g. "escape," "c")
20-- Logical events are application defined (e.g. "move up")
21require("Utility/List.lua")
22
23Input = {
24    joystickButtonNames = { },
25    joystickAxisNames = { },
26}
27
28local maxJoysticks = 4
29local maxJoystickButtons = 16
30local maxJoystickAxes = 4
31local joystickAxisThreshold = 0.4
32local j = 1
33
34while j <= maxJoysticks do
35    local buttons = { }
36    local b = 1
37
38    while b <= maxJoystickButtons do
39        local rawEvent = nil
40
41        if j == 1 then
42            rawEvent = "jb" .. b
43        else
44            rawEvent = "j" .. j .. "b" .. b
45        end
46
47        buttons[b] = rawEvent
48        b = b + 1
49    end
50
51    local axes = { }
52    local a = 0
53
54    while a <= maxJoystickAxes do
55        local axis = { }
56
57        if j == 1 then
58            axis[1] = "ja" .. a .. "+"
59            axis[-1] = "ja" .. a .. "-"
60        else
61            axis[1] = "j" .. j .. "a" .. a .. "+"
62            axis[-1] = "j" .. j .. "a" .. a .. "-"
63        end
64
65        axes[a] = axis
66        a = a + 1
67    end
68
69    Input.joystickButtonNames[j] = buttons
70    Input.joystickAxisNames[j] = axes
71    j = j + 1
72end
73
74local function unbind(self, rawEvent, logicalEvent)
75    local rawEvents = self.logicalToRaw[logicalEvent]
76    local acceptable = false
77
78    if not isNil(rawEvents) then
79        -- Make sure the binding can be removed (i.e. the existing binding is not the only binding to a necessary logical event)
80        local properties = self.logicalEvents[logicalEvent]
81
82        if isNil(properties)
83            or not isBoolean(properties.necessary)
84            or not properties.necessary
85            or rawEvents:getCount() > 1
86        then
87            rawEvents:remove(rawEvent)
88            acceptable = true
89        end
90    end
91
92    if acceptable then
93        self.rawToLogical[rawEvent] = nil
94    end
95
96    return acceptable
97end
98
99local function notify(self)
100    -- Notify all listeners that input bindings have been updated
101    self.listeners:forEach(function(handler)
102        handler()
103    end)
104end
105
106function Input.translateJoystickButtonToRawEvent(joystick, button)
107    local name = nil
108
109    if joystick <= maxJoysticks and button <= maxJoystickButtons then
110        name = Input.joystickButtonNames[joystick][button]
111    end
112
113    return name
114end
115
116function Input.translateJoystickAxisMoveToRawEvent(layer, joystick, axis, value)
117    -- Check for previous state tables
118    local joystickAxisPositions = layer.joystickAxisPositions
119
120    if not isTable(joystickAxisPositions) then
121        joystickAxisPositions = { }
122        layer.joystickAxisPositions = joystickAxisPositions
123    end
124
125    local joystickAxisPosition = joystickAxisPositions[joystick]
126
127    if not isTable(joystickAxisPosition) then
128        joystickAxisPosition = { }
129        layer.joystickAxisPositions[joystick] = joystickAxisPosition
130    end
131
132    -- Determine current position
133    local position = 0
134
135    if abs(value) >= joystickAxisThreshold then
136        if value > 0 then
137            position = 1
138        else
139            position = -1
140        end
141    end
142
143    -- Compare to previous position
144    local rawEventPressed = nil
145    local rawEventReleased = nil
146    local previousPosition = joystickAxisPosition[axis]
147
148    if isNil(previousPosition) then
149        previousPosition = 0
150    end
151
152    if position ~= previousPosition then
153        if previousPosition ~= 0 then
154            -- Previously set, but no longer set, so send a release event
155            rawEventReleased = Input.joystickAxisNames[joystick][axis][previousPosition]
156        end
157
158        if position ~= 0 then
159            -- Newly set; send a press event
160            rawEventPressed = Input.joystickAxisNames[joystick][axis][position]
161        end
162    end
163
164    joystickAxisPosition[axis] = position
165
166    return rawEventPressed, rawEventReleased
167end
168
169function Input.new(logicalEvents, defaultRawToLogical)
170    -- Mapping from a raw event to a logical event
171    local input = { }
172
173    function input.bind(self, rawEvent, logicalEvent)
174        -- Remove the old binding (if one exists)
175        local oldLogicalEvent = self.rawToLogical[rawEvent]
176        local acceptable = true
177
178        if not isNil(oldLogicalEvent) then
179            -- Check to make sure the existing binding can be removed
180            acceptable = unbind(self, rawEvent, oldLogicalEvent)
181        end
182
183        if acceptable then
184            -- Insert the new binding
185            self.rawToLogical[rawEvent] = logicalEvent
186
187            if not isNil(logicalEvent) then
188                -- Ensure a list of raw events is present
189                local rawEvents = self.logicalToRaw[logicalEvent]
190
191                if isNil(rawEvents) then
192                    rawEvents = List.new()
193                    self.logicalToRaw[logicalEvent] = rawEvents
194                end
195
196                rawEvents:add(rawEvent)
197            end
198
199            -- Notify all listeners that input bindings have been updated
200            notify(self)
201        end
202
203        return acceptable
204    end
205
206    function input.unbind(self, rawEvent, logicalEvent)
207        unbind(self, rawEvent, logicalEvent)
208
209        -- Notify all listeners that input bindings have been updated
210        notify(self)
211    end
212
213    function input.setupEventHandlers(self, layer)
214        -- Key presses
215        local defaultKeyPressed = layer.keyPressed
216
217        layer.keyPressed = function(l, key, pressed, character)
218            local logicalEvent = self.rawToLogical[key]
219            local handled = false
220
221            -- Call the logical event handler if a mapping exists
222            if isString(logicalEvent) then
223                handled = l:inputReceived(logicalEvent, pressed, "keyPressed", key, character)
224            end
225
226            -- If no mapping exists or the logical event was not handled, send the raw event
227            if not handled and isFunction(defaultKeyPressed) then
228                handled = defaultKeyPressed(l, key, pressed, character)
229            end
230
231            return handled
232        end
233
234        -- Mouse buttons
235        local defaultMouseButtonPressed = layer.mouseButtonPressed
236
237        layer.mouseButtonPressed = function(l, button, pressed, x, y)
238            -- Convert mouse button to raw event
239            local rawEvent = button
240            local logicalEvent = self.rawToLogical[rawEvent]
241            local handled = false
242
243            if isString(logicalEvent) then
244                handled = l:inputReceived(logicalEvent, pressed, "mouseButtonPressed")
245            end
246
247            if not handled and isFunction(defaultMouseButtonPressed) then
248                handled = defaultMouseButtonPressed(l, button, pressed, x, y)
249            end
250
251            return handled
252        end
253
254        -- Joystick buttons
255        local defaultJoystickButtonPressed = layer.joystickButtonPressed
256
257        layer.joystickButtonPressed = function(l, joystick, button, pressed)
258            local rawEvent = Input.translateJoystickButtonToRawEvent(joystick, button)
259            local handled = false
260
261            if isString(rawEvent) then
262                local logicalEvent = self.rawToLogical[rawEvent]
263
264                -- Call the logical event handler if a mapping exists
265                if isString(logicalEvent) then
266                    handled = l:inputReceived(logicalEvent, pressed, "joystickButtonPressed")
267                end
268            end
269
270            -- If no mapping exists or the logical event was not handled, send the raw event
271            if not handled and isFunction(defaultJoystickButtonPressed) then
272                handled = defaultJoystickButtonPressed(l, joystick, button, character)
273            end
274
275            return handled
276        end
277
278        -- Joystick axes
279        local defaultJoystickAxisMoved = layer.joystickAxisMoved
280
281        layer.joystickAxisMoved = function(l, joystick, axis, value)
282            local rawEventPressed
283            local rawEventReleased
284            local handledLogicalPress = false
285            local handledLogicalRelease = false
286            local handledRaw = false
287
288            -- Check both possible logical events for handlers
289            rawEventPressed, rawEventReleased = Input.translateJoystickAxisMoveToRawEvent(l, joystick, axis, value)
290
291            if isString(rawEventPressed) then
292                local logicalEvent = self.rawToLogical[rawEventPressed]
293
294                if isString(logicalEvent) then
295                    handledLogicalPress = l:inputReceived(logicalEvent, true, "joystickAxisMoved")
296                end
297            end
298
299            if isString(rawEventReleased) then
300                local logicalEvent = self.rawToLogical[rawEventReleased]
301
302                if isString(logicalEvent) then
303                    handledLogicalRelease = l:inputReceived(logicalEvent, false, "joystickAxisMoved")
304                end
305            end
306
307            -- For joystick axes (which are actually stateless), always call the raw callback
308            if isFunction(defaultJoystickAxisMoved) then
309                handledRaw = defaultJoystickAxisMoved(l, joystick, button, character)
310            end
311
312            return handledLogicalPress or handledLogicalRelease or handledRaw
313        end
314    end
315
316    function input.addListener(self, handler)
317        if isFunction(handler) then
318            self.listeners:add(handler)
319        end
320    end
321
322    function input.getRawEvents(self, logicalEvent)
323        return self.logicalToRaw[logicalEvent]
324    end
325
326    function input.getRawToLogical(self)
327        return input.rawToLogical
328    end
329
330    function input.getLogicalEventProperties(self, logicalEvent)
331        return input.logicalEvents[logicalEvent]
332    end
333
334    input.listeners = List.new()
335
336    if not isNil(defaultRawToLogical) then
337        input.rawToLogical = { }
338        input.logicalToRaw = { }
339
340        Table.forEach(defaultRawToLogical, function(key, value)
341            input:bind(key, value)
342        end)
343    else
344        input.rawToLogical = { }
345        input.logicalToRaw = { }
346    end
347
348    input.logicalEvents = logicalEvents
349
350    return input
351end
352
353