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