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)


2018-10-27

编译好程序后,换一台计算机运行,会有报错如下:

root@localhost:/usr/local/bin# ./test_ncurses
Error opening terminal: xterm.

这时,应该在xshell登陆后,输入如下语句,设置环境变量后,test_ncurses运行正常.

export TERM=vt100
export TERMINFO=/usr/share/terminfo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值