diff --git a/src/constants.c b/src/constants.c index f938470c..0a662cc4 100644 --- a/src/constants.c +++ b/src/constants.c @@ -345,6 +345,16 @@ static int luv_constants(lua_State* L) { lua_pushinteger(L, UV_PIPE_NO_TRUNCATE); lua_setfield(L, -2, "PIPE_NO_TRUNCATE"); #endif + +#if LUV_UV_VERSION_GEQ(1, 2, 0) + lua_pushinteger(L, UV_TTY_MODE_NORMAL); + lua_setfield(L, -2, "TTY_MODE_NORMAL"); + lua_pushinteger(L, UV_TTY_MODE_RAW); + lua_setfield(L, -2, "TTY_MODE_RAW"); + lua_pushinteger(L, UV_TTY_MODE_IO); + lua_setfield(L, -2, "TTY_MODE_IO"); +#endif + return 1; } diff --git a/src/tty.c b/src/tty.c index 5b6ac268..048f4691 100644 --- a/src/tty.c +++ b/src/tty.c @@ -39,9 +39,25 @@ static int luv_new_tty(lua_State* L) { return 1; } +static int luv_check_tty_mode(lua_State *L, int i) { + const char* modes[] = { "normal", "raw", "io", NULL}; + int mode; + + if (lua_isnumber(L, i)) + mode = lua_tonumber(L, i); + else + mode = luaL_checkoption(L, i, NULL, modes); + +#if LUV_UV_VERSION_GEQ(1, 2, 0) + luaL_argcheck(L, mode >= UV_TTY_MODE_NORMAL && mode <= UV_TTY_MODE_IO, + i, "Unknown tty mode value"); +#endif + return mode; +} + static int luv_tty_set_mode(lua_State* L) { uv_tty_t* handle = luv_check_tty(L, 1); - int mode = luaL_checkinteger(L, 2); + int mode = luv_check_tty_mode(L, 2); int ret = uv_tty_set_mode(handle, mode); return luv_result(L, ret); } diff --git a/tests/test-tty.lua b/tests/test-tty.lua new file mode 100644 index 00000000..f1d74bac --- /dev/null +++ b/tests/test-tty.lua @@ -0,0 +1,113 @@ +-- come from https://github.com/libuv/libuv/blob/v1.x/test/test-tty.c +local _, ffi = pcall(require, 'ffi') +if not ffi then + print('skip, without luajit ffi') + return +end + +if not (ffi.os == "Linux" or ffi.os == "OSX") then + print('skip, not on linux or macos') + return +end + +return require('lib/tap')(function (test) + + test("tty normal", function (print, p, expect, uv) + -- uv.tty_set_vterm_state("supported") + -- print(uv.tty_get_vterm_state()) + + local stdin = uv.new_tty(0, true) + local stdout = uv.new_tty(1, false) + + local stype = os.getenv('GITHUB_ACTION') and 'pipe' or 'tty' + + assert(stype == uv.guess_handle(0)) + assert(stype == uv.guess_handle(1)) + assert(uv.is_readable(stdin)) + assert(uv.is_writable(stdout)) + + uv.close(stdin) + uv.close(stdout) + end) + + test("tty pty", function (print, p, expect, uv) + ffi.cdef[[ + struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; /* unused */ + unsigned short ws_ypixel; /* unused */ + }; + int openpty(int *amaster, int *aslave, char *name, + const void* termp, const struct winsize* winp); + ]] + + local master_fd = ffi.new('int[1]') + local slave_fd = ffi.new('int[1]') + local winp = ffi.new('struct winsize[1]') + + local r, util = pcall(ffi.load, 'util') + if r then + r = util.openpty(master_fd, slave_fd, nil, nil, winp); + else + r = ffi.C.openpty(master_fd, slave_fd, nil, nil, winp); + end + assert(tonumber(r)==0) + + local master_tty = uv.new_tty(tonumber(master_fd[0]), false) + local slave_tty = uv.new_tty(tonumber(slave_fd[0]), false) + + assert(uv.is_readable(master_tty)) + assert(uv.is_writable(master_tty)) + assert(uv.is_readable(slave_tty)) + assert(uv.is_writable(slave_tty)) + + master_tty:close() + slave_tty:close() + end) + + test("tty device", function (print, p, expect, uv) + ffi.cdef[[ + typedef unsigned mode_t; + int open(const char *pathname, int flags, mode_t mode); + ]] + + local ttyin_fd = ffi.C.open("/dev/tty", uv.constants.O_RDONLY, 0); + if tonumber(ttyin_fd) == -1 and ffi.errno() == 6 then + print("Skip, open /dev/tty fail") + return + end + + assert(tonumber(ttyin_fd) >= 0, ffi.errno()) + + local ttyout_fd = ffi.C.open("/dev/tty", uv.constants.O_WRONLY, 0); + assert(tonumber(ttyout_fd) >= 0, ffi.errno()) + + assert('tty' == uv.guess_handle(ttyin_fd)); + assert('tty' == uv.guess_handle(ttyout_fd)); + + local tty_in = uv.new_tty(ttyin_fd, true) + assert(uv.is_readable(tty_in)) + assert(not uv.is_writable(tty_in)) + + local tty_out = uv.new_tty(ttyout_fd, false) + assert(not uv.is_readable(tty_out)) + assert(uv.is_writable(tty_out)) + + local width, height = uv.tty_get_winsize(tty_out) + print(string.format("width=%d height=%d\n", width, height)); + + assert(width >= 0) + assert(height >= 0) + + assert(uv.tty_set_mode(tty_in, "raw")) + assert(uv.tty_set_mode(tty_in, "normal")) + + assert(uv.tty_reset_mode()) + assert(uv.tty_reset_mode()) + assert(uv.tty_reset_mode()) + + uv.close(tty_in) + uv.close(tty_out) + end) +end)