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