• C语言与VT100控制码编程



    C语言与VT100控制码编程 声明:
    1. 如果您打算阅读本文,希望您已经了解过C语言的基本语法,本文不对C语言的基本语法进行说明,因为那些东西几乎唾手可得; 2. 本文在vim中编辑,请尽量是用vim进行阅读,因为有不对齐的现象; 3. 本人强烈建议您先编译,运行本文最后提供的sinDemo源代码,再看本文的正文,因为您看了运行效果,您就知道本人为什么要写这篇文章; \\\\\\--*目录*--/////////// | 一. 需求背景 | | 二. VT100控制码是什么 | | 三. sin函数动态图sinDemo示例 | \\\\\\\\//////////////// 一. 需求背景 以前很长时间里,打开Ubuntu的终端,使用会产生动态效果的shell命令(如top)让我觉得不可思议,于是很多时候也希望自己的程序也能那样动起来,但由于自己的知识面的原因,不知道的东西太多: 1. 如何改变字符输出的位置? 2. 如何改变前景色,背景色? 3. 最重要的是,查资料时用什么关键字查也不知道? 当然,也许有人会说,去问人,可问题是我也不知道怎么去描述我的需求,更不知道谁知道这玩意.那时候觉这是一件挺难的事,于是我开始凭着感觉是用不同的关键字百度,最后是通过python找到tput,然后通过tput找到VT100码,因为时间已经过去挺久了,具体的经过也基本上忘记了. :) 在使用了一段时间的VT100码以后,我发现,我们学C语言的时候,就应该学会配合使用VT100码,因为这样你可以在黑白的终端世界里做出很酷的东西,比如俄罗斯方块,贪吃蛇等等,还有其他的一些经典的动画效果,个人觉得早期开发游戏的那些人,也许就是这么干的,本文的Demo提供了一个生成sin函数的动画效果.
    本人也使用VT100码实现了在终端绘制方框,填充方框,使用不同的字符绘制直线,等等内容,并且把这些做成了一个库供自己使用.其实shell命令里的tput也是这么做,Ncurses的底层也是这么干,只不过貌似他们做得比我好,考虑得更周到. :) 二. VT100控制码是什么 VT100是一个终端类型定义,VT100控制码是用来在终端扩展显示的代码.比如在终端上任意坐标用不同的颜色显示字符.所有的控制符是
    '33'(033是八进制的数,十进制对应的是27,即ESC的ASCII码,如果需要查看,可以使用shell命令:man ascii)开头.用输出字符语句来输出,在C程序中用printf来输出VT100的控制字符. 1. VT100 控制码归类如下。 033[0m 取消之前所有属性 033[1m 设置高亮度 033[4m 下划线 033[5m 闪烁 033[7m 反显 033[8m 消隐 033[30m -- 033[37m 设置前景色 |------------+ 033[40m -- 033[47m 设置背景色 |------------+ 033[nA 光标上移 n 行 | 033[nB 光标下移 n 行 | 033[nC 光标右移 n 行 | 033[nD 光标左移 n 行 | 033[y;xH 设置光标位置 | 033[2J 清屏 | 033[K 清除从光标到行尾的内容 | 033[s 保存光标位置 | 033[u 恢复光标位置 | 033[?25l 隐藏光标 | 033[?25h 显示光标 | V +------<----------------------------<--+ | V 2. VT100 的颜色输出分为,前景色和背景色可以分别输出,如果不需要之前所有的设置可以用033[0m取消。 1. 字背景颜色范围:40----49 40:黑 41:深红 42:绿 43:黄色 44:蓝色 45:紫色 46:深绿 47:白色 2. 字前景颜色范围:30----39 30:黑 31:红 32:绿 33:黄 34:蓝色 35:紫色 36:深绿 37:白色 3. 输出一个字符串( something here )有前景色和背景色代码如下: printf("33[41;36m something here 33[0m"); 三. sin函数动态图sinDemo示例 1. 示例终端输出一屏图像: | SinDemo | 0123456789012345678901234567890123456789 0000 --------------------+------@--------------> Y 0001 |----------* 0002 |---------------* 0003 |------------------* 0004 |------------------* 0005 |------------------* 0006 |---------------* 0007 |----------* 0008 |-----* 0009 * 0010 *-----| 0011 *----------| 0012 *---------------| 0013 *------------------| 0014 *------------------| 0015 *------------------| 0016 *---------------| 0017 *----------| 0018 *-----| 0019 * 0020 V X 2. sinDemo源代码:
      1 /******************************************************************
      2  *                        sinDemo
      3  *
      4  *   1. 本Demo主要目标是为了实现在终端下实现sin函数的动态效果;
      5  *   2. 本Demo之前是使用shell tput和C语言实现的,这次将其改为
      6  *       C语言+VT100控制码的形式;
      7  *
      8  *                              2015-3-27 阴 深圳  曾剑峰
      9  *
     10  *****************************************************************/
     11 #include <stdio.h>
     12 #include <math.h>
     13 #include <unistd.h>
     14 
     15 /**
     16  * 定义圆周率的值
     17  */
     18 #define PI  3.14
     19 /**
     20  * 本Demo中假设sin曲线周期为20,幅值也是20,幅值分正负幅值,
     21  * 所以后面的很多地方有SIN_AMPLITUDE*2,也就是Y轴方向上的值.
     22  */
     23 #define SIN_AMPLITUDE  20      
     24 /**
     25  * 定义每次刷新图形时间间隔为100ms
     26  */
     27 #define DELAY_TIME  100000  
     28 /**
     29  * 定义圆的一周角度为360度
     30  */
     31 #define TRIANGLE  360.0   
     32 /**
     33  * 输出的时候,数字行放在哪一行,也就是输出图形中的这行数字:
     34  *   0123456789012345678901234567890123456789
     35  * 本Demo中把上面这行数字放在界面的第3行
     36  */
     37 #define Y_NUMBER_BEGIN_LINE  3
     38 /**
     39  * 在本Demo中,图形就在上面数字行的下一行,也就是输出图形中如下面的内容:
     40  *     0000    --------------------@--------------------> Y
     41  *     0001                        |-----*             
     42  *     0002                        |----------*        
     43  *     0003                        |---------------*   
     44  *     0004                        |------------------*
     45  *     0005                        |------------------*
     46  *     0006                        |------------------*
     47  *     0007                        |---------------*   
     48  *     0008                        |----------*        
     49  *     0009                        |-----*             
     50  *     0010                        *                   
     51  *     0011                  *-----|                   
     52  *     0012             *----------|                   
     53  *     0013        *---------------|                   
     54  *     0014     *------------------|                   
     55  *     0015     *------------------|                   
     56  *     0016     *------------------|                   
     57  *     0017        *---------------|                   
     58  *     0018             *----------|                   
     59  *     0019                  *-----|                   
     60  *     0020                        V X  
     61  */
     62 #define SIN_GRAPH_BEGIN_LINE (Y_NUMBER_BEGIN_LINE+1)
     63 
     64 int main(int argc, char* argv[]){
     65 
     66     /**
     67      * 局部变量说明:
     68      *     1. i                 : 主要用于循环计算;
     69      *     2. lineNumber        : 用于保存行号;
     70      *     3. offsetCenter      : 用于保存sin曲线上的点的相对于中心轴的偏移;
     71      *     4. nextInitAngle     : 保存下一屏要输出图形的初始角度制角度(如30度);
     72      *     5. currentInitAngle  : 当前一屏要输出的图形的初始角度制角度(如30度);
     73      *     6. currentInitradian : 当前一屏要输出的图形的初始弧度制弧度(如PI/6)
     74      *                            根据currentInitAngle换算而来,因为sin函数需要
     75      *                            角度制进行求值;
     76      *
     77      */
     78     int    i                   = 0;    
     79     int    lineNumber          = 0;    
     80     int    offsetCenter        = 0;    
     81     int    nextInitAngle       = 0;
     82     double currentInitAngle    = 0;    
     83     double currentInitradian   = 0;    
     84 
     85     //软件开始运行,清一次屏,保证屏幕上没有无关内容
     86     printf("33[2J");
     87         
     88     //输出标题,因为这个软件名字叫: SinDemo
     89     printf("33[1;1H                       | SinDemo |	");
     90 
     91     /**
     92      * 这里主要是完成那一行重复的0-9,SIN_AMPLITUDE*2是因为sin曲线的
     93      * 最高点和最低点是2倍的幅值
     94      */
     95     printf("33[%d;1H	", Y_NUMBER_BEGIN_LINE);
     96     for (i = 0; i < SIN_AMPLITUDE*2; i++) 
     97         printf("%d", i%10);
     98     printf("
    ");
     99 
    100     /**
    101      * while循环主要完成内容:
    102      *     1. 每次循环对局部变量重新初始化;
    103      *     2. 将下一屏图形的初始角度赋值给当前的图形初始角;              
    104      *     3. 将下一屏图形的初始角度加上间隔角度(TRIANGLE/SIN_AMPLITUDE),
    105      *        TRIANGLE/SIN_AMPLITUDE在本Demo中是360/20=18度,就相当于X轴 
    106      *        每格代表18度 
    107      *     2. 调整光标到固定的位置;
    108      *     3. 重新绘制整屏图形;
    109      */
    110     while(1){
    111 
    112         //重新初始化局部变量,因为每一屏图形都像一个新的开始
    113         i                  = 0;        
    114         offsetCenter       = 0;       
    115         lineNumber         = 0;      
    116         currentInitradian  = 0;     
    117 
    118         //从nextInitAngle中获取当前的初始化角度
    119         currentInitAngle   =  nextInitAngle;
    120 
    121         //为下一次循环提供下一次的初始化角度
    122         nextInitAngle     += TRIANGLE/SIN_AMPLITUDE;  
    123 
    124         //将光标移动到开始绘图的位置去
    125         printf("33[%d;1H", SIN_GRAPH_BEGIN_LINE);
    126 
    127         /**
    128          * 根据不同的情况绘制图形, 每一次循环,就是绘制了图形中的一行
    129          */
    130         while(1){
    131             //判断是不是最后一行,lineNumber起始行是从0开始
    132             if(lineNumber == SIN_AMPLITUDE){
    133                 //打印最后一行前面的数字行号
    134                 printf("33[%d;1H%04d	", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber);
    135                 for (i = 0; i < SIN_AMPLITUDE*2; i++)
    136                     /**
    137                      * 判断是否到达中间位置,因为中间位置要放V的箭头,同时在旁边输出一个X,
    138                      * 代表这是X轴方向.
    139                      */
    140                     i == SIN_AMPLITUDE ? printf("V X") : printf(" ");    
    141                 break;
    142             }
    143 
    144 
    145             /**
    146              * 对currentInitAngle角度进行修整,比如370度和10度是对应相同的sin值
    147              * 其实这一步可以不用,但是这里保留了,后面是将currentInitAngle角度制的值
    148              * 换算成对应的弧度制的值,便于sin求值.
    149              */
    150             currentInitAngle = ((int)currentInitAngle)%((int)TRIANGLE);
    151             currentInitradian = currentInitAngle/(TRIANGLE/2)*PI;    
    152 
    153             /**
    154              * 算出当前次currentInitradian对应的sin值,并乘以幅值SIN_AMPLITUDE,获取sin曲线
    155              * 在Y轴上相对于中心轴的偏移offsetCenter,offsetCenter可能是正值,也可能是负值,
    156              * 因为中心轴在中间.
    157              */
    158             offsetCenter = (int)(sin(currentInitradian)*SIN_AMPLITUDE);                
    159 
    160             /**
    161              * 在正确的地方输出正确的行号   :)
    162              */
    163             printf("33[%d;1H%04d", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber);
    164 
    165             //用一个制表符,给出行号与图形的空间距离
    166             printf("	");
    167 
    168             /**
    169              * 第一行,和其他的行不一样,有区别,输出结果如下:
    170              * 0000    ------------@-------+--------------------> Y
    171              */
    172             if(lineNumber == 0){
    173                 for (i = 0; i < SIN_AMPLITUDE*2; i++){
    174                     /**
    175                      * 判断当前输出的字符位置是否是X,Y轴交叉的位置,如果是就输出'+',
    176                      * 不是就输出'-'
    177                      */
    178                     i == SIN_AMPLITUDE ? printf("+") : printf("-");
    179                     /**
    180                      * 判断当前输出的字符位置是否是sin曲线上的点对应的位置,
    181                      * 如果是就输出'@'
    182                      */
    183                     if(i == offsetCenter+SIN_AMPLITUDE)
    184                         printf("@");
    185                 }
    186                 //代表这个方向是Y轴
    187                 printf("-> Y
    ");
    188             } else { 
    189                 for (i = 0; i < SIN_AMPLITUDE*2; i++){
    190                     //判断当前输出的字符位置是否是sin曲线上的点对应的位置,如果是就输出'*'
    191                     if(i == (offsetCenter+SIN_AMPLITUDE)){
    192                         printf("*");
    193                     //判断当前输出的字符位置是否是X轴上对应的位置,如果是就输出'|'
    194                     }else if(i == SIN_AMPLITUDE){
    195                         printf("|");
    196                     }else{
    197                         /**
    198                          * 这里主要是要处理一行里面除了画'*'、'|'、之外的'-'、' '
    199                          * 其中的SIN_AMPLITUDE到SIN_AMPLITUDE+offsetCenter正好就是需要输出'-'的地方
    200                          * 其他的地方输出' '
    201                          */
    202                         (((i > SIN_AMPLITUDE) && (i < SIN_AMPLITUDE+offsetCenter)) || 
    203                             ((i < SIN_AMPLITUDE) && (i > SIN_AMPLITUDE+offsetCenter))) 
    204                                 ? printf("-") : printf(" ");
    205                     }
    206                     //行尾,输出换行符
    207                     if(i == (SIN_AMPLITUDE*2-1)) 
    208                         printf("
    ");
    209                 }
    210             }
    211 
    212             /**
    213              * 一行输出完成,为下一行输出作准备,下一行比上一行在角度上多加TRIANGLE/SIN_AMPLITUDE,
    214              * 在本Demo中相当于360/20=18,也就是加18度.
    215              */
    216             currentInitAngle += TRIANGLE/SIN_AMPLITUDE;      
    217 
    218             //行号加1
    219             lineNumber++;                                    
    220         }
    221         /**
    222          * 一屏图像输出完毕,最后输出一个换行符,并且延时一段时间再开始绘制下一屏图形
    223          */
    224         printf("
    ");
    225         usleep(DELAY_TIME);
    226     }
    227 
    228     return 0;
    229 }
  • 相关阅读:
    上传本地项目到git服务器
    linux服务器部署web环境(一)
    nginx+tomcat负载集群部署
    selenium学习历程(二)
    selenium学习历程(一)
    在 Acer p236 上装 win7 和 ubuntu 双系统
    Ubuntu (14.04) 如何安装和配置Qt
    Android 蓝牙的常用操作
    OpenCL: Shared memory between CPU and GPU in Android development of Qaulcomm plateform
    Android 系统支持的编解码器
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4373564.html
Copyright © 2020-2023  润新知