diff --git a/pd.lua b/pd.lua index da9166c..53f32d8 100644 --- a/pd.lua +++ b/pd.lua @@ -29,15 +29,15 @@ pd._loadpath = "" pd._currentpath = "" -- add a path to Lua's "require" search paths -pd._setrequirepath = function(path, pdlua_path) +pd._setrequirepath = function(path) pd._packagepath = package.path pd._packagecpath = package.cpath if (pd._iswindows) then - package.path = path .. "\\?;" .. path .. "\\?.lua;" .. pdlua_path .. "\\?;" .. pdlua_path .. "\\?.lua;" .. package.path - package.cpath = path .. "\\?.dll;" .. pdlua_path .. "\\?.dll;" .. package.cpath + package.path = path .. "\\?;" .. path .. "\\?.lua;" .. package.path + package.cpath = path .. "\\?.dll;" .. package.cpath else - package.path = path .. "/?;" .. path .. "/?.lua;" .. pdlua_path .. "/?;" .. pdlua_path .. "/?.lua;" .. package.path - package.cpath = path .. "/?.so;" .. pdlua_path .. "/?.so;" .. package.cpath + package.path = path .. "/?;" .. path .. "/?.lua;" .. package.path + package.cpath = path .. "/?.so;" .. package.cpath end end @@ -344,6 +344,9 @@ function pd.Class:construct(sel, atoms) self.inlets = 0 self.outlets = 0 self._canvaspath = pd._canvaspath(self._object) .. "/" + if pdx then + pdx.reload(self) + end if self:initialize(sel, atoms) then pd._createinlets(self._object, self.inlets) pd._createoutlets(self._object, self.outlets) @@ -353,6 +356,9 @@ function pd.Class:construct(sel, atoms) self:postinitialize() return self else + if pdx then + pdx.unreload(self) + end return nil end end @@ -517,4 +523,9 @@ end DATA = 0 SIGNAL = 1 Colors = {background = 0, foreground = 1, outline = 2} + +-- pre-load pdx.lua (live coding support); if you don't want this, just +-- comment out the line below +pdx = require 'pdx' + -- fin pd.lua diff --git a/pdlua.c b/pdlua.c index d73c53b..60b2063 100644 --- a/pdlua.c +++ b/pdlua.c @@ -175,7 +175,7 @@ void initialise_lua_state() // In plugdata we're linked statically and thus c_externdir is empty. // So we pass a data directory to the setup function instead and store it here. // ag: Renamed to pdlua_datadir since we also need this in vanilla when -// setting up the Lua search path when loading a pd_lua file. +// setting up Lua's package.path. char pdlua_datadir[MAXPDSTRING]; #if PLUGDATA // Hook to inform plugdata which class names are lua objects @@ -2037,6 +2037,29 @@ static int pdlua_error(lua_State *L) return 0; } +static void pdlua_packagepath(lua_State *L, const char *path) +{ + PDLUA_DEBUG("pdlua_packagepath: stack top %d", lua_gettop(L)); + lua_getglobal(L, "package"); + lua_pushstring(L, "path"); + lua_gettable(L, -2); + const char *packagepath = lua_tostring(L, -1); + char *buf = malloc(2*strlen(path)+20+strlen(packagepath)); + if (!buf) return; +#ifdef _WIN32 + sprintf(buf, "%s\\?;%s\\?.lua;%s", path, path, packagepath); +#else + sprintf(buf, "%s/?;%s/?.lua;%s", path, path, packagepath); +#endif + lua_pop(L, 1); + lua_pushstring(L, "path"); + lua_pushstring(L, buf); + lua_settable(L, -3); + free(buf); + lua_pop(L, 1); + PDLUA_DEBUG("pdlua_packagepath: end. stack top %d", lua_gettop(L)); +} + static void pdlua_setrequirepath ( /* FIXME: documentation (is this of any use at all?) */ lua_State *L, @@ -2048,8 +2071,7 @@ static void pdlua_setrequirepath lua_pushstring(L, "_setrequirepath"); lua_gettable(L, -2); lua_pushstring(L, path); - lua_pushstring(L, pdlua_datadir); - if (lua_pcall(L, 2, 0, 0) != 0) + if (lua_pcall(L, 1, 0, 0) != 0) { pd_error(NULL, "lua: internal error in `pd._setrequirepath': %s", lua_tostring(L, -1)); lua_pop(L, 1); @@ -2579,6 +2601,12 @@ void pdlua_setup(void) if (fd >= 0) { /* pd.lua was opened */ reader.fd = fd; + // We need to set up Lua's package.path here so that pdx.lua can be + // found (and possibly other pre-loaded extension modules in the + // future). Note that we can't just use pdlua_setrequirepath() here + // because it calls pd._setrequirepath in pd.lua which isn't loaded + // yet at this point. + pdlua_packagepath(__L(), pdlua_datadir); #if LUA_VERSION_NUM < 502 result = lua_load(__L(), pdlua_reader, &reader, "pd.lua"); #else // 5.2 style diff --git a/pdlua/tutorial/examples/luatab.pd_lua b/pdlua/tutorial/examples/luatab.pd_lua index 6fec07d..569dd21 100644 --- a/pdlua/tutorial/examples/luatab.pd_lua +++ b/pdlua/tutorial/examples/luatab.pd_lua @@ -1,15 +1,10 @@ local luatab = pd.Class:new():register("luatab") --- our own pdlua extension, needed for the reload functionality -local pdx = require 'pdx' - function luatab:initialize(sel, atoms) -- single inlet for the frequency, bang goes to the single outlet when we -- finished generating a new waveform self.inlets = 1 self.outlets = 1 - -- enable the reload callback - pdx.reload(self) -- the name of the array/table should be in the 1st creation argument if type(atoms[1]) == "string" then self.tabname = atoms[1] diff --git a/pdlua/tutorial/examples/pdx.lua b/pdlua/tutorial/examples/pdx.lua index 1b37d5b..b132a10 100644 --- a/pdlua/tutorial/examples/pdx.lua +++ b/pdlua/tutorial/examples/pdx.lua @@ -3,10 +3,8 @@ pdx.lua: useful extensions to pd.lua Copyright (C) 2020 Albert Gräf -To use this in your pd-lua scripts: local pdx = require 'pdx' - Currently there's only the pdx.reload() function, which implements a kind of -remote reload functionality based on dofile and receivers, as explained in the +live coding functionality based on dofile and receivers, as explained in the pd-lua tutorial. More may be added in the future. This program is free software; you can redistribute it and/or @@ -30,6 +28,13 @@ local pdx = {} Reload functionality. Call pdx.reload() on your object to enable, and pdx.unreload() to disable this functionality again. +NOTE: As of pd-lua 0.12.8, this module is now pre-loaded, and pdx.reload() +gets called automatically before running the initialize method of any object, +so calling pdx.reload() explicitly is no longer needed. (Old code importing +the module and doing the call will continue to work, though.) Instead, you +will now have to call pdx.unreload() in your initialize method if you want to +*disable* this feature for some reason. + pdx.reload installs a "pdluax" receiver which reloads the object's script file when it receives the "reload" message without any arguments, or a "reload class" message with a matching class name.