lua: allow scripts to snoop messages

Adds the following Lua function to enable message events:

    mp.enable_messages(size, level)

size is the maximum number of messages the ringbuffer consists of. level
is the minimum log level for a message to be added to the ringbuffer,
and uses the same values as the mp.log() function. (Actually not yet,
but this will be fixed in the following commit.)

The messages will be delivered via the mp_event() in the user script,
using "message" as event name. The event argument is a table with the
following fields:

    level: log level of the message (string as in mp.log())
    prefix: string identifying the module of origin
    text: contents of the message

As of currently, the message text will contain newline characters. A
message can consist of several lines. It is also possible that a
message doesn't end with a newline, and a caller can use multiple
messages to "build" a line. Most messages will contain exactly 1 line
ending with a single newline character, though.

If the message buffer overflows (messages are not read quickly enough),
new messages are lost until the queued up messages are read. At the
point of the overflow, a special overflow message is inserted. It will
have prefix set to "overflow", and the message text is set to "".

Care should be taken not to print any messages from the message event
handler. This would lead to an infinite loop (the event handler would be
called again after returning, because a new message is available). This
includes mp.log() and all mp.msg.* functions. Keep in mind that the Lua
print() function is mapped to mp.msg.info().
This commit is contained in:
wm4 2014-01-16 21:34:58 +01:00
parent 8c5ea38cda
commit d646d78ccb
1 changed files with 60 additions and 10 deletions

View File

@ -10,6 +10,7 @@
#include "common/common.h"
#include "options/m_property.h"
#include "common/msg.h"
#include "common/msg_control.h"
#include "options/m_option.h"
#include "input/input.h"
#include "options/path.h"
@ -39,6 +40,7 @@ static const char *builtin_lua_scripts[][2] = {
struct script_ctx {
const char *name;
lua_State *state;
struct mp_log_buffer *messages;
struct mp_log *log;
struct MPContext *mpctx;
};
@ -244,6 +246,8 @@ static void kill_script(struct script_ctx *ctx)
if (!ctx)
return;
struct lua_ctx *lctx = ctx->mpctx->lua_ctx;
if (ctx->messages)
mp_msg_log_buffer_destroy(ctx->messages);
lua_close(ctx->state);
for (int n = 0; n < lctx->num_scripts; n++) {
if (lctx->scripts[n] == ctx) {
@ -263,20 +267,22 @@ static const char *log_level[] = {
[MSGL_DEBUG] = "debug",
};
static int check_loglevel(lua_State *L, int arg)
{
const char *level = luaL_checkstring(L, arg);
for (int n = 0; n < MP_ARRAY_SIZE(log_level); n++) {
if (log_level[n] && strcasecmp(log_level[n], level) == 0)
return n;
}
luaL_error(L, "Invalid log level '%s'", level);
abort();
}
static int script_log(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
const char *level = luaL_checkstring(L, 1);
int msgl = -1;
for (int n = 0; n < MP_ARRAY_SIZE(log_level); n++) {
if (log_level[n] && strcasecmp(log_level[n], level) == 0) {
msgl = n;
break;
}
}
if (msgl < 0)
luaL_error(L, "Invalid log level '%s'", level);
int msgl = check_loglevel(L, 1);
int last = lua_gettop(L);
lua_getglobal(L, "tostring"); // args... tostring
@ -319,6 +325,34 @@ static int run_event(lua_State *L)
return 0;
}
static void poll_messages(struct script_ctx *ctx)
{
lua_State *L = ctx->state;
if (!ctx->messages)
return;
while (1) {
struct mp_log_buffer_entry *msg = mp_msg_log_buffer_read(ctx->messages);
if (!msg)
break;
lua_pushstring(L, "message"); // msg
lua_newtable(L); // msg t
lua_pushstring(L, msg->prefix); // msg t s
lua_setfield(L, -2, "prefix"); // msg t
lua_pushstring(L, mp_log_levels[msg->level]); // msg t s
lua_setfield(L, -2, "level"); // msg t
lua_pushstring(L, msg->text); // msg t s
lua_setfield(L, -2, "text"); // msg t
if (mp_cpcall(L, run_event, 2) != 0)
report_error(L);
talloc_free(msg);
}
}
void mp_lua_event(struct MPContext *mpctx, const char *name, const char *arg)
{
// There is no proper subscription mechanism yet, so all scripts get it.
@ -334,9 +368,24 @@ void mp_lua_event(struct MPContext *mpctx, const char *name, const char *arg)
}
if (mp_cpcall(L, run_event, 2) != 0)
report_error(L);
poll_messages(ctx);
}
}
static int script_enable_messages(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
if (ctx->messages)
luaL_error(L, "messages already enabled");
int size = luaL_checkinteger(L, 1);
int level = check_loglevel(L, 2);
if (size < 2 || size > 100000)
luaL_error(L, "size argument out of range");
ctx->messages = mp_msg_log_buffer_new(ctx->mpctx->global, size, level);
return 0;
}
static int run_script_dispatch(lua_State *L)
{
int id = lua_tointeger(L, 1);
@ -661,6 +710,7 @@ static struct fn_entry fn_list[] = {
FN_ENTRY(input_disable_section),
FN_ENTRY(input_set_section_mouse_area),
FN_ENTRY(format_time),
FN_ENTRY(enable_messages),
};
// On stack: mp table