diff options
author | 2014-07-12 15:01:30 -0400 | |
---|---|---|
committer | 2014-07-12 15:01:30 -0400 | |
commit | d0796f1bdab199c4d1bdb9b2b133684192c7eff7 (patch) | |
tree | ac2393cc52934663b33db9c250dd3507b84a59c9 | |
parent | 23617735d0b920bb5e995674a9143672932500d8 (diff) | |
download | textadept-d0796f1bdab199c4d1bdb9b2b133684192c7eff7.tar.gz textadept-d0796f1bdab199c4d1bdb9b2b133684192c7eff7.zip |
Implement spawn functionality for terminal version.
Requires lspawn r21. Thanks to Chris Emerson for proof-of-concept code.
Spawning still does not work for Win32 terminal version, though.
-rw-r--r-- | core/init.lua | 49 | ||||
-rw-r--r-- | src/Makefile | 46 | ||||
-rw-r--r-- | src/textadept.c | 33 |
3 files changed, 78 insertions, 50 deletions
diff --git a/core/init.lua b/core/init.lua index bdc9c21d..f20a6d08 100644 --- a/core/init.lua +++ b/core/init.lua @@ -16,27 +16,24 @@ keys = require('keys') _M = {} -- language modules table -- LuaJIT compatibility. if jit then module, package.searchers, bit32 = nil, package.loaders, bit end --- curses and OSX compatibility. -if CURSES or OSX then - local spawn_ = spawn +-- OSX and pdcurses compatibility. +if OSX or (CURSES and WIN32) then +-- local spawn_ = spawn function spawn(argv, working_dir, stdout_cb, stderr_cb, exit_cb) --- if OSX then --- -- Workaround for GLib abort caused by failed assertion. --- local p, err = spawn_(argv, working_dir, stdout_cb, stderr_cb) --- if not p then return p, err end --- timeout(1, function() --- if p:status() == 'running' then return true end --- exit_cb('Process completed') --- end) --- else - local current_dir = lfs.currentdir() - lfs.chdir(working_dir) - local p = io.popen(argv..' 2>&1') - stdout_cb(p:read('*a')) - exit_cb(select(3, p:close())) - lfs.chdir(current_dir) - return p --- end +-- -- Workaround for GLib abort caused by failed assertion. +-- local p, err = spawn_(argv, working_dir, stdout_cb, stderr_cb) +-- if not p then return p, err end +-- timeout(1, function() +-- if p:status() == 'running' then return true end +-- exit_cb('Process completed') +-- end) + local current_dir = lfs.currentdir() + lfs.chdir(working_dir) + local p = io.popen(argv..' 2>&1') + stdout_cb(p:read('*a')) + exit_cb(select(3, p:close())) + lfs.chdir(current_dir) + return p end end @@ -151,7 +148,8 @@ local timeout --- -- Spawns an interactive child process *argv* in a separate thread, returning -- a handle to that process. --- The terminal version spawns processes in the same thread. +-- At the moment, the Mac OSX GUI version and Win32 terminal version spawn +-- processes in the same thread. -- @param argv A command line string containing the program's name followed by -- arguments to pass to it. `PATH` is searched for program names. -- @param working_dir Optional current working directory (cwd) for the child @@ -160,8 +158,9 @@ local timeout -- block of standard output read from the child. Stdout is read asynchronously -- in 1KB or 0.5KB blocks (depending on the platform), or however much data is -- available at the time. --- The terminal version sends all output, whether it be stdout or stderr, to --- this callback after the process finishes. +-- At the moment, the Mac OSX GUI version and Win32 terminal version send all +-- output, whether it be stdout or stderr, to this callback after the process +-- finishes. -- @param stderr_cb Optional Lua function that accepts a string parameter for a -- block of standard error read from the child. Stderr is read asynchronously -- in 1KB or 0.5kB blocks (depending on the platform), or however much data is @@ -170,8 +169,8 @@ local timeout -- finishes. The child's exit status is passed. -- @return proc -- @usage spawn('lua buffer.filename', nil, print) --- @usage proc = spawn('lua -e "print(io.read())", nil, print) --- proc:write('foo\\n') +-- @usage proc = spawn('lua -e "print(io.read())"', nil, print) +-- proc:write('foo\n') -- @class function -- @name spawn local spawn diff --git a/src/Makefile b/src/Makefile index cf81d43a..8cf60e4b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -133,11 +133,6 @@ lua_lib_objs = lpeg.o lfs.o luajit_lib_objs = lpegjit.o lfsjit.o #luajit_lib_objs = lpcapjit.o lpcodejit.o lpprintjit.o lptreejit.o lpvmjit.o \ # lfsjit.o -ifneq (curses, $(findstring curses, $(MAKECMDGOALS))) - # Compile in lspawn module. - lua_lib_objs += lspawn.o - luajit_lib_objs += lspawnjit.o -endif termkey_objs = termkey.o driver-ti.o driver-csi.o windowman_objs = windowman.o cdk_objs = binding.o buttonbox.o button.o cdk.o cdk_display.o cdk_objs.o \ @@ -181,13 +176,13 @@ textadept.o: textadept.c $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -Ilua/src $(GTK_CFLAGS) \ $< -o $@ textadeptjit.o: textadept.c - $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -DLUAJIT -Iluajit/src \ + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -Iluajit/src \ $(GTK_CFLAGS) $< -o $@ textadept-curses.o: textadept.c $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -Ilua/src \ -Iscintilla/term -Itermkey -Iwindowman -Icdk $(CURSES_CFLAGS) $< -o $@ textadeptjit-curses.o: textadept.c - $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -DLUAJIT -Iluajit/src \ + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(ta_flags) -Iluajit/src \ -Iscintilla/term -Itermkey -Iwindowman -Icdk $(CURSES_CFLAGS) $< -o $@ $(lua_objs): %.o: lua/src/%.c $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) -Ilua/src $< -o $@ @@ -197,6 +192,18 @@ $(lua_lib_objs): %.o: lua/src/lib/%.c $(luajit_lib_objs): %jit.o: lua/src/lib/%.c $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(lua_api) -Iluajit/src \ $(GLIB_CFLAGS) $< -o $@ +spawn.o: lua/src/lib/lspawn.c + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(plat_flag) $(lua_api) -Ilua/src \ + $(GLIB_CFLAGS) $< -o $@ +spawnjit.o: lua/src/lib/lspawn.c + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(plat_flag) $(lua_api) \ + -Iluajit/src $(GLIB_CFLAGS) $< -o $@ +spawn-curses.o: lua/src/lib/lspawn.c + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(plat_flag) $(lua_api) -Ilua/src \ + $< -o $@ +spawnjit-curses.o: lua/src/lib/lspawn.c + $(CROSS)$(CC) -c $(CFLAGS) $(LUA_CFLAGS) $(plat_flag) $(lua_api) \ + -Iluajit/src $< -o $@ luajit/src/libluajit.a: ; $(MAKE) -C luajit CC="$(CC) $(CFLAGS)" luajit/src/lua51.dll: $(MAKE) -C luajit HOST_CC="$(CC) -m32" CROSS=$(CROSS) TARGET_SYS=Windows @@ -219,39 +226,42 @@ textadept_rc.o: textadept.rc ; $(CROSS)$(WINDRES) $< $@ # Executables. textadept: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \ - LexLPeg.o textadept.o $(lua_objs) $(lua_lib_objs) gtdialog.o + LexLPeg.o textadept.o $(lua_objs) $(lua_lib_objs) spawn.o gtdialog.o $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS) textadeptjit: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \ LexLPegjit.o textadeptjit.o $(luajit_lib_objs) $(libluajit) \ - gtdialog.o + spawnjit.o gtdialog.o $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS) textadept-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o LexLPeg-curses.o \ textadept-curses.o $(lua_objs) $(lua_lib_objs) \ - gtdialog-curses.o $(termkey_objs) $(windowman_objs) \ - $(cdk_objs) + spawn-curses.o gtdialog-curses.o $(termkey_objs) \ + $(windowman_objs) $(cdk_objs) $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS) textadeptjit-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \ LexLPegjit-curses.o textadeptjit-curses.o \ - $(luajit_lib_objs) $(libluajit) gtdialog-curses.o \ - $(termkey_objs) $(windowman_objs) $(cdk_objs) + $(luajit_lib_objs) $(libluajit) spawnjit-curses.o \ + gtdialog-curses.o $(termkey_objs) $(windowman_objs) \ + $(cdk_objs) $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS) textadept.exe: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \ LexLPeg.o textadept.o textadept_rc.o $(lua_objs) \ - $(lua_lib_objs) gtdialog.o + $(lua_lib_objs) spawn.o gtdialog.o $(CROSS)$(CXX) $(CXXFLAGS) -o $@ $^ $(GTK_LIBS) $(LDFLAGS) textadeptjit.exe: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) \ scintilla-marshal.o LexLPegjit.o textadeptjit.o \ - textadept_rc.o $(luajit_lib_objs) $(libluajit) gtdialog.o + textadept_rc.o $(luajit_lib_objs) $(libluajit) spawnjit.o \ + gtdialog.o $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS) textadept-curses.exe: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \ LexLPeg-curses.o textadept-curses.o textadept_rc.o \ - $(lua_objs) $(lua_lib_objs) gtdialog-curses.o \ - $(windowman_objs) $(cdk_objs) + $(lua_objs) $(lua_lib_objs) spawn-curses.o \ + gtdialog-curses.o $(windowman_objs) $(cdk_objs) $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS) textadeptjit-curses.exe: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \ LexLPegjit-curses.o textadeptjit-curses.o \ textadept_rc.o $(luajit_lib_objs) $(libluajit) \ - gtdialog-curses.o $(windowman_objs) $(cdk_objs) + spawnjit-curses.o gtdialog-curses.o $(windowman_objs) \ + $(cdk_objs) $(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS) # Install/uninstall. diff --git a/src/textadept.c b/src/textadept.c index ebc48bc2..1cc906b8 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -30,6 +30,7 @@ #if !_WIN32 #include <signal.h> #include <sys/ioctl.h> +#include <sys/select.h> #include <termios.h> #else #undef main @@ -89,7 +90,7 @@ typedef GtkWidget Scintilla; lua_setmetatable(l, (n > 0) ? n : n - 1); \ } // Translate Lua 5.2 API to LuaJIT API (Lua 5.1) for compatibility. -#if LUAJIT +#if LUA_VERSION_NUM == 501 #define LUA_OK 0 #define lua_rawlen lua_objlen #define LUA_OPEQ 0 @@ -210,9 +211,8 @@ static void new_buffer(sptr_t); static Scintilla *new_view(sptr_t); static int lL_init(lua_State *, int, char **, int); LUALIB_API int luaopen_lpeg(lua_State *), luaopen_lfs(lua_State *); -#if !CURSES LUALIB_API int luaopen_spawn(lua_State *); -#endif +LUALIB_API int lspawn_pushfds(lua_State *), lspawn_readfds(lua_State *); /** * Emits an event. @@ -1593,7 +1593,7 @@ static int lL_init(lua_State *L, int argc, char **argv, int reinit) { lua_getglobal(L, "package"), lua_getfield(L, -1, "loaded"); lL_cleartable(L, -1); lua_pop(L, 2); // package.loaded and package -#if !LUAJIT +#if LUA_VERSION_NUM >= 502 lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); lL_cleartable(L, -1); lua_pop(L, 1); // _G @@ -1605,9 +1605,7 @@ static int lL_init(lua_State *L, int argc, char **argv, int reinit) { luaL_openlibs(L); lL_openlib(L, "lpeg", luaopen_lpeg); lL_openlib(L, "lfs", luaopen_lfs); -#if !CURSES lL_openlib(L, "spawn", luaopen_spawn); -#endif lua_newtable(L); lua_newtable(L); @@ -2363,6 +2361,27 @@ static void resize(int signal) { lL_event(lua, "update_ui", -1); refresh_all(); } + +/** Replacement for `termkey_waitkey()` that handles asynchronous I/O. */ +static TermKeyResult textadept_waitkey(TermKey *tk, TermKeyKey *key) { + int force = FALSE; + struct timeval timeout = {0, termkey_get_waittime(tk)}; + while (1) { + TermKeyResult res = !force ? termkey_getkey(tk, key) + : termkey_getkey_force(tk, key); + if (res != TERMKEY_RES_AGAIN && res != TERMKEY_RES_NONE) return res; + if (res == TERMKEY_RES_AGAIN) force = TRUE; + // Wait for input. + int nfds = lspawn_pushfds(lua); + fd_set *fds = (fd_set *)lua_touserdata(lua, -1); + FD_SET(0, fds); // monitor stdin + if (select(nfds, fds, NULL, NULL, force ? &timeout : NULL) > 0) { + if (FD_ISSET(0, fds)) termkey_advisereadable(tk); + if (lspawn_readfds(lua) > 0) refresh_all(); + } + lua_pop(lua, 1); // fd_set + } +} #endif /** @@ -2502,7 +2521,7 @@ int main(int argc, char **argv) { TermKeyResult res; TermKeyKey key; int keysyms[] = {0,SCK_BACK,SCK_TAB,SCK_RETURN,SCK_ESCAPE,0,0,SCK_UP,SCK_DOWN,SCK_LEFT,SCK_RIGHT,0,0,SCK_INSERT,SCK_DELETE,0,SCK_PRIOR,SCK_NEXT,SCK_HOME,SCK_END}; - while ((res = termkey_waitkey(ta_tk, &key)) != TERMKEY_RES_EOF) { + while ((res = textadept_waitkey(ta_tk, &key)) != TERMKEY_RES_EOF) { if (res == TERMKEY_RES_ERROR) continue; if (key.type == TERMKEY_TYPE_UNICODE) c = key.code.codepoint; |