1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "wrap_Joystick.h"
23#include "wrap_JoystickModule.h"
24
25#include <vector>
26
27namespace love
28{
29namespace joystick
30{
31
32Joystick *luax_checkjoystick(lua_State *L, int idx)
33{
34 return luax_checktype<Joystick>(L, idx);
35}
36
37int w_Joystick_isConnected(lua_State *L)
38{
39 Joystick *j = luax_checkjoystick(L, 1);
40 luax_pushboolean(L, j->isConnected());
41 return 1;
42}
43
44int w_Joystick_getName(lua_State *L)
45{
46 Joystick *j = luax_checkjoystick(L, 1);
47 lua_pushstring(L, j->getName());
48 return 1;
49}
50
51int w_Joystick_getID(lua_State *L)
52{
53 Joystick *j = luax_checkjoystick(L, 1);
54
55 // IDs are 1-based in Lua.
56 lua_pushinteger(L, j->getID() + 1);
57
58 int instanceid = j->getInstanceID();
59 if (instanceid >= 0)
60 lua_pushinteger(L, instanceid + 1);
61 else
62 lua_pushnil(L);
63
64 return 2;
65}
66
67int w_Joystick_getGUID(lua_State *L)
68{
69 Joystick *j = luax_checkjoystick(L, 1);
70 luax_pushstring(L, j->getGUID());
71 return 1;
72}
73
74int w_Joystick_getDeviceInfo(lua_State *L)
75{
76 Joystick *j = luax_checkjoystick(L, 1);
77
78 int vendorID = 0, productID = 0, productVersion = 0;
79 j->getDeviceInfo(vendorID, productID, productVersion);
80
81 lua_pushnumber(L, vendorID);
82 lua_pushnumber(L, productID);
83 lua_pushnumber(L, productVersion);
84
85 return 3;
86}
87
88int w_Joystick_getAxisCount(lua_State *L)
89{
90 Joystick *j = luax_checkjoystick(L, 1);
91 lua_pushinteger(L, j->getAxisCount());
92 return 1;
93}
94
95int w_Joystick_getButtonCount(lua_State *L)
96{
97 Joystick *j = luax_checkjoystick(L, 1);
98 lua_pushinteger(L, j->getButtonCount());
99 return 1;
100}
101
102int w_Joystick_getHatCount(lua_State *L)
103{
104 Joystick *j = luax_checkjoystick(L, 1);
105 lua_pushinteger(L, j->getHatCount());
106 return 1;
107}
108
109int w_Joystick_getAxis(lua_State *L)
110{
111 Joystick *j = luax_checkjoystick(L, 1);
112 int axisindex = (int) luaL_checkinteger(L, 2) - 1;
113 lua_pushnumber(L, j->getAxis(axisindex));
114 return 1;
115}
116
117int w_Joystick_getAxes(lua_State *L)
118{
119 Joystick *j = luax_checkjoystick(L, 1);
120 std::vector<float> axes = j->getAxes();
121
122 for (float value : axes)
123 lua_pushnumber(L, value);
124
125 return (int) axes.size();
126}
127
128int w_Joystick_getHat(lua_State *L)
129{
130 Joystick *j = luax_checkjoystick(L, 1);
131 int hatindex = (int) luaL_checkinteger(L, 2) - 1;
132
133 Joystick::Hat h = j->getHat(hatindex);
134
135 const char *direction = "";
136 Joystick::getConstant(h, direction);
137
138 lua_pushstring(L, direction);
139 return 1;
140}
141
142int w_Joystick_isDown(lua_State *L)
143{
144 Joystick *j = luax_checkjoystick(L, 1);
145
146 bool istable = lua_istable(L, 2);
147 int num = istable ? (int) luax_objlen(L, 2) : (lua_gettop(L) - 1);
148
149 if (num == 0)
150 luaL_checkinteger(L, 2);
151
152 std::vector<int> buttons;
153 buttons.reserve(num);
154
155 if (istable)
156 {
157 for (int i = 0; i < num; i++)
158 {
159 lua_rawgeti(L, 2, i + 1);
160 buttons.push_back((int) luaL_checkinteger(L, -1) - 1);
161 lua_pop(L, 1);
162 }
163 }
164 else
165 {
166 for (int i = 0; i < num; i++)
167 buttons.push_back((int) luaL_checkinteger(L, i + 2) - 1);
168 }
169
170 luax_pushboolean(L, j->isDown(buttons));
171 return 1;
172}
173
174int w_Joystick_isGamepad(lua_State *L)
175{
176 Joystick *j = luax_checkjoystick(L, 1);
177 luax_pushboolean(L, j->isGamepad());
178 return 1;
179}
180
181int w_Joystick_getGamepadAxis(lua_State *L)
182{
183 Joystick *j = luax_checkjoystick(L, 1);
184
185 const char *str = luaL_checkstring(L, 2);
186 Joystick::GamepadAxis axis;
187
188 if (!joystick::Joystick::getConstant(str, axis))
189 return luax_enumerror(L, "gamepad axis", str);
190
191 lua_pushnumber(L, j->getGamepadAxis(axis));
192 return 1;
193}
194
195int w_Joystick_isGamepadDown(lua_State *L)
196{
197 Joystick *j = luax_checkjoystick(L, 1);
198
199 bool istable = lua_istable(L, 2);
200 int num = istable ? (int) luax_objlen(L, 2) : (lua_gettop(L) - 1);
201
202 if (num == 0)
203 luaL_checkstring(L, 2);
204
205 std::vector<Joystick::GamepadButton> buttons;
206 buttons.reserve(num);
207
208 Joystick::GamepadButton button;
209
210 if (istable)
211 {
212 for (int i = 0; i < num; i++)
213 {
214 lua_rawgeti(L, 2, i + 1);
215 const char *str = luaL_checkstring(L, -1);
216
217 if (!joystick::Joystick::getConstant(str, button))
218 return luax_enumerror(L, "gamepad button", str);
219
220 buttons.push_back(button);
221
222 lua_pop(L, 1);
223 }
224 }
225 else
226 {
227 for (int i = 0; i < num; i++)
228 {
229 const char *str = luaL_checkstring(L, i + 2);
230
231 if (!joystick::Joystick::getConstant(str, button))
232 return luax_enumerror(L, "gamepad button", str);
233
234 buttons.push_back(button);
235 }
236 }
237
238 luax_pushboolean(L, j->isGamepadDown(buttons));
239 return 1;
240}
241
242int w_Joystick_getGamepadMapping(lua_State *L)
243{
244 Joystick *j = luax_checkjoystick(L, 1);
245
246 const char *gpbindstr = luaL_checkstring(L, 2);
247 Joystick::GamepadInput gpinput;
248
249 if (Joystick::getConstant(gpbindstr, gpinput.axis))
250 gpinput.type = Joystick::INPUT_TYPE_AXIS;
251 else if (Joystick::getConstant(gpbindstr, gpinput.button))
252 gpinput.type = Joystick::INPUT_TYPE_BUTTON;
253 else
254 return luax_enumerror(L, "gamepad axis/button", gpbindstr);
255
256 Joystick::JoystickInput jinput;
257 jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
258
259 luax_catchexcept(L, [&](){ jinput = j->getGamepadMapping(gpinput); });
260
261 if (jinput.type == Joystick::INPUT_TYPE_MAX_ENUM)
262 return 0;
263
264 const char *inputtypestr;
265 if (!Joystick::getConstant(jinput.type, inputtypestr))
266 return luaL_error(L, "Unknown joystick input type.");
267
268 lua_pushstring(L, inputtypestr);
269
270 const char *hatstr;
271 switch (jinput.type)
272 {
273 case Joystick::INPUT_TYPE_AXIS:
274 lua_pushinteger(L, jinput.axis + 1);
275 return 2;
276 case Joystick::INPUT_TYPE_BUTTON:
277 lua_pushinteger(L, jinput.button + 1);
278 return 2;
279 case Joystick::INPUT_TYPE_HAT:
280 lua_pushinteger(L, jinput.hat.index + 1);
281 if (Joystick::getConstant(jinput.hat.value, hatstr))
282 {
283 lua_pushstring(L, hatstr);
284 return 3;
285 }
286 else
287 return luaL_error(L, "Unknown joystick hat.");
288 default:
289 return luaL_error(L, "Unknown joystick input type.");
290 }
291
292 return 1;
293}
294
295int w_Joystick_getGamepadMappingString(lua_State *L)
296{
297 Joystick *j = luax_checkjoystick(L, 1);
298 std::string mapping = j->getGamepadMappingString();
299 if (mapping.empty())
300 lua_pushnil(L);
301 else
302 luax_pushstring(L, mapping);
303 return 1;
304}
305
306int w_Joystick_isVibrationSupported(lua_State *L)
307{
308 Joystick *j = luax_checkjoystick(L, 1);
309 luax_pushboolean(L, j->isVibrationSupported());
310 return 1;
311}
312
313int w_Joystick_setVibration(lua_State *L)
314{
315 Joystick *j = luax_checkjoystick(L, 1);
316 bool success = false;
317
318 if (lua_isnoneornil(L, 2))
319 {
320 // Disable joystick vibration if no argument is given.
321 success = j->setVibration();
322 }
323 else
324 {
325 float left = (float) luaL_checknumber(L, 2);
326 float right = (float) luaL_optnumber(L, 3, left);
327 float duration = (float) luaL_optnumber(L, 4, -1.0); // -1 is infinite.
328 success = j->setVibration(left, right, duration);
329 }
330
331 luax_pushboolean(L, success);
332 return 1;
333}
334
335int w_Joystick_getVibration(lua_State *L)
336{
337 Joystick *j = luax_checkjoystick(L, 1);
338 float left, right;
339 j->getVibration(left, right);
340 lua_pushnumber(L, left);
341 lua_pushnumber(L, right);
342 return 2;
343}
344
345// List of functions to wrap.
346static const luaL_Reg w_Joystick_functions[] =
347{
348 { "isConnected", w_Joystick_isConnected },
349 { "getName", w_Joystick_getName },
350 { "getID", w_Joystick_getID },
351 { "getGUID", w_Joystick_getGUID },
352 { "getDeviceInfo", w_Joystick_getDeviceInfo },
353 { "getAxisCount", w_Joystick_getAxisCount },
354 { "getButtonCount", w_Joystick_getButtonCount },
355 { "getHatCount", w_Joystick_getHatCount },
356 { "getAxis", w_Joystick_getAxis },
357 { "getAxes", w_Joystick_getAxes },
358 { "getHat", w_Joystick_getHat },
359 { "isDown", w_Joystick_isDown },
360
361 { "isGamepad", w_Joystick_isGamepad },
362 { "getGamepadAxis", w_Joystick_getGamepadAxis },
363 { "isGamepadDown", w_Joystick_isGamepadDown },
364 { "getGamepadMapping", w_Joystick_getGamepadMapping },
365 { "getGamepadMappingString", w_Joystick_getGamepadMappingString },
366
367 { "isVibrationSupported", w_Joystick_isVibrationSupported },
368 { "setVibration", w_Joystick_setVibration },
369 { "getVibration", w_Joystick_getVibration },
370
371 // From wrap_JoystickModule.
372 { "getConnectedIndex", w_getIndex },
373
374 { 0, 0 },
375};
376
377extern "C" int luaopen_joystick(lua_State *L)
378{
379 return luax_register_type(L, &Joystick::type, w_Joystick_functions, nullptr);
380}
381
382} // joystick
383} // love
384