【IMX6ULL项目】IMX6ULL上Linux系统实现产测工具框架(二)

简介: 【IMX6ULL项目】IMX6ULL上Linux系统实现产测工具框架(二)

续:【IMX6ULL项目】IMX6ULL上Linux系统实现产测工具框架(一):https://developer.aliyun.com/article/1532307

四、 文字系统:

*4.1 FontBitMap

在font_manager.h描述一个文字的位图, 定义字体位图结构体,用于存储字体的位图信息

// 定义字体位图结构体,用于存储字体的位图信息
typedef struct FontBitMap {
    int iLeftUpX; // 字体位图左上角的X坐标
    int iLeftUpY; // 字体位图左上角的Y坐标
    int iWidth;   // 字体位图的宽度
    int iRows;    // 字体位图的行数
    int iCurOriginX; // 当前字符的原点X坐标
    int iCurOriginY; // 当前字符的原点Y坐标
    int iNextOriginX; // 下一个字符的原点X坐标
    int iNextOriginY; // 下一个字符的原点Y坐标
    unsigned char *pucBuffer; // 指向字体位图数据的指针
} FontBitMap, *PFontBitMap; // FontBitMap是结构体类型,PFontBitMap是指向该结构体的指针类型
 

 Region

因为显示系统和文字系统都需要用到这个区域结构体,所以我们直接把它拿出来定义成一个公共的头文件common.h

 
// 定义矩形区域结构体
typedef struct Region {
  int iLeftUpX; // 区域左上角的X坐标
  int iLeftUpY; // 区域左上角的Y坐标
  int iWidth;   // 区域的宽度
  int iHeigh;   // 区域的高度
}Region, *PRegion; // 定义矩形区域及其指针类型
 

 

 

*4.2 FontOpr

在font_manager.h定义字体操作结构体,用于管理不同字体的操作函数

// 定义字体操作结构体,用于管理不同字体的操作函数
typedef struct FontOpr {
    char *name; // 字体操作的名称
    int (*FontInit)(char *aFineName); // 初始化字体的函数指针
    int (*SetFontSize)(int iFontSize); // 设置字体大小的函数指针
    int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap); // 获取字体位图的函数指针
    struct FontOpr *ptNext; // 指向下一个字体操作结构体的指针,用于链表管理
} FontOpr, *PFontOpr; // FontOpr是结构体类型,PFontOpr是指向该结构体的指针类型

*4.3 FontOpr g_tFreetypeOpr

在freetype.c中定义一个字体操作结构体,包含字体名字、字体初始化、设置字体大小、获取字体位图等函数指针

 
// 定义一个字体操作结构体,包含字体初始化、设置字体大小、获取字体位图等函数指针
static FontOpr g_tFreetypeOpr = {
  .name          = "freetype",
  .FontInit      = FreeTypeFontInit,
  .SetFontSize   = FreeTypeSetFontSize,
  .GetFontBitMap = FreeTypeGetFontBitMap,
};

4.4 FreeTypeFontInit(char *aFineName)

在freetype.c中初始化FreeType字体库

// 初始化FreeType字体库
static int FreeTypeFontInit(char *aFineName)
{
    FT_Library    library;
    int error;
 
    // 初始化FreeType库
    error = FT_Init_FreeType( &library );                 
  if (error)
  {
    printf("FT_Init_FreeType err\n");
    return -1;
  }
  
    // 从指定文件创建字体面
    error = FT_New_Face(library, aFineName, 0, &g_tFace ); 
  if (error)
  {
    printf("FT_New_Face err\n");
    return -1;
  }
 
    // 设置字体大小
    FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0);
 
  return 0;
}
 

4.5 FreeTypeSetFontSize(int iFontSize)

在freetype.c中设置字体大小

 
// 设置字体大小
static int FreeTypeSetFontSize(int iFontSize)
{
    // 设置字体大小
    FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0);
  return 0;
}

4.6 FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

在freetype.c中获取字体位图

// 获取字体位图
static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
  int error;
    FT_Vector pen;
    FT_Glyph  glyph;
    
 
    // 设置当前光标位置,单位为1/64像素
    pen.x = ptFontBitMap->iCurOriginX * 64; 
    pen.y = ptFontBitMap->iCurOriginY * 64; 
 
    // 设置变换矩阵
    FT_Set_Transform(g_tFace, 0, &pen);
 
    // 加载字符位图
    error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER);
  if (error)
  {
    printf("FT_Load_Char error\n");
    return -1;
  }
 
    // 获取位图缓冲区
  ptFontBitMap->pucBuffer = slot->bitmap.buffer;
  
    // 设置字体位图的区域信息
  ptFontBitMap->tRegion.iLeftUpX = slot->bitmap_left;
  ptFontBitMap->tRegion.iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top;
  ptFontBitMap->tRegion.iWidth   = slot->bitmap.width;
  ptFontBitMap->tRegion.iHeigh   = slot->bitmap.rows;
  ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64;
  ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY;
 
  return 0;
}
 

4.7 FreetypeRegister(void)

在freetype.c中注册FreeType字体操作结构体

 
// 注册FreeType字体操作结构体
void FreetypeRegister(void)
{
  RegisterFont(&g_tFreetypeOpr);
}

&& 4.8 字符管理器框架

我们可能要用到的字体有多种,那么怎么选择用哪个字符呢,所以我们要编写一个程序管理多种字符。

4.9 FontsRegister(void)

在font_manager.c注册字体操作结构体,这里示例了注册FreeType字体

// 注册字体操作结构体,这里示例了注册FreeType字体
void FontsRegister(void)
{
  extern void FreetypeRegister(void);
  FreetypeRegister();
}

4.10 SelectAndInitFont(char *aFontOprName, char *aFontFileName)

在font_manager.c选择并初始化一个字体操作结构体

 
// 选择并初始化一个字体操作结构体
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{
  PFontOpr ptTmp = g_ptFonts;
  while (ptTmp)
  {
    // 比较字体操作结构体的名称,找到匹配的结构体
    if (strcmp(ptTmp->name, aFontOprName) == 0)
      break;
    ptTmp = ptTmp->ptNext;
  }
 
  // 如果没有找到匹配的结构体,返回错误
  if (!ptTmp)
    return -1;
 
  // 设置默认的字体操作结构体
  g_ptDefaulFontOpr = ptTmp;
  // 调用结构体的初始化函数,初始化字体
  return ptTmp->FontInit(aFontFileName);
}
 

4.10 SetFontSize(int iFontSize)

在font_manager.c设置当前默认字体的字体大小

 
// 设置当前默认字体的字体大小
int SetFontSize(int iFontSize)
{
  return g_ptDefaulFontOpr->SetFontSize(iFontSize);
}

4.11 GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

在font_manager.c获取当前默认字体的字符位图

// 获取当前默认字体的字符位图
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
    return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap);
}

*4.12 DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)

在LCD上显示出来,在disp_manager.c中还要 实现一个绘制函数:DrawFontBitMap

void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
{
    // 定义并初始化各种变量
    int i, j, p, q;
  // 获取绘制区域的左上角X坐标
  int x = ptFontBitMap->tRegion.iLeftUpX;
  // 获取绘制区域的左上角Y坐标
  int y = ptFontBitMap->tRegion.iLeftUpY;
    // 计算绘制区域的右下角X坐标
    int x_max = x + ptFontBitMap->tRegion.iWidth;
    // 计算绘制区域的右下角Y坐标
    int y_max = y + ptFontBitMap->tRegion.iHeigh;
  // 获取区域宽度
  int width = ptFontBitMap->tRegion.iWidth;
  // 获取位图缓冲区
  unsigned char *buffer = ptFontBitMap->pucBuffer;
 
    // 遍历绘制区域中的每个像素
    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
        for ( i = x, p = 0; i < x_max; i++, p++ )
        {
            // 如果像素位置超出屏幕范围,忽略该像素
            if ( i < 0      || j < 0       ||
                i >= g_tDispBuff.iXres || j >= g_tDispBuff.iYres )
            continue;
 
            // 如果位图缓冲区中该像素点有数据(即需要绘制的点),则用指定颜色在屏幕上绘制该像素
            if (buffer[q * width + p])
              PutPixel(i, j, dwColor);
        }
    }
}

*4.13 测试代码:

 
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <font_manager.h>
#include <disp_manager.h>
 
#define FONTDATAMAX 4096
 
// 8x16 字体数据
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
    // 省略了点阵数据
};
 
// 在LCD指定位置上显示一个8*16的字符
void lcd_put_ascii(int x, int y, unsigned char c)
{
    unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
    int i, b;
    unsigned char byte;
 
    for (i = 0; i < 16; i++)
    {
        byte = dots[i];
        for (b = 7; b >= 0; b--)
        {
            if (byte & (1<<b))
            {
                // 显示像素点
                PutPixel(x+7-b, y+i, 0xffffff); // 白色
            }
            else
            {
                // 不显示像素点
                PutPixel(x+7-b, y+i, 0); // 黑色
            }
        }
    }
}
 
int main(int argc, char **argv)
{
    PDispBuff ptBuffer;
    int error;
 
    FontBitMap tFontBitMap;
    char *str= "Hello Linux";
    int i = 0;
    int lcd_x;
    int lcd_y;
    int font_size;
 
    // 检查输入参数个数
    if (argc != 5)
    {
        printf("Usage: %s <font_file> <lcd_x> <lcd_y> <font_size>\n", argv[0]);
        return -1;
    }
 
    lcd_x = strtol(argv[2], NULL, 0);
    lcd_y = strtol(argv[3], NULL, 0);
    font_size  = strtol(argv[4], NULL, 0);
 
    // 初始化显示设备
    DisplayInit();
    SelectDefaultDisplay("fb");
    InitDefaultDisplay();
    ptBuffer = GetDisplayBuffer();
 
    // 注册并初始化字体
    FontsRegister();
    error = SelectAndInitFont("freetype", argv[1]);
    if (error)
    {
        printf("SelectAndInitFont err\n");
        return -1;
    }
 
    // 设置字体大小
    SetFontSize(font_size);
 
    // 循环显示每个字符
    while (str[i])
    {
        // 获取字符位图
        tFontBitMap.iCurOriginX = lcd_x;
        tFontBitMap.iCurOriginY = lcd_y;
        error = GetFontBitMap(str[i], &tFontBitMap);
        if (error)
        {
            printf("SelectAndInitFont err\n");
            return -1;
        }
 
        // 绘制字符到缓冲区
        DrawFontBitMap(&tFontBitMap,0xff0000); // 红色
 
        // 刷新显示区域
        FlushDisplayRegion(&tFontBitMap.tRegion, ptBuffer);
 
        // 更新下一个字符的位置
        lcd_x = tFontBitMap.iNextOriginX;
        lcd_y = tFontBitMap.iNextOriginY;
        i++;
    }
 
    return 0;
}

五、UI系统:

       所谓UI,就是User Interface(用户界面),有图像界面(GUI)等,我们的UI系统,就是构造各类GUI元素,比如按钮(目前只实现按钮)

*5.1 Button

在ui.h定义Button结构体,包含按钮的名称、状态、区域、绘制函数和按下处理函数

 
// 定义两个函数指针类型,用于按钮的绘制和按下事件处理
typedef int (*ONDRAW_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff); // 绘制函数指针类型
typedef int (*ONPRESSED_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent); // 按下处理函数指针类型
 
 
// 定义Button结构体,包含按钮的名称、状态、区域、绘制函数和按下处理函数
typedef struct Button {
  char *name; // 按钮名称
  int status; // 按钮状态
  Region tRegion; // 按钮区域
  ONDRAW_FUNC OnDraw; // 绘制函数指针
  ONPRESSED_FUNC OnPressed; // 按下处理函数指针
}Button, *PButton; // Button是结构体类型,PButton是指向Button的指针类型

*5.2 InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)

在button.c初始化按钮的函数

// 初始化按钮的函数
void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
{
  ptButton->status = 0; // 初始化状态为0,即未按下
  ptButton->name = name; // 设置按钮名称
  ptButton->tRegion = *ptRegion; // 设置按钮区域
  ptButton->OnDraw = OnDraw ? OnDraw : DefaultOnDraw; // 设置绘制函数,如果未提供,则使用默认绘制函数
  ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed; // 设置按下处理函数,如果未提供,则使用默认处理函数
}

5.3 DrawRegion(PRegion ptRegion, unsigned int dwColor)

在disp_manager.c中将ptRegion这一部分区域绘制成dwColor的颜色

// 函数DrawRegion用于绘制一个指定颜色和区域的矩形
void DrawRegion(PRegion ptRegion, unsigned int dwColor)
{
  // 获取区域的左上角坐标
  int x = ptRegion->iLeftUpX;
  int y = ptRegion->iLeftUpY;
 
  // 获取区域的宽度和高度
  int width = ptRegion->iWidth;
  int heigh = ptRegion->iHeigh;
 
  // 使用两个嵌套的循环来遍历区域内的每一个像素
  int i, j;
  for (j = y; j < y + heigh; j++)
  {
    for (i = x; i < x + width; i++)
    {
      // 对于每个像素,调用PutPixel函数来设置像素的颜色
      PutPixel(i, j, dwColor);
    }
  }
}

5.4 DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)

在disp_manager.c中实现函数DrawTextInRegionCentral用于在指定区域内居中显示文本

// 函数DrawTextInRegionCentral用于在指定区域内居中显示文本
void DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)
{
  // 计算文本字符串的长度
  int n = strlen(name);
 
  // 计算每个字符的宽度,以便文本能够居中显示
  int iFontSize = ptRegion->iWidth / n / 2;
  FontBitMap tFontBitMap; // 字体位图结构体
 
  int iOriginX, iOriginY; // 字符绘制的起始坐标
  int i = 0; // 用于遍历字符串的索引
  int error; // 用于存储错误代码
 
  // 如果计算出的字体大小超过了区域的高度,则将字体大小设置为区域的高度
  if (iFontSize > ptRegion->iHeigh)
    iFontSize =  ptRegion->iHeigh;
 
  // 计算文本在区域内的起始X坐标,确保文本居中
  iOriginX = (ptRegion->iWidth - n * iFontSize)/2 + ptRegion->iLeftUpX;
  // 计算文本在区域内的起始Y坐标,确保文本居中
  iOriginY = (ptRegion->iHeigh - iFontSize)/2 + iFontSize + ptRegion->iLeftUpY;
 
  // 设置字体大小
  SetFontSize(iFontSize);
 
  // 遍历文本字符串中的每个字符
  while (name[i])
  {
    // 获取当前字符的字体位图
    tFontBitMap.iCurOriginX = iOriginX;
    tFontBitMap.iCurOriginY = iOriginY;
    error = GetFontBitMap(name[i], &tFontBitMap);
    if (error)
    {
      printf("SelectAndInitFont err\n");
      return;
    }
 
    // 在缓冲区上绘制当前字符的字体位图
    DrawFontBitMap(&tFontBitMap, dwColor);
 
    // 更新下一个字符的起始坐标
    iOriginX = tFontBitMap.iNextOriginX;
    iOriginY = tFontBitMap.iNextOriginY;
    i++;
  }
}

* 5.5 DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff)

在button.c默认的绘制按钮的函数

// 默认的绘制按钮的函数
static int DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff)
{
  /* 绘制按钮的底色 */
  DrawRegion(&ptButton->tRegion, BUTTON_DEFAULT_COLOR);
 
  /* 在按钮的中央绘制文本 */
  DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);
 
  /* 将绘制的区域刷新到显示设备上,比如LCD或网页 */
  FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);
 
    return 0; // 返回0表示成功
}

*5.6  DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)

在button.c默认的按钮按下处理函数

// 默认的按钮按下处理函数
static int DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{
  unsigned int dwColor = BUTTON_DEFAULT_COLOR; // 默认底色
  
  ptButton->status = !ptButton->status; // 切换按钮状态
  if (ptButton->status)
    dwColor = BUTTON_PRESSED_COLOR; // 如果按钮被按下,改变底色
 
  /* 绘制按钮的底色 */
  DrawRegion(&ptButton->tRegion, dwColor);
 
  /* 在按钮的中央绘制文本 */
  DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);
 
  /* 将绘制的区域刷新到显示设备上 */
  FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);
}
 

以上在ui.h中定义按钮的默认颜色、按下时的颜色和文本颜色

#define BUTTON_DEFAULT_COLOR 0xff0000 // 默认颜色,通常是红色
#define BUTTON_PRESSED_COLOR 0x00ff00 // 按下时的颜色,通常是绿色
#define BUTTON_TEXT_COLOR    0x000000 // 文本颜色,通常是黑色

5.7 测试代码:

// 包含必要的系统头文件
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>
 
// 包含自定义的显示管理器和字体管理器头文件
#include <disp_manager.h>
#include <font_manager.h>
#include <ui.h> // 假设这是一个包含GUI相关定义的头文件
 
// 主函数
int main(int argc, char **argv)
{
  // 定义显示缓冲区和错误代码的指针,以及按钮和区域的结构体
  PDispBuff ptBuffer;
  int error;
  Button tButton;
  Region tRegion;
 
  // 检查命令行参数是否正确
  if (argc != 2)
  {
    printf("Usage: %s <font_size>\n", argv[0]);
    return -1;
  }
    
  // 初始化显示
  DisplayInit();
 
  // 选择默认的显示操作接口,这里假设"fb"是一个帧缓冲设备
  SelectDefaultDisplay("fb");
 
  // 初始化默认的显示操作接口
  InitDefaultDisplay();
 
  // 获取显示缓冲区
  ptBuffer = GetDisplayBuffer();
 
  // 注册字体
  FontsRegister();
  
  // 选择并初始化字体,这里使用的是"freetype"字体,字体大小由命令行参数指定
  error = SelectAndInitFont("freetype", argv[1]);
  if (error)
  {
    printf("SelectAndInitFont err\n");
    return -1;
  }
 
  // 初始化区域的坐标和大小
  tRegion.iLeftUpX = 200;
  tRegion.iLeftUpY = 200;
  tRegion.iWidth   = 300;
  tRegion.iHeigh   = 100;
  
  // 初始化按钮,设置按钮的文本和区域,以及回调函数
  InitButton(&tButton, "test", &tRegion, NULL, NULL);
  // 绘制按钮
  tButton.OnDraw(&tButton, ptBuffer);
 
  // 进入主循环,当按钮被按下时执行相应的操作
  while (1)
  {
    tButton.OnPressed(&tButton, ptBuffer, NULL);
    // 暂停2秒
    sleep(2);
  }
  
  // 程序正常结束
  return 0; 
}

六、页面系统:

*6.1 PageAction

定义一个结构体类型 PageAction,用于表示页面动作

// 定义一个结构体类型 PageAction,用于表示页面动作
typedef struct PageAction {
    // 页面动作的名称,是一个字符串指针
    char *name;
    // 指向执行页面动作的函数指针,该函数接受一个void指针参数
    void (*Run)(void *pParams);
    // 指向下一个PageAction结构体的指针,用于链表结构
    struct PageAction *ptNext;
} PageAction, *PPageAction;
 

&& 6.2 页面管理器框架

页面管理器用来管理页面,只需要实现2个函数:

       1. PagesRegister : 把多个页面注册进链表

       2. Page(name) :取出某个页面

 

6.3 PageRegister(PPageAction ptPageAction)

在page_manager.c 中实现页面动作注册函数,将新的页面动作添加到链表的头部

// 声明一个静态的全局变量,用于存储页面动作链表的头指针
static PPageAction g_ptPages = NULL;
 
 
// 实现页面动作注册函数,将新的页面动作添加到链表的头部
void PageRegister(PPageAction ptPageAction)
{
    // 将新动作的下一个指针指向当前链表的头
    ptPageAction->ptNext = g_ptPages;
    // 更新链表头指针,使其指向新添加的动作
    g_ptPages = ptPageAction;
}
 

6.4 PPageAction Page(char *name)

在page_manager.c 中实现根据名称查找页面动作的函数

 
 
// 实现根据名称查找页面动作的函数
PPageAction Page(char *name)
{
    // 从链表头开始遍历
    PPageAction ptTmp = g_ptPages;
 
    // 遍历链表直到找到匹配的名称或者遍历完整个链表
    while (ptTmp)
    {
        // 使用strcmp函数比较名称是否相等
        if (strcmp(name, ptTmp->name) == 0)
            // 如果找到匹配的名称,返回对应的页面动作指针
            return ptTmp;
        // 移动到下一个页面动作
        ptTmp = ptTmp->ptNext;
    }
 
    // 如果没有找到匹配的名称,返回NULL
    return NULL;
}

6.5 PageAction MainPage

在main_page.c中创建一个PageAction MainPage,定义一个静态的PageAction结构体变量,表示主页面动作

// 包含页面管理器头文件,这个文件中声明了页面动作相关的结构体和函数
#include <page_manager.h>
 
// 包含标准输入输出头文件,提供了printf等函数
#include <stdio.h>
 
// 定义一个静态函数,用于执行主页面动作
static void MainPageRun(void *pParams)
{
    // 使用printf打印文件名、函数名和当前行号,用于调试信息
    printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}
 
// 定义一个静态的PageAction结构体变量,表示主页面动作
static PageAction g_tMainPage = {
    // 设置页面动作的名称为"main"
    .name = "main",
    // 设置执行页面动作的函数为MainPageRun
    .Run  = MainPageRun,
};
 
// 定义一个函数,用于注册主页面动作
void MainPageRegister(void)
{
    // 调用页面管理器的注册函数,将主页面动作添加到页面动作链表中
    PageRegister(&g_tMainPage);
}

6.6 测试代码:

// 包含内存映射相关的头文件
#include <sys/mman.h>
 
// 包含系统类型定义的头文件
#include <sys/types.h>
 
// 包含文件状态相关的头文件
#include <sys/stat.h>
 
// 包含Unix标准函数定义的头文件
#include <unistd.h>
 
// 包含Linux帧缓冲设备相关的头文件
#include <linux/fb.h>
 
// 包含文件控制选项相关的头文件
#include <fcntl.h>
 
// 包含标准输入输出函数定义的头文件
#include <stdio.h>
 
// 包含字符串处理函数定义的头文件
#include <string.h>
 
// 包含输入输出控制函数定义的头文件
#include <sys/ioctl.h>
 
// 包含标准库函数定义的头文件
#include <stdlib.h>
 
// 包含页面管理器头文件,这个文件中声明了页面动作相关的结构体和函数
#include <page_manager.h>
 
// 程序入口点,接受命令行参数
int main(int argc, char **argv)
{
    // 注册所有页面动作,这通常会在程序启动时执行
    PagesRegister();
 
    // 查找名为"main"的页面动作,并执行其Run函数
    // 这里传递了一个NULL参数,表示没有额外的参数传递给Run函数
    Page("main")->Run(NULL);
 
    // 程序正常退出,返回0
    return 0;
}

七、业务系统

7.1 业务系统流程图

在main.c中实现业务系统

// 包含内存管理、系统类型、文件状态、Unix标准函数、Linux帧缓冲设备、文件控制、标准输入输出、字符串处理、输入输出控制和标准库函数相关的头文件
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>
 
// 包含显示管理、字体管理、输入管理和页面管理相关的头文件
#include <disp_manager.h>
#include <font_manager.h>
#include <input_manager.h>
#include <page_manager.h>
 
// 程序的主函数,接受命令行参数
int main(int argc, char **argv)
{
    // 声明一个显示缓冲区指针和错误码变量
    PDispBuff ptBuffer;
    int error;
 
    // 检查命令行参数的数量,如果不等于2(程序名和字体文件名),则打印使用说明并返回错误码
    if (argc != 2)
    {
        printf("Usage: %s <font_file>\n", argv[0]);
        return -1;
    }
 
    // 初始化显示系统
    DisplayInit();
    // 选择默认的显示设备,这里是帧缓冲设备
    SelectDefaultDisplay("fb");
    // 初始化默认的显示设备
    InitDefaultDisplay();
 
    // 初始化输入系统,包括输入设备的初始化
    InputInit();
    IntpuDeviceInit();
 
    // 注册并初始化字体系统
    FontsRegister();
    error = SelectAndInitFont("freetype", argv[1]);
    // 如果字体选择和初始化失败,打印错误信息并返回错误码
    if (error)
    {
        printf("SelectAndInitFont err\n");
        return -1;
    }
 
    // 注册页面系统
    PagesRegister();
 
    // 运行主页面
    Page("main")->Run(NULL);
 
    // 程序正常退出,返回0
    return 0;
}
  1. 程序开始运行,进入main函数,首先检查命令行参数的数量。如果参数数量不等于2(一个是程序自身的路径,另一个是字体文件的路径),则打印使用说明并返回错误码,程序结束。
  2. 如果命令行参数数量正确,程序会进行显示系统的初始化(DisplayInit)。这可能包括分配内存、设置显示分辨率等操作。
  3. 然后,程序会选择默认的显示设备(SelectDefaultDisplay),在这个例子中是帧缓冲设备(frame buffer),并初始化该显示设备(InitDefaultDisplay)。帧缓冲设备是一种可以直接在内存中操作像素来绘制图像的设备。
  4. 接下来,程序会初始化输入系统(InputInit和IntpuDeviceInit)。输入设备的初始化。
  5. 然后,程序会注册并初始化字体系统(FontsRegister和SelectAndInitFont)。字体文件的路径从命令行参数中获取。如果字体选择和初始化失败,程序会打印错误信息并返回错误码,结束运行。
  6. 接着,程序会注册页面系统(PagesRegister)。页面系统通常用于管理GUI的各个页面(如主页、菜单页等)。
  7. 最后,程序会运行主页面(Page("main")->Run(NULL))。这通常意味着显示主页面并等待用户的输入。

7.2 主页面流程图

在main_page.c中实现主页面流程

&&& 7.3 抽象出配置文件结构体(ItemCfg)

 

在config.h中定义一个结构体,用于表示配置文件中的一个条目

// 定义一个结构体,用于表示配置文件中的一个条目
typedef struct ItemCfg {
    int index; // 条目的索引
    char name[100]; // 条目的名称,最大长度为100个字符
    int bCanBeTouched; // 条目是否可以被触摸(例如,在GUI中)
    char command[100]; // 条目对应的命令,最大长度为100个字符
} ItemCfg, *PItemCfg; // ItemCfg是结构体类型,PItemCfg是指向该结构体的指针类型
 

7.4 ParseConfigFile(char *strFileName)

在config.h中定义一个函数,用于解析配置文件

 
// 定义一个静态数组,用于存储配置文件中的条目,最大数量由ITEMCFG_MAX_NUM定义
static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
 
// 定义一个静态变量,用于记录当前配置文件中条目的数量
static int g_iItemCfgCount = 0;
 
// 定义一个函数,用于解析配置文件
int ParseConfigFile(char *strFileName)
{
    FILE *fp; // 文件指针,用于打开和读取文件
    char buf[100]; // 缓冲区,用于存储从文件读取的一行数据
    char *p = buf; // 指向缓冲区的指针,用于处理读取的数据
 
    /* 1. 打开配置文件 */
    fp = fopen(CFG_FILE, "r");
    if (!fp)
    {
        printf("can not open cfg file %s\n", CFG_FILE);
        return -1; // 打开文件失败,返回-1
    }
 
    while (fgets(buf, 100, fp)) // 循环读取文件的每一行
    {
        /* 2.1 读取每一行数据 */
        buf[99] = '\0'; // 确保缓冲区末尾有一个字符串结束符
 
        /* 2.2 跳过开头的空格或制表符 */
        p = buf;
        while (*p == ' ' || *p =='\t')
            p++;
 
        /* 2.3 忽略注释行(以#开头的行) */
        if (*p == '#')
            continue;
 
        /* 2.4 处理有效行数据 */
        g_tItemCfgs[g_iItemCfgCount].command[0] = '\0'; // 初始化命令字段为空字符串
        g_tItemCfgs[g_iItemCfgCount].index = g_iItemCfgCount; // 设置索引为当前条目计数器值
        sscanf(p, "%s %d %s", g_tItemCfgs[g_iItemCfgCount].name, &g_tItemCfgs[g_iItemCfgCount].bCanBeTouched, \
            g_tItemCfgs[g_iItemCfgCount].command); // 从缓冲区读取数据到结构体字段
        g_iItemCfgCount++; // 增加条目计数器
    }
    return 0; // 解析成功,返回0
}
 

7.4 GetItemCfgCount(void)

在config.h中定义一个函数,用于获取配置文件中条目的数量

// 定义一个函数,用于获取配置文件中条目的数量
int GetItemCfgCount(void)
{
    return g_iItemCfgCount; // 返回条目计数器的值
}

7.5 GetItemCfgByIndex(int index)

在config.h中定义一个函数,用于根据索引获取配置文件中的条目

// 定义一个函数,用于根据索引获取配置文件中的条目
PItemCfg GetItemCfgByIndex(int index)
{
    if (index < g_iItemCfgCount) // 检查索引是否在有效范围内
        return &g_tItemCfgs[index]; // 返回指向对应条目的指针
    else
        return NULL; // 索引无效,返回NULL
}
 

7.6 GetItemCfgByName(char *name)

在config.h中定义一个函数,用于根据名称获取配置文件中的条目

// 定义一个函数,用于根据名称获取配置文件中的条目
PItemCfg GetItemCfgByName(char *name)
{
    int i;
    for (i = 0; i < g_iItemCfgCount; i++) // 遍历所有条目
    {
        if (strcmp(name, g_tItemCfgs[i].name) == 0) // 比较名称是否匹配
            return &g_tItemCfgs[i]; // 返回指向匹配条目的指针
    }
    return NULL; // 没有找到匹配的条目,返回NULL
}

*7.7 生成界面

想显示这样的界面

通过配置文件可以知道有哪些按钮,知道每个按钮的名字

只剩最后一项了:怎么确定每个按钮的位置、大小?需要计算!

7.8 *MainPageRun(void *pParams)

在main_page.c中定义了主页面的运行函数 MainPageRun,它负责读取配置文件、生成按钮和界面,并进入一个无限循环来监听和处理

static void MainPageRun(void *pParams)
{
   int error; // 用于存储函数执行过程中的错误代码
   InputEvent tInputEvent; // 用于存储输入事件的结构体
   PButton ptButton; // 指向按钮对象的指针
   PDispBuff ptDispBuff = GetDisplayBuffer(); // 获取显示缓冲区的指针
 
   /* 读取配置文件 */
   error = ParseConfigFile(); // 解析配置文件,将错误代码存储在error变量中
   if (error) // 如果解析配置文件出错
       return; // 直接返回,不继续执行
 
   /* 根据配置文件生成按钮、界面 */
   GenerateButtons(); // 根据配置文件中的信息生成按钮和界面
 
   while (1) // 无限循环,用于持续监听和处理输入事件
   {
       /* 读取输入事件 */
       error = GetInputEvent(&tInputEvent); // 获取用户的输入事件,将错误代码存储在error变量中
       if (error) // 如果获取输入事件出错
           continue; // 跳过本次循环,继续下一次循环
 
       /* 根据输入事件找到按钮 */
       ptButton = GetButtonByInputEvent(&tInputEvent); // 根据输入事件找到对应的按钮对象
       if (!ptButton) // 如果没有找到对应的按钮
           continue; // 跳过本次循环,继续下一次循环
 
       /* 调用按钮的OnPressed函数 */
       ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent); // 调用找到的按钮的OnPressed函数,处理按钮按下事件
   }
}

7.9 GenerateButtons(void)

在main_page.c中根据屏幕的分辨率和按钮总数计算每个按钮的大小和位置,并居中显示这些按钮。按钮的宽度和高度根据屏幕分辨率和黄金分割比(0.618)进行计算以确定其尺寸,同时保留了一定的间隔(X_GAP 和 Y_GAP)。代码中利用两层嵌套循环计算并设置每个按钮的位置和区域,并对每个按钮对象进行初始化和绘制。

static void GenerateButtons(void)
{
    int width, height;  // 用于存储单个按钮的宽度和高度
    int n_per_line;     // 用于存储每行能放置的按钮数量
    int row, rows;      // row用于记录当前行号,rows用于记录总行数
    int col;            // 用于记录当前列号
    int n;              // 用于存储按钮总数
    PDispBuff pDispBuff; // 指向显示缓冲区的指针
    int xres, yres;     // 用于存储显示区域的水平和垂直分辨率
    int start_x, start_y; // 用于存储按钮区域开始的x,y坐标
    int pre_start_x, pre_start_y; // 用于记录前一个按钮的起始x,y坐标
    PButton pButton;    // 用于指向当前操作的按钮对象
    int i = 0;          // 用于在循环中迭代按钮对象数组
 
    /* 算出单个按钮的width/height */
    g_tButtonCnt = n = GetItemCfgCount(); // 从配置获取按钮总数
    
    pDispBuff = GetDisplayBuffer(); // 获取显示缓冲区对象
    xres = pDispBuff->iXres; // 获取屏幕的水平分辨率
    yres = pDispBuff->iYres; // 获取屏幕的垂直分辨率
    width = sqrt(1.0 / 0.618 * xres * yres / n); // 根据页面比例和按钮总数计算单个按钮的宽度
    n_per_line = xres / width + 1; // 计算每行可以放置的按钮数目
    width = xres / n_per_line; // 调整按钮宽度,以适应每行的按钮数目
    height = 0.618 * width;  // 按照黄金比例计算按钮的高度
 
    /* 居中显示: 计算每个按钮的region */
    start_x = (xres - width * n_per_line) / 2; // 计算按钮起始x坐标,使其居中显示
    rows = n / n_per_line; // 计算总行数
    if (rows * n_per_line < n) // 如果按钮总数无法被整除,则行数+1
        rows++;
    
    start_y = (yres - rows * height) / 2; // 计算按钮起始y坐标,使其居中显示
 
    /* 计算每个按钮的region */
    for (row = 0; (row < rows) && (i < n); row++) // 遍历所有行
    {
        pre_start_y = start_y + row * height; // 计算当前行的y坐标
        pre_start_x = start_x - width; // 设置初始x坐标值
        for (col = 0; (col < n_per_line) && (i < n); col++) // 遍历每行的按钮
        {
            pButton = &g_tButtons[i]; // 获取当前按钮对象的引用
            pButton->tRegion.iLeftUpX = pre_start_x + width; // 设置按钮的左上角x坐标
            pButton->tRegion.iLeftUpY = pre_start_y; // 设置按钮的左上角y坐标
            pButton->tRegion.iWidth = width - X_GAP; // 设置按钮的宽度,并减去预设的间隔
            pButton->tRegion.iHeigh = height - Y_GAP; // 设置按钮的高度,并减去预设的间隔
            pre_start_x = pButton->tRegion.iLeftUpX; // 更新x坐标以供下一个按钮使用
 
            /* InitButton */
            InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, MainPageOnPressed); // 初始化按钮,设置按钮的名称和事件处理函数
            i++; // 按钮索引递增,移动到下一个按钮对象
        }
    }
 
    /* OnDraw */
    for (i = 0; i < n; i++) // 遍历所有按钮对象
        g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff); // 调用按钮的OnDraw方法绘制按钮
}

*7.10 处理输入事件:GetButtonByInputEvent(PInputEvent ptInputEvent)

在main_page.c中定义了一个名为 GetButtonByInputEvent 的函数,其目的是根据提供的输入事件来寻找对应的按钮对象。输入事件 PInputEvent 包含事件类型 iType 以及与特定事件类型相关的数据(例如触摸事件的坐标或网络事件的字符串数据)。

static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{
    int i;
    char name[100]; // 用于存储从网络事件中解析出的按钮名称
    
    if (ptInputEvent->iType == INPUT_TYPE_TOUCH) // 检查输入事件是否为触摸类型
    {
        for (i = 0; i < g_tButtonCnt; i++) // 遍历所有按钮
        {
            // 检查触摸点是否在当前遍历按钮的区域内
            if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))
                return &g_tButtons[i]; // 如果是,返回对应的按钮对象
        }
    }
    else if (ptInputEvent->iType == INPUT_TYPE_NET) // 检查输入事件是否为网络事件
    {
        // 解析网络事件中的字符串,提取按钮名称
        sscanf(ptInputEvent->str, "%s", name);
        return GetButtonByName(name); // 根据按钮名称查找并返回对应的按钮对象
    }
    else
    {
        return NULL; // 如果事件类型既不是触摸也不是网络,返回NULL
    }
    return NULL; // 如果没有找到匹配的按钮,返回NULL
}

7.11 isTouchPointInRegion(int iX, int iY, PRegion ptRegion)

在main_page.c中定义了一个名为 isTouchPointInRegion 的静态函数,用于检查给定的触摸点坐标 (iX, iY) 是否位于指定的区域 ptRegion 内。区域由其左上角坐标 (iLeftUpX, iLeftUpY) 和宽度 iWidth 以及高度 iHeigh 定义。

  • 函数首先检查触摸点的X坐标是否在区域的左边界和右边界之间。如果触摸点的X坐标小于区域的左上角X坐标,或者大于或等于左上角X坐标加上区域的宽度(即右下角X坐标),则触摸点不在区域内,函数返回0。
  • 接着,函数检查触摸点的Y坐标是否在区域的上边界和下边界之间。如果触摸点的Y坐标小于区域的左上角Y坐标,或者大于或等于左上角Y坐标加上区域的高度(即右下角Y坐标),则触摸点不在区域内,函数返回0。
  • 如果触摸点的X和Y坐标都通过了上述检查,即触摸点完全位于区域内,函数返回1,表示触摸点在区域内。
static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
{
   // 检查触摸点的X坐标是否在区域的左上角X坐标和右下角X坐标之间
   if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth)
       return 0; // 如果不在,返回0,表示触摸点不在区域内
 
   // 检查触摸点的Y坐标是否在区域的左上角Y坐标和右下角Y坐标之间
   if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh)
       return 0; // 如果不在,返回0,表示触摸点不在区域内
 
   return 1; // 如果触摸点的X和Y坐标都在区域内,返回1,表示触摸点在区域内
}

 

7.12 GetButtonByName(char *name)

在main_page.c中定义了一个名为 GetButtonByName 的静态函数,其目的是根据提供的名称来查找并返回对应的按钮对象。函数接受一个字符串指针 name 作为参数,该参数代表要查找的按钮的名称。

  • 函数通过一个循环遍历全局数组 g_tButtons 中的所有按钮,数组的大小由 g_tButtonCnt 决定。
  • 在循环中,使用 strcmp 函数来比较传入
1. static PButton GetButtonByName(char *name)
2. {
3. int i;
4. 
5. // 遍历所有按钮
6. for (i = 0; i < g_tButtonCnt; i++)
7.    {
8. // 使用strcmp函数比较传入的名称与当前按钮的名称是否相同
9. if (strcmp(name, g_tButtons[i].name) == 0)
10. return &g_tButtons[i]; // 如果名称匹配,返回当前按钮的指针
11.    }
12. 
13. return NULL; // 如果遍历完所有按钮都没有找到匹配的名称,返回NULL
14. }

7.13 MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)

在main_page.c中定义了一个名为 MainPageOnPressed 的静态函数,用于处理按钮的按下事件。函数接受三个参数:一个指向按钮结构体的指针 ptButton,一个指向显示缓冲区的指针 ptDispBuff,以及一个指向输入事件结构体的指针 ptInputEvent。

  • 函数首先初始化按钮的颜色为默认颜色,并准备用于存储名称和状态的缓冲区。
  • 然后,根据输入事件的类型(触摸屏事件或网络事件),执行不同的逻辑。
  • 对于触摸屏事件,函数检查按钮是否可以被点击,如果可以,则切换按钮的状态并相应地修改按钮的颜色。
  • 对于网络事件,函数从输入事件中解析出名称和状态,并根据状态的不同设置按钮的颜色。如果状态以数字开头,则将按钮名称更新为该数字,并设置为百分比颜色。
  • 如果事件类型不是触摸屏或网络事件,或者状态不符合任何条件,函数返回-1。
  • 最后,函数绘制按钮的底色,在按钮区域中央绘制文字,并将更新后的按钮区域刷新到显示缓冲区,然后返回0表示成功处理事件。
static PButton GetButtonByName(char *name)
{
   int i;
   
   // 遍历所有按钮
   for (i = 0; i < g_tButtonCnt; i++)
   {
       // 使用strcmp函数比较传入的名称与当前按钮的名称是否相同
       if (strcmp(name, g_tButtons[i].name) == 0)
           return &g_tButtons[i]; // 如果名称匹配,返回当前按钮的指针
   }
 
   return NULL; // 如果遍历完所有按钮都没有找到匹配的名称,返回NULL
}
目录
相关文章
|
5天前
|
存储 Linux Android开发
Volatility3内存取证工具安装及入门在Linux下的安装教程
Volatility 是一个完全开源的工具,用于从内存 (RAM) 样本中提取数字工件。支持Windows,Linux,MaC,Android等多类型操作系统系统的内存取证。针对竞赛这块(CTF、技能大赛等)基本上都是用在Misc方向的取证题上面,很多没有听说过或者不会用这款工具的同学在打比赛的时候就很难受。以前很多赛项都是使用vol2.6都可以完成,但是由于操作系统更新,部分系统2.6已经不支持了,如:Win10 等镜像,而Volatility3是支持这些新版本操作系统的。
|
1天前
|
Linux 数据处理
Linux命令`install`详解:不仅仅是安装工具
`install`命令在Linux中并非仅用于安装软件,而是用于精确复制文件和目录,设置权限及所有权。它能创建目标目录、处理符号链接并保留时间戳。例如,`install -m 644 source.txt /dest`用于复制文件并设置权限,`install -d -m 755 /dest/dir`创建目录。使用时要注意权限设置,避免误操作,并记录命令以备恢复。
|
1天前
|
Linux 程序员 芯片
【Linux驱动】普通字符设备驱动程序框架
【Linux驱动】普通字符设备驱动程序框架
|
2天前
|
Linux Shell 数据处理
探索Linux的`infokey`命令:终端键绑定的查询工具
探索Linux的`infokey`命令用于查询终端键绑定。它显示按键如何映射到命令,帮助用户理解终端行为。`infokey`与`ncurses`库配合,提供查询、清除和定制输出的功能。例如,`infokey -d`显示所有键绑定,`infokey -k &quot;\e[11~&quot;`检查F1键绑定。使用前确保安装了`ncurses`库,注意终端兼容性和权限问题。定期检查键绑定,备份配置,阅读文档以优化终端体验。
|
2天前
|
网络协议 Linux 网络安全
Linux命令hostnamectl:掌握系统主机信息的利器
`hostnamectl`是Linux系统管理的关键工具,用于查看和设置主机名、内核信息等。它集成在`systemd`中,通过修改配置文件交互。命令特点包括综合显示多种信息、简单语法和设置功能。例如,`hostnamectl status`显示系统详情,`sudo hostnamectl set-hostname NEWHOSTNAME`用于更改主机名。使用时注意权限、备份配置、更新网络和重启相关服务,避免频繁更改。
|
3天前
|
Java Linux Spring
在 Linux 系统中将 Spring Boot 应用作为系统服务运行
【6月更文挑战第11天】最近由于一些原因,服务器经常会重启,每次重启后需要手动启动 Spring Boot 的工程,因此我需要将其配置成开启自启动的服务。
14 1
|
5天前
|
Linux
Linux 系统日常巡检脚本 干货
Linux 系统日常巡检脚本 干货
8 0
|
5天前
|
Linux 测试技术 开发者
【Docker项目实战】使用Docker部署instantbox临时Linux系统环境搭建工具
【6月更文挑战第13天】使用Docker部署instantbox临时Linux系统环境搭建工具
17 3