From 273b38b49beec443428abdca27a3d7aa6203076b Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Thu, 15 May 2025 19:57:03 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20ncurses:=20-=20=D0=98=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=BD=D0=BE=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=8E=20-=20=D0=9A=D0=BE=D0=BD=D1=81=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BE=D0=BA=D0=BD=D0=BE=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dub.json | 8 +- dub.selections.json | 3 +- source/app.d | 8 ++ source/examples/ncurses/README.md | 9 ++ source/examples/ncurses/menu.d | 127 +++++++++++++++++ source/examples/ncurses/package.d | 4 + source/examples/ncurses/password.d | 215 +++++++++++++++++++++++++++++ source/examples/package.d | 1 + 8 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 source/examples/ncurses/README.md create mode 100644 source/examples/ncurses/menu.d create mode 100644 source/examples/ncurses/package.d create mode 100644 source/examples/ncurses/password.d diff --git a/dub.json b/dub.json index c3002e3..a3cd59f 100644 --- a/dub.json +++ b/dub.json @@ -9,6 +9,10 @@ "targetPath": "bin", "targetType": "executable", "dependencies": { - "commandr": "~>1.1.0" - } + "commandr": "~>1.1.0", + "ncurses": "~>1.0.0" + }, + "libs": [ + "formw" + ] } \ No newline at end of file diff --git a/dub.selections.json b/dub.selections.json index 0c49417..38b35c7 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -1,6 +1,7 @@ { "fileVersion": 1, "versions": { - "commandr": "1.1.0" + "commandr": "1.1.0", + "ncurses": "1.0.0" } } diff --git a/source/app.d b/source/app.d index 7407377..e9eb7aa 100644 --- a/source/app.d +++ b/source/app.d @@ -11,12 +11,20 @@ int main(string[] args) .add(new Command("pipe", "Чтение выходных данных на примере ip")) .add(new Command("spinner", "Эмуляция статуса выполнения процесса")) ) + .add(new Command("ncurses", "Использование библиотеки ncurses") + .add(new Command("menu", "Интерактивное консольное меню")) + .add(new Command("password", "Консольное окно для ввода пароля")) + ) .parse(args); argumets .on("shell", (shell) { shell .on("pipe", (pipe) { pipeShell(); }) .on("spinner", (loading) { spinnerShell(); }); + }) + .on("ncurses", (ncurses) { ncurses + .on("menu", (items) { menuNcurses(); }) + .on("password", (password) { passwordNcurses(); }); }); return EXIT_SUCCESS; diff --git a/source/examples/ncurses/README.md b/source/examples/ncurses/README.md new file mode 100644 index 0000000..640fe77 --- /dev/null +++ b/source/examples/ncurses/README.md @@ -0,0 +1,9 @@ +# Использование библиотеки ncurses + +## items + +Функция `itemsList` создаёт интерактивное консольное меню с использованием библиотеки `ncurses`. Инициализирует экран, отключает эхо ввода и курсор, задаёт цветовые пары для окна и фона. Отображает список из пяти пунктов в окне с красным фоном и белым текстом, позволяя пользователю перемещаться по пунктам клавишами `UP` и `DOWN`. Выделяет текущий пункт инверсией цвета. Завершает работу по нажатию `F2`. Использует модули `std.stdio`, `std.conv`, `std.string`, `deimos.ncurses`, `deimos.form` и `core.stdc.locale`. + +## password + +Функция `password` создаёт консольное окно для ввода пароля с использованием библиотеки `ncurses`. Инициализирует экран, отключает эхо, задаёт цветовые пары (синий фон, красное окно) и создаёт форму с двумя полями: статический текст "Введите пароль:" и поле ввода с поддержкой регулярного выражения для валидации символов. Введённые символы отображаются как звёздочки (`*`), сохраняются в строку `password`. Поддерживает обработку `Backspace` и завершение ввода по `Enter`. По завершении выводит введённый пароль. Использует модули `std.stdio`, `std.conv`, `std.string`, `deimos.ncurses`, `deimos.form`, `core.stdc.locale`. diff --git a/source/examples/ncurses/menu.d b/source/examples/ncurses/menu.d new file mode 100644 index 0000000..e47be25 --- /dev/null +++ b/source/examples/ncurses/menu.d @@ -0,0 +1,127 @@ +module examples.ncurses.menu; + +import std.conv; // Импорт модуля для преобразования типов +import std.string; // Импорт модуля для работы со строками +import std.stdio; // Импорт модуля для ввода-вывода +import deimos.ncurses; // Импорт библиотеки ncurses для работы с терминалом +import deimos.form; // Импорт библиотеки форм ncurses (в данном коде не используется) +import core.stdc.locale; // Импорт для работы с локализацией + +// Определение констант для цветовых пар +enum +{ + COLOR_WINDOW = 1, // Идентификатор цветовой пары для окна + COLOR_BACKGROUND = 2 // Идентификатор цветовой пары для фона +} + +void menuNcurses() +{ + // Установка локали для корректного отображения символов (например, кириллицы) + setlocale(LC_ALL, ""); + + // Инициализация ncurses + initscr(); + + // Включение режима raw (сырой ввод, без обработки специальных символов) + raw(); + + // Отключение эха ввода (символы не отображаются при вводе) + noecho(); + + // Скрытие курсора + curs_set(0); + + // Инициализация цветового режима + start_color(); + + // Определение цветовых пар: текст/фон + init_pair(COLOR_WINDOW, COLOR_WHITE, COLOR_RED); // Белый текст на красном фоне + init_pair(COLOR_BACKGROUND, COLOR_WHITE, COLOR_BLUE); // Белый текст на синем фоне + + // Получение размеров терминала (высота и ширина) + int width, height; + getmaxyx(stdscr, height, width); + + // Установка фона для основного окна (stdscr) с использованием цветовой пары COLOR_BACKGROUND + bkgd(COLOR_PAIR(COLOR_BACKGROUND)); + + // Обновление экрана для отображения изменений + refresh(); + + // Создание нового окна размером 7x16, центрированного на экране + WINDOW* win = newwin(7, 16, height / 2 - 4, width / 2 - 8); + + // Включение обработки специальных клавиш (например, стрелки) для окна + keypad(win, TRUE); + + // Отрисовка рамки вокруг окна + box(win, 0, 0); + + // Установка фона окна с использованием цветовой пары COLOR_WINDOW + wbkgd(win, COLOR_PAIR(COLOR_WINDOW)); + + // Обновление окна для отображения изменений + wrefresh(win); + + // Массив пунктов меню + string[] items = [ + "Пункт 1", "Пункт 2", "Пункт 3", "Пункт 4", + "Пункт 5" + ]; + + // Индекс текущего выбранного пункта меню + int current_item = 0; + + // Количество пунктов меню, преобразованное в int + int count_items = items.length.to!int; + + // Переменная для хранения введенной клавиши + int ch; + + // Основной цикл обработки ввода + do + { + // Обработка нажатий клавиш + switch (ch) + { + case KEY_UP: // Нажата стрелка вверх + // Переход к предыдущему пункту меню с циклическим переключением + current_item = (current_item - 1 + count_items) % count_items; + break; + case KEY_DOWN: // Нажата стрелка вниз + // Переход к следующему пункту меню с циклическим переключением + current_item = (current_item + 1) % count_items; + break; + case 10: // Нажата клавиша Enter (код 10) + case KEY_ENTER: // Альтернативный код для Enter + // Пока ничего не делает (можно добавить обработку выбора) + break; + default: + // Игнорирование других клавиш + break; + } + + // Отрисовка всех пунктов меню + foreach (i, item; items) + { + // Если текущий пункт выбран, включаем инверсию цвета + if (i == current_item) + { + wattron(win, A_REVERSE); // Включение атрибута инверсии + } + // Вывод пункта меню в окне на позиции (i+1, 4) + mvwprintw(win, i.to!int + 1, 4, item.toStringz); + + // Отключение инверсии цвета после вывода + wattroff(win, A_REVERSE); + } + + // Обновление окна для отображения изменений + wrefresh(win); + } + // Цикл продолжается, пока не нажата клавиша F2 + while ((ch = wgetch(win)) != KEY_F(2)); + + // Завершение работы ncurses и очистка экрана + endwin(); +} diff --git a/source/examples/ncurses/package.d b/source/examples/ncurses/package.d new file mode 100644 index 0000000..5840174 --- /dev/null +++ b/source/examples/ncurses/package.d @@ -0,0 +1,4 @@ +module examples.ncurses; + +public import examples.ncurses.menu; +public import examples.ncurses.password; diff --git a/source/examples/ncurses/password.d b/source/examples/ncurses/password.d new file mode 100644 index 0000000..679e925 --- /dev/null +++ b/source/examples/ncurses/password.d @@ -0,0 +1,215 @@ +module examples.ncurses.password; + +import std.conv; // Импорт модуля для преобразования типов +import std.string; // Импорт модуля для работы со строками +import std.stdio; // Импорт модуля для ввода-вывода +import deimos.ncurses; // Импорт библиотеки ncurses для работы с терминалом +import deimos.form; // Импорт библиотеки форм ncurses для создания полей ввода +import core.stdc.locale; // Импорт для работы с локализацией + +// Определение констант для цветовых пар +enum +{ + COLOR_WINDOW = 1, // Идентификатор цветовой пары для окна + COLOR_BACKGROUND = 2 // Идентификатор цветовой пары для фона +} + +void passwordNcurses() +{ + // Установка локали для корректного отображения символов (например, кириллицы) + setlocale(LC_ALL, ""); + + // Инициализация ncurses + initscr(); + + // Включение режима raw (сырой ввод, без обработки специальных символов) + raw(); + + // Отключение эха ввода (символы не отображаются при вводе) + noecho(); + + // Установка видимости курсора (2 — полностью видимый курсор) + curs_set(2); + + // Инициализация цветового режима + start_color(); + + // Определение цветовых пар: текст/фон + init_pair(COLOR_WINDOW, COLOR_WHITE, COLOR_RED); // Белый текст на красном фоне + init_pair(COLOR_BACKGROUND, COLOR_WHITE, COLOR_BLUE); // Белый текст на синем фоне + + // Получение размеров терминала (высота и ширина) + int width, height; + getmaxyx(stdscr, height, width); + + // Установка фона для основного окна (stdscr) с использованием цветовой пары COLOR_BACKGROUND + bkgd(COLOR_PAIR(COLOR_BACKGROUND)); + + // Обновление экрана для отображения изменений + refresh(); + + // Создание нового окна размером 5x44, центрированного на экране + WINDOW* win = newwin(5, 44, height / 2 - 3, width / 2 - 22); + + // Включение обработки специальных клавиш (например, Backspace) для окна + keypad(win, TRUE); + + // Отрисовка рамки вокруг окна + box(win, 0, 0); + + // Установка фона окна с использованием цветовой пары COLOR_WINDOW + wbkgd(win, COLOR_PAIR(COLOR_WINDOW)); + + // Обновление окна для отображения изменений + wrefresh(win); + + // Создание массива полей формы (два поля: метка и поле ввода пароля) + FIELD*[3] fields; + + // Поле для статической метки "Введите пароль:" + fields[0] = new_field(1, 16, 0, 2, 0, 0); + + // Поле для ввода пароля + fields[1] = new_field(1, 20, 0, 18, 0, 0); + + // Завершающий нулевой указатель для массива полей + fields[2] = null; + + // Установка текста метки в первом поле + set_field_buffer(fields[0], 0, "Введите пароль:"); + + // Настройка первого поля: видимое, публичное, автопропуск (нельзя редактировать) + set_field_opts(fields[0], O_VISIBLE | O_PUBLIC | O_AUTOSKIP); + + // Установка типа второго поля: регулярное выражение для валидации ввода + // Разрешены буквы, цифры, некоторые символы и пробелы + set_field_type(fields[1], TYPE_REGEXP, `^\**[-0-9A-Za-zА-Яа-я*,./!?%&#:$^_=+@~\]* *$`.ptr); + + // Настройка второго поля: видимое, публичное, редактируемое, активно + set_field_opts(fields[1], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE); + + // Установка фона для первого поля (цветовая пара COLOR_WINDOW) + set_field_back(fields[0], COLOR_PAIR(COLOR_WINDOW)); + + // Установка фона и подчеркивания для второго поля + set_field_back(fields[1], COLOR_PAIR(COLOR_WINDOW) | A_UNDERLINE); + + // Создание формы на основе массива полей + FORM* form = new_form(cast(FIELD**) fields); + + // Установка основного окна для формы + set_form_win(form, win); + + // Установка подокна для формы (область внутри окна для ввода) + set_form_sub(form, derwin(win, 1, 38, 2, 2)); + + // Отображение формы на экране + post_form(form); + + // Обновление окна для отображения изменений + wrefresh(win); + + // Переменная для хранения введенного пароля (в формате dstring для поддержки Unicode) + dstring password; + + // Флаг для выхода из цикла ввода + bool stop = false; + + // Переменная для хранения статуса операций с формой + int status; + + // Переменная для хранения введенного символа + dchar ch; + + // Переменная для хранения результата ввода + int ret; + + // Основной цикл обработки ввода + while (!stop) + { + // Получение символа или кода клавиши из окна + ret = wget_wch(win, &ch); + + switch (ret) + { + case KEY_CODE_YES: // Обработка специальных клавиш (например, Backspace) + switch (ch) + { + case KEY_BACKSPACE: // Нажата клавиша Backspace + // Удаление предыдущего символа из формы + form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); + // Удаление последнего символа из строки пароля, если она не пуста + if (password.length) + password = password[0 .. $ - 1]; + break; + default: + // Игнорирование других специальных клавиш + break; + } + break; + + case OK: // Обработка обычных символов + switch (ch) + { + case KEY_BACKSPACE: // Нажата клавиша Backspace (альтернативный код) + // Удаление предыдущего символа из формы + form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); + // Удаление последнего символа из строки пароля, если она не пуста + if (password.length) + password = password[0 .. $ - 1]; + break; + case 32: // Пробел (игнорируется) + break; + case 10: // Нажата клавиша Enter + case KEY_ENTER: // Альтернативный код для Enter + // Установка флага для выхода из цикла + stop = true; + break; + default: // Обработка введенного символа + // Отправка символа в форму + status = form_driver_w(form, OK, ch); + if (status == OK) // Если символ принят + { + // Валидация содержимого поля + status = form_driver_w(form, KEY_CODE_YES, REQ_VALIDATION); + if (status == OK) // Если валидация прошла успешно + { + // Добавление символа в строку пароля + password ~= ch.to!dchar; + // Удаление последнего введенного символа + form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); + // Замена введенного символа на '*' для маскировки + form_driver_w(form, OK, '*'.to!int); + } + else // Если валидация не прошла + { + // Удаление последнего введенного символа + status = form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); + } + } + break; + } + break; + + default: + // Игнорирование других случаев + break; + } + } + + // Снятие формы с экрана + unpost_form(form); + + // Освобождение памяти, занятой формой + free_form(form); + + // Освобождение памяти, занятой полями + free_field(fields[0]); + free_field(fields[1]); + + // Завершение работы ncurses и очистка экрана + endwin(); + + // Вывод введенного пароля в консоль + writefln("Password: %s\n", password); +} diff --git a/source/examples/package.d b/source/examples/package.d index e3169c8..a4f5442 100644 --- a/source/examples/package.d +++ b/source/examples/package.d @@ -2,3 +2,4 @@ module examples; public import examples.version_; public import examples.shell; +public import examples.ncurses;