ncurses-6.1库实验

前言

同事遇到在ncurses库编程时,中文乱码的问题。
他实验时,要退到ncurses-5.8版本,才能正常显示中文,但是panel库还是中文乱码。

今天手头的事整完了,我也看看这个问题。
其实中文乱码这事,一想就不可能,这库发展这么多年,还有人用这库做控制台版本的游戏。
那utf8支持是必然的,连和用户交互的信息提示都不支持国际化,那还玩啥啊。

公司里的同事都没玩过ncurses, 都是从头找资料来学。
网上有一篇NCURSES编程入门.pdf, 刚开始学时,是要先看下这个文档,知道api的大致用法。
但是这个文档只讲api的用法,没有讲库编译,代码页,国际化的问题。
可能的中文乱码原因:
* 使用了不支持utf8的库版本引起的问题。
* 库连接错了
* 也有可能连接的库和头文件不对应.
总之,有问题时,就要看官方文档+官方例子+自己实验,自己去找到底问题出哪了。在出现问题的情况下,再抱着旧的或第三方做的公开资料看,对排错就没啥效果了。

当前最新的版本时ncurses-6.1
遇到问题时,还是要看官方文档(README, INSTALL), 然后做些实验试一试。
具体api的用法,可能由于版本的不同,也要去看随源码发布的例子工程.

实验

解开源码包

root@localhost:/home/test# pwd
/home/test
root@localhost:/home/test# tar -xzvf ./ncurses-6.1.tar.gz
root@localhost:/home/test# ls
ncurses-6.1  ncurses-6.1.tar.gz
root@localhost:/home/test# cd ncurses-6.1/

编译源码工程

先看README,知道要去看INSTALL
在README中,作者特意提到:
* 要有配置选项 –enable-widec 才支持宽字符文本
* 为了防止库连接库,影响到自己的程序, 或覆盖已有的不提供版本的库,影响到其他人的程序(如果系统中已经安装了其他版本的ncurses库), 可以指定配置 –enable-reentrant 生成不同的库名称
在INSTALL中看到:
* 为了使用宽字符文本,要包含的头文件也不同

the normal ncurses
    header would be included using

        #include <ncurses/curses.h>
        #include <ncurses/term.h>

    while the ncursesw headers would be found this way:

        #include <ncursesw/curses.h>
        #include <ncursesw/term.h>

看了官方文档,就大概知道中文乱码的原因了.

确定配置选项

看INSTALL中, configure的配置选项很多, –enable-widec –enable-reentrant 肯定要选, 其他的看INSTALL中的说明,适当选一下。
查看可用的配置选项

./configure --help
...
--enable-widec          compile with wide-char/UTF-8 code
--enable-reentrant      compile with reentrant code
./configure --prefix=/home/ncurses-6.1-build --enable-widec --enable-reentrant
...
** Configuration summary for NCURSES 6.1 20180127:

       extended funcs: yes
       xterm terminfo: xterm-new

        bin directory: /home/ncurses-6.1-build/bin
        lib directory: /home/ncurses-6.1-build/lib
    include directory: /home/ncurses-6.1-build/include/ncursestw
        man directory: /home/ncurses-6.1-build/share/man
   terminfo directory: /home/ncurses-6.1-build/share/terminfo

** Include-directory is not in a standard location

编译工程

make
...
compiling demo (obj_s)
/usr/bin/g++  -o demo ../objects/demo.o -L../lib -lncurses++tw -L../lib -lformtw -lmenutw -lpaneltw -lncursestw    -lutil    -DHAVE_CONFIG_H -I../c++ -I. -I../include  -D_GNU_SOURCE -D_DEFAULT_SOURCE -DNDEBUG -O2 
make[1]: Leaving directory '/home/test/ncurses-6.1/c++'
root@localhost:/home/test/ncurses-6.1# 

可以看到编译出来的库为命名为xtw, 也能看到要编译一个宽字符版的ncurses工程,要连接的库为 -lncurses++tw -L../lib -lformtw -lmenutw -lpaneltw -lncursestw -lutil

按照说明,运行测试程序./test/x, 看看能否运行
看到报错信息:

root@localhost:/home/test/ncurses-6.1/test# ./background 
Error opening terminal: xterm.

去查INSTALL中关于xterm的配置选项, 需要加入–without-xterm-new
重新配置,编译,测试

./configure --prefix=/home/ncurses-6.1-build --enable-widec --enable-reentrant --without-xterm-new
...
** Configuration summary for NCURSES 6.1 20180127:

       extended funcs: yes
       xterm terminfo: xterm-old

        bin directory: /home/ncurses-6.1-build/bin
        lib directory: /home/ncurses-6.1-build/lib
    include directory: /home/ncurses-6.1-build/include/ncursestw
        man directory: /home/ncurses-6.1-build/share/man
   terminfo directory: /home/ncurses-6.1-build/share/terminfo

** Include-directory is not in a standard location
可以看到现在使用旧版xterm

make
...
compiling demo (obj_s)
/usr/bin/g++  -o demo ../objects/demo.o -L../lib -lncurses++tw -L../lib -lformtw -lmenutw -lpaneltw -lncursestw    -lutil    -DHAVE_CONFIG_H -I../c++ -I. -I../include  -D_GNU_SOURCE -D_DEFAULT_SOURCE -DNDEBUG -O2 
make[1]: Leaving directory '/home/test/ncurses-6.1/c++'

测试
cd ./test
root@localhost:/home/test/ncurses-6.1/test# ./background 
Error opening terminal: xterm.
root@localhost:/home/test/ncurses-6.1/test# ./lrtest 
Error opening terminal: xterm.
root@localhost:/home/test/ncurses-6.1/test# aptitude install xterm
看来不是xterm版本问题,而是我实验的系统中没有装xterm。

root@localhost:/home/test/ncurses-6.1/test# aptitude install xterm
root@localhost:/home/test/ncurses-6.1/test# apt-get install xterm
Reading package lists... Done
Building dependency tree       
Reading state information... Done
xterm is already the newest version.

root@localhost:/home/test/ncurses-6.1/test# xterm 
Warning: This program is an suid-root program or is being run by the root user.
The full text of the error or warning message cannot be safely formatted
in this environment. You may get a more descriptive message by running the
program as a non-root user or by removing the suid bit on the executable.
xterm: Xt error: Can't open display: %s
xterm: DISPLAY is not set

Is X11 forwarding enabled in your sshd config?

grep -i x11 /etc/ssh/sshd_config
You should have:

X11Forwarding yes

on debian
/etc/init.d/ssh restart

用putty选上x11选项,再去连接,也打不开xtrm.

最后做了一个实验,在xwindow桌面上右击打开控制台,在这个控制台上运行库自带的编译好的程序,是正常运行的。
然后,按ALT+CTRL+F1, 从xwindow桌面切到本地控制台, 这时运行库自带的编译好的程序,也是打不带xterm.

我又用xterm直接做了测试。
按下ALT+CTRL+F7, 从本地控制台切回xwindow, 在xwindow中的shell窗口运行xterm,是正常的。
再按下ALT+CTRL+F1,从xwindow桌面切到本地控制台, 这时,运行xterm是不行的,报错信息一样。

这说明xterm只能在xwindow下用,这个加入xterm支持的版本真坑人。
为了能使用此库来显示utf8文本信息,需要从2018年1月的这个最新版本往前找一个稳定的发布版,在那个版本上编译utf-8支持和实验.

用配置 ./configure –prefix=/home/my-ncurses-build –enable-widec –enable-reentrant
退到5.8还不行(还报xterm打不开),醉了。
突然想到,先make install 然后再实验测试程序的效果,这下可以了。INSTALL写的不靠谱啊.
人家6.1版本就是可以的 :)

先用库自带的测试程序看看中文(宽字符)是否能显示输入正常, 结果是可以。
测试程序目录为 ./test/, 测试宽字符的程序如下
./test/inch_wide
./test/ins_wide
./form_driver_w

想写个测试程序,看看panel是否显示中文正常.这是本次实验的目的.
我想一定也是正常的吧? 要不就不科学了:)

测试程序

测试过了,在panel中也能正常显示中文

测试的效果

这里写图片描述

测试工程下载

src_test_ncurses_panel.7z
实验环境:debian8.8 + ncurses-6.1
实验目的:验证ncurses-6.1的编译选项支持宽字符后,工程中可以使用utf8版的中文字符串.

测试代码

// @file main.cpp
// @note test chinise text ncurses panel, see if display ok?

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>

#include "ncursestw/panel.h"

#define NLINES 10
#define NCOLS 40

void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);

int main()
{   WINDOW *my_wins[3];
    PANEL  *my_panels[3];
    PANEL  *top;
    int ch;

    // 系统的代码页描述在 /etc/default/locale
    // 查看系统的代码页 
    // root@localhost:/home/dev/test_ncurses_panel# cat /etc/default/locale
    // #  File generated by update-locale
    // LANG="en_US.UTF-8"

    // setlocale(LC_ALL, ""); // 为了显示中文, 必须有这句设置代码页, 没填写就按/etc/default/locale走
    setlocale(LC_ALL, "en_US.UTF-8"); // 也可以自己填写代码页, 如果填的不对, 会显示乱码
    // setlocale(LC_ALL, "chs"); // 这个就乱码, 因为本实现的编码是utf8

    /* Initialize curses */
    initscr();
    start_color();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);

    /* Initialize all the colors */
    init_pair(1, COLOR_RED, COLOR_BLACK);
    init_pair(2, COLOR_GREEN, COLOR_BLACK);
    init_pair(3, COLOR_BLUE, COLOR_BLACK);
    init_pair(4, COLOR_CYAN, COLOR_BLACK);

    init_wins(my_wins, 3);

    /* Attach a panel to each window */     /* Order is bottom up */
    my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */
    my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1 */
    my_panels[2] = new_panel(my_wins[2]);   /* Push 2, order: stdscr-0-1-2 */

    /* Set up the user pointers to the next panel */
    set_panel_userptr(my_panels[0], my_panels[1]);
    set_panel_userptr(my_panels[1], my_panels[2]);
    set_panel_userptr(my_panels[2], my_panels[0]);

    /* Update the stacking order. 2nd panel will be on top */
    update_panels();

    /* Show it on the screen */
    attron(COLOR_PAIR(4));

    // LINES 代表console的底部行数, 库自带的宏
    mvprintw(LINES - 1, 0, "使用TAB键切换面板, F1键退出程序");
    attroff(COLOR_PAIR(4));
    doupdate();

    top = my_panels[2];
    while((ch = getch()) != KEY_F(1))
    {   switch(ch)
        {   case 9:
                top = (PANEL *)panel_userptr(top);
                top_panel(top);
                break;
        }
        update_panels();
        doupdate();
    }
    endwin();
    return 0;
}

/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{   int x, y, i;
    char label[80];

    y = 2;
    x = 10;
    for(i = 0; i < n; ++i)
    {   wins[i] = newwin(NLINES, NCOLS, y, x);
        sprintf(label, "窗口号码 【%d】", i + 1);
        win_show(wins[i], label, i + 1);
        y += 3;
        x += 7;
    }
}

/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{   
    int startx = 0;
    int starty = 0;
    int height = 0;
    int width = 0;

    getbegyx(win, starty, startx);
    getmaxyx(win, height, width);

    box(win, 0, 0);
    mvwaddch(win, 2, 0, ACS_LTEE); 
    mvwhline(win, 2, 1, ACS_HLINE, width - 2); 
    mvwaddch(win, 2, width - 1, ACS_RTEE); 

    print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
}

void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{   int length, x, y;
    float temp;

    if(win == NULL)
        win = stdscr;
    getyx(win, y, x);
    if(startx != 0)
        x = startx;
    if(starty != 0)
        y = starty;
    if(width == 0)
        width = 80;

    length = strlen(string);
    temp = (width - length)/ 2;
    x = startx + (int)temp;
    wattron(win, color);
    mvwprintw(win, y, x, "%s", string);
    wattroff(win, color);
    refresh();
}
# ==============================================================================
# makefile
#   command list:
#       make rebuild
# ==============================================================================

CC = g++

#   -Werror
CFLAGS = --std=c++11 \
    -Wall \
    -g

BIN = test_ncurses_panel

# ./configure --prefix=/home/ncurses-6.1-build --enable-widec --enable-reentrant
INC_PATH = -I./ \
    -I/home/ncurses-6.1-build/include/ \
    -I/home/ncurses-6.1-build/include/ncursestw/

LIBPATH = -L/home/ncurses-6.1-build/lib/

LIBS = -lstdc++ \
    -pthread \
    -lncurses++tw -lformtw -lmenutw -lpaneltw -lncursestw \

DEPEND_CODE_DIR = ./empty_dir

DEPEND_CODE_SRC = $(shell find $(DEPEND_CODE_DIR) -name '*.cpp')
DEPEND_CODE_OBJ = $(DEPEND_CODE_SRC:.cpp=.o)

ROOT_CODE_SRC = $(wildcard *.cpp)
ROOT_CODE_OBJ = $(ROOT_CODE_SRC:.cpp=.o)

SUB_CODE_DIR = ./empty_dir/

SUB_CODE_SRC = $(shell find $(SUB_CODE_DIR) -name '*.cpp')
SUB_CODE_OBJ = $(SUB_CODE_SRC:.cpp=.o)

all:$(BIN)
    @echo Makefile all...

    @echo **==============================================================================
    if [ -f $(BIN) ] ; \
    then \
        cp $(BIN) ../bin/ ; \
        echo "build ok :)" ; \
    else \
        echo "build failed :(" ; \
    fi;
    @echo **==============================================================================

$(BIN): $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ)
    ${CC} ${CFLAGS} ${INC_PATH} \
    $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ) \
    ${LIBS} ${LIBPATH} \
    -o ${BIN} \

.cpp.o:
    @echo $<
    @echo build $^ ...
    ${CC}  ${CFLAGS} ${INC_PATH} -c $^ -o $@

help:
    clear
    @echo make help
    @echo   make rebuild

clean:
    clear
    @echo ================================================================================
    rm -f $(BIN)
    @echo ================================================================================
    rm -f $(ROOT_CODE_OBJ)
    @echo ================================================================================
    rm -f $(DEPEND_CODE_OBJ)
    @echo ================================================================================
    rm -f $(SUB_CODE_OBJ)
    @echo ================================================================================

rebuild:
    make clean
    make all

rebuild_and_run:
    make rebuild
    ./$(BIN)

猜你喜欢

转载自blog.csdn.net/LostSpeed/article/details/82184429
6.1