美文网首页
sl小火车开源项目分析

sl小火车开源项目分析

作者: 知止乎尔 | 来源:发表于2018-08-06 21:43 被阅读0次

    今天心情不错分析下这个玩具! 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这个类的用法,可以增加颜色、闪烁等一些特效。还记得当初看见一个在终端中显示初音舞蹈的视频,大概也是这种实现思路吧。笔者抽空在终端中写了汉诺塔和贪吃蛇小游戏。欢迎大家交流探讨。

    相关文章

      网友评论

          本文标题:sl小火车开源项目分析

          本文链接:https://www.haomeiwen.com/subject/oxknvftx.html