├── .gitignore
├── res
├── axes.png
├── logo.png
├── mouse.png
└── xbox_360_controller.png
├── .gitmodules
├── run.sh
├── LICENSE
├── main.lua
├── README.md
├── MANUAL.md
└── Lynput.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | *~*
2 | *#*
3 | *.love
--------------------------------------------------------------------------------
/res/axes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lydzje/lynput/HEAD/res/axes.png
--------------------------------------------------------------------------------
/res/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lydzje/lynput/HEAD/res/logo.png
--------------------------------------------------------------------------------
/res/mouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lydzje/lynput/HEAD/res/mouse.png
--------------------------------------------------------------------------------
/res/xbox_360_controller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lydzje/lynput/HEAD/res/xbox_360_controller.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/classic"]
2 | path = lib/classic
3 | url = git@github.com:Lydzje/classic.git
4 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ./build.sh
3 |
4 | printf "\n[RUNNING LOVE PROJECT...]\n\n"
5 | love lynput.love
6 | printf "\n[LOVE PROJECT TERMINATED]\n\n"
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lydzje
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.lua:
--------------------------------------------------------------------------------
1 | function love.load()
2 | Lynput = require("Lynput")
3 | Lynput.load_key_callbacks()
4 | Lynput.load_mouse_callbacks()
5 | Lynput.load_gamepad_callbacks()
6 | lynput = Lynput()
7 |
8 | lynput:bind("exit", "release escape")
9 |
10 | lynput:attachGamepad("GPAD_1")
11 |
12 | lynput:bind("pressing", {"press p", "press LMB", "press G_A"})
13 | lynput:bind("releasing", {"release r", "release RMB", "release G_B"})
14 | lynput:bind("holding", {"hold h", "hold MMB", "hold G_X"})
15 |
16 | lynput:unbindAll("holding")
17 |
18 | lynput:bind("moveLeft", {"-100:-50 G_LEFTSTICK_X"})
19 | lynput:bind("moveRight", {"50:100 G_LEFTSTICK_X"})
20 |
21 | lynput:unbind("moveRight", "50:100 G_LEFTSTICK_X")
22 |
23 | lynput:bind("RTing", "0:100 G_RT")
24 |
25 | lynput:bind("pressAny", "press any")
26 | lynput:bind("releaseAny", "release any")
27 | lynput:bind("holdAny", "hold any")
28 | end
29 |
30 |
31 | function love.update(dt)
32 | if lynput.exit then
33 | love.event.quit()
34 | end -- if exit
35 |
36 | -- if lynput.pressAny then
37 | -- print("Pressed ANY")
38 | -- end --
39 |
40 | -- if lynput.releaseAny then
41 | -- print("Released ANY")
42 | -- end --
43 |
44 | -- if lynput.holdAny then
45 | -- print("Holding ANY")
46 | -- end
47 |
48 | print(lynput:getAxis("righty"))
49 |
50 | if lynput.pressing then
51 | print("Pressing")
52 | end -- if pressing
53 |
54 | if lynput.releasing then
55 | print("Releasing")
56 | lynput:unbind("releasing", {"release r", "release RMB", "release G_B"})
57 | end -- if releasing
58 |
59 | if lynput.holding then
60 | print("Holding")
61 | end -- if holding
62 |
63 | if lynput.moveLeft then
64 | print("moving left")
65 | end -- if moveLeft
66 |
67 | if lynput.moveRight then
68 | print("Moving right")
69 | end -- if moveRight
70 |
71 | if lynput.RTing then
72 | print("RTing")
73 | end -- if RTing
74 |
75 |
76 | Lynput.update_(dt)
77 | end
78 |
79 |
80 | function love.draw()
81 | love.graphics.setColor(255, 255, 255)
82 | end
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lynput
2 |
3 | [](https://love2d.org/wiki/Category:Versions)
4 | [](LICENSE)
5 |
6 | 
7 |
8 | ## Index
9 | - [What is Lynput?](#what-is-lynput)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [Devices supported](#devices-supported)
13 | - [Features](#features)
14 | - [What does Lynput mean?](#what-does-lynput-mean)
15 | - [I've found a bug, what do I do?](#ive-found-a-bug-what-do-i-do)
16 | - [Contact](#contact)
17 | - [License](#license)
18 |
19 | ## What is Lynput?
20 | **Lynput** is an input library for [LÖVE](https://love2d.org/) that makes input handling very easy and intuitive 💙. It will make you able to do things like this:
21 |
22 | ```lua
23 | function love.load()
24 | Lynput = require("Lynput") -- Load Lynput
25 | Lynput.load_key_callbacks() -- Load keyboard callbacks
26 | control = Lynput() -- Create a Lynput object
27 |
28 | control:bind(
29 | "moveLeft",
30 | {
31 | "hold left",
32 | "hold a",
33 | }
34 | )
35 | control:bind(
36 | "moveRight",
37 | {
38 | "hold right",
39 | "hold d",
40 | }
41 | )
42 | control:bind("action", "press f")
43 | control:bind("obey", "release any")
44 | end
45 |
46 | function love.update(dt)
47 | if control.moveLeft then x = x - speed * dt end
48 | if control.moveRight then x = x + speed * dt end
49 | if control.action then triggerAction() end
50 | if control.obey then obey() end
51 |
52 | Lynput.update_(dt) -- Update Lynput
53 | end
54 | ```
55 |
56 | ## Installation
57 | Just download the [latest release version of Lynput.lua](https://github.com/Lydzje/lynput/releases/latest) and extract the contents from the zip file. Place the Lynput.lua file anywhere you want inside your game folder, just be careful with the path when requiring the library. Also remember that this file name starts with a capital letter.
58 |
59 | ## Usage
60 | See [MANUAL](MANUAL.md) for more information.
61 |
62 | ## Devices supported
63 | - [x] Keyboard
64 | - [x] Mouse buttons
65 | - [x] Gamepad buttons
66 | - [x] Gamepad analog input
67 | - [ ] Touch screen
68 | - [ ] ...
69 |
70 | ## Features
71 | - [x] Multiple independent input objects
72 | - [x] Easy and intuitive input binding and unbindig
73 | - [ ] Saving and loading input configuration files
74 | - [ ] Things like this: lynput:bind("superPunch", "press G_RB+G_X")
75 | - [ ] ...
76 |
77 | ## What does Lynput mean?
78 | ```lua
79 | if not creativity then
80 | name = Lydzje + input
81 | print(name)
82 | end -- if not creativity
83 | ```
84 | > **Output:**
85 | >
86 | > Lynput
87 |
88 | ## I've found a bug, what do I do?
89 | If you want to report a bug (please do!), [open a new issue](https://github.com/Lydzje/lynput/issues). As another option just [contact me](#contact).
90 |
91 | ## Contact
92 | If you need to contact me don't hesitate to [send me an email](mailto:to.lydzje@gmail.com). If you preffer other way, please visit the contact section in my website [lydzje.com](https://lydzje.com).
93 |
94 | ## License
95 | This software is licensed under the MIT license. Check the details by clicking [here](LICENSE).
96 |
--------------------------------------------------------------------------------
/MANUAL.md:
--------------------------------------------------------------------------------
1 | # Lynput Manual
2 |
3 | [](https://love2d.org/wiki/Category:Versions)
4 | [](LICENSE)
5 |
6 | 
7 |
8 | ## Index
9 | - [Requirements](#requirements)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [Basics](#basics)
13 | - [Lynput callbacks](#lynput-callbacks)
14 | - [Input states](#input-states)
15 | - [Button states](#buttons-states)
16 | - [Axis states](#axis-states)
17 | - [Keyboard](#keyboard)
18 | - [Mouse](#mouse)
19 | - [Gamepad](#gamepad)
20 | - [Configuring multiple inputs at once](#configuring-multiple-inputs-at-once)
21 | - [The "any" input](#the-any-input)
22 | - [I've bind my controls, what's next?](#ive-bind-my-controls-whats-next)
23 | - [Names that can't be used as an action](#names-that-cant-be-used-as-an-action)
24 | - [All available functions](#all-available-functions)
25 | - [I've found a bug, what do I do?](#ive-found-a-bug-what-do-i-do)
26 | - [Contact](#contact)
27 | - [License](#license)
28 |
29 | ## Requirements
30 | Lynput requires LÖVE version 0.10.0 or later.
31 |
32 | ## Installation
33 | Just download the [latest release version of Lynput.lua](https://github.com/Lydzje/lynput/releases/latest) and extract the contents from the zip file. Place the Lynput.lua file anywhere you want inside your game folder, just be careful with the path when requiring the library. Also remember that this file name starts with a capital letter.
34 |
35 | ## Usage
36 | ### Basics
37 | First you need to load the library:
38 | ```lua
39 | Lynput = require("path.to.Lynput") -- Notice that the file starts with a capital letter
40 | ```
41 | Then, you need to create a Lynput object:
42 | ```lua
43 | control = Lynput()
44 | ```
45 | Lynput objects need to be updated in order to work:
46 | ```lua
47 | -- put this after all your game logic happens, for example at the bottom of love.update(dt)
48 | Lynput.update_(dt) -- Notice the underscore
49 | ```
50 | Once you don't need the object anymore, destroy it with:
51 | ```lua
52 | control:remove()
53 | ```
54 |
55 | ### Lynput callbacks
56 | To make Lynput able to check your computer input, it's necessary to set its callbacks. You can set them by yourself, or you can let Lynput do this job. Do it yourself if you need to override the LÖVE callbacks with more stuff aside from Lynput callbacks.
57 |
58 | Only set those that will be used for better performance.
59 |
60 | #### Keyboard callbacks
61 | To make Lynput load the keyboard callbacks:
62 | ```lua
63 | Lynput.load_key_callbacks()
64 | ```
65 | To load them by yourself, override this LÖVE functions as indicated:
66 | ```lua
67 | function love.keypressed(key)
68 | -- your stuff
69 | Lynput.onkeypressed(key)
70 | -- your stuff
71 | end
72 |
73 | function love.keyreleased(key)
74 | -- your stuff
75 | Lynput.onkeyreleased(key)
76 | -- your stuff
77 | end
78 | ```
79 |
80 | #### Mouse callbacks
81 | To make Lynput load the mouse callbacks:
82 | ```lua
83 | Lynput.load_mouse_callbacks()
84 | ```
85 | To load them by yourself, override this LÖVE functions as indicated:
86 | ```lua
87 | function love.mousepressed(x, y, button, istouch)
88 | -- your stuff
89 | Lynput.onmousepressed(button)
90 | -- your stuff
91 | end
92 |
93 | function love.mousereleased(x, y, button, istouch)
94 | -- your stuff
95 | Lynput.onmousereleased(button)
96 | -- your stuff
97 | end
98 | ```
99 |
100 | #### Gamepad callbacks
101 | To make Lynput load the gamepad callbacks:
102 | ```lua
103 | Lynput.load_gamepad_callbacks()
104 | ```
105 | To load them by yourself, override this LÖVE functions as indicated:
106 | ```lua
107 | function love.gamepadpressed(joystick, button)
108 | -- your stuff
109 | Lynput.ongamepadpressed(joystick:getID(), button)
110 | -- your stuff
111 | end
112 |
113 | function love.gamepadreleased(joystick, button)
114 | -- your stuff
115 | Lynput.ongamepadreleased(joystick:getID(), button)
116 | -- your stuff
117 | end
118 |
119 | function love.joystickadded(joystick)
120 | -- your stuff
121 | Lynput.ongamepadadded(joystick)
122 | -- your stuff
123 | end
124 | ```
125 |
126 | ### Input states
127 | For now, there are two kinds of states for inputs: **button states** and **axis states**.
128 |
129 | #### Button states
130 | Button states indicates the state of a button :sweat_smile:, and those can be: press, release or hold.
131 |
132 | So if you want the player to move left when holding left arrow, you'd do:
133 | ```lua
134 | playerControl:bind("moveLeft", "hold left")
135 | ```
136 |
137 | #### Axis states
138 | Axis states indicates the state of an axis :unamused:, and since its state is a number and it depends on how much you move your sticks or triggers, going from -1 to +1 in LÖVE (0 to +1 for triggers like G_LT or G_RT), there are infinite states :fearful:.
139 |
140 | But don't worry, we are not going to specify an specific state, but an interval, and we are going to multiply it by 100 because it's easier to read.
141 |
142 | 
143 |
144 | So, if you want the player to move left when moving the left stick of a gamepad along the x axis, you'd do:
145 | ```lua
146 | playerControl:bind("moveLeft", "-100:0 G_LEFTSTICK_X") -- It won't be -100:0, but -100:-30 because Lynput has a default dead zone of 30
147 | ```
148 | To make the player move right:
149 | ```lua
150 | playerControl:bind("moveRight", "0:100 G_LEFTSTICK_X") -- It won't be 0:100, but -100:-30 because Lynput has a default dead zone of 30
151 | ```
152 |
153 | ### Keyboard
154 | To specify a key you just need to know [how LÖVE names that key](https://love2d.org/wiki/KeyConstant). Then if you want the player to jump by pressing space, you'd do:
155 | ```lua
156 | playerControl:bind("jump", "press space")
157 | ```
158 |
159 | ### Mouse
160 | To specify a mouse button you just need to know how Lynput names them (see image below), since LÖVE uses numbers and Lynput can't because they are for keyboard keys.
161 |
162 | 
163 |
164 | Then if you want the player to shoot by pressing left click, you'd do:
165 | ```lua
166 | playerControl:bind("shoot", "press LMB")
167 | ```
168 |
169 | ### Gamepad
170 | In order to use gamepads, we have to bind a Lynput object with a gamepad. You can do this by:
171 |
172 | ```lua
173 | playerControl:attachGamepad("GPAD_1") -- This will be the gamepad number 1, for the second one use 2, third 3, and so on
174 | ```
175 |
176 | Once it's done, you can start to configure your controls.
177 |
178 | To specify a gamepad button or axis you just need to know how Lynput names them (see image below), since LÖVE uses names that Lynput can't because they are for keyboard keys.
179 |
180 | 
181 |
182 | Then if you want the player to jump by pressing A and move up with the left stick Y axis, you'd do:
183 | ```lua
184 | playerControl:bind("jump", "press G_A")
185 | playerControl:bind("moveUp", "-100:0 G_LEFTSTICK_Y")
186 | ```
187 |
188 | But if G_LEFTSTICK_Y has a value of -10, moveUp will be false, why? Because Lynput sets a default dead zone of 30 for gamepads when creating a Lynput object. You can change the dead zone doing this:
189 | ```lua
190 | playerControl.gpadDeadZone = 0
191 | ```
192 |
193 | ### Configuring multiple inputs at once
194 | If you want the player to move down using down arrow, key s, dpad down and left stick Y axis, you don't need to make four bindings. You can just do:
195 | ```lua
196 | playerControl:bind(
197 | "moveDown",
198 | {
199 | "hold down",
200 | "hold s",
201 | "hold G_DPAD_DOWN",
202 | "0:100 G_LEFTSTICK_Y"
203 | }
204 | )
205 | ```
206 |
207 | ### The "any" input
208 | Imagine you are making a game that starts with a intro with some logos, clips and other stuff, and then you want a screen title saying "Press any button to start". How would you do that with Lynput? It's really easy, you just need to bind all keys to an action called any :alien:. I'm just joking lol, for that kind of purposes there is an input called any. To make that screen title control you'd do:
209 |
210 | ```lua
211 | control:bind("start", "press any")
212 | ```
213 |
214 | ### I've bind my controls, what's next?
215 | Now, you need to check if your controls have been triggered. You can do this in your update functions as follows:
216 |
217 | ```lua
218 | if playerControl.moveLeft then moveLeft() end
219 | if playerControl.moveRight then moveRight() end
220 | if playerControl.moveUp then moveUp() end
221 | if playerControl.moveDown then moveDown() end
222 | if playerControl.jump then jump() end
223 | if playerControl.shoot then shoot() end
224 | if control.start then goToMainMenu() end
225 | ```
226 |
227 | ### Names that can't be used as an action
228 | What's an action? Well, for Lynput an action is the name you give to the thing you want the player to do. If you want your player to pause the game by pressing p you'd do:
229 |
230 | ```lua
231 | playerControls:bind("pause", "press p")
232 | ```
233 |
234 | In the case above, pause, the first argument, is the action. There is no problem if you call your action "pause", but you cannot use [words or characters reserved by Lua](https://www.lua.org/manual/5.1/manual.html#2.1) or Lynput (see table below).
235 |
236 | | Reserved by Lynput |
237 | |:--------------------------: |
238 | | inputsSet |
239 | | gpad |
240 | | gpadDeadZone |
241 | | id |
242 | | remove |
243 | | attachGamepad |
244 | | bind |
245 | | unbind |
246 | | unbindAll |
247 | | removeAction |
248 | | update |
249 |
250 | ## All available functions
251 | | Function | Description | Example |
252 | |---------------------------------------------- |-------------------------------------------------------------------------------- |----------------------------------------------------- |
253 | | Lynput.load_key_callbacks() | Sets all keyboard callbacks | Lynput.load_key_callbacks() |
254 | | Lynput.load_mouse_callbacks() | Sets all mouse callbacks | Lynput.load_mouse_callbacks() |
255 | | Lynput.load_gamepad_callbacks() | Sets all gamepad callbacks | Lynput.load_gamepad_callbacks() |
256 | | Lynput.update_(dt) | Update all Lynput objects | Lynput.update_(dt) |
257 | | Lynput:remove() | Remove the calling Lynput object | controls:remove() |
258 | | Lynput:attachGamepad(gamepad) | Attachs a gamepad to the calling Lynput object | controls:attachGamepad("GPAD_1") |
259 | | Lynput:getAxis(axis) | Returns the value of the specified axis. LÖVE and Lynput axes names can be both used. The value is the one LÖVE returns but multiplied by 100. | controls:getAxis("G_LEFTSTICK_X") |
260 | | Lynput:bind(action, commands) | Binds commands to an action for the calling Lynput object | controls:bind("jump", "press space") |
261 | | Lynput:unbind(action, commands) | Unbinds commands from an action for the calling Lynput object | controls:unbind("jump", "press space") |
262 | | Lynput:unbindAll(action) | Unbinds all commands from an action for the calling Lynput object | controls:unbindAll("jump") |
263 | | Lynput:removeAction(action) | Removes an action of the calling Lynput object | controls:removeAction("jump") |
264 |
265 | ## I've found a bug, what do I do?
266 | If you want to report a bug (please do!), [open a new issue](https://github.com/Lydzje/lynput/issues). As another option just [contact me](#contact).
267 |
268 | ## Contact
269 | If you need to contact me don't hesitate to [send me an email](mailto:to.lydzje@gmail.com). If you preffer other way, please visit the contact section in my website [lydzje.com](https://lydzje.com).
270 |
271 | ## License
272 | This software is licensed under the MIT license. Check the details by clicking [here](LICENSE).
273 |
--------------------------------------------------------------------------------
/Lynput.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- Lynput
3 | --
4 | -- Copyright (c) 2019, Lydzje
5 | --
6 | -- This module is free software; you can redistribute it and/or modify it under
7 | -- the terms of the MIT license. See LICENSE for details.
8 | --
9 |
10 | local Lynput = {}
11 | Lynput.__index = Lynput
12 |
13 | setmetatable(
14 | Lynput,
15 | {
16 | __call = function (cls, ...)
17 | return cls.new(...)
18 | end,
19 | }
20 | )
21 |
22 | Lynput.s_lynputs = {}
23 | Lynput.s_idCount = 0
24 | Lynput.s_count = 0
25 |
26 | Lynput.s_reservedNames = {
27 | -- Reserved by Lua
28 | "and", "break", "do", "else", "elseif", "end", "false", "for",
29 | "function", "if", "in", "local", "nil", "not", "or", "repeat",
30 | "return", "then", "true", "until", "while",
31 | -- Reserved by Lynput
32 | "inputsSet", "gpad", "gpadDeadZone", "id", "remove", "attachGamepad",
33 | "bind", "unbind", "unbindAll", "removeAction", "update"
34 | }
35 |
36 | Lynput.s_reservedCharacters = {
37 | "+", "-", "*", "/", "%", "^", "#", "==", "~=", "<=", ">=", "<", ">",
38 | "=", "(", ")", "{", "}", "[", "]", ";", ":", ",", ".", "..", "..."
39 | }
40 |
41 | ----------------
42 | -- DICTIONARIES
43 | ----------------
44 | Lynput.s_mouseButtons = {
45 | ["1"]="LMB", ["2"]="RMB", ["3"]="MMB", ["x1"]="MB4", ["x2"]="MB5"
46 | }
47 |
48 | Lynput.s_gamepadButtons = {
49 | ["a"]="G_A", ["b"]="G_B", ["x"]="G_X", ["y"]="G_Y",
50 | ["back"]="G_BACK", ["guide"]="G_GUIDE", ["start"]="G_START",
51 | ["leftstick"]="G_LEFTSTICK", ["rightstick"]="G_RIGHTSTICK",
52 | ["leftshoulder"]="G_LB", ["rightshoulder"]="G_RB",
53 | ["dpup"]="G_DPAD_UP", ["dpdown"]="G_DPAD_DOWN",
54 | ["dpleft"]="G_DPAD_LEFT", ["dpright"]="G_DPAD_RIGHT"
55 | }
56 |
57 | Lynput.s_gamepadAxes = {
58 | ["leftx"]="G_LEFTSTICK_X", ["lefty"]="G_LEFTSTICK_Y",
59 | ["rightx"]="G_RIGHTSTICK_X", ["righty"]="G_RIGHTSTICK_Y",
60 | ["triggerleft"]="G_LT", ["triggerright"]="G_RT"
61 | }
62 |
63 | -----------------------
64 | -- FUTURE DICTIONARIES
65 | -----------------------
66 | -- Lynput.s_mouse_axes = {
67 | -- "wd", "wu"
68 | -- }
69 |
70 |
71 | function Lynput.new()
72 | local self = setmetatable({}, Lynput)
73 | -- Maps Lynput inputs and states to actions, inputsSet[state][action]
74 | self.inputsSet = {}
75 |
76 | self.gpad = nil
77 | -- TODO: Different deadzones for joysticks and triggers
78 | self.gpadDeadZone = 30
79 |
80 | self.id = tostring(Lynput.s_idCount)
81 | Lynput.s_lynputs[self.id] = self
82 | Lynput.s_idCount = Lynput.s_idCount + 1
83 | Lynput.s_count = Lynput.s_count + 1
84 |
85 | return self
86 | end
87 |
88 |
89 | function Lynput.load_key_callbacks()
90 | function love.keypressed(key) Lynput.onkeypressed(key) end
91 | function love.keyreleased(key) Lynput.onkeyreleased(key) end
92 | end
93 |
94 |
95 | function Lynput.load_mouse_callbacks()
96 | function love.mousepressed(x, y, button, istouch) Lynput.onmousepressed(button) end
97 | function love.mousereleased(x, y, button, istouch) Lynput.onmousereleased(button) end
98 | end
99 |
100 |
101 | function Lynput.load_gamepad_callbacks()
102 | function love.gamepadpressed(joystick, button) Lynput.ongamepadpressed(joystick:getID(), button) end
103 | function love.gamepadreleased(joystick, button) Lynput.ongamepadreleased(joystick:getID(), button) end
104 | function love.joystickadded(joystick) Lynput.ongamepadadded(joystick) end
105 | end
106 |
107 |
108 | function Lynput.update_(dt)
109 | for _, lynput in pairs(Lynput.s_lynputs) do
110 | lynput:update(dt)
111 | end -- for each lynput
112 | end
113 |
114 |
115 | local function _isActionValid(action)
116 | if type(action) ~= "string" then
117 | return false
118 | end -- if not string
119 |
120 | for _, reservedName in ipairs(Lynput.s_reservedNames) do
121 | if reservedName == action then
122 | return false
123 | end -- if action name is reserved
124 | end -- for each reserved name
125 |
126 | for _, reservedChar in ipairs(Lynput.s_reservedCharacters) do
127 | if string.find(action, "%" .. reservedChar) then
128 | return false
129 | end -- if action name contains reserved characters
130 | end -- for each reserved character
131 |
132 | return true
133 | end
134 |
135 |
136 | local function _isCommandValid(command)
137 | local stateValid, inputValid = false, false
138 | local state, input = string.match(command, "(.+)%s(.+)")
139 |
140 | if not state or not input then
141 | goto exit
142 | end -- if state or input are nil
143 |
144 | -- Process state
145 | stateValid =
146 | state == "release" or
147 | state == "press" or
148 | state == "hold"
149 |
150 | if not stateValid then
151 | local min, max = string.match(state, "(.+)%:(.+)")
152 |
153 | min = tonumber(min)
154 | max = tonumber(max)
155 |
156 | if not min or not max then
157 | goto exit
158 | end -- if min or max are nil
159 |
160 | stateValid =
161 | min < max and
162 | min >= -100 and
163 | max <= 100
164 | end -- if state is not meant for buttons
165 |
166 | -- Process input
167 | if input == "any" then
168 | inputValid = true
169 | goto exit
170 | end -- if input == any
171 |
172 | for _, button in pairs(Lynput.s_mouseButtons) do
173 | if button == input then
174 | inputValid = true
175 | goto exit
176 | end -- if button == input
177 | end -- for each Lynput mouse button
178 |
179 | for _, button in pairs(Lynput.s_gamepadButtons) do
180 | if button == input then
181 | inputValid = true
182 | goto exit
183 | end -- if button == input
184 | end -- for each Lynput gamepad button
185 |
186 | for _, axis in pairs(Lynput.s_gamepadAxes) do
187 | if axis == input then
188 | inputValid = true
189 | goto exit
190 | end -- if axis == input
191 | end -- for each Lynput gamepad axis
192 |
193 | -- TODO: Touch screen
194 |
195 | inputValid = pcall(love.keyboard.getScancodeFromKey, input)
196 |
197 | ::exit::
198 |
199 | return stateValid and inputValid, state, input
200 | end
201 |
202 |
203 | function Lynput:remove()
204 | Lynput.s_lynputs[self.id] = nil
205 | Lynput.s_count = Lynput.s_count - 1
206 | end
207 |
208 |
209 | -- @param gamepad is a Lynput gamepad string name (e.g., "GPAD_1", "GPAD_2", ..., "GPAD_N")
210 | function Lynput:attachGamepad(gamepad)
211 | -- TODO: More code, this needs to check if the parameter given is like expected
212 |
213 | self.gpad = gamepad
214 | end
215 |
216 |
217 | function Lynput:getAxis(axis)
218 | for loveAxis, lynputAxis in pairs(Lynput.s_gamepadAxes) do
219 | if axis == loveAxis or axis == lynputAxis then
220 | return Lynput[self.gpad]:getGamepadAxis(loveAxis) * 100
221 | end
222 | end
223 |
224 | error(
225 | "Axis->" .. axis .. " is not a valid name. Use LÖVE or Lynput names for axes."
226 | )
227 | end
228 |
229 |
230 | function Lynput:bind(action, commands)
231 | -- Type checking for argument #1
232 | assert(
233 | type(action) == "string",
234 | "bad argument #1 to 'Lynput:bind' (string expected, got " .. type(action) .. ")" ..
235 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
236 | )
237 |
238 | -- Transforms 1 command to a table
239 | if type(commands) ~= "table" then
240 | local command = commands
241 | commands = {}
242 | commands[1] = command
243 | end -- if only one command was given
244 |
245 | -- Type checking for argument #2
246 | for i, command in ipairs(commands) do
247 | assert(
248 | type(command) == "string",
249 | "bad argument #2 to 'Lynput:bind' (string or table of strings expected, got " ..
250 | type(command) .. " in element #" .. i .. ")" ..
251 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
252 | )
253 | end -- for each element in commands table
254 |
255 | -- Process command
256 | for _, command in ipairs(commands) do
257 | -- Is action valid?
258 | assert(
259 | _isActionValid(action),
260 | "Could not bind command->" .. command ..
261 | -- TODO: Check documentation for valid actions message
262 | " to action->" .. action .. ", the action is not valid."
263 | )
264 | -- Is command valid?
265 | local commandValid, state, input = _isCommandValid(command)
266 | -- TODO: Use a "check the manual for commands format" instead of explaining it here
267 | assert(
268 | commandValid,
269 | "Could not bind command->" .. command ..
270 | " to action->" .. action .. ", the command is not valid." ..
271 | "\n\nCommands should be formated as follows: \n\n" ..
272 | " COMMAND = STATE INPUT (with a space in between)\n" ..
273 | " STATE =\n" ..
274 | " for buttons -> press, release or hold\n" ..
275 | " for analog inputs -> x:y (with a colon in between) where x and y are numbers\n" ..
276 | " INPUT = check the manual for all availible inputs\n\n"
277 | )
278 |
279 | if self[action] == nil then
280 | self[action] = false
281 | end -- if action not set
282 |
283 | if not self.inputsSet[input] then
284 | self.inputsSet[input] = {}
285 | end -- if input hasn't already been set
286 |
287 | self.inputsSet[input][state] = action
288 | end -- for each command
289 | end
290 |
291 |
292 | function Lynput:unbind(action, commands)
293 | -- Type checking for argument #1
294 | assert(
295 | type(action) == "string",
296 | "bad argument #1 to 'Lynput:unbind' (string expected, got " .. type(action) .. ")" ..
297 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
298 | )
299 |
300 | -- Transforms 1 command to a table
301 | if type(commands) ~= "table" then
302 | local command = commands
303 | commands = {}
304 | commands[1] = command
305 | end -- if only one command was given
306 |
307 | -- Type checking for argument #2
308 | for i, command in ipairs(commands) do
309 | assert(
310 | type(command) == "string",
311 | "bad argument #2 to 'Lynput:unbind' (string or table of strings expected, got " ..
312 | type(command) .. " in element #" .. i .. ")" ..
313 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
314 | )
315 | end -- for each element in commands table
316 |
317 | -- Process command
318 | for _, command in ipairs(commands) do
319 | -- Is action set?
320 | -- FIXME: Exception when indexing nil values are not being handled, fix everywhere
321 | assert(
322 | self[action] ~= nil,
323 | "Could not unbind command->" .. command ..
324 | " to action->" .. action .. ", the action is not set."
325 | )
326 | -- Is command set?
327 | local state, input = string.match(command, "(.+)%s(.+)")
328 | assert(
329 | self.inputsSet[input][state],
330 | "Could not unbind command->" .. command ..
331 | " to action->" .. action .. ", the command is not set."
332 | )
333 |
334 | self.inputsSet[input][state] = nil
335 | local inputStates = self.inputsSet[input]
336 |
337 | local statesNum = 0
338 | for state, _ in pairs(self.inputsSet[input]) do
339 | statesNum = statesNum + 1
340 | end -- for each state
341 |
342 | if statesNum == 0 then
343 | self.inputsSet[input] = nil
344 | end -- if there are no more states set
345 |
346 | self[action] = false
347 | end -- for each command
348 | end
349 |
350 |
351 | function Lynput:unbindAll(action)
352 | -- Type checking for argument #1
353 | assert(
354 | type(action) == "string",
355 | "bad argument #1 to 'Lynput:unbindAll' (string expected, got " .. type(action) .. ")" ..
356 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
357 | )
358 |
359 | -- Is action set?
360 | assert(
361 | self[action] ~= nil,
362 | "Could not unbind all commands in action->" .. action ..
363 | ", the action is not set."
364 | )
365 |
366 | for inputSet, states in pairs(self.inputsSet) do
367 | for state, actionSet in pairs(states) do
368 | if actionSet == action then
369 | self.inputsSet[inputSet][state] = nil
370 | end -- if actionSet == action
371 | end -- for each inputSet state
372 | end -- for each input set
373 |
374 | self[action] = false
375 | end
376 |
377 |
378 | function Lynput:removeAction(action)
379 | -- Type checking for argument #1
380 | assert(
381 | type(action) == "string",
382 | "bad argument #1 to 'Lynput:removeAction' (string expected, got " .. type(action) .. ")" ..
383 | "\nCheck the stack traceback to know where the invalid arguments have been passed"
384 | )
385 |
386 | -- Is action set?
387 | assert(
388 | self[action] ~= nil,
389 | "Could not remove action->" .. action ..
390 | ", this action does not exist."
391 | )
392 |
393 | self:unbindAll(action)
394 | self[action] = nil
395 | end
396 |
397 |
398 | function Lynput:update(dt)
399 | -- It's not possible to iterate actions through "self" because it
400 | -- also contains the inputsSet table and other data
401 | for _, states in pairs(self.inputsSet) do
402 | for state, actionSet in pairs(states) do
403 | if state == "press" or state == "release" then
404 | self[actionSet] = false
405 | end -- if state ~= hold
406 | end -- for each state
407 | end -- for each input set
408 |
409 | if Lynput[self.gpad] then
410 | for loveAxis, lynputAxis in pairs(Lynput.s_gamepadAxes) do
411 | if self.inputsSet[lynputAxis] then
412 | local val = Lynput[self.gpad]:getGamepadAxis(loveAxis) * 100
413 |
414 | for interval, action in pairs(self.inputsSet[lynputAxis]) do
415 | local min, max = string.match(interval, "(.+)%:(.+)")
416 | min = tonumber(min)
417 | max = tonumber(max)
418 | if val >= min and (math.abs(val) > self.gpadDeadZone) and val <= max then
419 | self[action] = true
420 | else
421 | self[action] = false
422 | end -- if val is in interval
423 | end -- for each interval
424 | end -- if the axis is set
425 | end -- for each axis
426 | end -- if the gamepad has been added
427 | end
428 |
429 |
430 | ---------------------------------
431 | -- KEYBOARD CALLBACKS
432 | ---------------------------------
433 | function Lynput.onkeypressed(key)
434 | for _, lynput in pairs(Lynput.s_lynputs) do
435 | if lynput.inputsSet["any"] then
436 | if lynput.inputsSet["any"]["press"] then
437 | local action = lynput.inputsSet["any"]["press"]
438 | lynput[action] = true
439 | end -- if press_any is set
440 | if lynput.inputsSet["any"]["hold"] then
441 | local action = lynput.inputsSet["any"]["hold"]
442 | lynput[action] = true
443 | end -- if hold_any is set
444 | end -- if "any" is set
445 |
446 | if lynput.inputsSet[key] then
447 | if lynput.inputsSet[key]["press"] then
448 | local action = lynput.inputsSet[key]["press"]
449 | lynput[action] = true
450 | end -- if press_key is set
451 | if lynput.inputsSet[key]["hold"] then
452 | local action = lynput.inputsSet[key]["hold"]
453 | lynput[action] = true
454 | end -- if hold_key is set
455 | end -- if key set
456 | end -- for each lynput
457 | end
458 |
459 |
460 | function Lynput.onkeyreleased(key)
461 | for _, lynput in pairs(Lynput.s_lynputs) do
462 | if lynput.inputsSet["any"] then
463 | if lynput.inputsSet["any"]["release"] then
464 | local action = lynput.inputsSet["any"]["release"]
465 | lynput[action] = true
466 | end -- if release_any is set
467 | if lynput.inputsSet["any"]["hold"] then
468 | local action = lynput.inputsSet["any"]["hold"]
469 | lynput[action] = false
470 | end -- if hold_any is set
471 | end -- if "any" is set
472 |
473 | if lynput.inputsSet[key] then
474 | if lynput.inputsSet[key]["release"] then
475 | local action = lynput.inputsSet[key]["release"]
476 | lynput[action] = true
477 | end -- if release_key is set
478 | if lynput.inputsSet[key]["hold"] then
479 | local action = lynput.inputsSet[key]["hold"]
480 | lynput[action] = false
481 | end -- if hold_key is set
482 | end -- if key is set
483 | end -- for each lynput
484 | end
485 |
486 |
487 | --------------------------------------
488 | -- MOUSE CALLBACKS
489 | --------------------------------------
490 | function Lynput.onmousepressed(button)
491 | -- Translate LÖVE button to Lynput button
492 | button = Lynput.s_mouseButtons[tostring(button)]
493 | -- Process button
494 | for _, lynput in pairs(Lynput.s_lynputs) do
495 | if lynput.inputsSet["any"] then
496 | if lynput.inputsSet["any"]["press"] then
497 | local action = lynput.inputsSet["any"]["press"]
498 | lynput[action] = true
499 | end -- if press_any is set
500 | if lynput.inputsSet["any"]["hold"] then
501 | local action = lynput.inputsSet["any"]["hold"]
502 | lynput[action] = true
503 | end -- if hold_any is set
504 | end -- if "any" is set
505 |
506 | if lynput.inputsSet[button] then
507 | if lynput.inputsSet[button]["press"] then
508 | local action = lynput.inputsSet[button]["press"]
509 | lynput[action] = true
510 | end -- if press_button is set
511 | if lynput.inputsSet[button]["hold"] then
512 | local action = lynput.inputsSet[button]["hold"]
513 | lynput[action] = true
514 | end -- if hold_button is set
515 | end -- if button is set
516 | end -- for each lynput
517 | end
518 |
519 |
520 | function Lynput.onmousereleased(button)
521 | -- Translate LÖVE button to Lynput button
522 | button = Lynput.s_mouseButtons[tostring(button)]
523 | -- Process button
524 | for _, lynput in pairs(Lynput.s_lynputs) do
525 | if lynput.inputsSet["any"] then
526 | if lynput.inputsSet["any"]["release"] then
527 | local action = lynput.inputsSet["any"]["release"]
528 | lynput[action] = true
529 | end -- if release_any is set
530 | if lynput.inputsSet["any"]["hold"] then
531 | local action = lynput.inputsSet["any"]["hold"]
532 | lynput[action] = false
533 | end -- if hold_any is set
534 | end -- if "any" is set
535 |
536 | if lynput.inputsSet[button] then
537 | if lynput.inputsSet[button]["release"] then
538 | local action = lynput.inputsSet[button]["release"]
539 | lynput[action] = true
540 | end -- if press_button is set
541 | if lynput.inputsSet[button]["hold"] then
542 | local action = lynput.inputsSet[button]["hold"]
543 | lynput[action] = false
544 | end -- if hold_button is set
545 | end -- if button is set
546 | end -- for each lynput
547 | end
548 |
549 |
550 | ---------------------------------------------------
551 | -- GAMEPAD CALLBACKS
552 | ---------------------------------------------------
553 | function Lynput.ongamepadpressed(gamepadID, button)
554 | -- Translate LÖVE button to Lynput button
555 | button = Lynput.s_gamepadButtons[button]
556 | -- Process Lynput button
557 | for _, lynput in pairs(Lynput.s_lynputs) do
558 | if lynput.gpad then
559 | if Lynput[lynput.gpad]:getID() == gamepadID then
560 | if lynput.inputsSet["any"] then
561 | if lynput.inputsSet["any"]["press"] then
562 | local action = lynput.inputsSet["any"]["press"]
563 | lynput[action] = true
564 | end -- if press_any is set
565 | if lynput.inputsSet["any"]["hold"] then
566 | local action = lynput.inputsSet["any"]["hold"]
567 | lynput[action] = true
568 | end -- if hold_any is set
569 | end -- if "any" is set
570 |
571 |
572 | if lynput.inputsSet[button] then
573 | if lynput.inputsSet[button]["press"] then
574 | local action = lynput.inputsSet[button]["press"]
575 | lynput[action] = true
576 | end -- if press_button is set
577 | if lynput.inputsSet[button]["hold"] then
578 | local action = lynput.inputsSet[button]["hold"]
579 | lynput[action] = true
580 | end -- if hold_button is set
581 | end -- if button is set
582 | end -- if gamepad is set
583 | end -- if lynput has a gamepad attached
584 | end -- for each lynput
585 | end
586 |
587 |
588 | function Lynput.ongamepadreleased(gamepadID, button)
589 | -- Translate LÖVE button to Lynput button
590 | button = Lynput.s_gamepadButtons[button]
591 | -- Process Lynput button
592 | for _, lynput in pairs(Lynput.s_lynputs) do
593 | if lynput.gpad then
594 | if Lynput[lynput.gpad]:getID() == gamepadID then
595 | if lynput.inputsSet["any"] then
596 | if lynput.inputsSet["any"]["release"] then
597 | local action = lynput.inputsSet["any"]["release"]
598 | lynput[action] = true
599 | end -- if release_any is set
600 | if lynput.inputsSet["any"]["hold"] then
601 | local action = lynput.inputsSet["any"]["hold"]
602 | lynput[action] = false
603 | end -- if hold_any is set
604 | end -- if "any" is set
605 |
606 | if lynput.inputsSet[button] then
607 | if lynput.inputsSet[button]["release"] then
608 | local action = lynput.inputsSet[button]["release"]
609 | lynput[action] = true
610 | end -- if release_button is set
611 | if lynput.inputsSet[button]["hold"] then
612 | local action = lynput.inputsSet[button]["hold"]
613 | lynput[action] = false
614 | end -- if hold_button is set
615 | end -- if button is set
616 | end -- if gamepad is set
617 | end -- if lynput has a gamepad attached
618 | end -- for each lynput
619 | end
620 |
621 |
622 | function Lynput.ongamepadadded(gamepad)
623 | local gamepadID = gamepad:getID()
624 | local i = 1
625 | local gpad = "GPAD_1"
626 | while Lynput[gpad] do
627 | if Lynput[gpad]:getID() == gamepadID then
628 | return
629 | end -- if gamepadID is already assigned
630 |
631 | i = i +1
632 | gpad = "GPAD_" .. i
633 | end -- while gpad exists
634 |
635 | -- gpad does no exists, so we assign the new gamepad to it
636 | Lynput[gpad] = gamepad
637 | end
638 |
639 |
640 | return Lynput
641 |
--------------------------------------------------------------------------------