/** * @file llluamanager.cpp * @brief classes and functions for interfacing with LUA. * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2023, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * */ #include "llviewerprecompiledheaders.h" #include "llluamanager.h" #include "llagent.h" #include "llappearancemgr.h" #include "llcallbacklist.h" #include "llfloaterreg.h" #include "llfloaterimnearbychat.h" #include "llfloatersidepanelcontainer.h" #include "llnotificationsutil.h" #include "llvoavatarself.h" #include "llviewermenu.h" #include "llviewermenufile.h" #include "llviewerwindow.h" #include "lluilistener.h" #include extern "C" { #include "lua/lua.h" #include "lua/lauxlib.h" #include "lua/lualib.h" } #if LL_WINDOWS #pragma comment(lib, "liblua54.a") #endif // FIXME extremely hacky way to get to the UI Listener framework. There's almost certainly a cleaner way. extern LLUIListener sUIListener; int lua_printWarning(lua_State *L) { std::string msg(lua_tostring(L, 1)); LL_WARNS() << msg << LL_ENDL; return 1; } bool checkLua(lua_State *L, int r, std::string &error_msg) { if (r != LUA_OK) { error_msg = lua_tostring(L, -1); LL_WARNS() << error_msg << LL_ENDL; return false; } return true; } int lua_avatar_sit(lua_State *L) { gAgent.sitDown(); return 1; } int lua_avatar_stand(lua_State *L) { gAgent.standUp(); return 1; } int lua_nearby_chat_send(lua_State *L) { std::string msg(lua_tostring(L, 1)); LLFloaterIMNearbyChat *nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); nearby_chat->sendChatFromViewer(msg, CHAT_TYPE_NORMAL, gSavedSettings.getBOOL("PlayChatAnim")); return 1; } int lua_wear_by_name(lua_State *L) { std::string folder_name(lua_tostring(L, 1)); LLAppearanceMgr::instance().wearOutfitByName(folder_name); return 1; } int lua_open_floater(lua_State *L) { std::string floater_name(lua_tostring(L, 1)); LLSD key; if (floater_name == "profile") { key["id"] = gAgentID; } LLFloaterReg::showInstance(floater_name, key); return 1; } int lua_close_floater(lua_State *L) { std::string floater_name(lua_tostring(L, 1)); LLSD key; if (floater_name == "profile") { key["id"] = gAgentID; } LLFloaterReg::hideInstance(floater_name, key); return 1; } int lua_close_all_floaters(lua_State *L) { close_all_windows(); return 1; } int lua_click_child(lua_State *L) { std::string parent_name(lua_tostring(L, 1)); std::string child_name(lua_tostring(L, 2)); LLFloater *floater = LLFloaterReg::findInstance(parent_name); LLUICtrl *child = floater->getChild(child_name, true); child->onCommit(); return 1; } int lua_snapshot_to_file(lua_State *L) { std::string filename(lua_tostring(L, 1)); //don't take snapshot from coroutine doOnIdleOneTime([filename]() { gViewerWindow->saveSnapshot(filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), gSavedSettings.getBOOL("RenderUIInSnapshot"), gSavedSettings.getBOOL("RenderHUDInSnapshot"), FALSE, LLSnapshotModel::SNAPSHOT_TYPE_COLOR, LLSnapshotModel::SNAPSHOT_FORMAT_PNG); }); return 1; } int lua_open_wearing_tab(lua_State *L) { LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "now_wearing")); return 1; } int lua_set_debug_setting_bool(lua_State *L) { std::string setting_name(lua_tostring(L, 1)); bool value(lua_toboolean(L, 2)); gSavedSettings.setBOOL(setting_name, value); return 1; } int lua_get_avatar_name(lua_State *L) { std::string name = gAgentAvatarp->getFullname(); lua_pushstring(L, name.c_str()); return 1; } int lua_is_avatar_flying(lua_State *L) { lua_pushboolean(L, gAgent.getFlying()); return 1; } int lua_env_setting_event(lua_State *L) { handle_env_setting_event(lua_tostring(L, 1)); return 1; } void handle_notification_dialog(const LLSD ¬ification, const LLSD &response, lua_State *L, std::string response_cb) { if (!response_cb.empty()) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); lua_pushinteger(L, option); lua_setglobal(L, response_cb.c_str()); } } int lua_show_notification(lua_State *L) { std::string notification(lua_tostring(L, 1)); if (lua_type(L, 2) == LUA_TTABLE) { LLSD args; // push first key lua_pushnil(L); while (lua_next(L, 2) != 0) { // right now -2 is key, -1 is value lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); std::string key = lua_tostring(L, -2); std::string value = lua_tostring(L, -1); args[key] = value; lua_pop(L, 3); } std::string response_cb; if (lua_type(L, 3) == LUA_TSTRING) { response_cb = lua_tostring(L, 3); } LLNotificationsUtil::add(notification, args, LLSD(), boost::bind(handle_notification_dialog, _1, _2, L, response_cb)); } else if (lua_type(L, 2) == LUA_TSTRING) { std::string response_cb = lua_tostring(L, 2); LLNotificationsUtil::add(notification, LLSD(), LLSD(), boost::bind(handle_notification_dialog, _1, _2, L, response_cb)); } else { LLNotificationsUtil::add(notification); } return 1; } int lua_run_ui_command(lua_State *L) { int top = lua_gettop(L); std::string func_name; if (top >= 1) { func_name = lua_tostring(L,1); } std::string parameter; if (top >= 2) { parameter = lua_tostring(L,2); } LL_WARNS("LUA") << "running ui func " << func_name << " parameter " << parameter << LL_ENDL; LLSD event; event["function"] = func_name; if (!parameter.empty()) { event["parameter"] = parameter; } sUIListener.call(event); return 1; } void initLUA(lua_State *L) { lua_register(L, "print_warning", lua_printWarning); lua_register(L, "avatar_sit", lua_avatar_sit); lua_register(L, "avatar_stand", lua_avatar_stand); lua_register(L, "nearby_chat_send", lua_nearby_chat_send); lua_register(L, "wear_by_name", lua_wear_by_name); lua_register(L, "open_floater", lua_open_floater); lua_register(L, "close_floater", lua_close_floater); lua_register(L, "close_all_floaters", lua_close_all_floaters); lua_register(L, "click_child", lua_click_child); lua_register(L, "open_wearing_tab", lua_open_wearing_tab); lua_register(L, "snapshot_to_file", lua_snapshot_to_file); lua_register(L, "get_avatar_name", lua_get_avatar_name); lua_register(L, "is_avatar_flying", lua_is_avatar_flying); lua_register(L, "env_setting_event", lua_env_setting_event); lua_register(L, "set_debug_setting_bool", lua_set_debug_setting_bool); lua_register(L, "show_notification", lua_show_notification); lua_register(L, "run_ui_command", lua_run_ui_command); } void LLLUAmanager::runScriptFile(const std::string &filename, script_finished_fn cb) { LLCoros::instance().launch("LUAScriptFileCoro", [filename, cb]() { lua_State *L = luaL_newstate(); luaL_openlibs(L); initLUA(L); auto LUA_sleep_func = [](lua_State *L) { F32 seconds = lua_tonumber(L, -1); llcoro::suspendUntilTimeout(seconds); return 0; }; lua_register(L, "sleep", LUA_sleep_func); std::string lua_error; if (checkLua(L, luaL_dofile(L, filename.c_str()), lua_error)) { lua_getglobal(L, "call_once_func"); if (lua_isfunction(L, -1)) { if (checkLua(L, lua_pcall(L, 0, 0, 0), lua_error)) {} } } lua_close(L); if (cb) { cb(lua_error); } }); } void LLLUAmanager::runScriptLine(const std::string &cmd, script_finished_fn cb) { LLCoros::instance().launch("LUAScriptFileCoro", [cmd, cb]() { lua_State *L = luaL_newstate(); luaL_openlibs(L); initLUA(L); int r = luaL_dostring(L, cmd.c_str()); std::string lua_error; if (r != LUA_OK) { lua_error = lua_tostring(L, -1); } lua_close(L); if (cb) { cb(lua_error); } }); } void LLLUAmanager::runScriptOnLogin() { std::string filename = gSavedSettings.getString("AutorunLuaScriptName"); if (filename.empty()) { LL_INFOS() << "Script name wasn't set." << LL_ENDL; return; } filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); if (!gDirUtilp->fileExists(filename)) { LL_INFOS() << filename << " was not found." << LL_ENDL; return; } runScriptFile(filename); }