diff --git a/desmume/src/lua-engine.cpp b/desmume/src/lua-engine.cpp index e1a0d2b4a..6d6f29719 100644 --- a/desmume/src/lua-engine.cpp +++ b/desmume/src/lua-engine.cpp @@ -20,6 +20,7 @@ #if defined(WIN32) #include #include + #include typedef HMENU PlatformMenu; // hMenu #define MAX_MENU_COUNT (IDC_LUAMENU_RESERVE_END - IDC_LUAMENU_RESERVE_START + 1) @@ -4656,6 +4657,90 @@ DEFINE_LUA_FUNCTION(stylus_write, "table") return 0; } +#if defined(_WIN32) + const char* s_dpadDirections[4] = {"up", "right", "down", "left"}; + const char* s_buttonNames[33] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}; +#endif +// this function takes two optional variables +// joystickID: a value (0-15) that identifies the controller to read; default is 0 which is the "preferred device" set in the control panel +// returnDiagonals: a boolean (true/false) that determines whether or not to return diagonal values from a supported D-pad; default is false, which ignores diagonal values +DEFINE_LUA_FUNCTION(controller_get, "[joystickID = 0 [,returnDiagonals = false]]") { + unsigned int i_joystickID = 0;bool f_returnDiagonals = 0; // initialize optional variables + switch (lua_gettop(L)) { // get number of arguments + case 1: + i_joystickID = lua_tonumber(L,1); + break; + case 2: + i_joystickID = lua_tonumber(L,1); + if (lua_isboolean(L,2)) { // only accept true/false + f_returnDiagonals = lua_toboolean(L,2); + } + break; + } + lua_newtable(L); // create new empty table + + JOYCAPS joycaps; + MMRESULT joygetdevcaps_result = joyGetDevCaps(i_joystickID, &joycaps, sizeof(joycaps)); // get controller's joystick (dpad) capabilities and number of buttons + if (joygetdevcaps_result != JOYERR_NOERROR) {return 1;} // joystick doesn't exist so return an empty table + + JOYINFOEX joyinfoex; + joyinfoex.dwSize = sizeof(joyinfoex); + joyinfoex.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNPOV; + MMRESULT joygetposex_result = joyGetPosEx(i_joystickID, &joyinfoex); // query joystick's pov and buttons + if (joygetposex_result != JOYERR_NOERROR) {return 1;} // joystick doesn't exist so return an empty table + + bool dpad[4] = {0,0,0,0}; // up, right, down, left; + if ((joycaps.wCaps & JOYCAPS_HASPOV) && (joycaps.wCaps & JOYCAPS_POV4DIR)) { // confirm joystick has a pov that supports discrete values + if (joyinfoex.dwPOV != JOY_POVCENTERED) { // dpad is neutral + if (!(joyinfoex.dwPOV % 9000)) { // dpad is not currently on a diagonal so don't bother checking them + dpad[(joyinfoex.dwPOV / 4500)/2] = true; // divide by 2 to ignore diagonals + } else if (f_returnDiagonals) { // get the diagonal values if asked + switch (joyinfoex.dwPOV) { + case 4500: // up/right + dpad[0] = true; + dpad[1] = true; + break; + case 13500: // down/right + dpad[2] = true; + dpad[1] = true; + break; + case 22500: // down/left + dpad[2] = true; + dpad[3] = true; + break; + case 31500: // up/left + dpad[0] = true; + dpad[3] = true; + break; + } + } + + } + } else { + // there is no supported dpad so all dpad booleans will default to false + // should add support for JOYCAPS_POVCTS here but my three test controllers don't use it; unsure if it's even needed + } + + for (int i = 0; i < 4; i++) { // add dpad data to the table + lua_pushboolean(L, dpad[i]); // dpad direction isPressed + lua_setfield(L, -2, s_dpadDirections[i]); // dpad direction name + } + + int pow = 1; // joyinfoex.dwButtons is the total value of all pressed buttons; button values are powers of 2, starting with 2^0 for button 1 + for (unsigned int i = 1; i <= joycaps.wNumButtons; i++) { // add button data to the table + lua_pushboolean(L, (joyinfoex.dwButtons & pow) ? (true) : (false)); // button isPressed + lua_setfield(L, -2, s_buttonNames[i]); // button number + pow *= 2; + } + + return 1; // return the table +} +static const struct luaL_reg controllerlib [] = { + {"get", controller_get}, + {"read", controller_get}, + {NULL, NULL} +}; + static int gcEMUFILE_MEMORY(lua_State *L) { EMUFILE_MEMORY** ppEmuFile = (EMUFILE_MEMORY**)luaL_checkudata(L, 1, "EMUFILE_MEMORY*"); @@ -5021,6 +5106,9 @@ void registerLibs(lua_State* L) luaL_register(L, "agg", aggbasicshapes); luaL_register(L, "agg", agggeneralattributes); luaL_register(L, "agg", aggcustom); + + luaL_register(L, "controller", controllerlib); + lua_settop(L, 0); // clean the stack, because each call to luaL_register leaves a table on top