Нашёл баг в Mono
На днях простудился, и пока сидел дома, вспомнил о своей библиотеке для создания консольных окошек в псевдографике. Одна из самых важных задач в её разработке – сделать её совместимой с Linux, потому как в Windows такого рода инструменты в принципе мало кому могут пригодиться. И чтобы она работала под Linux в Mono, я в своё время потратил немало времени. Научил её выводить в терминал псевдографику (сейчас для этого используется ncurses, но только как буфер для вывода), потом разобрался с тем, как обрабатывать пользовательский ввод (для этого я попробовал LibTermKey, и мне понравилось). Кстати, особенности реализации event-driven модели для консольного ввода я уже описывал в заметке об eventfd.
А в этот раз я исследовал проблему того, что после выхода из программы у терминала, из-под которого она запускалась (и gnome-terminal, и xterm), сбивались настройки, и в нём становилось невозможно работать до тех пор, пока пользователь не выполнял команду stty sane.
После локализации воспроизведения проблемы в отдельном консольном приложении выяснилось забавное. Если внутри блока работы с ncurses (между вызовами initscr() и endwin() ) выполнить один из read-методов консоли (Console.ReadLine(), Console.ReadKey()), то терминал убивается после выхода из приложения. Если же read-методы не вызывать вообще, то всё завершается корректно. Но если один из read-методов вызвать до инициализации ncurses, то всё тоже работает хорошо, и уже не важно, сколько раз и где после этого будут вызваны “опасные” read-методы. То есть, первый вызов read-методов в Mono Runtime имеет какие-то побочные эффекты, приводящие к таким последствиям.
// If uncomment next line - all will work OK // May be first access to Console.ReadKey(Line) or Console.KeyAvailable // has side effects and modifies current term settings //bool x = Console.KeyAvailable; IntPtr stdscr = initscr (); cbreak (); noecho (); nonl (); intrflush (stdscr, false); keypad (stdscr, true); start_color (); mvaddstr (4, 5, "Test string!"); refresh (); // If remove this line and dont call reading methods inside ncurses code - // all will work OK. But after first call (if reading method called before ncurses init) // we can call it safe. Console.ReadLine (); // or Console.ReadKey() endwin (); |
Но ведь не будешь же вызывать Console.ReadKey() при старте приложения, прося пользователя нажать клавишу, чтобы всё у нас было хорошо 🙂 К счастью, оказалось достаточно обратиться к свойству Console.KeyAvailable, чтобы выполнить эту инициализацию “первого чтения”. Таким образом, проблема была побеждена малой кровью.
Однако, я всё-таки собрался с силами и оформил баг в багзилле Mono. Надеюсь, это поведение будет исправлено, или хотя бы документировано (если так оно и должно работать).
0