summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorMnikolenko Productengine <mnikolenko@productengine.com>2024-06-24 11:58:33 +0300
committerMnikolenko Productengine <mnikolenko@productengine.com>2024-06-24 12:00:16 +0300
commit61fad5fd04f90542b2842dec86a6f74a4f801de7 (patch)
treea30cac169de8cde1f822af2c5d1806300b62197f /indra/newview
parentb8e0c16ab1eb3cdd1d11e869bd3fd6196de51d4b (diff)
parent75accbefdbe7741d57bf093690d65ad1100f82d4 (diff)
Merge branch 'release/luau-scripting' into lua-appearance-listener
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/llappviewerlistener.cpp9
-rw-r--r--indra/newview/llappviewerlistener.h5
-rw-r--r--indra/newview/llfloaterluadebug.cpp32
-rw-r--r--indra/newview/llfloaterluadebug.h2
-rw-r--r--indra/newview/llluamanager.cpp91
-rw-r--r--indra/newview/llwindowlistener.cpp2
-rw-r--r--indra/newview/scripts/lua/LLNotification.lua15
-rw-r--r--indra/newview/scripts/lua/UI.lua16
-rw-r--r--indra/newview/scripts/lua/qtest.lua12
-rw-r--r--indra/newview/scripts/lua/require/ErrorQueue.lua (renamed from indra/newview/scripts/lua/ErrorQueue.lua)5
-rw-r--r--indra/newview/scripts/lua/require/Floater.lua (renamed from indra/newview/scripts/lua/Floater.lua)3
-rw-r--r--indra/newview/scripts/lua/require/LLChat.lua (renamed from indra/newview/scripts/lua/LLChat.lua)2
-rw-r--r--indra/newview/scripts/lua/require/LLChatListener.lua (renamed from indra/newview/scripts/lua/LLChatListener.lua)3
-rw-r--r--indra/newview/scripts/lua/require/LLDebugSettings.lua (renamed from indra/newview/scripts/lua/LLDebugSettings.lua)2
-rw-r--r--indra/newview/scripts/lua/require/LLFloaterAbout.lua (renamed from indra/newview/scripts/lua/LLFloaterAbout.lua)2
-rw-r--r--indra/newview/scripts/lua/require/LLGesture.lua (renamed from indra/newview/scripts/lua/LLGesture.lua)2
-rw-r--r--indra/newview/scripts/lua/require/Queue.lua (renamed from indra/newview/scripts/lua/Queue.lua)4
-rw-r--r--indra/newview/scripts/lua/require/UI.lua125
-rw-r--r--indra/newview/scripts/lua/require/WaitQueue.lua (renamed from indra/newview/scripts/lua/WaitQueue.lua)7
-rw-r--r--indra/newview/scripts/lua/require/coro.lua (renamed from indra/newview/scripts/lua/coro.lua)0
-rw-r--r--indra/newview/scripts/lua/require/fiber.lua (renamed from indra/newview/scripts/lua/fiber.lua)0
-rw-r--r--indra/newview/scripts/lua/require/inspect.lua (renamed from indra/newview/scripts/lua/inspect.lua)0
-rw-r--r--indra/newview/scripts/lua/require/leap.lua (renamed from indra/newview/scripts/lua/leap.lua)13
-rw-r--r--indra/newview/scripts/lua/require/login.lua19
-rw-r--r--indra/newview/scripts/lua/require/logout.lua7
-rw-r--r--indra/newview/scripts/lua/require/mapargs.lua73
-rw-r--r--indra/newview/scripts/lua/require/popup.lua53
-rw-r--r--indra/newview/scripts/lua/require/printf.lua (renamed from indra/newview/scripts/lua/printf.lua)0
-rw-r--r--indra/newview/scripts/lua/require/startup.lua (renamed from indra/newview/scripts/lua/startup.lua)9
-rw-r--r--indra/newview/scripts/lua/require/timers.lua (renamed from indra/newview/scripts/lua/timers.lua)3
-rw-r--r--indra/newview/scripts/lua/require/util.lua (renamed from indra/newview/scripts/lua/util.lua)25
-rw-r--r--indra/newview/scripts/lua/test_LLChatListener.lua2
-rw-r--r--indra/newview/scripts/lua/test_atexit.lua3
-rw-r--r--indra/newview/scripts/lua/test_login.lua7
-rw-r--r--indra/newview/scripts/lua/test_logout.lua3
-rw-r--r--indra/newview/scripts/lua/test_luafloater_demo.lua77
-rw-r--r--indra/newview/scripts/lua/test_luafloater_demo2.lua2
-rw-r--r--indra/newview/scripts/lua/test_luafloater_gesture_list.lua75
-rw-r--r--indra/newview/scripts/lua/test_luafloater_gesture_list2.lua2
-rw-r--r--indra/newview/scripts/lua/test_luafloater_speedometer.lua11
-rw-r--r--indra/newview/scripts/lua/test_mapargs.lua68
-rw-r--r--indra/newview/scripts/lua/test_popup.lua6
-rw-r--r--indra/newview/scripts/lua/test_timers.lua16
-rw-r--r--indra/newview/tests/llluamanager_test.cpp2
-rwxr-xr-xindra/newview/viewer_manifest.py4
45 files changed, 556 insertions, 263 deletions
diff --git a/indra/newview/llappviewerlistener.cpp b/indra/newview/llappviewerlistener.cpp
index 6d519b6fef..d02b1b4a79 100644
--- a/indra/newview/llappviewerlistener.cpp
+++ b/indra/newview/llappviewerlistener.cpp
@@ -42,6 +42,9 @@ LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter):
mAppViewerGetter(getter)
{
// add() every method we want to be able to invoke via this event API.
+ add("userQuit",
+ "Ask to quit with user confirmation prompt",
+ &LLAppViewerListener::userQuit);
add("requestQuit",
"Ask to quit nicely",
&LLAppViewerListener::requestQuit);
@@ -50,6 +53,12 @@ LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter):
&LLAppViewerListener::forceQuit);
}
+void LLAppViewerListener::userQuit(const LLSD& event)
+{
+ LL_INFOS() << "Listener requested user quit" << LL_ENDL;
+ mAppViewerGetter()->userQuit();
+}
+
void LLAppViewerListener::requestQuit(const LLSD& event)
{
LL_INFOS() << "Listener requested quit" << LL_ENDL;
diff --git a/indra/newview/llappviewerlistener.h b/indra/newview/llappviewerlistener.h
index 5ade3d3e04..e116175eb7 100644
--- a/indra/newview/llappviewerlistener.h
+++ b/indra/newview/llappviewerlistener.h
@@ -30,7 +30,7 @@
#define LL_LLAPPVIEWERLISTENER_H
#include "lleventapi.h"
-#include <boost/function.hpp>
+#include <functional>
class LLAppViewer;
class LLSD;
@@ -39,11 +39,12 @@ class LLSD;
class LLAppViewerListener: public LLEventAPI
{
public:
- typedef boost::function<LLAppViewer*(void)> LLAppViewerGetter;
+ typedef std::function<LLAppViewer*(void)> LLAppViewerGetter;
/// Bind the LLAppViewer instance to use (e.g. LLAppViewer::instance()).
LLAppViewerListener(const LLAppViewerGetter& getter);
private:
+ void userQuit(const LLSD& event);
void requestQuit(const LLSD& event);
void forceQuit(const LLSD& event);
diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp
index 60571d6247..f715327ec8 100644
--- a/indra/newview/llfloaterluadebug.cpp
+++ b/indra/newview/llfloaterluadebug.cpp
@@ -41,6 +41,7 @@
#include "llsdutil.h"
#include "lua_function.h"
#include "stringize.h"
+#include "tempset.h"
LLFloaterLUADebug::LLFloaterLUADebug(const LLSD &key)
@@ -77,6 +78,17 @@ LLFloaterLUADebug::~LLFloaterLUADebug()
void LLFloaterLUADebug::onExecuteClicked()
{
+ // Empirically, running Lua code that indirectly invokes the
+ // "LLNotifications" listener can result (via mysterious labyrinthine
+ // viewer UI byways) in a recursive call to this handler. We've seen Bad
+ // Things happen to the viewer with a second call to runScriptLine() with
+ // the same cmd on the same LuaState.
+ if (mExecuting)
+ {
+ LL_DEBUGS("Lua") << "recursive call to onExecuteClicked()" << LL_ENDL;
+ return;
+ }
+ TempSet executing(mExecuting, true);
mResultOutput->setValue("");
std::string cmd = mLineInput->getText();
@@ -94,6 +106,12 @@ void LLFloaterLUADebug::onBtnBrowse()
void LLFloaterLUADebug::onBtnRun()
{
+ if (mExecuting)
+ {
+ LL_DEBUGS("Lua") << "recursive call to onBtnRun()" << LL_ENDL;
+ return;
+ }
+ TempSet executing(mExecuting, true);
std::vector<std::string> filenames;
std::string filepath = mScriptPath->getText();
if (!filepath.empty())
@@ -105,6 +123,12 @@ void LLFloaterLUADebug::onBtnRun()
void LLFloaterLUADebug::runSelectedScript(const std::vector<std::string> &filenames)
{
+ if (mExecuting)
+ {
+ LL_DEBUGS("Lua") << "recursive call to runSelectedScript()" << LL_ENDL;
+ return;
+ }
+ TempSet executing(mExecuting, true);
mResultOutput->setValue("");
std::string filepath = filenames[0];
@@ -129,13 +153,19 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result)
mResultOutput->endOfDoc();
return;
}
+ if (count == 0)
+ {
+ // no results
+ mResultOutput->pasteTextWithLinebreaks(stringize("ok ", ++mAck));
+ return;
+ }
if (count == 1)
{
// single result
mResultOutput->pasteTextWithLinebreaks(stringize(result));
return;
}
- // 0 or multiple results
+ // multiple results
const char* sep = "";
for (const auto& item : llsd::inArray(result))
{
diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h
index 7418174570..ae30b7cf25 100644
--- a/indra/newview/llfloaterluadebug.h
+++ b/indra/newview/llfloaterluadebug.h
@@ -66,6 +66,8 @@ private:
LLLineEditor* mLineInput;
LLLineEditor* mScriptPath;
LuaState mState;
+ U32 mAck{ 0 };
+ bool mExecuting{ false };
};
#endif // LL_LLFLOATERLUADEBUG_H
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 97779a12ad..3ed72c34f3 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -139,10 +139,10 @@ lua_function(get_event_pumps,
"post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).")
{
luaL_checkstack(L, 2, nullptr);
- auto listener{ LuaState::obtainListener(L) };
+ auto& listener{ LuaState::obtainListener(L) };
// return the reply pump name and the command pump name on caller's lua_State
- lua_pushstdstring(L, listener->getReplyName());
- lua_pushstdstring(L, listener->getCommandName());
+ lua_pushstdstring(L, listener.getReplyName());
+ lua_pushstdstring(L, listener.getCommandName());
return 2;
}
@@ -153,8 +153,8 @@ lua_function(get_event_next,
"event becomes available.")
{
luaL_checkstack(L, 2, nullptr);
- auto listener{ LuaState::obtainListener(L) };
- const auto& [pump, data]{ listener->getNext() };
+ auto& listener{ LuaState::obtainListener(L) };
+ const auto& [pump, data]{ listener.getNext() };
lua_pushstdstring(L, pump);
lua_pushllsd(L, data);
lluau::set_interrupts_counter(L, 0);
@@ -254,7 +254,7 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r
if (shortchunk.length() > shortlen)
shortchunk = stringize(shortchunk.substr(0, shortlen), "...");
- std::string desc{ stringize("lua: ", shortchunk) };
+ std::string desc{ "lua: " + shortchunk };
LLCoros::instance().launch(desc, [&L, desc, chunk, cb]()
{
auto [count, result] = L.expr(desc, chunk);
@@ -386,10 +386,10 @@ void LLRequireResolver::findModule()
std::vector<fsyspath> lib_paths
{
- gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"),
+ gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua", "require"),
#ifdef LL_TEST
// Build-time tests don't have the app bundle - use source tree.
- fsyspath(__FILE__).parent_path() / "scripts" / "lua",
+ fsyspath(__FILE__).parent_path() / "scripts" / "lua" / "require",
#endif
};
@@ -454,45 +454,70 @@ bool LLRequireResolver::findModuleImpl(const std::string& absolutePath)
void LLRequireResolver::runModule(const std::string& desc, const std::string& code)
{
// Here we just loaded a new module 'code', need to run it and get its result.
- // Module needs to run in a new thread, isolated from the rest.
- // Note: we create ML on main thread so that it doesn't inherit environment of L.
- lua_State *GL = lua_mainthread(L);
-// lua_State *ML = lua_newthread(GL);
- // Try loading modules on Lua's main thread instead.
- lua_State *ML = GL;
- // lua_newthread() pushed the new thread object on GL's stack. Move to L's.
-// lua_xmove(GL, L, 1);
-
- // new thread needs to have the globals sandboxed
-// luaL_sandboxthread(ML);
+ lua_State *ML = lua_mainthread(L);
{
// If loadstring() returns (! LUA_OK) then there's an error message on
// the stack. If it returns LUA_OK then the newly-loaded module code
// is on the stack.
- if (lluau::loadstring(ML, desc, code) == LUA_OK)
+ LL_DEBUGS("Lua") << "Loading module " << desc << LL_ENDL;
+ if (lluau::loadstring(ML, desc, code) != LUA_OK)
{
- // luau uses Lua 5.3's version of lua_resume():
- // run the coroutine on ML, "from" L, passing no arguments.
-// int status = lua_resume(ML, L, 0);
- // we expect one return value
- int status = lua_pcall(ML, 0, 1, 0);
+ // error message on stack top
+ LL_DEBUGS("Lua") << "Error loading module " << desc << ": "
+ << lua_tostring(ML, -1) << LL_ENDL;
+ lua_pushliteral(ML, "loadstring: ");
+ // stack contains error, "loadstring: "
+ // swap: insert stack top at position -2
+ lua_insert(ML, -2);
+ // stack contains "loadstring: ", error
+ lua_concat(ML, 2);
+ // stack contains "loadstring: " + error
+ }
+ else // module code on stack top
+ {
+ // push debug module
+ lua_getglobal(ML, "debug");
+ // push debug.traceback
+ lua_getfield(ML, -1, "traceback");
+ // stack contains module code, debug, debug.traceback
+ // ditch debug
+ lua_replace(ML, -2);
+ // stack contains module code, debug.traceback
+ // swap: insert stack top at position -2
+ lua_insert(ML, -2);
+ // stack contains debug.traceback, module code
+ LL_DEBUGS("Lua") << "Loaded module " << desc << ", running" << LL_ENDL;
+ // no arguments, one return value
+ // pass debug.traceback as the error function
+ int status = lua_pcall(ML, 0, 1, -2);
+ // lua_pcall() has popped the module code and replaced it with its
+ // return value. Regardless of status or the type of the stack
+ // top, get rid of debug.traceback on the stack.
+ lua_remove(ML, -2);
if (status == LUA_OK)
{
- if (lua_gettop(ML) == 0)
- lua_pushfstring(ML, "module %s must return a value", desc.data());
- else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
+ auto top{ lua_gettop(ML) };
+ std::string type{ (top == 0)? "nothing"
+ : lua_typename(ML, lua_type(ML, -1)) };
+ LL_DEBUGS("Lua") << "Module " << desc << " returned " << type << LL_ENDL;
+ if ((top == 0) || ! (lua_istable(ML, -1) || lua_isfunction(ML, -1)))
+ {
lua_pushfstring(ML, "module %s must return a table or function, not %s",
- desc.data(), lua_typename(ML, lua_type(ML, -1)));
+ desc.data(), type.data());
+ }
}
else if (status == LUA_YIELD)
{
+ LL_DEBUGS("Lua") << "Module " << desc << " yielded" << LL_ENDL;
lua_pushfstring(ML, "module %s can not yield", desc.data());
}
- else if (!lua_isstring(ML, -1))
+ else
{
- lua_pushfstring(ML, "unknown error while running module %s", desc.data());
+ llassert(lua_isstring(ML, -1));
+ LL_DEBUGS("Lua") << "Module " << desc << " error: "
+ << lua_tostring(ML, -1) << LL_ENDL;
}
}
}
@@ -502,8 +527,4 @@ void LLRequireResolver::runModule(const std::string& desc, const std::string& co
{
lua_xmove(ML, L, 1);
}
- // remove ML from L's stack
-// lua_remove(L, -2);
-// // DON'T call lua_close(ML)! Since ML is only a thread of L, corrupts L too!
-// lua_close(ML);
}
diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp
index 87d18cfc00..efe395d9ca 100644
--- a/indra/newview/llwindowlistener.cpp
+++ b/indra/newview/llwindowlistener.cpp
@@ -55,7 +55,7 @@ LLWindowListener::LLWindowListener(LLViewerWindow *window, const KeyboardGetter&
"Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified ";
std::string keyExplain =
"(integer keycode values, or keysym string from any addKeyName() call in\n"
- "http://bitbucket.org/lindenlab/viewer-release/src/tip/indra/llwindow/llkeyboard.cpp )\n";
+ "https://github.com/secondlife/viewer/blob/main/indra/llwindow/llkeyboard.cpp#L68-L124)\n";
std::string mask =
"Specify optional [\"mask\"] as an array containing any of \"CTL\", \"ALT\",\n"
"\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n"
diff --git a/indra/newview/scripts/lua/LLNotification.lua b/indra/newview/scripts/lua/LLNotification.lua
deleted file mode 100644
index f47730d1cc..0000000000
--- a/indra/newview/scripts/lua/LLNotification.lua
+++ /dev/null
@@ -1,15 +0,0 @@
--- Engage the LLNotificationsListener LLEventAPI
-
-leap = require 'leap'
-
-local LLNotification = {}
-
-function LLNotification.add(name, substitutions)
- leap.send('LLNotifications', {op='requestAdd', name=name, substitutions=substitutions})
-end
-
-function LLNotification.requestAdd(name, substitutions)
- return leap.request('LLNotifications', {op='requestAdd', name=name, substitutions=substitutions})['response']
-end
-
-return LLNotification
diff --git a/indra/newview/scripts/lua/UI.lua b/indra/newview/scripts/lua/UI.lua
deleted file mode 100644
index 24f822bbd9..0000000000
--- a/indra/newview/scripts/lua/UI.lua
+++ /dev/null
@@ -1,16 +0,0 @@
--- Engage the UI LLEventAPI
-
-leap = require 'leap'
-
-local UI = {}
-
-function UI.call(func, parameter)
- -- 'call' is fire-and-forget
- leap.request('UI', {op='call', ['function']=func, parameter=parameter})
-end
-
-function UI.getValue(path)
- return leap.request('UI', {op='getValue', path=path})['value']
-end
-
-return UI
diff --git a/indra/newview/scripts/lua/qtest.lua b/indra/newview/scripts/lua/qtest.lua
index 009446d0c3..9526f58b04 100644
--- a/indra/newview/scripts/lua/qtest.lua
+++ b/indra/newview/scripts/lua/qtest.lua
@@ -21,8 +21,8 @@ function resume(co, ...)
end
-- ------------------ Queue variables are instance-specific ------------------
-q1 = Queue:new()
-q2 = Queue:new()
+q1 = Queue()
+q2 = Queue()
q1:Enqueue(17)
@@ -33,8 +33,8 @@ assert(q1:Dequeue() == nil)
assert(q2:Dequeue() == nil)
-- ----------------------------- test WaitQueue ------------------------------
-q1 = WaitQueue:new()
-q2 = WaitQueue:new()
+q1 = WaitQueue()
+q2 = WaitQueue()
result = {}
values = { 1, 1, 2, 3, 5, 8, 13, 21 }
@@ -76,7 +76,7 @@ print('result:', inspect(result))
assert(util.equal(values, result))
-- try incrementally enqueueing values
-q3 = WaitQueue:new()
+q3 = WaitQueue()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }
@@ -124,7 +124,7 @@ print(string.format('%q', table.concat(result, ' ')))
assert(util.equal(values, result))
-- ----------------------------- test ErrorQueue -----------------------------
-q4 = ErrorQueue:new()
+q4 = ErrorQueue()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }
diff --git a/indra/newview/scripts/lua/ErrorQueue.lua b/indra/newview/scripts/lua/require/ErrorQueue.lua
index 13e4e92941..e6e9a5ef48 100644
--- a/indra/newview/scripts/lua/ErrorQueue.lua
+++ b/indra/newview/scripts/lua/require/ErrorQueue.lua
@@ -5,8 +5,11 @@
local WaitQueue = require('WaitQueue')
local function dbg(...) end
-- local dbg = require('printf')
+local util = require('util')
-local ErrorQueue = WaitQueue:new()
+local ErrorQueue = WaitQueue()
+
+util.classctor(ErrorQueue)
function ErrorQueue:Error(message)
-- Setting Error() is a marker, like closing the queue. Once we reach the
diff --git a/indra/newview/scripts/lua/Floater.lua b/indra/newview/scripts/lua/require/Floater.lua
index 75696533e4..d057a74386 100644
--- a/indra/newview/scripts/lua/Floater.lua
+++ b/indra/newview/scripts/lua/require/Floater.lua
@@ -2,6 +2,7 @@
local leap = require 'leap'
local fiber = require 'fiber'
+local util = require 'util'
-- list of all the events that a LLLuaFloater might send
local event_list = leap.request("LLFloaterReg", {op="getFloaterEvents"}).events
@@ -45,6 +46,8 @@ function Floater:new(path, extra)
return obj
end
+util.classctor(Floater)
+
function Floater:show()
-- leap.eventstream() returns the first response, and launches a
-- background fiber to call the passed callback with all subsequent
diff --git a/indra/newview/scripts/lua/LLChat.lua b/indra/newview/scripts/lua/require/LLChat.lua
index 7db538e837..78dca765e8 100644
--- a/indra/newview/scripts/lua/LLChat.lua
+++ b/indra/newview/scripts/lua/require/LLChat.lua
@@ -1,4 +1,4 @@
-leap = require 'leap'
+local leap = require 'leap'
local LLChat = {}
diff --git a/indra/newview/scripts/lua/LLChatListener.lua b/indra/newview/scripts/lua/require/LLChatListener.lua
index b4e90d272c..428dca881e 100644
--- a/indra/newview/scripts/lua/LLChatListener.lua
+++ b/indra/newview/scripts/lua/require/LLChatListener.lua
@@ -1,6 +1,7 @@
local fiber = require 'fiber'
local inspect = require 'inspect'
local leap = require 'leap'
+local util = require 'util'
local LLChatListener = {}
local waitfor = {}
@@ -14,6 +15,8 @@ function LLChatListener:new()
return obj
end
+util.classctor(LLChatListener)
+
function LLChatListener:handleMessages(event_data)
print(inspect(event_data))
return true
diff --git a/indra/newview/scripts/lua/LLDebugSettings.lua b/indra/newview/scripts/lua/require/LLDebugSettings.lua
index c250019a00..cff1a63c21 100644
--- a/indra/newview/scripts/lua/LLDebugSettings.lua
+++ b/indra/newview/scripts/lua/require/LLDebugSettings.lua
@@ -1,4 +1,4 @@
-leap = require 'leap'
+local leap = require 'leap'
local LLDebugSettings = {}
diff --git a/indra/newview/scripts/lua/LLFloaterAbout.lua b/indra/newview/scripts/lua/require/LLFloaterAbout.lua
index 44afee2e5c..a6e42d364f 100644
--- a/indra/newview/scripts/lua/LLFloaterAbout.lua
+++ b/indra/newview/scripts/lua/require/LLFloaterAbout.lua
@@ -1,6 +1,6 @@
-- Engage the LLFloaterAbout LLEventAPI
-leap = require 'leap'
+local leap = require 'leap'
local LLFloaterAbout = {}
diff --git a/indra/newview/scripts/lua/LLGesture.lua b/indra/newview/scripts/lua/require/LLGesture.lua
index cb410446d7..343b611e2c 100644
--- a/indra/newview/scripts/lua/LLGesture.lua
+++ b/indra/newview/scripts/lua/require/LLGesture.lua
@@ -1,6 +1,6 @@
-- Engage the LLGesture LLEventAPI
-leap = require 'leap'
+local leap = require 'leap'
local LLGesture = {}
diff --git a/indra/newview/scripts/lua/Queue.lua b/indra/newview/scripts/lua/require/Queue.lua
index 5ab2a8a72c..5bc72e4057 100644
--- a/indra/newview/scripts/lua/Queue.lua
+++ b/indra/newview/scripts/lua/require/Queue.lua
@@ -7,6 +7,8 @@
-- But had to resist
-- For fear it might be too obscua.
+local util = require 'util'
+
local Queue = {}
function Queue:new()
@@ -20,6 +22,8 @@ function Queue:new()
return obj
end
+util.classctor(Queue)
+
-- Check if the queue is empty
function Queue:IsEmpty()
return self._first > self._last
diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua
new file mode 100644
index 0000000000..eb1a4017c7
--- /dev/null
+++ b/indra/newview/scripts/lua/require/UI.lua
@@ -0,0 +1,125 @@
+-- Engage the viewer's UI
+
+local leap = require 'leap'
+local Timer = (require 'timers').Timer
+local mapargs = require 'mapargs'
+
+local UI = {}
+
+-- ***************************************************************************
+-- registered menu actions
+-- ***************************************************************************
+function UI.call(func, parameter)
+ -- 'call' is fire-and-forget
+ leap.request('UI', {op='call', ['function']=func, parameter=parameter})
+end
+
+function UI.getValue(path)
+ return leap.request('UI', {op='getValue', path=path})['value']
+end
+
+-- ***************************************************************************
+-- UI views
+-- ***************************************************************************
+-- Either:
+-- wreq{op='Something', a=1, b=2, ...}
+-- or:
+-- (args should be local, as this wreq() call modifies it)
+-- local args = {a=1, b=2, ...}
+-- wreq('Something', args)
+local function wreq(op_or_data, data_if_op)
+ if data_if_op ~= nil then
+ -- this is the wreq(op, data) form
+ data_if_op.op = op_or_data
+ op_or_data = data_if_op
+ end
+ return leap.request('LLWindow', op_or_data)
+end
+
+-- omit 'parent' to list all view paths
+function UI.listviews(parent)
+ return wreq{op='getPaths', under=parent}
+end
+
+function UI.viewinfo(path)
+ return wreq{op='getInfo', path=path}
+end
+
+-- ***************************************************************************
+-- mouse actions
+-- ***************************************************************************
+-- pass a table:
+-- UI.click{path=path
+-- [, button='LEFT' | 'CENTER' | 'RIGHT']
+-- [, x=x, y=y]
+-- [, hold=duration]}
+function UI.click(...)
+ local args = mapargs('path,button,x,y,hold', ...)
+ args.button = args.button or 'LEFT'
+ local hold = args.hold or 1.0
+ wreq('mouseMove', args)
+ wreq('mouseDown', args)
+ Timer(hold, 'wait')
+ wreq('mouseUp', args)
+end
+
+-- pass a table as for UI.click()
+function UI.doubleclick(...)
+ local args = mapargs('path,button,x,y', ...)
+ args.button = args.button or 'LEFT'
+ wreq('mouseDown', args)
+ wreq('mouseUp', args)
+ wreq('mouseDown', args)
+ wreq('mouseUp', args)
+end
+
+-- UI.drag{path=, xoff=, yoff=}
+function UI.drag(...)
+ local args = mapargs('path,xoff,yoff', ...)
+ -- query the specified path
+ local rect = UI.viewinfo(args.path).rect
+ local centerx = math.floor(rect.left + (rect.right - rect.left)/2)
+ local centery = math.floor(rect.bottom + (rect.top - rect.bottom)/2)
+ wreq{op='mouseMove', path=args.path, x=centerx, y=centery}
+ wreq{op='mouseDown', path=args.path, button='LEFT'}
+ wreq{op='mouseMove', path=args.path, x=centerx + args.xoff, y=centery + args.yoff}
+ wreq{op='mouseUp', path=args.path, button='LEFT'}
+end
+
+-- ***************************************************************************
+-- keyboard actions
+-- ***************************************************************************
+-- pass a table:
+-- UI.keypress{
+-- [path=path] -- if omitted, default input field
+-- [, char='x'] -- requires one of char, keycode, keysym
+-- [, keycode=120]
+-- keysym per https://github.com/secondlife/viewer/blob/main/indra/llwindow/llkeyboard.cpp#L68-L124
+-- [, keysym='Enter']
+-- [, mask={'SHIFT', 'CTL', 'ALT', 'MAC_CONTROL'}] -- some subset of these
+-- }
+function UI.keypress(...)
+ local args = mapargs('path,char,keycode,keysym,mask', ...)
+ if args.char == '\n' then
+ args.char = nil
+ args.keysym = 'Enter'
+ end
+ return wreq('keyDown', args)
+end
+
+-- UI.type{text=, path=}
+function UI.type(...)
+ local args = mapargs('text,path', ...)
+ if #args.text > 0 then
+ -- The caller's path may be specified in a way that requires recursively
+ -- searching parts of the LLView tree. No point in doing that more than
+ -- once. Capture the actual path found by that first call and use that for
+ -- subsequent calls.
+ local path = UI.keypress{path=args.path, char=string.sub(args.text, 1, 1)}.path
+ for i = 2, #args.text do
+ UI.keypress{path=path, char=string.sub(args.text, i, i)}
+ end
+ end
+end
+
+return UI
diff --git a/indra/newview/scripts/lua/WaitQueue.lua b/indra/newview/scripts/lua/require/WaitQueue.lua
index 6bcb9d62c2..7e10d03295 100644
--- a/indra/newview/scripts/lua/WaitQueue.lua
+++ b/indra/newview/scripts/lua/require/WaitQueue.lua
@@ -4,14 +4,15 @@
local fiber = require('fiber')
local Queue = require('Queue')
+local util = require('util')
local function dbg(...) end
-- local dbg = require('printf')
-local WaitQueue = Queue:new()
+local WaitQueue = Queue()
function WaitQueue:new()
- local obj = Queue:new()
+ local obj = Queue()
setmetatable(obj, self)
self.__index = self
@@ -20,6 +21,8 @@ function WaitQueue:new()
return obj
end
+util.classctor(WaitQueue)
+
function WaitQueue:Enqueue(value)
if self._closed then
error("can't Enqueue() on closed Queue")
diff --git a/indra/newview/scripts/lua/coro.lua b/indra/newview/scripts/lua/require/coro.lua
index 616a797e95..616a797e95 100644
--- a/indra/newview/scripts/lua/coro.lua
+++ b/indra/newview/scripts/lua/require/coro.lua
diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/require/fiber.lua
index cae27b936b..cae27b936b 100644
--- a/indra/newview/scripts/lua/fiber.lua
+++ b/indra/newview/scripts/lua/require/fiber.lua
diff --git a/indra/newview/scripts/lua/inspect.lua b/indra/newview/scripts/lua/require/inspect.lua
index 9900a0b81b..9900a0b81b 100644
--- a/indra/newview/scripts/lua/inspect.lua
+++ b/indra/newview/scripts/lua/require/inspect.lua
diff --git a/indra/newview/scripts/lua/leap.lua b/indra/newview/scripts/lua/require/leap.lua
index 8caae24e94..82f91ce9e9 100644
--- a/indra/newview/scripts/lua/leap.lua
+++ b/indra/newview/scripts/lua/require/leap.lua
@@ -43,6 +43,7 @@ local ErrorQueue = require('ErrorQueue')
local inspect = require('inspect')
local function dbg(...) end
-- local dbg = require('printf')
+local util = require('util')
local leap = {}
@@ -129,7 +130,7 @@ local function requestSetup(pump, data)
-- because, unlike the WaitFor base class, WaitForReqid does not
-- self-register on our waitfors list. Instead, capture the new
-- WaitForReqid object in pending so dispatch() can find it.
- local waitfor = leap.WaitForReqid:new(reqid)
+ local waitfor = leap.WaitForReqid(reqid)
pending[reqid] = waitfor
-- Pass reqid to send() to stamp it into (a copy of) the request data.
dbg('requestSetup(%s, %s) storing %s', pump, data, waitfor.name)
@@ -432,7 +433,7 @@ function leap.WaitFor:new(priority, name)
self._id += 1
obj.name = 'WaitFor' .. self._id
end
- obj._queue = ErrorQueue:new()
+ obj._queue = ErrorQueue()
obj._registered = false
-- if no priority, then don't enable() - remember 0 is truthy
if priority then
@@ -442,6 +443,8 @@ function leap.WaitFor:new(priority, name)
return obj
end
+util.classctor(leap.WaitFor)
+
-- Re-enable a disable()d WaitFor object. New WaitFor objects are
-- enable()d by default.
function leap.WaitFor:enable()
@@ -514,13 +517,13 @@ function leap.WaitFor:exception(message)
end
-- ------------------------------ WaitForReqid -------------------------------
-leap.WaitForReqid = leap.WaitFor:new()
+leap.WaitForReqid = leap.WaitFor()
function leap.WaitForReqid:new(reqid)
-- priority is meaningless, since this object won't be added to the
-- priority-sorted waitfors list. Use the reqid as the debugging name
-- string.
- local obj = leap.WaitFor:new(nil, 'WaitForReqid(' .. reqid .. ')')
+ local obj = leap.WaitFor(nil, 'WaitForReqid(' .. reqid .. ')')
setmetatable(obj, self)
self.__index = self
@@ -529,6 +532,8 @@ function leap.WaitForReqid:new(reqid)
return obj
end
+util.classctor(leap.WaitForReqid)
+
function leap.WaitForReqid:filter(pump, data)
-- Because we expect to directly look up the WaitForReqid object of
-- interest based on the incoming ["reqid"] value, it's not necessary
diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua
new file mode 100644
index 0000000000..0d8591cace
--- /dev/null
+++ b/indra/newview/scripts/lua/require/login.lua
@@ -0,0 +1,19 @@
+local UI = require 'UI'
+local leap = require 'leap'
+
+local function login(username, password)
+ if username and password then
+ local userpath = '//username_combo/Combo Text Entry'
+ local passpath = '//password_edit'
+ -- first clear anything presently in those text fields
+ for _, path in pairs({userpath, passpath}) do
+ UI.click(path)
+ UI.keypress{keysym='Backsp', path=path}
+ end
+ UI.type{path=userpath, text=username}
+ UI.type{path=passpath, text=password}
+ end
+ leap.send('LLPanelLogin', {op='onClickConnect'})
+end
+
+return login
diff --git a/indra/newview/scripts/lua/require/logout.lua b/indra/newview/scripts/lua/require/logout.lua
new file mode 100644
index 0000000000..63dcd7f01f
--- /dev/null
+++ b/indra/newview/scripts/lua/require/logout.lua
@@ -0,0 +1,7 @@
+local leap = require 'leap'
+
+local function logout()
+ leap.send('LLAppViewer', {op='userQuit'});
+end
+
+return logout
diff --git a/indra/newview/scripts/lua/require/mapargs.lua b/indra/newview/scripts/lua/require/mapargs.lua
new file mode 100644
index 0000000000..45f5a9c556
--- /dev/null
+++ b/indra/newview/scripts/lua/require/mapargs.lua
@@ -0,0 +1,73 @@
+-- Allow a calling function to be passed a mix of positional arguments with
+-- keyword arguments. Reference them as fields of a table.
+-- Don't use this for a function that can accept a single table argument.
+-- mapargs() assumes that a single table argument means its caller was called
+-- with f{table constructor} syntax, and maps that table to the specified names.
+-- Usage:
+-- function f(...)
+-- local a = mapargs({'a1', 'a2', 'a3'}, ...)
+-- ... a.a1 ... etc.
+-- end
+-- f(10, 20, 30) -- a.a1 == 10, a.a2 == 20, a.a3 == 30
+-- f{10, 20, 30} -- a.a1 == 10, a.a2 == 20, a.a3 == 30
+-- f{a3=300, a1=100} -- a.a1 == 100, a.a2 == nil, a.a3 == 300
+-- f{1, a3=3} -- a.a1 == 1, a.a2 == nil, a.a3 == 3
+-- f{a3=3, 1} -- a.a1 == 1, a.a2 == nil, a.a3 == 3
+local function mapargs(names, ...)
+ local args = table.pack(...)
+ local posargs = {}
+ local keyargs = {}
+ -- For a mixed table, no Lua operation will reliably tell you how many
+ -- array items it contains, if there are any holes. Track that by hand.
+ -- We must be able to handle f(1, nil, 3) calls.
+ local maxpos = 0
+
+ -- For convenience, allow passing 'names' as a string 'n0,n1,...'
+ if type(names) == 'string' then
+ names = string.split(names, ',')
+ end
+
+ if not (args.n == 1 and type(args[1]) == 'table') then
+ -- If caller passes more than one argument, or if the first argument
+ -- is not a table, then it's classic positional function-call syntax:
+ -- f(first, second, etc.). In that case we need not bother teasing
+ -- apart positional from keyword arguments.
+ posargs = args
+ maxpos = args.n
+ else
+ -- Single table argument implies f{mixed} syntax.
+ -- Tease apart positional arguments from keyword arguments.
+ for k, v in pairs(args[1]) do
+ if type(k) == 'number' then
+ posargs[k] = v
+ maxpos = math.max(maxpos, k)
+ else
+ if table.find(names, k) == nil then
+ error('unknown keyword argument ' .. tostring(k))
+ end
+ keyargs[k] = v
+ end
+ end
+ end
+
+ -- keyargs already has keyword arguments in place, just fill in positionals
+ args = keyargs
+ -- Don't exceed the number of parameter names. Loop explicitly over every
+ -- index value instead of using ipairs() so we can support holes (nils) in
+ -- posargs.
+ for i = 1, math.min(#names, maxpos) do
+ if posargs[i] ~= nil then
+ -- As in Python, make it illegal to pass an argument both positionally
+ -- and by keyword. This implementation permits func(17, first=nil), a
+ -- corner case about which I don't particularly care.
+ if args[names[i]] ~= nil then
+ error(string.format('parameter %s passed both positionally and by keyword',
+ tostring(names[i])))
+ end
+ args[names[i]] = posargs[i]
+ end
+ end
+ return args
+end
+
+return mapargs
diff --git a/indra/newview/scripts/lua/require/popup.lua b/indra/newview/scripts/lua/require/popup.lua
new file mode 100644
index 0000000000..3aaadf85ba
--- /dev/null
+++ b/indra/newview/scripts/lua/require/popup.lua
@@ -0,0 +1,53 @@
+local leap = require 'leap'
+local mapargs = require 'mapargs'
+
+-- notification is any name defined in notifications.xml as
+-- <notification name=>
+-- vars is a table providing values for [VAR] substitution keys in the
+-- notification body
+-- payload prepopulates the response table
+-- wait=false means fire and forget, otherwise wait for user response
+local popup_meta = {
+ -- setting this function as getmetatable(popup).__call() means this gets
+ -- called when a consumer calls popup(notification, vars, payload)
+ __call = function(self, ...)
+ local args = mapargs('notification,vars,payload,wait', ...)
+ -- we use convenience argument names different from 'LLNotifications'
+ -- listener
+ args.name = args.notification
+ args.notification = nil
+ args.substitutions = args.vars
+ args.vars = nil
+ local wait = args.wait
+ args.wait = nil
+ args.op = 'requestAdd'
+ -- Specifically test (wait == false), NOT (not wait), because we treat
+ -- nil (omitted, default true) differently than false (explicitly
+ -- DON'T wait).
+ if wait == false then
+ leap.send('LLNotifications', args)
+ else
+ return leap.request('LLNotifications', args).response
+ end
+ end
+}
+
+local popup = setmetatable({}, popup_meta)
+
+function popup:alert(message)
+ return self('GenericAlert', {MESSAGE=message})
+end
+
+function popup:alertOK(message)
+ return self('GenericAlertOK', {MESSAGE=message})
+end
+
+function popup:alertYesCancel(message)
+ return self('GenericAlertYesCancel', {MESSAGE=message})
+end
+
+function popup:tip(message)
+ self{'SystemMessageTip', {MESSAGE=message}, wait=false}
+end
+
+return popup
diff --git a/indra/newview/scripts/lua/printf.lua b/indra/newview/scripts/lua/require/printf.lua
index e84b2024df..e84b2024df 100644
--- a/indra/newview/scripts/lua/printf.lua
+++ b/indra/newview/scripts/lua/require/printf.lua
diff --git a/indra/newview/scripts/lua/startup.lua b/indra/newview/scripts/lua/require/startup.lua
index 4311bb9a60..c3040f94b8 100644
--- a/indra/newview/scripts/lua/startup.lua
+++ b/indra/newview/scripts/lua/require/startup.lua
@@ -12,6 +12,8 @@ local function dbg(...) end
-- local dbg = require 'printf'
-- ---------------------------------------------------------------------------
+local startup = {}
+
-- Get the list of startup states from the viewer.
local bynum = leap.request('LLStartUp', {op='getStateTable'})['table']
@@ -19,7 +21,7 @@ local byname = setmetatable(
{},
-- set metatable to throw an error if you look up invalid state name
{__index=function(t, k)
- local v = t[k]
+ local v = rawget(t, k)
if v then
return v
end
@@ -35,7 +37,7 @@ end
-- specialize a WaitFor to track the viewer's startup state
local startup_pump = 'StartupState'
-local waitfor = leap.WaitFor:new(0, startup_pump)
+local waitfor = leap.WaitFor(0, startup_pump)
function waitfor:filter(pump, data)
if pump == self.name then
return data
@@ -57,8 +59,6 @@ leap.request(leap.cmdpump(),
leap.send('LLStartUp', {op='postStartupState'})
-- ---------------------------------------------------------------------------
-startup = {}
-
-- wait for response from postStartupState
while not startup._state do
dbg('startup.state() waiting for first StartupState event')
@@ -98,4 +98,3 @@ function startup.wait(state)
end
return startup
-
diff --git a/indra/newview/scripts/lua/timers.lua b/indra/newview/scripts/lua/require/timers.lua
index e0d27a680d..e4938078dc 100644
--- a/indra/newview/scripts/lua/timers.lua
+++ b/indra/newview/scripts/lua/require/timers.lua
@@ -1,6 +1,7 @@
-- Access to the viewer's time-delay facilities
local leap = require 'leap'
+local util = require 'util'
local timers = {}
@@ -78,6 +79,8 @@ function timers.Timer:new(delay, callback, iterate)
return obj
end
+util.classctor(timers.Timer)
+
function timers.Timer:tick()
error('Pass a callback to Timer:new(), or override Timer:tick()')
end
diff --git a/indra/newview/scripts/lua/util.lua b/indra/newview/scripts/lua/require/util.lua
index a2191288f6..bfbfc8637c 100644
--- a/indra/newview/scripts/lua/util.lua
+++ b/indra/newview/scripts/lua/require/util.lua
@@ -2,6 +2,31 @@
local util = {}
+-- Allow MyClass(ctor args...) equivalent to MyClass:new(ctor args...)
+-- Usage:
+-- local MyClass = {}
+-- function MyClass:new(...)
+-- ...
+-- end
+-- ...
+-- util.classctor(MyClass)
+-- or if your constructor is named something other than MyClass:new(), e.g.
+-- MyClass:construct():
+-- util.classctor(MyClass, MyClass.construct)
+-- return MyClass
+function util.classctor(class, ctor)
+ -- get the metatable for the passed class
+ local mt = getmetatable(class)
+ if mt == nil then
+ -- if it doesn't already have a metatable, then create one
+ mt = {}
+ setmetatable(class, mt)
+ end
+ -- now that class has a metatable, set its __call method to the specified
+ -- constructor method (class.new if not specified)
+ mt.__call = ctor or class.new
+end
+
-- check if array-like table contains certain value
function util.contains(t, v)
return table.find(t, v) ~= nil
diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua
index b9696e7cfc..18363ed43b 100644
--- a/indra/newview/scripts/lua/test_LLChatListener.lua
+++ b/indra/newview/scripts/lua/test_LLChatListener.lua
@@ -11,7 +11,7 @@ function openOrEcho(message)
end
end
-local listener = LLChatListener:new()
+local listener = LLChatListener()
function listener:handleMessages(event_data)
if string.find(event_data.message, '[LUA]') then
diff --git a/indra/newview/scripts/lua/test_atexit.lua b/indra/newview/scripts/lua/test_atexit.lua
new file mode 100644
index 0000000000..6fbc0f3eb1
--- /dev/null
+++ b/indra/newview/scripts/lua/test_atexit.lua
@@ -0,0 +1,3 @@
+LL.atexit(function() print('Third') end)
+LL.atexit(function() print('Second') end)
+LL.atexit(function() print('First') end)
diff --git a/indra/newview/scripts/lua/test_login.lua b/indra/newview/scripts/lua/test_login.lua
new file mode 100644
index 0000000000..a8c31807bc
--- /dev/null
+++ b/indra/newview/scripts/lua/test_login.lua
@@ -0,0 +1,7 @@
+startup = require 'startup'
+login = require 'login'
+
+startup.wait('STATE_LOGIN_WAIT')
+login()
+-- Fill in valid credentials as they would be entered on the login screen
+-- login('My Username', 'password')
diff --git a/indra/newview/scripts/lua/test_logout.lua b/indra/newview/scripts/lua/test_logout.lua
new file mode 100644
index 0000000000..b1ac59e38c
--- /dev/null
+++ b/indra/newview/scripts/lua/test_logout.lua
@@ -0,0 +1,3 @@
+logout = require 'logout'
+
+logout()
diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua
deleted file mode 100644
index ab638dcdd1..0000000000
--- a/indra/newview/scripts/lua/test_luafloater_demo.lua
+++ /dev/null
@@ -1,77 +0,0 @@
-XML_FILE_PATH = LL.abspath("luafloater_demo.xml")
-
-scriptparts = string.split(LL.source_path(), '/')
-scriptname = scriptparts[#scriptparts]
-print('Running ' .. scriptname)
-
-leap = require 'leap'
-fiber = require 'fiber'
-startup = require 'startup'
-
---event pump for sending actions to the floater
-local COMMAND_PUMP_NAME = ""
-local reqid
---table of floater UI events
-event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events
-
-local function _event(event_name)
- if not table.find(event_list, event_name) then
- LL.print_warning("Incorrect event name: " .. event_name)
- end
- return event_name
-end
-
-function post(action)
- leap.send(COMMAND_PUMP_NAME, action)
-end
-
-function getCurrentTime()
- local currentTime = os.date("*t")
- return string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec)
-end
-
-function handleEvents(event_data)
- post({action="add_text", ctrl_name="events_editor", value = event_data})
- if event_data.event == _event("commit") then
- if event_data.ctrl_name == "disable_ctrl" then
- post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)})
- elseif event_data.ctrl_name == "title_cmb" then
- post({action="set_title", value= event_data.value})
- elseif event_data.ctrl_name == "open_btn" then
- floater_name = leap.request(COMMAND_PUMP_NAME, {action="get_value", ctrl_name='openfloater_cmd'})['value']
- leap.send("LLFloaterReg", {name = floater_name, op = "showInstance"})
- end
- elseif event_data.event == _event("double_click") then
- if event_data.ctrl_name == "show_time_lbl" then
- post({action="set_value", ctrl_name="time_lbl", value= getCurrentTime()})
- end
- elseif event_data.event == _event("floater_close") then
- LL.print_warning("Floater was closed")
- return false
- end
- return true
-end
-
-startup.wait('STATE_LOGIN_WAIT')
-local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"}
---sign for additional events for defined control {<control_name>= {action1, action2, ...}}
-key.extra_events={show_time_lbl = {_event("right_mouse_down"), _event("double_click")}}
-local resp = leap.request("LLFloaterReg", key)
-COMMAND_PUMP_NAME = resp.command_name
-reqid = resp.reqid
-
-catch_events = leap.WaitFor:new(-1, "all_events")
-function catch_events:filter(pump, data)
- if data.reqid == reqid then
- return data
- end
-end
-
-function process_events(waitfor)
- event_data = waitfor:wait()
- while event_data and handleEvents(event_data) do
- event_data = waitfor:wait()
- end
-end
-
-fiber.launch("catch_events", process_events, catch_events)
diff --git a/indra/newview/scripts/lua/test_luafloater_demo2.lua b/indra/newview/scripts/lua/test_luafloater_demo2.lua
index 9e24237d28..3903d01e65 100644
--- a/indra/newview/scripts/lua/test_luafloater_demo2.lua
+++ b/indra/newview/scripts/lua/test_luafloater_demo2.lua
@@ -2,7 +2,7 @@ local Floater = require 'Floater'
local leap = require 'leap'
local startup = require 'startup'
-local flt = Floater:new(
+local flt = Floater(
'luafloater_demo.xml',
{show_time_lbl = {"right_mouse_down", "double_click"}})
diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua
deleted file mode 100644
index 3d9a9b0ad4..0000000000
--- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua
+++ /dev/null
@@ -1,75 +0,0 @@
-XML_FILE_PATH = LL.abspath("luafloater_gesture_list.xml")
-
-scriptparts = string.split(LL.source_path(), '/')
-scriptname = scriptparts[#scriptparts]
-print('Running ' .. scriptname)
-
-leap = require 'leap'
-fiber = require 'fiber'
-LLGesture = require 'LLGesture'
-startup = require 'startup'
-
---event pump for sending actions to the floater
-local COMMAND_PUMP_NAME = ""
-local reqid
---table of floater UI events
-event_list=leap.request("LLFloaterReg", {op="getFloaterEvents"}).events
-
-local function _event(event_name)
- if not table.find(event_list, event_name) then
- LL.print_warning("Incorrect event name: " .. event_name)
- end
- return event_name
-end
-
-function post(action)
- leap.send(COMMAND_PUMP_NAME, action)
-end
-
-function handleEvents(event_data)
- if event_data.event == _event("floater_close") then
- return false
- end
-
- if event_data.event == _event("post_build") then
- COMMAND_PUMP_NAME = event_data.command_name
- reqid = event_data.reqid
- gestures_uuid = LLGesture.getActiveGestures()
- local action_data = {}
- action_data.action = "add_list_element"
- action_data.ctrl_name = "gesture_list"
- gestures = {}
- for uuid, info in pairs(gestures_uuid) do
- table.insert(gestures, {value = uuid, columns ={column = "gesture_name", value = info.name}})
- end
- action_data.value = gestures
- post(action_data)
- elseif event_data.event == _event("double_click") then
- if event_data.ctrl_name == "gesture_list" then
- LLGesture.startGesture(event_data.value)
- end
- end
- return true
-end
-
-startup.wait('STATE_STARTED')
-local key = {xml_path = XML_FILE_PATH, op = "showLuaFloater"}
---receive additional events for defined control {<control_name>= {action1, action2, ...}}
-key.extra_events={gesture_list = {_event("double_click")}}
-handleEvents(leap.request("LLFloaterReg", key))
-
-catch_events = leap.WaitFor:new(-1, "all_events")
-function catch_events:filter(pump, data)
- if data.reqid == reqid then
- return data
- end
-end
-
-function process_events(waitfor)
- event_data = waitfor:wait()
- while event_data and handleEvents(event_data) do
- event_data = waitfor:wait()
- end
-end
-
-fiber.launch("catch_events", process_events, catch_events)
diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua
index d702d09c51..bd397ef2a6 100644
--- a/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua
+++ b/indra/newview/scripts/lua/test_luafloater_gesture_list2.lua
@@ -2,7 +2,7 @@ local Floater = require 'Floater'
local LLGesture = require 'LLGesture'
local startup = require 'startup'
-local flt = Floater:new(
+local flt = Floater(
"luafloater_gesture_list.xml",
{gesture_list = {"double_click"}})
diff --git a/indra/newview/scripts/lua/test_luafloater_speedometer.lua b/indra/newview/scripts/lua/test_luafloater_speedometer.lua
index a9d3a70330..af7189a2cb 100644
--- a/indra/newview/scripts/lua/test_luafloater_speedometer.lua
+++ b/indra/newview/scripts/lua/test_luafloater_speedometer.lua
@@ -1,10 +1,10 @@
local Floater = require 'Floater'
local leap = require 'leap'
-local LLNotification = require 'LLNotification'
+local popup = require 'popup'
local startup = require 'startup'
local Timer = (require 'timers').Timer
local max_speed = 0
-local flt = Floater:new("luafloater_speedometer.xml")
+local flt = Floater("luafloater_speedometer.xml")
startup.wait('STATE_STARTED')
local timer
@@ -13,8 +13,7 @@ function flt:floater_close(event_data)
if timer then
timer:cancel()
end
- msg = "Registered max speed: " .. string.format("%.2f", max_speed) .. " m/s";
- LLNotification.add('SystemMessageTip', {MESSAGE = msg})
+ popup:tip(string.format("Registered max speed: %.2f m/s", max_speed))
end
local function idle(event_data)
@@ -24,9 +23,9 @@ local function idle(event_data)
end
msg = 'Are you sure you want to run this "speedometer" script?'
-response = LLNotification.requestAdd('GenericAlertYesCancel', {MESSAGE = msg})
+response = popup:alertYesCancel(msg)
if response.OK_okcancelbuttons then
flt:show()
- timer = Timer:new(1, idle, true) -- iterate
+ timer = Timer(1, idle, true) -- iterate
end
diff --git a/indra/newview/scripts/lua/test_mapargs.lua b/indra/newview/scripts/lua/test_mapargs.lua
new file mode 100644
index 0000000000..999a57acb4
--- /dev/null
+++ b/indra/newview/scripts/lua/test_mapargs.lua
@@ -0,0 +1,68 @@
+local mapargs = require 'mapargs'
+local inspect = require 'inspect'
+
+function tabfunc(...)
+ local a = mapargs({'a1', 'a2', 'a3'}, ...)
+ print(inspect(a))
+end
+
+print('----------')
+print('f(10, 20, 30)')
+tabfunc(10, 20, 30)
+print('f(10, nil, 30)')
+tabfunc(10, nil, 30)
+print('f{10, 20, 30}')
+tabfunc{10, 20, 30}
+print('f{10, nil, 30}')
+tabfunc{10, nil, 30}
+print('f{a3=300, a1=100}')
+tabfunc{a3=300, a1=100}
+print('f{1, a3=3}')
+tabfunc{1, a3=3}
+print('f{a3=3, 1}')
+tabfunc{a3=3, 1}
+print('----------')
+
+if false then
+ -- the code below was used to explore ideas that became mapargs()
+ mixed = { '[1]', nil, '[3]', abc='[abc]', '[3]', def='[def]' }
+ local function showtable(desc, t)
+ print(string.format('%s (len %s)\n%s', desc, #t, inspect(t)))
+ end
+ showtable('mixed', mixed)
+
+ print('ipairs(mixed)')
+ for k, v in ipairs(mixed) do
+ print(string.format('[%s] = %s', k, tostring(v)))
+ end
+
+ print('table.pack(mixed)')
+ print(inspect(table.pack(mixed)))
+
+ local function nilarg(desc, a, b, c)
+ print(desc)
+ print('a = ' .. tostring(a))
+ print('b = ' .. tostring(b))
+ print('c = ' .. tostring(c))
+ end
+
+ nilarg('nilarg(1)', 1)
+ nilarg('nilarg(1, nil, 3)', 1, nil, 3)
+
+ local function nilargs(desc, ...)
+ args = table.pack(...)
+ showtable(desc, args)
+ end
+
+ nilargs('nilargs{a=1, b=2, c=3}', {a=1, b=2, c=3})
+ nilargs('nilargs(1, 2, 3)', 1, 2, 3)
+ nilargs('nilargs(1, nil, 3)', 1, nil, 3)
+ nilargs('nilargs{1, 2, 3}', {1, 2, 3})
+ nilargs('nilargs{1, nil, 3}', {1, nil, 3})
+
+ print('table.unpack({1, nil, 3})')
+ a, b, c = table.unpack({1, nil, 3})
+ print('a = ' .. tostring(a))
+ print('b = ' .. tostring(b))
+ print('c = ' .. tostring(c))
+end
diff --git a/indra/newview/scripts/lua/test_popup.lua b/indra/newview/scripts/lua/test_popup.lua
new file mode 100644
index 0000000000..e48f89c3a7
--- /dev/null
+++ b/indra/newview/scripts/lua/test_popup.lua
@@ -0,0 +1,6 @@
+popup = require 'popup'
+
+response = popup:alert('This just has a Close button')
+response = popup:alertOK(string.format('You said "%s", is that OK?', next(response)))
+response = popup:alertYesCancel(string.format('You said "%s"', next(response)))
+popup:tip(string.format('You said "%s"', next(response)))
diff --git a/indra/newview/scripts/lua/test_timers.lua b/indra/newview/scripts/lua/test_timers.lua
index ed0de070f7..be5001aa16 100644
--- a/indra/newview/scripts/lua/test_timers.lua
+++ b/indra/newview/scripts/lua/test_timers.lua
@@ -6,9 +6,9 @@ local timers = require 'timers'
-- is true, that timeUntilCall() is (true, close to 10), that cancel() returns
-- true. After that, isRunning() is false, timeUntilCall() returns (false, 0),
-- and a second cancel() returns false.
-print('t0:new(10)')
+print('t0(10)')
start = os.clock()
-t0 = timers.Timer:new(10, function() print('t0 fired at', os.clock() - start) end)
+t0 = timers.Timer(10, function() print('t0 fired at', os.clock() - start) end)
print('t0:isRunning(): ', t0:isRunning())
print('t0:timeUntilCall(): ', t0:timeUntilCall())
print('t0:cancel(): ', t0:cancel())
@@ -18,16 +18,16 @@ print('t0:cancel(): ', t0:cancel())
-- t1 is supposed to fire after 5 seconds, but it doesn't wait, so you see the
-- t2 messages immediately after.
-print('t1:new(5)')
+print('t1(5)')
start = os.clock()
-t1 = timers.Timer:new(5, function() print('t1 fired at', os.clock() - start) end)
+t1 = timers.Timer(5, function() print('t1 fired at', os.clock() - start) end)
-- t2 illustrates that instead of passing a callback to new(), you can
-- override the timer instance's tick() method. But t2 doesn't wait either, so
-- you see the Timer(5) message immediately.
-print('t2:new(2)')
+print('t2(2)')
start = os.clock()
-t2 = timers.Timer:new(2)
+t2 = timers.Timer(2)
function t2:tick()
print('t2 fired at', os.clock() - start)
end
@@ -37,7 +37,7 @@ end
-- then the t1 callback message before the Timer(5) completion message.
print('Timer(5) waiting')
start = os.clock()
-timers.Timer:new(5, 'wait')
+timers.Timer(5, 'wait')
print(string.format('Timer(5) waited %f seconds', os.clock() - start))
-- This test demonstrates a repeating timer. It also shows that you can (but
@@ -50,7 +50,7 @@ print(string.format('Timer(5) waited %f seconds', os.clock() - start))
-- it's worth knowing that a coroutine timer callback can be used to manage
-- more complex control flows.
start = os.clock()
-timers.Timer:new(
+timers.Timer(
2,
coroutine.wrap(function()
for i = 1,5 do
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index cf1bf25b5c..2d525f7913 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -398,7 +398,7 @@ namespace tut
LuaState L;
auto future = LLLUAmanager::startScriptLine(L, lua);
- auto replyname{ L.obtainListener()->getReplyName() };
+ auto replyname{ L.obtainListener().getReplyName() };
auto& replypump{ LLEventPumps::instance().obtain(replyname) };
// LuaState::expr() periodically interrupts a running chunk to ensure
// the rest of our coroutines get cycles. Nonetheless, for this test
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 53039fbd99..20dced2341 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -169,6 +169,8 @@ class ViewerManifest(LLManifest):
with self.prefix(src_dst="scripts/lua"):
self.path("*.lua")
self.path("*.xml")
+ with self.prefix(src_dst='require'):
+ self.path("*.lua")
#build_data.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems
#platform is computed above with other arg parsing
@@ -285,7 +287,7 @@ class ViewerManifest(LLManifest):
# A line that starts with a non-whitespace character is a name; all others describe contributions, so collect the names
names = []
for line in lines :
- if re.match("\S", line) :
+ if re.match(r"\S", line) :
names.append(line.rstrip())
# It's not fair to always put the same people at the head of the list
random.shuffle(names)