7

在 ncurses 中使用 readline

 3 years ago
source link: https://blog.lilydjwg.me/2011/7/31/using-readline-in-ncurses.28431.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在 ncurses 中使用 readline

本文来自依云's Blog,转载请注明。

有一天,我发现了一个很好用的 Python shell——bpython。它使用了 ncurses 来做界面,使用了 pygments 来高亮代码,怎么看都比 ipython 漂亮,更不用说 Python 自己的了。不过既然它使用了 ncurses,麻烦也就来了——ncurses 不支持 readline!虽然有些模拟,但终究是不好用,M-f M-b不起使用,M-数字也不能用。于是我再次去 google 同时使用 ncurses 和 readline 这两个库的办法。

功夫不负有心人,这次终于 google 到了点有用的东西

The basic idea is to use call rl_callback_read_char() when input from the user is available (determined with select, or similar), then print 'rl_line_buffer' as you would any other string in ncurses, and optionally set A_REVERSE on the position indicated by rl_point. (or just reposition the cursor I guess, either works...)

不过可惜的是,这封邮件给出的代码在我这里并没有跑起来。其实跑起来了也用处不大,因为我需要的那部分代码独立性太差了,还是得重写。

花了一个下午,我终于弄出了一个雏形。

首先,这个rl_callback_read_char()是这么用的(文档):

#include<stdio.h>
#include<readline/readline.h>
int main(void){
int cont = 1;
void callback(char *text){
if(text == NULL){
rl_callback_handler_remove();
putchar('\n');
cont = 0;
}else{
printf("%s.\n", text);
}
}
rl_callback_handler_install(">> ", callback);
while(cont){
rl_callback_read_char();
}
return 0;
}

首先安装个回调函数,它将在 readline 读取到一行内容时调用。当标准输入可用的时候,调用rl_callback_read_char()来读取字符。另外注意,这里我用了 gcc 的嵌套函数支持,免得弄出不少全局变量。

知道怎么用rl_callback_read_char()之后,就可以按那封邮件所说的,把它和 ncurses 联合起来了。代码修改自NCURSES Programming HOWTO。思路很简单,readline 负责读取并处理用户输入,显示是自己处理的。不过作为中文用户,纠结了半天的中文问题。最开始是有了 ncurses 之后,中文显示异常。这个是通过setlocale解决的。然后又是光标放的位置不对。于是又用上了我同样不熟悉的 wchar,自己计算光标应该放在哪里。

#define _XOPEN_SOURCE 700       /* for wcswidth and 700 is for mbsnrtowcs */
#include<wchar.h>
#include<ncurses.h>       /* ncurses.h includes stdio.h */
#include<stdlib.h>
#include<string.h>
#include<readline/readline.h>
#include<locale.h>
int mygetstr(char *str, int y, int x){
WINDOW *win;
int size, col;
int ok = 0;
int width;
wchar_t wstr[80];
char *p;
getmaxyx(stdscr, size, col);
void getaline(char *s){
str = s;
rl_callback_handler_remove();
ok = 1;
}
rl_callback_handler_install("", getaline);
win = newwin(1, col-x, y, x);
while(1){
rl_callback_read_char();
if(ok)
break;
werase(win);
strncpy(str, rl_line_buffer, 80);
p = str;
/* how many column chars before cursor occupies? */
size = mbsnrtowcs(wstr, (const char**)&p, rl_point, 80, NULL);
width = wcswidth(wstr, size);
mvwprintw(win, 0, 0, "%s", str);
/* put the cursor at right column */
wmove(win, 0, width);
wrefresh(win);
}
delwin(win);
return 0;
}
int main(){
char mesg[] = "Enter a string: ";
char str[80];
int row, col;
setlocale(LC_ALL, "");        /* make ncurses handle Chinese correctly */
initscr();
getmaxyx(stdscr, row, col);
mvprintw(row / 2, (col - strlen(mesg)) / 2, "%s", mesg);
refresh();
mygetstr(str, row / 2, (col + strlen(mesg)) / 2);
mvprintw(LINES - 2, 0, "You Entered: %s", str);
getch();
endwin();
return 0;
}

注意:此代码只是演示用,缓冲区溢出什么的我都没处理。

终于搞定了 C 下结合两者的使用,接下来 Python 版思路是有了,但因为其标准库 readline 中没有提供rl_callback_read_char()函数,所以只能用 ctypes 了。下面是在 Python 里使用rl_callback_read_char()的示例,ncurses 部分我暂时不想折腾了。

#!/usr/bin/env python3
# vim:fileencoding=utf-8
import sys
import readline
import ctypes
import ctypes.util
rllib_path = ctypes.util.find_library('readline')
rllib = ctypes.CDLL(rllib_path)
def callback(s):
if s is None:
rllib.rl_callback_handler_remove()
sys.stdout.write('\n')
sys.exit()
elif not s:
pass
else:
print('%s.' % s.decode())
# 这样也可以
# print(readline.get_line_buffer())
cbfunc = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
rllib.rl_callback_handler_install.restype = None
rllib.rl_callback_handler_install(ctypes.c_char_p(b">> "), cbfunc(callback))
while True:
rllib.rl_callback_read_char()

2011年8月4日更新:今天终于完成了个 quick and dirty 的 Python 版:

发送到 Kindle

Category: Linux | Tags: C代码 ncurses python readline | Read Count: 11286

评论 (5)
万里 说:
9 年前

博主是做什么工作的? 这些文章看不懂。

alswl 说:
9 年前

我也好奇许久,依云仙子活跃在 vim / linux / python 各大社区,实乃热心同志,关于页面却没有什么实质性内容…

xiomaomi 说:
4 年前

博主你好,我想问下使用ncurses的getstr(),我输入的汉字,删除的话要按三下,该怎么办呢。

依云 说:
4 年前

是支持 UTF-8 的 ncurses 吗?(ncursesw)

xiomaomi 说:
4 年前

不是ncursesw吧,我是通过setlocale()来显示中文的。

[取消回复评论]

昵称 登录 E-mail: *
Web:
Twitter:
当有新评论通过 E-mail 通知我

loading captcha image...
(输入验证码)

or Ctrl+Enter


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK