diff --git a/docs/apps.rst b/docs/apps.rst index a73008f..e1f19e1 100644 --- a/docs/apps.rst +++ b/docs/apps.rst @@ -7,7 +7,7 @@ List of Functions * :func:`apps.getCurrentGameLanguage` * :func:`apps.isDlcInstalled` -* :func:`apps.getLaunchCommandLineParam` +* :func:`apps.getLaunchCommandLine` List of Callbacks @@ -37,7 +37,7 @@ Function Reference :param number appID: The App ID of the DLC to check. :returns: (`boolean`) true if the user owns the DLC and it's currently installed, otherwise false. - :SteamWorks: `GetCurrentGameLanguage `_ + :SteamWorks: `BIsDlcInstalled `_ Checks if the user owns a specific DLC and if the DLC is installed. diff --git a/docs/utils.rst b/docs/utils.rst index bb0d56e..ec03581 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -6,19 +6,130 @@ List of Functions ----------------- * :func:`utils.getAppID` +* :func:`utils.getEnteredGamepadTextInput` +* :func:`utils.getEnteredGamepadTextLength` +* :func:`utils.isSteamInBigPictureMode` +* :func:`utils.isSteamRunningOnSteamDeck` +* :func:`utils.showGamepadTextInput` +* :func:`utils.showFloatingGamepadTextInput` +List of Callbacks +----------------- + +* :func:`utils.onGamepadTextInputDismissed` +* :func:`utils.onFloatingGamepadTextInputDismissed` Function Reference ------------------ -.. function:: utils.getAppID () +.. function:: utils.getAppID() :returns: (`number`) The AppID. :SteamWorks: `GetAppID `_ - Gets the App ID of the current process. + Gets the App ID of the current process. **Example**:: print("My app id is " .. Steam.utils.getAppID()) +.. function:: utils.getEnteredGamepadTextInput(buffer,length) + + :param number buffer: 1024 + :param number length: Number returned by getEnteredGamepadTextLength(). + :returns: (`string`) + :SteamWorks: `GetEnteredGamepadTextInput `_ + + Called within onGamepadTextInputDismissed: see that example. + +.. function:: utils.getEnteredGamepadTextLength() + + :returns: (`number`) + :SteamWorks: `GetEnteredGamepadTextLength `_ + + Called within onGamepadTextInputDismissed: see that example. + +.. function:: utils.isSteamInBigPictureMode() + + :returns: (`boolean`) + :SteamWorks: `IsSteamInBigPictureMode `_ + +.. function:: utils.isSteamRunningOnSteamDeck() + + :returns: (`boolean`) + :SteamWorks: `IsSteamRunningOnSteamDeck `_ + +.. function:: utils.showGamepadTextInput(input_mode,input_line_mode,description,char_max,existing_text) + + :param string input_mode: Valid options are: "Normal", "Password". + :param string input_line_mode: Valid options are: "SingleLine", "MultipleLines". + :param string description: Sets the description that should inform the user what the input dialog is for. + :param number char_max: 1024 + :param string existing_text: Sets the preexisting text which the user can edit. + :returns: (`boolean`) true if the big picture overlay is running; otherwise, false + :SteamWorks: `ShowGamepadTextInput `_ + + Activates the Big Picture text input dialog which only supports gamepad input. + + Notes: Steam must be in Big Picture Mode. Non-Steam games ran through Steam will not overlay properly; however, you can minimize Steam BPMode and test with steam_api64.dll. + +**Example**:: + + Steam.utils.showGamepadTextInput("Normal","SingleLine",description,1024,existing_text) + +.. function:: utils.showFloatingGamepadTextInput(floating_input_mode,TextFieldXPosition,TextFieldYPosition,TextFieldWidth,TextFieldHeight) + + :param string floating_input_mode: Valid options are: "SingleLine", "MultipleLines", "Email", "Numeric". + :param number TextFieldXPosition: X coordinate of text field which shouldn't be obscured by the floating keyboard. + :param number TextFieldYPosition: Y coordinate of text field which shouldn't be obscured by the floating keyboard. + :param number TextFieldWidth: Width of text field which shouldn't be obscured by the floating keyboard. + :param number TextFieldHeight: Height of text field which shouldn't be obscured by the floating keyboard. + :returns: (`boolean`) true if the floating keyboard was shown, otherwise, false + :SteamWorks: `ShowFloatingGamepadTextInput `_ + + Opens a floating keyboard over the game content and sends OS keyboard keys directly to the game. + The text field position is specified in pixels relative the origin of the game window and is used to position the floating keyboard in a way that doesn't cover the text field. + + Notes: Steam must be in Big Picture Mode. Non-Steam games ran through Steam will not overlay properly; however, you can minimize Steam BPMode and test with steam_api64.dll. + +**Example**:: + + -- For bottom of window use 0,0,0,0 + -- For top of window use 0,window height/2,window width,window height/2 + Steam.utils.showFloatingGamepadTextInput("SingleLine",x,y,w,h) + +Callbacks Reference +------------------- + +.. warning:: + + Remember callbacks are functions that you should override in order to receive the events, and not call directly. + + Also, you **must** constantly call ``Steam.runCallbacks()`` (preferably in your game loop) in order for your callbacks to be called. + +.. function:: utils.onGamepadTextInputDismissed(data) + + :param table data: A table similar to `GamepadTextInputDismissed_t `_ + + * **data.submitted** (`boolean`) -- true if user entered & accepted text (Call utils.getEnteredGamepadTextInput to receive the text), false if input was canceled. + * **data.submittedText** (`number`) -- Contains the length in bytes if there was text submitted. + :returns: nothing + :SteamWorks: `GamepadTextInputDismissed_t `_ + + Called when the big picture gamepad text input has been closed. + +**Example**:: + + function Steam.utils.onGamepadTextInputDismissed(data) + if not data.submitted then return end-- The user canceled + local length = Steam.utils.getEnteredGamepadTextLength(); + local newstring = Steam.utils.getEnteredGamepadTextInput(1024, length); + -- Use the newstring made by the user. + end + +.. function:: utils.onFloatingGamepadTextInputDismissed() + + :returns: nothing + :SteamWorks: `FloatingGamepadTextInputDismissed_t `_ + + Called when the floating keyboard invoked from utils.showFloatingGamepadTextInput has been closed. diff --git a/src/utils.cpp b/src/utils.cpp index 4d47a32..87686f8 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,25 +1,151 @@ #include "utils.hpp" +//#include // ========================== // ======= SteamUtils ======= // ========================== +using luasteam::CallResultListener; + +namespace { + +class CallbackListener; +CallbackListener *callback_listener = nullptr; +int utils_ref = LUA_NOREF; + +const char *input_modes[] = {"Normal", "Password", nullptr}; +const char *input_line_modes[] = {"SingleLine", "MultipleLines", nullptr}; +const char *floating_input_modes[] = {"SingleLine", "MultipleLines", "Email", "Numeric", nullptr}; + +class CallbackListener { + private: + STEAM_CALLBACK(CallbackListener, OnGamepadTextInputDismissed, GamepadTextInputDismissed_t); + STEAM_CALLBACK(CallbackListener, OnFloatingGamepadTextInputDismissed, FloatingGamepadTextInputDismissed_t); +}; + +void CallbackListener::OnGamepadTextInputDismissed(GamepadTextInputDismissed_t *data) { + if (data == nullptr) { + return; + } + lua_State *L = luasteam::global_lua_state; + if (!lua_checkstack(L, 4)) { + return; + } + + lua_rawgeti(L, LUA_REGISTRYINDEX, utils_ref); + lua_getfield(L, -1, "onGamepadTextInputDismissed"); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + } else { + lua_createtable(L, 0, 2); + lua_pushboolean(L, data->m_bSubmitted); + lua_setfield(L, -2, "submitted"); + lua_pushnumber(L, data->m_unSubmittedText); + lua_setfield(L, -2, "submittedText"); // len in bytes + lua_call(L, 1, 0); + lua_pop(L, 1); + } +} + +void CallbackListener::OnFloatingGamepadTextInputDismissed(FloatingGamepadTextInputDismissed_t *data) { + if (data == nullptr) { + return; + } + lua_State *L = luasteam::global_lua_state; + if (!lua_checkstack(L, 4)) { + return; + } + lua_rawgeti(L, LUA_REGISTRYINDEX, utils_ref); + lua_getfield(L, -1, "onFloatingGamepadTextInputDismissed"); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + } else { + lua_call(L, 1, 0); + lua_pop(L, 1); + } +} + +} // namespace + // uint32 GetAppID(); EXTERN int luasteam_getAppID(lua_State *L) { lua_pushnumber(L, SteamUtils()->GetAppID()); return 1; } +// bool GetEnteredGamepadTextInput( char *pchText, uint32 cchText ); +EXTERN int luasteam_getEnteredGamepadTextInput(lua_State *L) { + char pchText[1024]; + SteamUtils()->GetEnteredGamepadTextInput(pchText, 1024); + lua_pushstring(L, pchText); + return 1; +} + +// uint32 GetEnteredGamepadTextLength(); +EXTERN int luasteam_getEnteredGamepadTextLength(lua_State *L) { + lua_pushnumber(L, SteamUtils()->GetEnteredGamepadTextLength()); + return 1; +} + +//bool IsSteamInBigPictureMode(); +EXTERN int luasteam_isSteamInBigPictureMode(lua_State *L) { + lua_pushboolean(L, SteamUtils()->IsSteamInBigPictureMode()); + return 1; +} + +//bool IsSteamRunningOnSteamDeck(); +EXTERN int luasteam_isSteamRunningOnSteamDeck(lua_State *L) { + lua_pushboolean(L, SteamUtils()->IsSteamRunningOnSteamDeck()); + return 1; +} + +// bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax, const char *pchExistingText ); +EXTERN int luasteam_showGamepadTextInput(lua_State *L) { + + int input_mode = luaL_checkoption(L, 1, nullptr, input_modes); + int input_line_mode = luaL_checkoption(L, 2, nullptr, input_line_modes); + // char pchDescription[1024]; + const char *pchDescription = luaL_checkstring(L, 3); + const char *pchExistingText = luaL_checkstring(L, 5); + lua_pushboolean(L, SteamUtils()->ShowGamepadTextInput(static_cast(input_mode), static_cast(input_line_mode), pchDescription, 1024, pchExistingText)); + return 1; +} + +//bool ShowFloatingGamepadTextInput(EFloatingGamepadTextInputMode eKeyboardMode, int nTextFieldXPosition, int nTextFieldYPosition, int nTextFieldWidth, int nTextFieldHeight); +EXTERN int luasteam_showFloatingGamepadTextInput(lua_State *L) { + int floating_input_mode = luaL_checkoption(L, 1, nullptr, floating_input_modes); + int nTextFieldXPosition = luaL_checkint(L, 2); + int nTextFieldYPosition = luaL_checkint(L, 3); + int nTextFieldWidth = luaL_checkint(L, 4); + int nTextFieldHeight = luaL_checkint(L, 5); + lua_pushboolean(L, SteamUtils()->ShowFloatingGamepadTextInput(static_cast(floating_input_mode), nTextFieldXPosition, nTextFieldYPosition, nTextFieldWidth, nTextFieldHeight)); + return 1; +} + namespace luasteam { void add_utils(lua_State *L) { - lua_createtable(L, 0, 1); + lua_createtable(L, 0, 7); add_func(L, "getAppID", luasteam_getAppID); + add_func(L, "getEnteredGamepadTextInput", luasteam_getEnteredGamepadTextInput); + add_func(L, "getEnteredGamepadTextLength", luasteam_getEnteredGamepadTextLength); + add_func(L, "isSteamInBigPictureMode", luasteam_isSteamInBigPictureMode); + add_func(L, "isSteamRunningOnSteamDeck", luasteam_isSteamRunningOnSteamDeck); + add_func(L, "showGamepadTextInput", luasteam_showGamepadTextInput); + add_func(L, "showFloatingGamepadTextInput", luasteam_showFloatingGamepadTextInput); + lua_pushvalue(L, -1); + + utils_ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_setfield(L, -2, "utils"); } -void init_utils(lua_State *L) {} +void init_utils(lua_State *L) { callback_listener = new CallbackListener(); } -void shutdown_utils(lua_State *L) {} +void shutdown_utils(lua_State *L) { + luaL_unref(L, LUA_REGISTRYINDEX, utils_ref); + utils_ref = LUA_NOREF; + delete callback_listener; + callback_listener = nullptr; +} } // namespace luasteam