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#include "wrap_JoystickModule.h"
22#include "wrap_Joystick.h"
23
24#include "filesystem/Filesystem.h"
25#include "filesystem/wrap_Filesystem.h"
26
27#include "sdl/JoystickModule.h"
28
29namespace love
30{
31namespace joystick
32{
33
34#define instance() (Module::getInstance<JoystickModule>(Module::M_JOYSTICK))
35
36int w_getJoysticks(lua_State *L)
37{
38 int stickcount = instance()->getJoystickCount();
39 lua_createtable(L, stickcount, 0);
40
41 for (int i = 0; i < stickcount; i++)
42 {
43 Joystick *stick = instance()->getJoystick(i);
44 luax_pushtype(L, stick);
45 lua_rawseti(L, -2, i + 1);
46 }
47
48 return 1;
49}
50
51int w_getIndex(lua_State *L)
52{
53 Joystick *j = luax_checkjoystick(L, 1);
54 int index = instance()->getIndex(j);
55 if (index >= 0)
56 lua_pushinteger(L, index + 1);
57 else
58 lua_pushnil(L);
59 return 1;
60}
61
62int w_getJoystickCount(lua_State *L)
63{
64 lua_pushinteger(L, instance()->getJoystickCount());
65 return 1;
66}
67
68int w_setGamepadMapping(lua_State *L)
69{
70 // Only accept a GUID string. We don't accept a Joystick object because
71 // the gamepad mapping applies to all joysticks with the same GUID (e.g. all
72 // Xbox 360 controllers on the system), rather than individual objects.
73 const char *guid = luaL_checkstring(L, 1);
74
75 const char *gpbindstr = luaL_checkstring(L, 2);
76 Joystick::GamepadInput gpinput;
77
78 if (Joystick::getConstant(gpbindstr, gpinput.axis))
79 gpinput.type = Joystick::INPUT_TYPE_AXIS;
80 else if (Joystick::getConstant(gpbindstr, gpinput.button))
81 gpinput.type = Joystick::INPUT_TYPE_BUTTON;
82 else
83 return luax_enumerror(L, "gamepad axis/button", gpbindstr);
84
85 const char *jinputtypestr = luaL_checkstring(L, 3);
86 Joystick::JoystickInput jinput;
87
88 if (!Joystick::getConstant(jinputtypestr, jinput.type))
89 return luax_enumerror(L, "joystick input type", jinputtypestr);
90
91 const char *hatstr;
92 switch (jinput.type)
93 {
94 case Joystick::INPUT_TYPE_AXIS:
95 jinput.axis = (int) luaL_checkinteger(L, 4) - 1;
96 break;
97 case Joystick::INPUT_TYPE_BUTTON:
98 jinput.button = (int) luaL_checkinteger(L, 4) - 1;
99 break;
100 case Joystick::INPUT_TYPE_HAT:
101 // Hats need both a hat index and a hat value.
102 jinput.hat.index = (int) luaL_checkinteger(L, 4) - 1;
103 hatstr = luaL_checkstring(L, 5);
104 if (!Joystick::getConstant(hatstr, jinput.hat.value))
105 return luax_enumerror(L, "joystick hat", hatstr);
106 break;
107 default:
108 return luax_enumerror(L, "joystick input type", jinputtypestr);
109 }
110
111 bool success = false;
112 luax_catchexcept(L, [&](){ success = instance()->setGamepadMapping(guid, gpinput, jinput); });
113
114 luax_pushboolean(L, success);
115 return 1;
116}
117
118int w_loadGamepadMappings(lua_State *L)
119{
120 bool isfile = false;
121 std::string mappings = luax_checkstring(L, 1);
122
123 auto fs = Module::getInstance<love::filesystem::Filesystem>(Module::M_FILESYSTEM);
124 if (fs)
125 {
126 love::filesystem::Filesystem::Info info = {};
127 bool exists = fs->getInfo(mappings.c_str(), info);
128 isfile = exists && info.type == love::filesystem::Filesystem::FILETYPE_FILE;
129 }
130
131 if (isfile)
132 {
133 love::filesystem::FileData *fd = love::filesystem::luax_getfiledata(L, 1);
134 mappings = std::string((const char *) fd->getData(), fd->getSize());
135 fd->release();
136 }
137 else
138 mappings = luax_checkstring(L, 1);
139
140 luax_catchexcept(L, [&](){ instance()->loadGamepadMappings(mappings); });
141 return 0;
142}
143
144int w_saveGamepadMappings(lua_State *L)
145{
146 lua_settop(L, 1);
147 std::string mappings = instance()->saveGamepadMappings();
148
149 // Optionally write the mappings string to a file.
150 if (!lua_isnoneornil(L, 1))
151 {
152 luax_pushstring(L, mappings);
153 int idxs[] = {1, 2};
154 luax_convobj(L, idxs, 2, "filesystem", "write");
155 lua_pop(L, 1); // Pop the return value.
156 }
157
158 // Return the actual string even if we also wrote it to a file.
159 luax_pushstring(L, mappings);
160 return 1;
161}
162
163int w_getGamepadMappingString(lua_State *L)
164{
165 const char *guid = luaL_checkstring(L, 1);
166 std::string mapping = instance()->getGamepadMappingString(guid);
167 if (mapping.empty())
168 lua_pushnil(L);
169 else
170 luax_pushstring(L, mapping);
171 return 1;
172}
173
174// List of functions to wrap.
175static const luaL_Reg functions[] =
176{
177 { "getJoysticks", w_getJoysticks },
178 { "getJoystickCount", w_getJoystickCount },
179 { "setGamepadMapping", w_setGamepadMapping },
180 { "loadGamepadMappings", w_loadGamepadMappings },
181 { "saveGamepadMappings", w_saveGamepadMappings },
182 { "getGamepadMappingString", w_getGamepadMappingString },
183 { 0, 0 }
184};
185
186static const lua_CFunction types[] =
187{
188 luaopen_joystick,
189 0,
190};
191
192extern "C" int luaopen_love_joystick(lua_State *L)
193{
194 JoystickModule *instance = instance();
195 if (instance == nullptr)
196 {
197 luax_catchexcept(L, [&](){ instance = new sdl::JoystickModule(); });
198 }
199 else
200 instance->retain();
201
202 WrappedModule w;
203 w.module = instance;
204 w.name = "joystick";
205 w.type = &Module::type;
206 w.functions = functions;
207 w.types = types;
208
209 return luax_register_module(L, w);
210}
211
212} // joystick
213} // love
214