ncurses 的字段中未显示更改

changes not showing in field in ncurses

我有一个 ncurses 程序,它是一个登录菜单,我使用字段作为用户名和密码。
问题是当我在字段中键入内容时,字符会注册但不会显示在终端中。换句话说,如果你执行代码 blow 并输入一些东西,你将无法在终端中看到它,但如果你按 F2,你可以看到这些字符已被注册。
这是我的代码:
test.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) = '[=10=]';

  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

提前感谢您的回复

您需要使用echonoecho来打开回显on/off。如果您愿意,可以将该部分烘焙到输入函数中:

#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/delete(和 new[]/delete[])。在您的 main 中,您可以将 WelcomeMenu 的动态创建替换为自动变量:

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

该示例的基本问题是它没有提供显示字符的功能。在 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 语句移动到可以从表单驱动程序调用的函数中。

ncurses 示例包括一些使用 form_driver. You might want to read through the test-code, seeing how the basic loop

 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.[= 的原因22=]