mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Introduce c2.later()
function to Lua API. (#5154)
This commit is contained in:
parent
101dc82ea0
commit
a737d4b755
|
@ -34,6 +34,7 @@
|
||||||
- Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130)
|
- Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130)
|
||||||
- Minor: Add a new Channel API for experimental plugins feature. (#5141, #5184, #5187)
|
- Minor: Add a new Channel API for experimental plugins feature. (#5141, #5184, #5187)
|
||||||
- Minor: Added the ability to change the top-most status of a window regardless of the _Always on top_ setting (right click the notebook). (#5135)
|
- Minor: Added the ability to change the top-most status of a window regardless of the _Always on top_ setting (right click the notebook). (#5135)
|
||||||
|
- Minor: Introduce `c2.later()` function to Lua API. (#5154)
|
||||||
- Minor: Live streams that are marked as reruns now mark a tab as yellow instead of red. (#5176)
|
- Minor: Live streams that are marked as reruns now mark a tab as yellow instead of red. (#5176)
|
||||||
- Minor: Updated to Emoji v15.1. Google emojis are now used as the fallback instead of Twitter emojis. (#5182)
|
- Minor: Updated to Emoji v15.1. Google emojis are now used as the fallback instead of Twitter emojis. (#5182)
|
||||||
- Minor: Allow theming of tab live and rerun indicators. (#5188)
|
- Minor: Allow theming of tab live and rerun indicators. (#5188)
|
||||||
|
|
1
docs/chatterino.d.ts
vendored
1
docs/chatterino.d.ts
vendored
|
@ -95,4 +95,5 @@ declare module c2 {
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
function register_callback<T>(type: T, func: CbFunc<T>): void;
|
function register_callback<T>(type: T, func: CbFunc<T>): void;
|
||||||
|
function later(callback: () => void, msec: number): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,3 +185,9 @@ function c2.register_callback(type, func) end
|
||||||
---@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
---@param ... any Values to log. Should be convertible to a string with `tostring()`.
|
||||||
function c2.log(level, ...) end
|
function c2.log(level, ...) end
|
||||||
|
|
||||||
|
--- Calls callback around msec milliseconds later. Does not freeze Chatterino.
|
||||||
|
---
|
||||||
|
---@param callback fun() The callback that will be called.
|
||||||
|
---@param msec number How long to wait.
|
||||||
|
function c2.later(callback, msec) end
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,63 @@ int c2_log(lua_State *L)
|
||||||
return 0;
|
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)
|
int g_load(lua_State *L)
|
||||||
{
|
{
|
||||||
# ifdef NDEBUG
|
# ifdef NDEBUG
|
||||||
|
|
|
@ -86,6 +86,15 @@ int c2_register_callback(lua_State *L);
|
||||||
*/
|
*/
|
||||||
int c2_log(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
|
// These ones are global
|
||||||
int g_load(lua_State *L);
|
int g_load(lua_State *L);
|
||||||
int g_print(lua_State *L);
|
int g_print(lua_State *L);
|
||||||
|
|
|
@ -140,6 +140,18 @@ StackIdx push(lua_State *L, const int &b)
|
||||||
return lua_gettop(L);
|
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)
|
bool peek(lua_State *L, bool *out, StackIdx idx)
|
||||||
{
|
{
|
||||||
StackGuard guard(L);
|
StackGuard guard(L);
|
||||||
|
|
|
@ -66,6 +66,7 @@ StackIdx push(lua_State *L, const bool &b);
|
||||||
StackIdx push(lua_State *L, const int &b);
|
StackIdx push(lua_State *L, const int &b);
|
||||||
|
|
||||||
// returns OK?
|
// 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, bool *out, StackIdx idx = -1);
|
||||||
bool peek(lua_State *L, double *out, StackIdx idx = -1);
|
bool peek(lua_State *L, double *out, StackIdx idx = -1);
|
||||||
bool peek(lua_State *L, QString *out, StackIdx idx = -1);
|
bool peek(lua_State *L, QString *out, StackIdx idx = -1);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||||
# include "controllers/plugins/Plugin.hpp"
|
# include "controllers/plugins/Plugin.hpp"
|
||||||
|
|
||||||
|
# include "common/QLogging.hpp"
|
||||||
# include "controllers/commands/CommandController.hpp"
|
# include "controllers/commands/CommandController.hpp"
|
||||||
|
|
||||||
# include <lua.h>
|
# include <lua.h>
|
||||||
|
@ -167,11 +168,38 @@ std::unordered_set<QString> Plugin::listRegisteredCommands()
|
||||||
|
|
||||||
Plugin::~Plugin()
|
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)
|
if (this->state_ != nullptr)
|
||||||
{
|
{
|
||||||
lua_close(this->state_);
|
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
|
} // namespace chatterino
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -126,6 +127,9 @@ public:
|
||||||
return this->error_;
|
return this->error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int addTimeout(QTimer *timer);
|
||||||
|
void removeTimeout(QTimer *timer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir loadDirectory_;
|
QDir loadDirectory_;
|
||||||
lua_State *state_;
|
lua_State *state_;
|
||||||
|
@ -134,6 +138,8 @@ private:
|
||||||
|
|
||||||
// maps command name -> function name
|
// maps command name -> function name
|
||||||
std::unordered_map<QString, QString> ownedCommands;
|
std::unordered_map<QString, QString> ownedCommands;
|
||||||
|
std::vector<QTimer *> activeTimeouts;
|
||||||
|
int lastTimerId = 0;
|
||||||
|
|
||||||
friend class PluginController;
|
friend class PluginController;
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,8 +118,7 @@ void PluginController::openLibrariesFor(lua_State *L, const PluginMeta &meta,
|
||||||
luaL_Reg{LUA_GNAME, luaopen_base},
|
luaL_Reg{LUA_GNAME, luaopen_base},
|
||||||
// - load - don't allow in release mode
|
// - load - don't allow in release mode
|
||||||
|
|
||||||
//luaL_Reg{LUA_COLIBNAME, luaopen_coroutine},
|
luaL_Reg{LUA_COLIBNAME, luaopen_coroutine},
|
||||||
// - needs special support
|
|
||||||
luaL_Reg{LUA_TABLIBNAME, luaopen_table},
|
luaL_Reg{LUA_TABLIBNAME, luaopen_table},
|
||||||
// luaL_Reg{LUA_IOLIBNAME, luaopen_io},
|
// luaL_Reg{LUA_IOLIBNAME, luaopen_io},
|
||||||
// - explicit fs access, needs wrapper with permissions, no usage ideas yet
|
// - 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_command", lua::api::c2_register_command},
|
||||||
{"register_callback", lua::api::c2_register_callback},
|
{"register_callback", lua::api::c2_register_callback},
|
||||||
{"log", lua::api::c2_log},
|
{"log", lua::api::c2_log},
|
||||||
|
{"later", lua::api::c2_later},
|
||||||
{nullptr, nullptr},
|
{nullptr, nullptr},
|
||||||
};
|
};
|
||||||
lua_pushglobaltable(L);
|
lua_pushglobaltable(L);
|
||||||
|
@ -339,6 +339,11 @@ bool PluginController::isPluginEnabled(const QString &id)
|
||||||
|
|
||||||
Plugin *PluginController::getPluginByStatePtr(lua_State *L)
|
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_)
|
for (auto &[name, plugin] : this->plugins_)
|
||||||
{
|
{
|
||||||
if (plugin->state_ == L)
|
if (plugin->state_ == L)
|
||||||
|
|
Loading…
Reference in a new issue