изменения не отображаются в поле в ncurses - PullRequest
0 голосов
/ 08 февраля 2020

У меня есть программа ncurses, которая является меню входа в систему, и я использую поле для имени пользователя и пароля.
Проблема в том, что когда я что-то набираю в полях, символы регистрируются, но не отображаются в терминале. Другими словами, если вы выполните код и наберете что-нибудь, вы не сможете увидеть его в терминале, но если вы наберете sh F2, вы увидите, что символы были зарегистрированы.
Вот мой код:
тест. cpp

#include <curses.h>
#include <form.h>
#include <menu.h>
#include <string>
#include <cstring>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

class WelcomeMenu {
private:
  int _row; // number of rows of the terminal
  int _col; // number of columns of the terminal
public:
  WelcomeMenu();
  ~WelcomeMenu();
  void welcomeBox();
  void loginMenu();
  void registerMenu();
};

WelcomeMenu::WelcomeMenu(){
  initscr();
  noecho();
  cbreak();
  keypad(stdscr, true);
  int row, col;
  getmaxyx(stdscr,row,col);   /* get the number of rows and columns */
  this->_row = row; this->_col = col;
  loginMenu();
}

WelcomeMenu::~WelcomeMenu(){
  refresh();
  endwin();
}

/*
 * This is useful because ncurses fill fields blanks with spaces.
 */
char* trim_whitespaces(char *str)
{
  char *end;

  // trim leading space
  while(isspace(*str))
    str++;

  if(*str == 0) // all spaces?
    return str;

  // trim trailing space
  end = str + strnlen(str, 128) - 1;

  while(end > str && isspace(*end))
    end--;

  // write new null terminator
  *(end+1) = '\0';

  return str;
}

void WelcomeMenu::loginMenu(){
  // erase();
  FORM *form;
  FIELD *fields[5];
  WINDOW *win_body, *win_form;

  int ch;

  win_body = newwin(24, 80, 0, 0);
  assert(win_body != NULL);
  box(win_body, 0, 0);
  win_form = derwin(win_body, 20, 78, 3, 1);
  assert(win_form != NULL);
  box(win_form, 0, 0);
  mvwprintw(win_body, 1, 2, "Press F1 to quit and F2 to print fields content");

  fields[0] = new_field(1, 10, 0, 0, 0, 0);
  fields[1] = new_field(1, 40, 0, 15, 0, 0);
  fields[2] = new_field(1, 10, 2, 0, 0, 0);
  fields[3] = new_field(1, 40, 2, 15, 0, 0);
  fields[4] = NULL;
  assert(fields[0] != NULL && fields[1] != NULL && fields[2] != NULL && fields[3] != NULL);

  set_field_buffer(fields[0], 0, "Username: ");
  set_field_buffer(fields[1], 0, "username");
  set_field_buffer(fields[2], 0, "Password: ");
  set_field_buffer(fields[3], 0, "password");

  set_field_opts(fields[0], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
  set_field_opts(fields[1], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
  set_field_opts(fields[2], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
  set_field_opts(fields[3], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);

  set_field_back(fields[1], A_UNDERLINE);
  set_field_back(fields[3], A_UNDERLINE);

  form = new_form(fields);
  assert(form != NULL);
  set_form_win(form, win_form);
  set_form_sub(form, derwin(win_form, 18, 76, 1, 1));
  post_form(form);

  refresh();
  wrefresh(win_body);
  wrefresh(win_form);

  while ((ch = getch()) != KEY_F(1)){

    switch (ch) {
      case KEY_F(2):
        // Or the current field buffer won't be sync with what is displayed
        form_driver(form, REQ_NEXT_FIELD);
        form_driver(form, REQ_PREV_FIELD);
        move(LINES-3, 2);

        for (int i = 0; fields[i]; i++) {
          printw("%s", trim_whitespaces(field_buffer(fields[i], 0)));

          if (field_opts(fields[i]) & O_ACTIVE)
            printw("\"\t");
          else
            printw(": \"");
        }

        refresh();
        pos_form_cursor(form);
        break;

      case KEY_DOWN:
        form_driver(form, REQ_NEXT_FIELD);
        form_driver(form, REQ_END_LINE);
        break;

      case KEY_UP:
        form_driver(form, REQ_PREV_FIELD);
        form_driver(form, REQ_END_LINE);
        break;

      case KEY_LEFT:
        form_driver(form, REQ_PREV_CHAR);
        break;

      case KEY_RIGHT:
        form_driver(form, REQ_NEXT_CHAR);
        break;

      // Delete the char before cursor
      case KEY_BACKSPACE:
      case 127:
        form_driver(form, REQ_DEL_PREV);
        break;

      // Delete the char under the cursor
      case KEY_DC:
        form_driver(form, REQ_DEL_CHAR);
        break;

      default:
        form_driver(form, ch);
        break;
    }
  }

  wrefresh(win_form);

  unpost_form(form);
  free_form(form);
  free_field(fields[0]);
  free_field(fields[1]);
  free_field(fields[2]);
  free_field(fields[3]);
  delwin(win_form);
  delwin(win_body);
}

int main(){
  WelcomeMenu * myConWin = new WelcomeMenu();
  delete myConWin;
  return 0;
}

вы можете скомпилировать его так: g++ -lncurses -lform test.cpp

Заранее благодарим вас за ваш ответ

Ответы [ 2 ]

0 голосов
/ 08 февраля 2020

Основная проблема c в примере состоит в том, что он не обеспечивает отображение символов. В ncurses это будет сделано с использованием функции form_driver:

form_driver

   Once a form has been posted (displayed), you should funnel input events
   to it through form_driver.  This routine has three major input cases:

   o   The input is a form navigation request.  Navigation  request  codes
       are constants defined in <form.h>, which are distinct from the key-
       and character codes returned by wgetch(3x).

   o   The input is a printable character.   Printable  characters  (which
       must  be positive, less than 256) are checked according to the pro-
       gram's locale settings.

   o   The input is the KEY_MOUSE special key  associated  with  an  mouse
       event.

Использование вашей программы form_driver повлечет за собой некоторую реорганизацию программы, перевод этого оператора switch в функцию, которую можно вызывать из драйвера формы.

Некоторые примеры включают в себя form_driver. Возможно, вы захотите прочитать тестовый код, увидев, как basi c l oop

 while (!finished) {
     switch (form_driver(form, c = form_virtualize(form, w))) {
     case E_OK:
          MvAddStr(5, 57, field_buffer(secure, 1));
          clrtoeol();
          refresh();
          break;
     case E_UNKNOWN_COMMAND:
          finished = my_form_driver(form, c);
          break;
     default:
          beep();
          break;
     }

использует свою функцию form_virtualize для чтения символов (или формы Запросы). Печатные символы, которые вы видите (не), обрабатываются form_driver, который обновляет текущее поле в форме. Вы можете манипулировать буфером поля и выполнять другие специальные действия, как в form_virtualize, но при обновлении полей в форме вы должны использовать form_driver.

0 голосов
/ 08 февраля 2020

Вам нужно использовать echo и noecho, чтобы включить / выключить эхо. Вы можете запечь эту часть в функции ввода, если хотите:

#include <memory>

struct input_context {
    input_context() {
        echo();       // echo typed characters
        curs_set(1);  // show the cursor
    }
    ~input_context() {
        curs_set(0);  // hide the cursor
        noecho();     // turn off echoing
    }
};

std::string get_string(WINDOW* win, size_t len) {
    std::unique_ptr<char[]> buf = std::make_unique<char[]>(len);
    input_context dummy;
    if(wgetnstr(win, buf.get(), len) != ERR) return std::string{buf.get()};
    return {};
}

Примечание 1. Вы должны использовать new / deletenew[] / delete[]), когда все другие варианты были исчерпаны. В вашем main вы можете просто заменить динамическое c создание вашего WelcomeMenu на автоматическую c переменную:

int main(){
  WelcomeMenu myConWin;
}

Примечание 2. Используйте nullptr, а не NULL .

fields[4] = nullptr; // was NULL

Примечание 3: Как указано @kebs в комментариях, вы должны использовать версии заголовочных файлов на C ++:

#include <cassert>   // was assert.h
#include <cstdio>    // was stdio.h
#include <cstdlib>   // was stdlib.h
...