Introduce c2.later() function to Lua API. (#5154)

This commit is contained in:
Mm2PL
2024-02-25 12:45:59 +01:00
committed by GitHub
parent 101dc82ea0
commit a737d4b755
10 changed files with 128 additions and 2 deletions
+57
View File
@@ -147,6 +147,63 @@ int c2_log(lua_State *L)
return 0;
}
int c2_later(lua_State *L)
{
auto *pl = getIApp()->getPlugins()->getPluginByStatePtr(L);
if (pl == nullptr)
{
return luaL_error(L, "c2.later: internal error: no plugin?");
}
if (lua_gettop(L) != 2)
{
return luaL_error(
L, "c2.later expects two arguments (a callback that takes no "
"arguments and returns nothing and a number the time in "
"milliseconds to wait)\n");
}
int time{};
if (!lua::pop(L, &time))
{
return luaL_error(L, "cannot get time (2nd arg of c2.later, "
"expected a number)");
}
if (!lua_isfunction(L, lua_gettop(L)))
{
return luaL_error(L, "cannot get callback (1st arg of c2.later, "
"expected a function)");
}
auto *timer = new QTimer();
timer->setInterval(time);
auto id = pl->addTimeout(timer);
auto name = QString("timeout_%1").arg(id);
auto *coro = lua_newthread(L);
QObject::connect(timer, &QTimer::timeout, [pl, coro, name, timer]() {
timer->deleteLater();
pl->removeTimeout(timer);
int nres{};
lua_resume(coro, nullptr, 0, &nres);
lua_pushnil(coro);
lua_setfield(coro, LUA_REGISTRYINDEX, name.toStdString().c_str());
if (lua_gettop(coro) != 0)
{
stackDump(coro,
pl->id +
": timer returned a value, this shouldn't happen "
"and is probably a plugin bug");
}
});
stackDump(L, "before setfield");
lua_setfield(L, LUA_REGISTRYINDEX, name.toStdString().c_str());
lua_xmove(L, coro, 1); // move function to thread
timer->start();
return 0;
}
int g_load(lua_State *L)
{
# ifdef NDEBUG
+9
View File
@@ -86,6 +86,15 @@ int c2_register_callback(lua_State *L);
*/
int c2_log(lua_State *L);
/**
* Calls callback around msec milliseconds later. Does not freeze Chatterino.
*
* @lua@param callback fun() The callback that will be called.
* @lua@param msec number How long to wait.
* @exposed c2.later
*/
int c2_later(lua_State *L);
// These ones are global
int g_load(lua_State *L);
int g_print(lua_State *L);
+12
View File
@@ -140,6 +140,18 @@ StackIdx push(lua_State *L, const int &b)
return lua_gettop(L);
}
bool peek(lua_State *L, int *out, StackIdx idx)
{
StackGuard guard(L);
if (lua_isnumber(L, idx) == 0)
{
return false;
}
*out = lua_tointeger(L, idx);
return true;
}
bool peek(lua_State *L, bool *out, StackIdx idx)
{
StackGuard guard(L);
+1
View File
@@ -66,6 +66,7 @@ StackIdx push(lua_State *L, const bool &b);
StackIdx push(lua_State *L, const int &b);
// returns OK?
bool peek(lua_State *L, int *out, StackIdx idx = -1);
bool peek(lua_State *L, bool *out, StackIdx idx = -1);
bool peek(lua_State *L, double *out, StackIdx idx = -1);
bool peek(lua_State *L, QString *out, StackIdx idx = -1);
+28
View File
@@ -1,6 +1,7 @@
#ifdef CHATTERINO_HAVE_PLUGINS
# include "controllers/plugins/Plugin.hpp"
# include "common/QLogging.hpp"
# include "controllers/commands/CommandController.hpp"
# include <lua.h>
@@ -167,11 +168,38 @@ std::unordered_set<QString> Plugin::listRegisteredCommands()
Plugin::~Plugin()
{
for (auto *timer : this->activeTimeouts)
{
QObject::disconnect(timer, nullptr, nullptr, nullptr);
timer->deleteLater();
}
qCDebug(chatterinoLua) << "Destroyed" << this->activeTimeouts.size()
<< "timers for plugin" << this->id
<< "while destroying the object";
this->activeTimeouts.clear();
if (this->state_ != nullptr)
{
lua_close(this->state_);
}
}
int Plugin::addTimeout(QTimer *timer)
{
this->activeTimeouts.push_back(timer);
return ++this->lastTimerId;
}
void Plugin::removeTimeout(QTimer *timer)
{
for (auto it = this->activeTimeouts.begin();
it != this->activeTimeouts.end(); ++it)
{
if (*it == timer)
{
this->activeTimeouts.erase(it);
break;
}
}
}
} // namespace chatterino
#endif
+6
View File
@@ -14,6 +14,7 @@
# include <vector>
struct lua_State;
class QTimer;
namespace chatterino {
@@ -126,6 +127,9 @@ public:
return this->error_;
}
int addTimeout(QTimer *timer);
void removeTimeout(QTimer *timer);
private:
QDir loadDirectory_;
lua_State *state_;
@@ -134,6 +138,8 @@ private:
// maps command name -> function name
std::unordered_map<QString, QString> ownedCommands;
std::vector<QTimer *> activeTimeouts;
int lastTimerId = 0;
friend class PluginController;
};
+7 -2
View File
@@ -118,8 +118,7 @@ void PluginController::openLibrariesFor(lua_State *L, const PluginMeta &meta,
luaL_Reg{LUA_GNAME, luaopen_base},
// - load - don't allow in release mode
//luaL_Reg{LUA_COLIBNAME, luaopen_coroutine},
// - needs special support
luaL_Reg{LUA_COLIBNAME, luaopen_coroutine},
luaL_Reg{LUA_TABLIBNAME, luaopen_table},
// luaL_Reg{LUA_IOLIBNAME, luaopen_io},
// - explicit fs access, needs wrapper with permissions, no usage ideas yet
@@ -147,6 +146,7 @@ void PluginController::openLibrariesFor(lua_State *L, const PluginMeta &meta,
{"register_command", lua::api::c2_register_command},
{"register_callback", lua::api::c2_register_callback},
{"log", lua::api::c2_log},
{"later", lua::api::c2_later},
{nullptr, nullptr},
};
lua_pushglobaltable(L);
@@ -339,6 +339,11 @@ bool PluginController::isPluginEnabled(const QString &id)
Plugin *PluginController::getPluginByStatePtr(lua_State *L)
{
lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
// Use the main thread for identification, not a coroutine instance
auto *mainL = lua_tothread(L, -1);
lua_pop(L, 1);
L = mainL;
for (auto &[name, plugin] : this->plugins_)
{
if (plugin->state_ == L)