Category Archives: Linux/Unix
Недавно я наткнулся на проблему с рендерингом console framework в стандартном эмуляторе терминала для KDE – Konsole. Проблема заключалась в том, что при перемещении окошек влево в терминале с шириной, равной или меньше ширины буфера экрана, справа от окна появлялись артефакты:
До этого я тестировал в других эмуляторах терминала (gnome-terminal, xterm, putty), и таких проблем не возникало никогда. После некоторых исследований стало ясно, что это бага в самом Konsole. Я был очень удивлён и не питал особых надежд на то, что такой баг можно легко воспроизвести, а тем более убедить мейнтейнеров проекта. Во всяком случае прикладывать к багрепорту аттач с программой на моно, которая внутри неочевидно (для мейнтейнеров) работает, имело бы мало смысла. Поэтому я решил попробовать сделать тестовую программу на чистом Си, минимального размера, но чтобы проблема была наглядно продемонстрирована. И это получилось ! Текст программы можно посмотреть в описании бага. Демонстрация проблемы была наглядна:
После этого я попросил в конференции гентоводов проверить, воспроизводится ли проблема у них. Пользователи Gentoo устанавливают одни из самых последних билдов программ, поэтому проверка у них – это важно. Оказалось, что баг действительно воспроизводится. Прошла неделя, я потихоньку уже сам стал разбираться в исходниках Konsole, научился собирать из исходников, логировать данные в момент обновления экрана, и вот сегодня – баг получил статус CONFIRMED, а значит, и некоторый шанс на то, что им кроме меня кто-то займётся.
На работе коллега Тимур Шакуров придумал шикарнейшее словечко ПРОКАСТ для обозначения всяких длинных полумагических команд наподобие следующей:
<@insomnia> Нужно выполнить всего три команды, чтобы поставить Gentoo
<@insomnia> cfdisk /dev/hda && mkfs.xfs /dev/hda1 && mount /dev/hda1 /mnt/gentoo/ && chroot /mnt/gentoo/ && env-update && . /etc/profile && emerge sync && cd /usr/portage && scripts/bootsrap.sh && emerge system && emerge vim && vi /etc/fstab && emerge gentoo-dev-sources && cd /usr/src/linux && make menuconfig && make install modules_install && emerge gnome mozilla-firefox openoffice && emerge grub && cp /boot/grub/grub.conf.sample /boot/grub/grub.conf && vi /boot/grub/grub.conf && grub && init 6
<@insomnia> это первая
Для тех, кто не в курсе, что такое ПРОКАСТ:
На днях простудился, и пока сидел дома, вспомнил о своей библиотеке для создания консольных окошек в псевдографике. Одна из самых важных задач в её разработке – сделать её совместимой с 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