Ответ 1
Этот чуть не сбил меня с ума, мне пришлось вырыть столько нитей, которые я не могу сказать. Я добавлю все потоки, имеющие отношение к решению, все они заслуживают чтения.
Итак, во-первых, мы не можем использовать System.cmd
, System.cmd запускается без tty
iex(1)> System.cmd("tty", [])
{"not a tty\n", 1}
То, что мы пытаемся сделать, требует TTY. Таким образом, есть несколько интересных библиотек для одного и того же
https://github.com/alco/porcelain
Но это также не работает с TTY
iex(1)> Porcelain.shell("tty")
%Porcelain.Result{err: nil, out: "not a tty\n", status: 1}
Затем перешел в другую библиотеку
https://github.com/aleandros/shell_stream
Кажется, что он разделяет TTY
iex(3)> ShellStream.shell("tty") |> Enum.to_list
["/dev/pts/6"]
Этот TTY совпадает с TTY текущего терминала, что означает, что TTY распространяется на дочерний процесс
Далее нужно было проверить, можем ли мы получить координаты
iex(8)> ShellStream.shell("echo -en '033[6n'") |> Enum.to_list
[]
Итак, после многих хитов и испытаний я придумал подход
defmodule CursorPos do
def get_pos do
settings = ShellStream.shell("stty -g") |> Enum.to_list
#ShellStream.shell("stty -echo -echoctl -imaxbel -isig -icanon min 1 time 0")
ShellStream.shell("stty raw -echo")
#settings |> IO.inspect
spawn(fn ->
IO.write "\e[6n"
#ShellStream.shell "echo -en \"\033[6n\" > `tty`"
:timer.sleep(50)
IO.write "\n"
end)
io = IO.stream(:stdio,1)
data = io |> Stream.take_while(&(&1 != "R"))
data|> Enum.join |> IO.inspect
ShellStream.shell("stty #{settings}")
end
def main(args) do
get_pos
end
end
Этот вид работ, но вам все еще нужно нажать enter, чтобы читать stdio
$ ./cursorpos
^[[24;1R
"\e[24;1"
Он также изменяет координаты экрана, чтобы получить их, что не то, что хотелось бы. Но проблема в том, что символы управления координатами должны обрабатываться вашей оболочкой, а не дочерней оболочкой. Моя попытка использовать
ShellStream.shell("stty -echo -echoctl -imaxbel -isig -icanon min 1 time 0")
Не работает, поскольку stty
не влияет на родительскую оболочку, где нам нужно получить координаты. Итак, следующее возможное решение - сделать ниже
$ EXISTING=$(stty -g);stty -echo -echonl -imaxbel -isig -icanon min 1 time 0; ./cursorpos ; stty $EXISTING
"\e[24;1"
Это работает, потому что мы можем изменять свойства текущего tty. Теперь вы можете захотеть углубиться и найти, как вы можете это сделать изнутри кода.
Я поставил весь свой код ниже проекта Github
https://github.com/tarunlalwani/elixir-get-current-cursor-position
Также вы должны посмотреть ниже проект
https://github.com/henrik/progress_bar
Если ваша позиция курсора не важна, вы можете самостоятельно зафиксировать курсор в некоторой позиции.
Ссылки
https://unix.stackexchange.com/questions/88296/get-vertical-cursor-position
Как получить позицию курсора в bash?
https://groups.google.com/forum/#!topic/elixir-lang-talk/9B1oe3KgjnE
https://hexdocs.pm/elixir/IO.html#getn/3
https://unix.stackexchange.com/questions/264920/why-doesnt-the-enter-key-send-eol
http://man7.org/linux/man-pages/man1/stty.1.html
https://github.com/jfreeze/ex_ncurses
Как получить позицию курсора в терминале ANSI?
Получить пользовательский ввод консоли, напечатанный, char на char