今天心情不错分析下这个玩具! sl比较有趣,当手快按错文件查看命令顺序时,我经常会被突然跑出来的小火车给逗乐。给我个sl我可以玩一天。我们接下来就看看这个开源小玩意是怎么做的。
项目地址: https://github.com/mtoyoda/sl
作者:Toyoda Masashi (mtoyoda@acm.org)
SL (Steam Locomotive)是一个基于终端运行的小工具。当你想输入ls
时,却误输入成sl
,就会运行。只是一个玩笑,然并没什么卵用 --Toyoda Masashi(作者)。
安装(以Ubuntu为例):
apt install sl
让我们看看运行效果:
sltrain.gif
这是一个C语言开发的小工程,那么我们从哪里开始呢?
按照正常的分析流程,我们应该做的是:
- 找到文件的依赖关系
- 从程序入口(main)开始分析
1.明确文件依赖关系
为了找到明确的依赖关系,让我们先看看这个工程的makefile:
CC=gcc
CFLAGS=-O -Wall
all: sl
sl: sl.c sl.h
$(CC) $(CFLAGS) -o sl sl.c -lncurses
clean:
rm -f sl
distclean: clean
这个makefile非常简单(不熟悉makefile语法的同学自行补脑推荐陈浩老师的教程下载-【跟我一起写Makefile-陈皓】)
只有两个文件sl.c 和 sl.h,并且依赖于ncurses库,这个库中提供了字符终端处理的一些方法。
内容如此简单,我们继续看,在看程序入口之前,我们先看看这个sl.h文件里面是什么东西❓
竟然是满脸的小火车零件...
∩_∩哈!原来这个神奇了小火车就是,这些定义的宏(火车零件),组装出来的!
贴出来看看:
/* 火车神兽头 */
#define LOGO1 " ++ +------ "
#define LOGO2 " || |+-+ | "
#define LOGO3 " /---------|| | | "
#define LOGO4 " + ======== +-+ | "
#define LWHL11 " _|--O========O~\\-+ "
#define LWHL12 "//// \\_/ \\_/ "
2.分析主程序入口
nt main(int argc, char *argv[])
{
int x, i;
for (i = 1; i < argc; ++i) {
if (*argv[i] == '-') {
option(argv[i] + 1);
}
}
initscr(); /*curses模式初始化方法*/
signal(SIGINT, SIG_IGN); /*忽略键盘中断*/
noecho(); /*关闭字符回显*/
curs_set(0); /*设置光标不可见 0 不可见;1 普通模式;2 可见*/
nodelay(stdscr, TRUE); /*终端设置为非阻塞式*/
leaveok(stdscr, TRUE); /*不使用光标,节省移动光标的时间*/
scrollok(stdscr, FALSE); /*不允许窗口滚屏*/
for (x = COLS - 1; ; --x) {
if (LOGO == 1) {
if (add_sl(x) == ERR) break;
}
else if (C51 == 1) {
if (add_C51(x) == ERR) break;
}
else {
if (add_D51(x) == ERR) break;
}
getch();
refresh();
usleep(40000);
}
mvcur(0, COLS - 1, LINES - 1, 0);
endwin();
return 0;
}
我们一行行的看,5 - 9行是个for循环,从这个option方法中可以看出,是为了接收不同的参数时,进行不同的响应。有4种小火车图案,分别对应-a
、-F
、-l
、-c
四种参数。10 - 16行设置初始化状态,包括了光标滚动和中断状态等。那18行的COLS是个什么👻?不用找了这个是终端中的一个常量,表示行宽,字符数。那行高是什么❓是的,没错就是32行的LINES。 18 - 33行就是将字符串显示到终端中。并且40ms刷新一次。
作者写了这几个方法来分别写不同样式,和不同的组件,这里不展开,方法类似,逻辑相同。
void add_smoke(int y, int x); /*火车上的烟*/
void add_man(int y, int x); /*这是火车中呼救的小人*/
int add_C51(int x);
int add_D51(int x); /*不同样式*/
int add_sl(int x);
最终调用的写字符的方法是这样的,是经过组装过的mvaddch(int, int, char *)
方法。
int my_mvaddstr(int y, int x, char *str)
{
for ( ; x < 0; ++x, ++str)
if (*str == '\0') return ERR;
for ( ; *str != '\0'; ++str, ++x)
if (mvaddch(y, x, *str) == ERR) return ERR;
return OK;
}
这位作者的语法不是很规范(不推荐的哈),不过思路还是很清晰,先校验,然后逐个遍历对应位置显示。
是不是很简单,逻辑确实很简单,复杂的是字符位置计算。当然作者没有在字符中加任何特效,感兴趣的同学可以看看ncurses这个类的用法,可以增加颜色、闪烁等一些特效。还记得当初看见一个在终端中显示初音舞蹈的视频,大概也是这种实现思路吧。笔者抽空在终端中写了汉诺塔和贪吃蛇小游戏。欢迎大家交流探讨。
网友评论