LINUX.ORG.RU

terminal size


0

0

Как узнать размер терминала?
Т.е. количество строчек и длину строки?
Желательно стандартными средствами без использования библиотек ncurses и подобных...

anonymous

#include <termio.h>
не знаю, устроит или нет, но вроде это не либа, а набор определений.
Вообщем дерзай.
А еще поищи в инете список кодов для ANSI/VT100 терминалов.
Или посмотри свой /etc/termcap (!)
Или напиши мне на мыло oxid@nteam.ru, я тебе документик маленький вышлю.

OxiD ★★★★
()

Для таких задач используются коды управления терминалом.
Почитать подробно: man console_codes.
Пример использования:


/*
 *  Сюда будут записаны размеры экрана.
 */
int width, height;


/*
 *  Отмена построчной буферизации в libc.
 *  Если этого не сделать, следующие команды не уйдут терминалу,
 *  пока libc не получит от программы код '\n'.
 */
setvbuf(stdout, NULL, _IONBF, 0);


/*
 *  Попытка установить координаты курсора в 255,255
 *  (терминал установит курсор в правый нижний угол).
 *  Затем запрос координат курсора у терминала.
 */
fputs("\x1B[255;255H\x1B[6n", stdout);


/*
 *  Чтение координат курсора.
 *  Координаты считаются с 1, поэтому полученные значения
 *  можно смело считать за размеры терминала.
 *  Функция scanf должна вернуть число 2.
 */
scanf("\x1B[%d;%dR", &height, &width);

nobody ★★
()

$ stty size

вообще поидее нужно что то типа
struct winsize wins;

err = ioctl(fd, TIOCGWINSZ, &wins);
/* TODO: err check here */
printf("cols=%d rows=%d\n", wins.ws_col, wins.ws_row);

lg ★★
()

> err = ioctl(fd, TIOCGWINSZ, &wins);
А если терминал -- не локальный, а подключен через RS232C?

nobody ★★
()

> какая разница? :)
Работать не будет.

Причем необязательно через RS232C -- вообще на любом терминале, кроме локальной консоли linux.

Подумай: ioctl'ы обрабатываются ядром. Как о них узнает терминал (или его эмулятор -- xterm, например). Никак. Поэтому и работать такой способ будет только на локальной linux-консоли. То есть на мониторе, подключенном к видеоадаптеру данной машины, и только в том случае, если выводом управляет ядро, а не X Windiw, или Midnight Commander, или какая другая прога, запустившая данную программу на псевдотерминале.

nobody ★★
()

какой-то у тебя бессвязный поток сознания. :)
1) ioctl передается драйверу терминала. Можешь сам поглядеть в linux/drivers/char/vt.c
2) это как еще xterm в обход ядра ходит? Вы что-то путаете, батенька

SadStork
()

> ioctl передается драйверу терминала.
И что? Драйверу _какого_ терминала передается? Драйверу виртуальной консоли linux. Я и не спорю, что на локальной linux-консоли ioctl'ы будут работать. Ты, наверно, не в курсе, что, кроме виртуальных linux-консолей, существуют другие терминалы? Так вот, они существуют. И к linux'у не имеют никакого отношения. Если ты зашел по сети на linux-сервер, куда пойдет твой ioctl? Удаленному хосту? Даже и не надейся на это. Драйверу vt сервера? Ну подумай, как он тебе узнает размеры экрана удаленного хоста?

> это как еще xterm в обход ядра ходит?
В исходники его погляди -- увидишь как. Для вывода xterm использует запрос ImageText8 X сервера (не напрямую, а черех Xlib, но это сути не меняет). X сервер делает видеовывод собственными средствами, не используя для этого графические возможности ядра (у которого их нет).

Ну напиши ты простую программу, которая делает какой-нибудь ioctl на STDOUT_FILENO. Запусти ее на виртуальной консоли linux. Убедись, что она работает (под mc не пытайся -- не заработает). Запусти X Window. Открой окно xterm. Запусти в нем эту же прогу. Убедись, что она _не_ работает (также как и под mc). Почему? Да потому что ее stdout'ом будет не /dev/ttyN, а /dev/pts/N (где N -- какое-то число). Если уж ты способен читать исходники ядра, то погляди, какие ioctl'ы поддерживает псевдотерминал (/usr/src/linux/drivers/char/pty.c).

ЗЫ: если еще раз заявишь про бессвязный поток сознания -- ответа не получишь (мне не платят за ответы на твои вопросы, а хамства мне и в реале хватает).

nobody ★★
()

уважаемый nobody давайте сделаем проще. размер терминала можно узнать командой stty -a запускаем в консоли и в xterm команду strace -olog stty -a внимательно смотрим в файл log и видим (о чудо!) что размер терминала в том и другом случае вернулся через ioctl TIOCGWINSZ, который отработал нормально. Я это проделал, а вы можете мне поверить на слово если такой занятой человек :)

То, что там иксы повесили на master side pty, и какими средствами они организуют вывод в данном случае никакой роли не играет - все равно все пройдет через драйвер терминала в ядре.

С Уважением SadStork

SadStork
()

Ok, насчет программного терминала ты оказался прав (щелчок мне по носу). Одна программа вызывает ioctl для установки переменной "размеры_терминала", другая вызовом ioctl может считать эту переменную. В случае vt инициализирует эту переменную драйвер linux-консоли, в случае pts -- программа на стороне master.

А как насчет аппаратного терминала? Ведь он не сможет установить эту переменную. Чтобы этот способ работал, должна быть какая-то программа на данной машине, которая знает размеры экрана подключенного терминала и может установить переменную ядра соответствующим образом.

Способ, основанный на кодах управления терминалом (приведенный мной выше) будет работать на любом терминале, потому что эти коды интерпретируются самим терминалом.

nobody ★★
()

что-то я вас не понимаю - для того, чтобы слать по линии инициализационные данные о размерах терминала нужна программа, а чтобы ловить esc-последовательности которые интерпретируются "самим терминалом" никакой программы не требуюется? :) Там Святой Дух их перехватывает и интерпретирует, видимо...

Извините за сарказм

SadStork
()

Дело в том, что ни один терминал не умеет "слать по линии инициализационные данные о размерах терминала". Зато любой совместимый по кодам с vt100 умеет интерпретировать их.

Простой пример. Захожу из дома по модему на рабочую машину. Делаю stty -a. Мне сообщается, что размер терминала = 0 строк и 0 столбцов.

Что делать? Альтернатив тут всего 3:
1) Юзать ncurses (эта либа использует базу данных terminfo для получения информации о терминале, в т.ч. о его размерах).
2) Парсить terminfo вручную.
3) Юзать коды управления терминалом.

Способ #1 не подходит: anonymous, запостивший данный вопрос, явно указал, что ncurses ему не катит.
Способ #2 лично мне кажется сложнее, чем #3, для которого функция состоит из 3 строк:

int get_term_size(int *width, int *height)
{
setvbuf(stdout, NULL, _IONBF, 0);
fputs("\x1B[255;255H\x1B[6n", stdout);
return scanf("\x1B[%d;%dR", height, width) == 2;
}

Реально же будет всего 2 строки, потому что функция setvbuf будет вызвана еще раньше: если программеру нужны размеры экрана, значит он собирается работать в полноэкранном режиме, а значит построчная буферизация stdout должна быть отключена.

nobody ★★
()

вообще говоря это недореализация в linux - по идеи все терминальные девайсы(такие как виртуальный, псевдо и сериальный терминалы) должны иметь одинаковый базовый ioctl(как во FreeBSD например) .. вообще говоря у тебя терминал может и не быть vt100 компатабельным и ты все равно не зная ньюансов сможешь узнать размер через TIOCGWINSZ .. так что все зависит от того что надо в конце концов получить ..

в твоем случае ты должен быть уверен что терминал заинтерпретирует тебя корректно, если этого не произойдет то может возникнуть десинхронизация .. в случае с ioctl ты должен быть уверен что дескриптор засосациирован(на самом деле не должен просто можешь получить левые данные) с дерминальным тевайсом

>1) Юзать ncurses (эта либа использует базу данных
>terminfo для получения информации о терминале, в т.ч. о
>его размерах).
>2) Парсить terminfo вручную.

помоему для получения terminfo используется ioctl

lg ★★
()

> все терминальные девайсы(такие как виртуальный,
> псевдо и сериальный терминалы) должны
> иметь одинаковый базовый ioctl
Непонятно, что ты имеешь в виду под словом `ioctl' применительно к аппаратному терминалу? ioctl -- это системный вызов. Прога из юзерспэйса делает на своей машине int 80h (для linux@PC). Ядро ОС этой же самой машины этот вызов обрабатывает. Дальше этой машины ioctl не уходит. Системные вызова не передаются по кабелям (будь то сетевой кабель или RS232C). Каким образом терминал, подключенный через кабель, узнает о каком-то там ioctl?

Может быть, ты имеешь в виду следующее: ядро, обнаружив на линии терминал, посылает ему Esc-последовательность для получения его размеров и прочей инфы? А потом проги на этой машине могут обращаться по ioctl к ядру для получения этой инфы, так? Если я правильно угадал, то объясни, как ядро может обнаружить подключение терминала? Ведь по RS232C может подключаться все, что угодно. И посылаемые подключенному девайсу коды могут сделать с ним что-то, что юзеру не надо. И потом, зачем все это? Есть база данных terminfo, в которой все существующие терминалы описаны. А если прога не хочет (или не может) юзать terminfo, то пускай и посылает сама Esc-коды терминалу. Зачем ядру эти заморочки?

> в твоем случае ты должен быть уверен
> что терминал заинтерпретирует тебя корректно
Ты обратил внимание на последнюю строку функции?
return scanf("\x1B[%d;%dR", height, width) == 2;
Если терминал не поддерживает Esc-коды vt100 (а точнее, ECMA-48 CSI sequences), то функция возвратит 0. В этом случае размеры терминала останутся неизвестными. Ну и что? Если Esc-последовательности не поддерживаются, ты все равно не сможешь работать в полноэкранном режиме (как ты собираешься без них устанавливать курсор в произвольную позицию экрана?).

> помоему для получения terminfo используется ioctl
Неа, обычная юзерспэйсная база данных. В ядре она нафиг не нужна. Хранится на диске. У меня это каталог /usr/share/terminfo. Идет вместе с ncurses (в том же архиве). Устанавливается также вместе с ncurses (по make install).

Кстати, у юзера вполне может оказаться /etc/termcap вместо terminfo. Если не ошибаюсь, на фряхе используется именно он. Его парсить еще веселее -- это очень большой текстовый файл. БД terminfo была придумана для замены /etc/termcap (она бинарная, и каждый файл содержит описание только одного терминала, а значит с ней быстрее работать).

nobody ★★
()

> Непонятно, что ты имеешь в виду под словом `ioctl' применительно к
> аппаратному терминалу?

ну батенька .. апаратные терминалы к линуксу вообще отношения не имеют
и я про них ни словом не обмолвился .. а под сериальным терминалом
имелся ввиду псевдо девайс(чарактер девайс) который может слушаться и
обрабатываться каким нибудь getty например при попадании данных в
линию RS232 .. после этого getty устанавливает запрашиваемые размеры
(например по получении соотв ESC-последовательноти) и ты можешь
пользоваться ioctl(на стороне getty) для получения установленных
размеров (то есть получился твой апаратный терминал - это коробка с
линуском и с торчащим RS232 нульмодемом).

> Ты обратил внимание на последнюю строку функции?
> return scanf("\x1B[%d;%dR", height, width) == 2;

обратил .. именно из-за этой последней строчки и может возникнуть
десинхронизация .. например имеем терминал не vt100 компатабельный -
ты послал ему эту ESC-посл для получения размера и висишь в scanfе
ждешь ответа, но он(терминал) не поддерживает эту посл и в этот момент
решил послать тебе какие-то данные - соответсвенно если во время
работы с терминалом ты проделаешь подобное то ты потеряешь эти данные
в отсосанном(вернувшем 0) scanf вызове.

> Неа, обычная юзерспэйсная база данных. В ядре она нафиг не
> нужна. Хранится на диске. У меня это каталог
> /usr/share/terminfo. Идет вместе с ncurses (в том же
> архиве). Устанавли вается также вместе с ncurses (по make install).

а - ну пожалуйста что там получается .. у любого терминала описаного в
terminfo должен быть заранее оговоренный размер? - тухлость.

вобщем не хочу тебя ни в чем убеждать - пользовать
ESC-последовательности в соответствующих местах катируется.

> ioctl -- это системный вызов. Прога из
> юзерспэйса делает на своей машине int 80h (для linux@PC). Ядро ОС
> этой же самой машины этот вызов обрабатывает. Дальше этой машины
> ioctl не уходит. Системные вызова не передаются по кабелям (будь то
> сетевой кабель или RS232C). Каким образом терминал, подключенный
> через кабель, узнает о каком-то там ioctl?

:))) прикинь системные вызовы да даже аппаратные прерывания могу
передаваться по кабелям. Кстати если системный вызовы не могут
передаваться через кабеля - то как работает клиентская часть NFS, а
точнее ее RPC часть?

lg ★★
()

> как работает клиентская часть NFS, а точнее ее RPC часть?
А как работает запрос файла у FTP сервера? Сервер делает open, read и close. То есть как минимум 3 сискола. Ты готов считать запрос файла передачей сисколов по сети?

Я вообще-то понимаю, о чем ты говоришь: с точки зрения клиента происходит следующее: приложение обратилось к ядру, а ядро полезло в сеть. По ведь ядро не передает в сеть именно _сискол_. Это просто невозможно -- сеть же может быть гетерогенной. Представь: я в вызове open указываю права доступа 755, а сервер работает на Windows. Если считать это именно передачей сискола, то должно быть так: ядро Windows получает от ядра linux сискол open. Опаньки -- нет такого сискола. Есть CreateFile -- ладно, вызываем его. Но с какими аргументами?

Понимаешь, это не есть передача сискола. Это просто запрос обслуживания по сети. А то что с точки зрения программы-клиента какой-то локальный сискол привел к сетевой активности -- ну и что? Когда ты обращаешься к FTP серверу, ты делаешь сискол send, в результате которого на сервере происходит сискол read (или ReadFile, или что-то еще). Та же сетевая активность, только вид сбоку.

> аппаратные прерывания могу передаваться по кабелям.
Ты говоришь об экзотических архитектурах, где несколько машин, связанных сетью, образуют одну большую машину, или что-то типа того? Если да, то в этом случае сеть -- уже не сеть, а разновидность системной шины. Я понимаю: шина -- она, конечно, тоже провода (даже шина данных персоналки), но я все-таки не имел ее в виду. Когда я говорил про кабеля, то имел в виду связь отдельных устройств, не представляющих собой в связанном виде единое целое с общей шиной данных/адреса/управления/чего-то-еще.

nobody ★★
()

функции можно вызывать по сети .. see RPC(Remote Procedure Call) .. во многих ядрах есть kern-rpc. Hа самом деле представь себе картину - все девайсы имеют Ethernet как системную шину .. в том числе и биос и память и винт и все что угодно :)) - или например с помощью FireWire ты _хардверно_ можешь замапить себе чужую память и работать с ней как со своей собственной .. а что если именно в этом куске памяти расположить новый сискол(например насильно запихнуть модуль(который содержит новый сискол) в этот участок памяти)?

lg ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.