Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Free Pascal, Pascal ABC и другие _ Вопрос о ncurses

Автор: camac 7.06.2019 14:21

На мой http://forum.pascal.net.ru/index.php?showtopic=32668 ответа нет (или никто ничего не знает). Зайду к моей проблемке с другой стороны.
Нигде не нашел информации об использовании ncurses в паскале. Есть малость для Си. Но это для меня сложно.
Есть ли где-то какая-нибудь информация именно для паскаля?

Автор: OCTAGRAM 7.06.2019 16:00

Я знаю, что в самом ncurses есть привязки для языка Ada, но я так посмотрел, там юникод не в явном виде. http://www.rtsdd.ru/Ada2012_Unicode_NCURSES.aspx. Не знаю, как это далеко от порта на Linux.

Цитата
информации об использовании ncurses в паскале. Есть малость для Си. Но это для меня сложно


Напрасно. Надо уметь переводить с Си в Паскаль на автомате. В этом вопросе я могу помочь. Пишите, что нужно перевести.

Автор: camac 7.06.2019 21:45

Цитата(OCTAGRAM @ 7.06.2019 12:00) *

Надо уметь переводить с Си в Паскаль на автомате.


Я престарелый гуманитарий. Изучением Паскаля я занялся, потому что так захотелось. Пытался Си, но не идет. Какой-то он бардачный, сумбурный и корявый. А Паскаль в отличие от Си более стройный, логичный и понятный.

Изучаю самостоятельно, продираясь через откровенную макулатуру и выискивая ценную информацию.
Спасают, в основном, форумы. Этот сайт, в частности, очень помогает. Правда информация не редко подается слишком сжато. Хотелось бы некой разжеванности.
В интернете полезной информации практически не найти. Поиск выдает совершенно не нужную/неверную информацию (коммерция, что поделаешь). И эта тенденция растет год от года. Жаль.

По теме. О ncurses есть, например, очень не плохой http://alexber220.narod.ru/ncurses/index.htm. Но сугубо для Си.
Что-то пытался, но ступорится уже на подключении модуля. Сам ncurses установлен.

Спасибо за отзыв.

Автор: OCTAGRAM 7.06.2019 23:06

Надо понимать, что там за кулисами, и 1:1 переписывать. Машинный фундамент у Паскаля и Си общий.

Цитата
Что-то пытался, но ступорится уже на подключении модуля.


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

По ссылке я увидел:

main(int argc,char *argv[])
{
// инициализация (должна быть выполнена
// перед использованием ncurses)
initscr();

// перемещение курсора в стандартном экране y=10 x=30
move(10,30);

printw("Hello world !!!"); // вывод строки
refresh(); // обновить
getch(); // ждём нажатия символа

endwin(); // завершение работы с ncurses
}


Здесь используются функции initscr, move, printw, refresh, getch, endwin.

Смотрим https://github.com/mirror/ncurses/blob/9a0b985989d0aeb66b66b5711d432322aa994969/include/curses.h.in#L667:

extern NCURSES_EXPORT(WINDOW *) initscr (void); /* implemented */


NCURSES_EXPORT — судя по названию, это макрос, который навешивает компилятороспецифичные атрибуты, а также они зависят от того, компилируется ли ncurses DLL или какой-то другой код, который от DLL зависит. Внутри этого макроса некий «WINDOW *», то есть, указатель на что-то. В примере на Си, как видно, результат отбрасывается, так что и WINDOW * полноценно можно не переносить. Впрочем, может, и не нужно переносить. Вот в Win32API все структуры достаточно стабильны, и если переписать их с Си на Делфи или Аду, то они сохраняют актуальность, а вот другие програиисты на Си такой дисциплиной не отличаются, и у них и размер, и прочее может без предупреждения меняться.

Короче, пишем:

const
Ncurses = 'ncurses.so';

type
PCursesWindow = Pointer;

function initscr: PCursesWindow; cdecl; external Ncurses name 'initscr';


Идём дальше.

#define move(y,x) wmove(stdscr,(y),(x))


Макрос. Ясно. Значит, на самом деле нужен wmove. Ищем wmove.

extern NCURSES_EXPORT(int) wmove (WINDOW *,int,int); /* implemented */


Мда, названия бы аргументов бы ещё найти. Надо найти реализацию, в реализации-то они точно будут:

NCURSES_EXPORT(int)
wmove(WINDOW *win, int y, int x)


Переписываем на Паскаль:

function wmove(win: PCursesWindow; y, x: Integer): Integer; cdecl; external Ncurses name 'wmove';


Далее нужно понять, что такое stdscr.

#if NCURSES_REENTRANT
NCURSES_WRAPPED_VAR(WINDOW *, stdscr);
#define stdscr NCURSES_PUBLIC_VAR(stdscr())
#else
extern NCURSES_EXPORT_VAR(WINDOW *) stdscr;
#endif



Блин, тяжёлый случай. Я вот вижу этот код первый раз и без понятия, объявлен ли NCURSES_REENTRANT. Лучше бы ему быть объявленным, потому что Паскаль умеет импортировать только процедуры и функции. Переменные и константы умеет экспортировать, но не импортировать. В Win32 API такой фигни нет. Лишний повод понять, до чего же хорошо в Microsoft пишут на Си, кое-кому бы поучиться.

Понадеемся, что объявлен.

Тогда:

function stdscr: PCursesWindow; cdecl; external Ncurses name 'stdscr';


Теперь возвращаемся к макросу:

function move(y, x: Integer): Integer; inline;
begin
Result := wmove(stdscr, y, x);
end;


Идём далее.

extern NCURSES_EXPORT(int) printw (const char *,...)			/* implemented */
GCC_PRINTFLIKE(1,2);


Какие же козлы. Ну как им объяснить, что нельзя использовать varargs. В хороших языках программирования именно ради безопасности его нет, но вдруг приходится обращаться к библиотеке на Си, и тут же там какой-то козёл написал varargs. Вот в Win32 API такого почти не бывает. Я только одну функцию навскидку знаю. До чего же хорош Microsoft.

В языке Ада их нормально ни вызвать, ни реализовать. В Delphi можно вызвать, но нельзя реализовать. В FPC, наверное, тоже.

Так как в Паскале вызвать можно (синтаксис будет cdecl; varargs;), я подумал, может, так и сделать. Но потом решил сделать как надо. А будет лучше заменить printw на addstr, а форматирование, если хочется, через SysUtils.Format всегда доступно.

Итак.

#define addstr(str) waddnstr(stdscr,(str),-1)


Ладно. Макрос, так макрос.

extern NCURSES_EXPORT(int) waddnstr (WINDOW *,const char *,int); /* implemented */


Опять без имён параметров.

NCURSES_EXPORT(int)
waddnstr(WINDOW *win, const char *astr, int n)


Вот это уже можно переписать на Паскаль:

function waddnstr(win: PCursesWindow; const astr: PAnsiChar; n: Integer): Integer; cdecl; external Ncurses name 'waddnstr';


Теперь макрос:

function addstr(const str: PAnsiChar): Integer; inline;
begin
Result := waddnstr(stdscr, str, -1);
end;


Попутно я понял, зачем тут n и -1. Это же длина в байтах. Но это небезопасно, и мы же не хотим, чтоб процессор лишний раз бегал по строке, ведь у нас полноценный язык программирования, способный на такой подвиг, как запомнить длину строки. Значит, лучше будет

#define addnstr(str,n) waddnstr(stdscr,(str),(n))


function addnstr(const str: PAnsiChar; n: Integer): Integer; inline;
begin
Result := waddnstr(stdscr, str, n);
end;


Идём дальше.

#define refresh() wrefresh(stdscr)

extern NCURSES_EXPORT(int) wrefresh (WINDOW *); /* implemented */


Переписываем:

function wrefresh(win: PCursesWindow): Integer; cdecl; external Ncurses name 'wrefresh';

function refresh: Integer; inline;
begin
Result := wrefresh(stdscr);
end;



Дальше getch.

#define getch() wgetch(stdscr)
extern NCURSES_EXPORT(int) wgetch (WINDOW *); /* implemented */


function wgetch: Integer; cdecl; external Ncurses name 'wgetch';

function getch: Integer; inline;
begin
Result := wgetch(stdscr);
end;


И endwin.

extern NCURSES_EXPORT(int) endwin (void); /* implemented */


function endwin: Integer; external Ncurses name 'endwin';


Всё вместе:



const
Ncurses = 'ncurses.so';

type
PCursesWindow = Pointer;

function initscr: PCursesWindow; cdecl; external Ncurses name 'initscr';

function stdscr: PCursesWindow; cdecl; external Ncurses name 'stdscr';

function wmove(win: PCursesWindow; y, x: Integer): Integer; cdecl; external Ncurses name 'wmove';

function move(y, x: Integer): Integer; inline;
begin
Result := wmove(stdscr, y, x);
end;

function waddnstr(win: PCursesWindow; const astr: PAnsiChar; n: Integer): Integer; cdecl; external Ncurses name 'waddnstr';

function addnstr(const str: PAnsiChar; n: Integer): Integer; inline;
begin
Result := waddnstr(stdscr, str, n);
end;

function wrefresh(win: PCursesWindow): Integer; cdecl; external Ncurses name 'wrefresh';

function refresh: Integer; inline;
begin
Result := wrefresh(stdscr);
end;

function wgetch: Integer; cdecl; external Ncurses name 'wgetch';

function getch: Integer; inline;
begin
Result := wgetch(stdscr);
end;

function endwin: Integer; external Ncurses name 'endwin';

// выше была копия
// ниже вспомогательный код и переписанный пример

function addustr(const Str: UTF8String): Integer;
begin
Result := addnstr(PAnsiChar(Str), Length(Str));
end;

begin
// инициализация (должна быть выполнена
// перед использованием ncurses)
initscr;

// перемещение курсора в стандартном экране y=10 x=30
move(10, 30);

addustr('Hello world !!!'); // вывод строки
refresh; // обновить
getch; // ждём нажатия символа

endwin; // завершение работы с ncurses
end.



Скорее всего, это не заработает с первого раза. Я никогда не писал на Паскале под Linux. Но по ошибкам компиляции, думаю, смогу починить.

Добавлено через 17 мин.
Я сейчас прочитал, что ncursesw нужно подключать, а не ncurses.

const
Ncurses = 'ncursesw.so';