From f26357e88733cef30ff9fd9279f9362985c68de0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 17 Mar 2013 23:24:45 +0100 Subject: [PATCH] neutrino: add a prototype of a lua plugin interface this is just for preliminary tests, not yet really usable for anything useful --- src/Makefile.am | 1 + src/gui/Makefile.am | 3 + src/gui/luainstance.cpp | 287 ++++++++++++++++++++++++++++++++++++++++ src/gui/luainstance.h | 52 ++++++++ src/gui/plugins.cpp | 30 ++++- src/gui/plugins.h | 4 +- src/gui/user_menue.cpp | 4 +- src/plugin.h | 3 +- 8 files changed, 378 insertions(+), 6 deletions(-) create mode 100644 src/gui/luainstance.cpp create mode 100644 src/gui/luainstance.h diff --git a/src/Makefile.am b/src/Makefile.am index 38b3ca66f..f80eed707 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -122,6 +122,7 @@ neutrino_LDADD += -lgif else neutrino_LDADD += -lungif endif +neutrino_LDADD += -llua -ldl if ENABLE_UPNP neutrino_LDADD += \ diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index e26d7aab0..d0da43b10 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -114,6 +114,9 @@ libneutrino_gui_a_SOURCES += \ test_menu.cpp endif +libneutrino_gui_a_SOURCES += \ + luainstance.cpp + libneutrino_gui2_a_SOURCES = \ cam_menu.cpp \ color.cpp \ diff --git a/src/gui/luainstance.cpp b/src/gui/luainstance.cpp new file mode 100644 index 000000000..3060c135d --- /dev/null +++ b/src/gui/luainstance.cpp @@ -0,0 +1,287 @@ +/* + * neutrino-mp lua to c++ bridge + * + * (C) 2013 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "luainstance.h" + +#define DBG printf + +#define lua_boxpointer(L, u) \ + (*(void **)(lua_newuserdata(L, sizeof(void *))) = (u)) + +#define lua_unboxpointer(L, i) \ + (*(void **)(lua_touserdata(L, i))) + +const char CLUAInstance::className[] = "neutrino"; + +CLUAInstance::CLUAInstance() +{ + /* Create the intepreter object. */ + lua = luaL_newstate(); + + /* register standard + custom functions. */ + registerFunctions(); +} + +CLUAInstance::~CLUAInstance() +{ + if (lua != NULL) + { + lua_close(lua); + lua = NULL; + } +} + +#define SET_VAR1(NAME) \ + lua_pushinteger(lua, NAME); \ + lua_setglobal(lua, #NAME); +#define SET_VAR2(NAME, VALUE) \ + lua_pushinteger(lua, VALUE); \ + lua_setglobal(lua, #NAME); +#define SET_FONT(NAME) \ + lua_pushinteger(lua, SNeutrinoSettings::NAME); \ + lua_setglobal(lua, #NAME); + +/* Run the given script. */ +void CLUAInstance::runScript(const char *fileName) +{ + // luaL_dofile(lua, fileName); + /* run the script */ + int status = luaL_loadfile(lua, fileName); + if (status) { + fprintf(stderr, "[CLUAInstance::%s] Can't load file: %s\n", __func__, lua_tostring(lua, -1)); + return; + } + + /* set variables */ + SET_VAR1(COL_COLORED_EVENTS_CHANNELLIST); + SET_VAR1(COL_COLORED_EVENTS_INFOBAR); + SET_VAR1(COL_INFOBAR_SHADOW); + SET_VAR1(COL_INFOBAR); + SET_VAR1(COL_MENUHEAD); + SET_VAR1(COL_MENUCONTENT); + SET_VAR1(COL_MENUCONTENTDARK); + SET_VAR1(COL_MENUCONTENTSELECTED); + SET_VAR1(COL_MENUCONTENTINACTIVE); + SET_VAR1(COL_BACKGROUND); + + SET_VAR2(SCREEN_OFF_X, g_settings.screen_StartX); + SET_VAR2(SCREEN_OFF_Y, g_settings.screen_StartY); + SET_VAR2(SCREEN_END_X, g_settings.screen_EndX); + SET_VAR2(SCREEN_END_Y, g_settings.screen_EndY); + + SET_FONT(FONT_TYPE_MENU); + SET_FONT(FONT_TYPE_MENU_TITLE); + SET_FONT(FONT_TYPE_MENU_INFO); + SET_FONT(FONT_TYPE_EPG_TITLE); + SET_FONT(FONT_TYPE_EPG_INFO1); + SET_FONT(FONT_TYPE_EPG_INFO2); + SET_FONT(FONT_TYPE_EPG_DATE); + SET_FONT(FONT_TYPE_EVENTLIST_TITLE); + SET_FONT(FONT_TYPE_EVENTLIST_ITEMLARGE); + SET_FONT(FONT_TYPE_EVENTLIST_ITEMSMALL); + SET_FONT(FONT_TYPE_EVENTLIST_DATETIME); + SET_FONT(FONT_TYPE_GAMELIST_ITEMLARGE); + SET_FONT(FONT_TYPE_GAMELIST_ITEMSMALL); + SET_FONT(FONT_TYPE_CHANNELLIST); + SET_FONT(FONT_TYPE_CHANNELLIST_DESCR); + SET_FONT(FONT_TYPE_CHANNELLIST_NUMBER); + SET_FONT(FONT_TYPE_CHANNELLIST_EVENT); + SET_FONT(FONT_TYPE_CHANNEL_NUM_ZAP); + SET_FONT(FONT_TYPE_INFOBAR_NUMBER); + SET_FONT(FONT_TYPE_INFOBAR_CHANNAME); + SET_FONT(FONT_TYPE_INFOBAR_INFO); + SET_FONT(FONT_TYPE_INFOBAR_SMALL); + SET_FONT(FONT_TYPE_FILEBROWSER_ITEM); + SET_FONT(FONT_TYPE_MENU_HINT); + + status = lua_pcall(lua, 0, LUA_MULTRET, 0); + if (status) + fprintf(stderr, "[CLUAInstance::%s] error in script: %s\n", __func__, lua_tostring(lua, -1)); +} + +const luaL_Reg CLUAInstance::methods[] = +{ + { "PaintBox", CLUAInstance::PaintBox }, + { "RenderString", CLUAInstance::RenderString }, + { "PaintIcon", CLUAInstance::PaintIcon }, + { NULL, NULL } +}; + +/* load basic functions and register our own C callbacks */ +void CLUAInstance::registerFunctions() +{ + luaL_openlibs(lua); + luaopen_table(lua); + luaopen_io(lua); + luaopen_string(lua); + luaopen_math(lua); + + lua_newtable(lua); + int methodtable = lua_gettop(lua); + luaL_newmetatable(lua, className); + int metatable = lua_gettop(lua); + lua_pushliteral(lua, "__metatable"); + lua_pushvalue(lua, methodtable); + lua_settable(lua, metatable); + + lua_pushliteral(lua, "__index"); + lua_pushvalue(lua, methodtable); + lua_settable(lua, metatable); + + lua_pushliteral(lua, "__gc"); + lua_pushcfunction(lua, GCWindow); + lua_settable(lua, metatable); + + lua_pop(lua, 1); + + luaL_setfuncs(lua, methods, 0); + lua_pop(lua, 1); + + lua_register(lua, className, NewWindow); +} + +CFBWindow *CLUAInstance::CheckWindow(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + fprintf(stderr, "[CLUAInstance::%s] wrong type %p, %d, %s\n", __func__, L, narg, className); + return *(CFBWindow **)ud; // unbox pointer +} + +int CLUAInstance::NewWindow(lua_State *L) +{ + int count = lua_gettop(L); + int x = g_settings.screen_StartX; + int y = g_settings.screen_StartY; + int w = g_settings.screen_EndX - x; + int h = g_settings.screen_EndY - y; + if (count > 0) + x = luaL_checkint(L, 1); + if (count > 1) + y = luaL_checkint(L, 2); + if (count > 2) + w = luaL_checkint(L, 3); + if (count > 3) + h = luaL_checkint(L, 4); + CFBWindow *W = new CFBWindow(x, y, w, h); + lua_boxpointer(L, W); + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +int CLUAInstance::PaintBox(lua_State *L) +{ + DBG("CLUAInstance::%s %d\n", __func__, lua_gettop(L)); + int x, y, w, h; + unsigned int c; + + CFBWindow *W = CheckWindow(L, 1); + if (!W) + return 0; + x = luaL_checkint(L, 2); + y = luaL_checkint(L, 3); + w = luaL_checkint(L, 4); + h = luaL_checkint(L, 5); + c = luaL_checkint(L, 6); + /* those checks should be done in CFBWindow instead... */ + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (w < 0 || x + w > W->dx) + w = W->dx - x; + if (h < 0 || y + h > W->dy) + h = W->dy - y; + /* use the color constants */ + c = CFrameBuffer::getInstance()->realcolor[c & 0xff]; + W->paintBoxRel(x, y, w, h, c); + return 0; +} + +int CLUAInstance::PaintIcon(lua_State *L) +{ + DBG("CLUAInstance::%s %d\n", __func__, lua_gettop(L)); + int x, y, h; + unsigned int o; + const char *fname; + + CFBWindow *W = CheckWindow(L, 1); + if (!W) + return 0; + fname = luaL_checkstring(L, 2); + x = luaL_checkint(L, 3); + y = luaL_checkint(L, 4); + h = luaL_checkint(L, 5); + o = luaL_checkint(L, 6); + W->paintIcon(fname, x, y, h, o); + return 0; +} + +int CLUAInstance::RenderString(lua_State *L) +{ + int x, y, w, boxh, utf8, f; + unsigned int c; + const char *text; + int numargs = lua_gettop(L); + DBG("CLUAInstance::%s %d\n", __func__, numargs); + c = COL_MENUCONTENT; + boxh = 0; + utf8 = 1; + + CFBWindow *W = CheckWindow(L, 1); + if (!W) + return 0; + f = luaL_checkint(L, 2); /* font number, use FONT_TYPE_XXX in the script */ + text = luaL_checkstring(L, 3); /* text */ + x = luaL_checkint(L, 4); + y = luaL_checkint(L, 5); + if (numargs > 5) + c = luaL_checkint(L, 6); + if (numargs > 6) + w = luaL_checkint(L, 7); + else + w = W->dx - x; + if (numargs > 7) + boxh = luaL_checkint(L, 8); + if (numargs > 8) + utf8 = luaL_checkint(L, 9); + if (f >= FONT_TYPE_COUNT || f < 0) + f = SNeutrinoSettings::FONT_TYPE_MENU; + W->RenderString(g_Font[f], x, y, w, text, c, boxh, utf8); + return 0; +} + +int CLUAInstance::GCWindow(lua_State *L) +{ + DBG("CLUAInstance::%s %d\n", __func__, lua_gettop(L)); + CFBWindow *w = (CFBWindow *)lua_unboxpointer(L, 1); + delete w; + return 0; +} diff --git a/src/gui/luainstance.h b/src/gui/luainstance.h new file mode 100644 index 000000000..89274bdc1 --- /dev/null +++ b/src/gui/luainstance.h @@ -0,0 +1,52 @@ +/* + * neutrino-mp lua to c++ bridge + * + * (C) 2013 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _LUAINSTANCE_H +#define _LUAINSTANCE_H + +/* LUA Is C */ +extern "C" { +#include +#include +#include +} +#include + +/* inspired by Steve Kemp http://www.steve.org.uk/ */ +class CLUAInstance +{ + static const char className[]; + static const luaL_Reg methods[]; + static CFBWindow *CheckWindow(lua_State *L, int narg); +public: + CLUAInstance(); + ~CLUAInstance(); + void runScript(const char *fileName); + +private: + lua_State* lua; + void registerFunctions(); + + static int NewWindow(lua_State *L); + static int PaintBox(lua_State *L); + static int PaintIcon(lua_State *L); + static int RenderString(lua_State *L); + static int GCWindow(lua_State *L); +}; + +#endif /* _LUAINSTANCE_H */ diff --git a/src/gui/plugins.cpp b/src/gui/plugins.cpp index 0c5022ad8..b56e535d7 100644 --- a/src/gui/plugins.cpp +++ b/src/gui/plugins.cpp @@ -69,6 +69,8 @@ #endif #include +#include + extern CPlugins * g_PluginList; /* neutrino.cpp */ extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ @@ -125,11 +127,11 @@ void CPlugins::scanDir(const char *dir) if (plugin_ok) { new_plugin.pluginfile = fname; if (new_plugin.type == CPlugins::P_TYPE_SCRIPT) - { new_plugin.pluginfile.append(".sh"); - } else { + else if (new_plugin.type == CPlugins::P_TYPE_LUA) + new_plugin.pluginfile.append(".lua"); + else new_plugin.pluginfile.append(".so"); - } // We do not check if new_plugin.pluginfile exists since .cfg in // PLUGINDIR_VAR can overwrite settings in read only dir // PLUGINDIR. This needs PLUGINDIR_VAR to be scanned at @@ -342,6 +344,21 @@ void CPlugins::startScriptPlugin(int number) } } +void CPlugins::startLuaPlugin(int number) +{ + const char *script = plugin_list[number].pluginfile.c_str(); + printf("[CPlugins] executing lua script %s\n",script); + if (!file_exists(script)) + { + printf("[CPlugins] could not find %s,\nperhaps wrong plugin type in %s\n", + script, plugin_list[number].cfgfile.c_str()); + return; + } + CLUAInstance *lua = new CLUAInstance(); + lua->runScript(script); + delete lua; +} + void CPlugins::startPlugin(int number,int /*param*/) { // always delete old output @@ -367,6 +384,11 @@ void CPlugins::startPlugin(int number,int /*param*/) startScriptPlugin(number); return; } + if (plugin_list[number].type == CPlugins::P_TYPE_LUA) + { + startLuaPlugin(number); + return; + } if (!file_exists(plugin_list[number].pluginfile.c_str())) { printf("[CPlugins] could not find %s,\nperhaps wrong plugin type in %s\n", @@ -633,6 +655,8 @@ CPlugins::p_type_t CPlugins::getPluginType(int type) case PLUGIN_TYPE_SCRIPT: return P_TYPE_SCRIPT; break; + case PLUGIN_TYPE_LUA: + return P_TYPE_LUA; default: return P_TYPE_DISABLED; } diff --git a/src/gui/plugins.h b/src/gui/plugins.h index 501e6172c..1bd6c614f 100644 --- a/src/gui/plugins.h +++ b/src/gui/plugins.h @@ -51,7 +51,8 @@ class CPlugins P_TYPE_DISABLED = 0x1, P_TYPE_GAME = 0x2, P_TYPE_TOOL = 0x4, - P_TYPE_SCRIPT = 0x8 + P_TYPE_SCRIPT = 0x8, + P_TYPE_LUA = 0x10 } p_type_t; @@ -122,6 +123,7 @@ class CPlugins void startPlugin(int number,int param); void start_plugin_by_name(const std::string & filename,int param);// start plugins by "name=" in .cfg void startScriptPlugin(int number); + void startLuaPlugin(int number); void startPlugin(const char * const filename); // start plugins also by name bool hasPlugin(CPlugins::p_type_t type); diff --git a/src/gui/user_menue.cpp b/src/gui/user_menue.cpp index 3758d1fad..914b04ca7 100644 --- a/src/gui/user_menue.cpp +++ b/src/gui/user_menue.cpp @@ -308,7 +308,9 @@ bool CUserMenu::showUserMenu(int button) int cnt = 0; for (unsigned int count = 0; count < (unsigned int) g_PluginList->getNumberOfPlugins(); count++) { - if (g_PluginList->getType(count)== CPlugins::P_TYPE_TOOL && !g_PluginList->isHidden(count)) + bool show = g_PluginList->getType(count) == CPlugins::P_TYPE_TOOL || + g_PluginList->getType(count) == CPlugins::P_TYPE_LUA; + if (show && !g_PluginList->isHidden(count)) { sprintf(id, "%d", count); menu_items++; diff --git a/src/plugin.h b/src/plugin.h index 3e45c0860..e0394b37a 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -39,7 +39,8 @@ typedef enum plugin_type PLUGIN_TYPE_DISABLED = 0, PLUGIN_TYPE_GAME = 1, PLUGIN_TYPE_TOOL = 2, - PLUGIN_TYPE_SCRIPT = 3 + PLUGIN_TYPE_SCRIPT = 3, + PLUGIN_TYPE_LUA = 4 } plugin_type_t;