【IMX6ULL项目】IMX6ULL下Linux实现产测工具框架

电子产品量产测试与烧写工具。这是一套软件,用在我们的实际生产中, 有如下特点:

1.简单易用:

         把这套软件烧写在 SD 卡上,插到 IMX6ULL 板子里并启动,它就会自动测试各个模块、烧写 EMMC 系统。

        工人只要按照说明接入几个模块,就可以完成整个测试、烧写过程。

         测试结果一目了然:等 LCD 上所有模块的图标都变绿时,就表示测试通过。

2.软件可配置、易扩展:

        通过配置文件添加测试项,可以添加不限个数的测试项。

        每个测试项有自己的测试程序,测试通过后把结果发送给 GUI 即可。各个测试程序互不影响。

3. 纯 C 语言编程

        工具设计的界面,它可以一边测试一边烧写:

上图中的 led、speaker 按钮,可以点击: 

1.当你看到 LED 闪烁时,就点击 led 按钮,它变成绿色表示测试通过;

2. 当你从耳机里听到声音时,就点击 speaker 按钮,它变成绿色表示测试通过。

        其他按钮无法点击,接上对应模块后会自动测试,测试通过时图标就会变绿。

        上图中的蓝色按钮表示烧写 EMMC 的进度,烧写成功后它也会变绿。

        LCD 上所有图标都变绿时,就表示测试、烧写全部完成;某项保持红色的话,就表示对应模块测试失败。

目录:

目录:

一、总设计思路:

二、显示系统:

*2.1 DisplayInit();         

2.2 framebuffer(); 

2.3 RegisterDisplay(); 

 && 2.4 显示管理器框架

​编辑

*2.5 SelectDefaultDisplay(char *name)  

*2.6 InitDefaultDisplay(void)

 2.7 DeviceInit(void);

2.8 FbGetBuffer(PDispBuff ptDispBuff)

*2.9 PutPixel(int x, int y, unsigned int dwColor)

*2.10 FlushDisplayRegion

2.11 测试代码 

详细介绍:Linux基础项目开发1:量产工具——显示系统(二)_linux入门项目-CSDN博客

三、输入系统:

*3.1  InputEvent   数据本身结构体

*3.2 InputDevice   输入设备结构体

*3.3 InputDevice结构体实例(Touchscreen)

3.4 TouchscreenGetInputEvent(PInputEvent ptInputEvent)

3.5 TouchscreenDeviceInit(void)

3.6 TouchscreenDeviceExit(void)

3.9 显示屏的测试函数

*3.10 InputDevice结构体实例(net)

3.11 NetinputGetInputEvent(PInputEvent ptInputEvent)

3.12 NetinputDeviceInit(void)

3.13 NetinputDeviceExit(void)

3.14 网络测试函数

3.15 client.c

&& 3.16  输入管理器框架 

* 3.17 InputInit(void)

 3.18 TouchscreenRegister(void)

3.19 NetInputRegister(void)

 *3.20 RegisterInputDevice(PInputDevice ptInputDev)

*3.21 IntpuDeviceInit(void)

 3.22 *input_recv_thread_func(void *data)

*3.23 GetInputEvent(PT_InputEvent ptInputEvent)

3.24 环形缓冲区

3.25 测试代码

详细介绍:Linux基础项目开发1:量产工具——输入系统(三)-CSDN博客

四、 文字系统:

*4.1 FontBitMap

  Region 

*4.2 FontOpr

*4.3 FontOpr g_tFreetypeOpr

4.4 FreeTypeFontInit(char *aFineName)

4.5 FreeTypeSetFontSize(int iFontSize)

4.6 FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

4.7 FreetypeRegister(void)

&& 4.8 字符管理器框架

 4.9 FontsRegister(void)

4.10 SelectAndInitFont(char *aFontOprName, char *aFontFileName)

4.10 SetFontSize(int iFontSize)

4.11 GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

*4.12 DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)

*4.13 测试代码:

详细介绍:Linux基础项目开发1:量产工具——文字系统(四)-CSDN博客

五、UI系统:

*5.1 Button

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

5.3 DrawRegion(PRegion ptRegion, unsigned int dwColor)

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

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

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

5.7 测试代码:

详细介绍:Linux基础项目开发1:量产工具——UI系统(五)_linux ui界面开发-CSDN博客

六、页面系统:

*6.1 PageAction

&& 6.2 页面管理器框架

 6.3 PageRegister(PPageAction ptPageAction)

6.4 PPageAction Page(char *name)

6.5 PageAction MainPage

6.6 测试代码:

详细介绍:Linux基础项目开发1:量产工具——页面系统(六)-CSDN博客

七、业务系统

7.1 业务系统流程图

7.2 主页面流程图: 

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

7.4 ParseConfigFile(char *strFileName)

7.4 GetItemCfgCount(void)

7.5 GetItemCfgByIndex(int index) 

7.6 GetItemCfgByName(char *name) 

*7.7 生成界面

7.8 *MainPageRun(void *pParams)

7.9 GenerateButtons(void)

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

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

7.12 GetButtonByName(char *name)

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

 详细介绍:Linux基础项目开发1:量产工具——业务系统(七)_量产工具开发-CSDN博客


一、总设计思路:

二、显示系统:

*2.1 DisplayInit();         

        初始化显示系统,在disp_manager.c这个中间管理器中初始化显示器,选择是framebuffer的应用端还是Web的网络端,这里如果还想要加入其他设备都可以在这里调用这个设备的初始化函数,这里我们先拿framebuffer举例。

// 函数实现:初始化显示管理器
void DisplayInit(void)
{void FramebufferInit(void); // 假设有一个FramebufferInit函数用于初始化framebufferFramebufferInit(); // 调用该函数
}

2.2 framebuffer(); 

        这里会在framebuffer.c注册framebuffer操作的结构体到显示管理器的链表中。

// 初始化framebuffer
void FramebufferInit(void)
{RegisterDisplay(&g_tFramebufferOpr); // 注册framebuffer操作到显示管理器
}

        定义一个DispOpr结构体实例,用于描述framebuffer的操作

static DispOpr g_tFramebufferOpr = {.name        = "fb", // 操作名称.DeviceInit  = FbDeviceInit, // 初始化函数指针.DeviceExit  = FbDeviceExit, // 退出函数指针.GetBuffer   = FbGetBuffer, // 获取缓冲区信息函数指针.FlushRegion = FbFlushRegion, // 刷新区域函数指针
};

2.3 RegisterDisplay(); 

        将上面的显示设备framebuffer操作的结构体注册到disp_manager.c这个链表中

// 函数实现:注册一个显示设备
void RegisterDisplay(PDispOpr ptDispOpr)
{ptDispOpr->ptNext = g_DispDevs; // 新设备指针指向当前第一个设备g_DispDevs = ptDispOpr; // 更新第一个设备指针为新设备
}

 && 2.4 显示管理器框架

*2.5 SelectDefaultDisplay(char *name)  

        当放到这个链表里后会有很多设备,那么我们选择哪个模块呢?这里我们就需要disp_manager.c中编写一个设备选择函数SelectDefaultDisplay(char *name)  从头遍历链表找设备名字。

// 函数实现:选择一个默认的显示设备
int SelectDefaultDisplay(char *name)
{PDispOpr pTmp = g_DispDevs; // 临时指针,用于遍历设备链表while (pTmp){if (strcmp(name, pTmp->name) == 0) // 如果找到匹配的设备名{g_DispDefault = pTmp; // 设置为默认设备return 0; // 返回成功}pTmp = pTmp->ptNext; // 移动到下一个设备}return -1; // 没有找到匹配的设备
}

        这里disp_manager.h定义一个结构体DispOpr,用于描述显示操作的接口


// 定义一个结构体DispOpr,用于描述显示操作的接口
typedef struct DispOpr {char *name;             // 显示操作的名称int (*DeviceInit)(void); // 指向初始化函数的指针int (*DeviceExit)(void); // 指向退出函数的指针int (*GetBuffer)(PDispBuff ptDispBuff); // 指向获取缓冲区信息函数的指针int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff); // 指向刷新区域函数的指针struct DispOpr *ptNext; // 指向下一个DispOpr结构体的指针,用于链表结构
} DispOpr, *PDispOpr; // DispOpr是结构体类型,PDispOpr是指向DispOpr的指针类型

*2.6 InitDefaultDisplay(void)

        选择好了显示设备需要在disp_manager.c中编写初始化代码进行设备初始化

int InitDefaultDisplay(void)
{int ret;ret = g_DispDefault->DeviceInit(); // 调用设备的初始化函数if (ret){printf("DeviceInit err\n"); // 初始化失败return -1;}ret = g_DispDefault->GetBuffer(&g_tDispBuff); // 获取缓冲区信息if (ret){printf("GetBuffer err\n"); // 获取失败return -1;}line_width = g_tDispBuff.iXres * g_tDispBuff.iBpp / 8; // 计算每行像素的宽度pixel_width = g_tDispBuff.iBpp / 8; // 计算每个像素的宽度return 0; // 初始化成功
}

        在这之前我们需要先在disp_manager.h中定义一个DispBuff,用于存储显示缓冲区的信息,这样会更好的调用显示缓冲信息。


typedef struct DispBuff {int iXres;     // x坐标分辨率int iYres;     // y坐标分辨率int iBpp;      // 每像素位数(bits per pixel)char *buff;    // 缓冲区地址
} DispBuff, *PDispBuff; // DispBuff是结构体类型,PDispBuff是指向DispBuff的指针类型

 2.7 DeviceInit(void);

        调用framebuffer.c中的DeviceInit(void)进行初始化设备。


static int DeviceInit(void)
{fd_fb = open("/dev/fb0", O_RDWR); // 打开/dev/fb0设备if (fd_fb < 0){printf("can't open /dev/fb0\n"); // 如果打开失败,打印错误信息return -1; // 返回错误代码}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) // 获取屏幕信息{printf("can't get var\n"); // 如果获取失败,打印错误信息return -1; // 返回错误代码}line_width  = var.xres * var.bits_per_pixel / 8; // 计算行宽度pixel_width = var.bits_per_pixel / 8; // 计算像素宽度screen_size = var.xres * var.yres * var.bits_per_pixel / 8; // 计算屏幕大小fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); // 内存映射framebufferif (fb_base == (unsigned char *)-1){printf("can't mmap\n"); // 如果映射失败,打印错误信息return -1; // 返回错误代码}return 0; // 初始化成功,返回0
}

2.8 FbGetBuffer(PDispBuff ptDispBuff)

        再调用framebuffer.c中的FbGetBuffer(PDispBuff ptDispBuff)获取设备的framebuffer缓冲区信息。


// 获取framebuffer缓冲区信息
static int FbGetBuffer(PDispBuff ptDispBuff)
{ptDispBuff->iXres = var.xres; // 设置X分辨率ptDispBuff->iYres = var.yres; // 设置Y分辨率ptDispBuff->iBpp  = var.bits_per_pixel; // 设置每像素位数ptDispBuff->buff  = (char *)fb_base; // 设置缓冲区地址return 0; // 返回0表示成功
}

*2.9 PutPixel(int x, int y, unsigned int dwColor)

        想在这块内存上绘制一个像素,再以这个像素作为起点确定图标来绘制图片,绘制像素这个最基本的函数应该在disp_manager.c这里实现,在屏幕上画一个像素点。


// 函数实现:在屏幕上画一个像素点
int PutPixel(int x, int y, unsigned int dwColor)
{// 根据屏幕的bpp(每像素位数)计算像素在缓冲区中的位置unsigned char *pen_8 = (unsigned char *)(g_tDispBuff.buff + y * line_width + x * pixel_width);unsigned short *pen_16;unsigned int *pen_32;unsigned int red, green, blue;pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;// 根据bpp选择不同的颜色表示方式switch (g_tDispBuff.iBpp){case 8: // 8bpp,直接设置颜色值{*pen_8 = dwColor;break;}case 16: // 16bpp,通常是565格式,需要转换颜色值{red = (dwColor >> 16) & 0xff;green = (dwColor >> 8) & 0xff;blue = (dwColor >> 0) & 0xff;dwColor = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = dwColor;break;}case 32: // 32bpp,直接设置颜色值{*pen_32 = dwColor;break;}default: // 不支持的bpp{printf("can't support %dbpp\n", g_tDispBuff.iBpp);return -1;break;}}return 0; // 成功画点
}

*2.10 FlushDisplayRegion

     我们绘制好图像以后需要刷到硬件上面,我们需要在disp_manager.c中再提供一个FlushDisplayRegion()函数。

// 函数实现:刷新屏幕上的一个区域
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{return g_DispDefault->FlushRegion(ptRegion, ptDispBuff); // 调用设备的刷新区域函数
}

2.11 测试代码 

int main(int argc, char **argv)
{Region region;                 // 定义刷新区域的大小PDispBuff ptBuffer;DisplayInit();                 // 初始化显示系统SelectDefaultDisplay("fb");    // 选择默认的显示设备,这里是帧缓冲设备InitDefaultDisplay();          // 初始化选定的显示设备lcd_put_ascii(100, 100, 'A');  // 在屏幕的(100, 100)位置显示字符'A'// 设置刷新区域的位置和大小region.iLeftUpX = 100;region.iLeftUpY = 100;region.iWidth   = 8;region.iHeigh   = 16;ptBuffer = GetDisplayBuffer();            // 获取显示缓冲区FlushDisplayRegion(&region, ptBuffer);    // 刷新指定区域的显示内容return 0;	}

lcd_put_ascii(100, 100, 'A'); 

// 在指定位置显示一个ASCII字符
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); // 黑色}}}
}

详细介绍:Linux基础项目开发1:量产工具——显示系统(二)_linux入门项目-CSDN博客

三、输入系统:

        我们需要抽象出来俩个结果体,一个数据本身,一个设备本身。

*3.1  InputEvent   数据本身结构体

        在input_manager.h中抽象出数据本身的结构体:

typedef struct InputEvent {// 时间戳结构体,包含事件发生的时间,精确到微秒struct timeval tTime;// 事件类型,可能表示不同的输入动作,如点击、滑动、长按等int iType;// 事件的X坐标,通常表示在屏幕或输入区域的水平位置int iX;// 事件的Y坐标,通常表示在屏幕或输入区域的垂直位置int iY;// 事件的压力值,对于触摸屏等设备,可能表示用户触摸的力度int iPressure;// 一个字符串,用于存储与事件相关的额外信息,长度限制为1024个字符char str[1024];
} InputEvent, *PInputEvent;

*3.2 InputDevice   输入设备结构体

        在input_manager.h中抽象出输入设备的结构体:

typedef struct InputDevice {// 指向设备名称的字符串指针,用于标识输入设备char *name;// 指向函数的指针,该函数用于获取输入事件。它接受一个指向InputEvent结构体的指针作为参数,并返回一个整数值int (*GetInputEvent)(PInputEvent ptInputEvent);// 指向函数的指针,该函数用于初始化输入设备。它不接受任何参数,并返回一个整数值int (*DeviceInit)(void);// 指向函数的指针,该函数用于退出或释放输入设备。它不接受任何参数,并返回一个整数值int (*DeviceExit)(void);// 指向另一个InputDevice结构体的指针,用于链接多个输入设备,形成链表struct InputDevice *ptNext;
} InputDevice, *PInputDevice;

*3.3 InputDevice结构体实例(Touchscreen)

touchscreen.c 中定义一个InputDevice结构体实例,表示触摸屏设备

// 定义一个InputDevice结构体实例,表示触摸屏设备
static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent  = TouchscreenGetInputEvent,.DeviceInit     = TouchscreenDeviceInit,.DeviceExit     = TouchscreenDeviceExit,
};

下面我们需要将触摸屏设备的各个模块函数进行实现。

3.4 TouchscreenGetInputEvent(PInputEvent ptInputEvent)

touchscreen.c 中定义静态函数,用于从触摸屏设备获取输入事件

// 静态函数,用于从触摸屏设备获取输入事件
static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{struct ts_sample samp;int ret;// 从触摸屏设备读取一个样本ret = ts_read(g_ts, &samp, 1);// 如果读取失败,返回-1if (ret != 1)return -1;// 填充输入事件结构体ptInputEvent->iType     = INPUT_TYPE_TOUCH;ptInputEvent->iX        = samp.x;ptInputEvent->iY        = samp.y;ptInputEvent->iPressure = samp.pressure;ptInputEvent->tTime     = samp.tv;// 读取成功,返回0return 0;
}

3.5 TouchscreenDeviceInit(void)

touchscreen.c 中定义静态函数,用于初始化触摸屏设备

static int TouchscreenDeviceInit(void)
{// 初始化触摸屏设备g_ts = ts_setup(NULL, 0);if (!g_ts){// 如果初始化失败,打印错误信息并返回-1printf("ts_setup err\n");return -1;}// 初始化成功,返回0return 0;
}

3.6 TouchscreenDeviceExit(void)

touchscreen.c 中定义静态函数,用于退出触摸屏设备

static int TouchscreenDeviceExit(void)
{// 关闭触摸屏设备ts_close(g_ts);return 0;
}

3.9 显示屏的测试函数

#if 1// 主函数,程序的入口点
int main(int argc, char **argv)
{// 定义一个输入事件的变量InputEvent event;int ret;// 初始化触摸屏设备g_tTouchscreenDev.DeviceInit();// 无限循环,持续从触摸屏获取事件while (1){// 从触摸屏设备获取一个输入事件ret = g_tTouchscreenDev.GetInputEvent(&event);// 检查是否成功获取事件if (ret) {// 如果获取事件失败,打印错误信息并退出程序printf("GetInputEvent err!\n");return -1;}else{// 如果成功获取事件,打印事件的详细信息printf("Type      : %d\n", event.iType);printf("iX        : %d\n", event.iX);printf("iY        : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}}// 理论上,无限循环不会结束,这行代码不会被执行return 0;
}#endif

*3.10 InputDevice结构体实例(net)

netiput.c 中定义一个InputDevice结构体实例,表示网络输入设备

/* 定义一个InputDevice结构体实例,表示网络输入设备 */
static InputDevice g_tNetinputDev ={.name = "netinput",.GetInputEvent  = NetinputGetInputEvent,.DeviceInit     = NetinputDeviceInit,.DeviceExit     = NetinputDeviceExit,
};

下面我们需要将网络设备的各个模块函数进行实现。

3.11 NetinputGetInputEvent(PInputEvent ptInputEvent)

netiput.c 中定义静态函数,用于从网络套接字获取输入事件

/* 静态函数,用于从网络套接字获取输入事件 */
static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{struct sockaddr_in tSocketClientAddr; /* 客户端地址结构体 */struct int iRecvLen;                  /* 接收到的数据长度,这里应该是int类型 */char aRecvBuf[1000];                  /* 接收缓冲区 */int iAddrLen = sizeof(struct sockaddr); /* 地址结构体长度 *//* 从套接字接收数据 */iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (iRecvLen > 0){aRecvBuf[iRecvLen] = '\0'; /* 在接收缓冲区末尾添加字符串结束符 *//* 填充输入事件结构体 */ptInputEvent->iType 	= INPUT_TYPE_NET;gettimeofday(&ptInputEvent->tTime, NULL);strncpy(ptInputEvent->str, aRecvBuf, 1000);ptInputEvent->str[999] = '\0';return 0; /* 成功获取事件,返回0 */}elsereturn -1; /* 获取事件失败,返回-1 */
}

3.12 NetinputDeviceInit(void)

netiput.c 中定义静态函数,用于初始化网络输入设备

static int NetinputDeviceInit(void)
{struct sockaddr_in tSocketServerAddr; /* 服务器地址结构体 */int iRet;                             /* 返回值 *//* 创建一个UDP套接字 */g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == g_iSocketServer){printf("socket error!\n");return -1; /* 创建套接字失败,返回-1 */}/* 设置服务器地址结构体 */tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* 将端口号转换为网络字节序 */tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;      /* 监听所有本地地址 */memset(tSocketServerAddr.sin_zero, 0, 8);            /* 将结构体中未使用的部分置零 *//* 绑定套接字到服务器地址 */iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1; /* 绑定失败,返回-1 */}return 0; /* 初始化成功,返回0 */
}

3.13 NetinputDeviceExit(void)

netiput.c 中定义静态函数,用于退出网络输入设备

static int NetinputDeviceExit(void)
{close(g_iSocketServer); /* 关闭套接字 */return 0;               /* 退出成功,返回0 */
}

3.14 网络测试函数

#if 1/* 主函数,程序的入口点 */
int main(int argc, char **argv)
{InputEvent event; /* 定义一个输入事件结构体变量 */int ret;          /* 用于存储函数返回值 *//* 调用网络输入设备的初始化函数 */g_tNetinputDev.DeviceInit();/* 进入无限循环,不断获取并处理输入事件 */while (1){/* 从网络输入设备获取输入事件 */ret = g_tNetinputDev.GetInputEvent(&event);if (ret) {/* 如果获取事件失败,打印错误信息并退出程序 */printf("GetInputEvent err!\n");return -1;}else{/* 如果获取事件成功,打印事件的类型和内容 */printf("Type      : %d\n", event.iType);printf("str       : %s\n", event.str);}}return 0; /* 正常退出程序 */
}#endif

3.15 client.c

这里充当客户端,用来发送数据

#include <sys/types.h>          /* 包含系统数据类型头文件 */
#include <sys/socket.h>         /* 包含套接字相关函数和结构体头文件 */
#include <string.h>             /* 包含字符串处理函数头文件 */
#include <sys/socket.h>         /* 重复包含,可以删除 */
#include <netinet/in.h>         /* 包含网络地址结构体头文件 */
#include <arpa/inet.h>          /* 包含IP地址转换函数头文件 */
#include <unistd.h>             /* 包含文件操作和进程控制函数头文件 */
#include <stdio.h>              /* 包含标准输入输出函数头文件 *//* 定义服务器端口号 */
#define SERVER_PORT 8888/* 主函数,程序的入口点 */
int main(int argc, char **argv)
{int iSocketClient;           /* 客户端套接字描述符 */struct sockaddr_in tSocketServerAddr; /* 服务器地址结构体 */int iRet;                    /* 用于存储函数返回值 */int iSendLen;                /* 用于存储发送数据的长度 */int iAddrLen;                /* 用于存储地址结构体的长度 *//* 检查命令行参数数量是否正确 */if (argc != 3){printf("Usage:\n");printf("%s <server_ip> <str>\n", argv[0]);return -1;}/* 创建一个UDP套接字 */iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);/* 设置服务器地址结构体 */tSocketServerAddr.sin_family      = AF_INET; /* 使用IPv4地址族 */tSocketServerAddr.sin_port        = htons(SERVER_PORT); /* 设置服务器端口号,转换为网络字节序 *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; /* 注释掉,改为使用命令行参数指定的IP地址 *//* 将命令行参数指定的IP地址转换为网络字节序,并设置到地址结构体中 */if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}/* 将地址结构体的剩余部分填充为0 */memset(tSocketServerAddr.sin_zero, 0, 8);#if 0/* 注释掉的部分是尝试进行TCP风格的连接,但由于是UDP套接字,这里实际上不会进行连接操作 */iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	if (-1 == iRet){printf("connect error!\n");return -1;}
#endif/* 计算地址结构体的长度 */iAddrLen = sizeof(struct sockaddr);/* 向服务器发送数据 */iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,(const struct sockaddr *)&tSocketServerAddr, iAddrLen);/* 关闭套接字 */close(iSocketClient);/* 正常退出程序 */return 0;
}

&& 3.16  输入管理器框架 

* 3.17 InputInit(void)

input_manager.c中向上提供一个InputInit(void) 函数,用于注册触摸屏设备和注册网络设备

/* 初始化输入系统 */
void InputInit(void)
{/* 注册触摸屏设备 */extern void TouchscreenRegister(void);TouchscreenRegister();/* 注册网络输入设备 */extern void NetInputRegister(void);NetInputRegister();
}

 3.18 TouchscreenRegister(void)

touchscreen.c 中注册TouchscreenRegister(void)函数,用于注册触摸屏设备到输入管理器

// 函数,用于注册触摸屏设备到输入管理器
void TouchscreenRegister(void)
{// 调用输入管理器的注册函数,将触摸屏设备注册到输入管理器中RegisterInputDevice(&g_tTouchscreenDev);
}

将以下结构体注册到输入管理器

// 定义一个InputDevice结构体实例,表示触摸屏设备
static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent  = TouchscreenGetInputEvent,.DeviceInit     = TouchscreenDeviceInit,.DeviceExit     = TouchscreenDeviceExit,
};

3.19 NetInputRegister(void)

netiput.c 中注册NetInputRegister(void)函数,用于注册网络设备到输入管理器

/* 函数,用于注册网络输入设备到输入管理器 */
void NetInputRegister(void)
{RegisterInputDevice(&g_tNetinputDev); /* 调用输入管理器的注册函数,将网络输入设备注册到输入管理器中 */
}

将以下结构体注册到输入管理器

/* 定义一个InputDevice结构体实例,表示网络输入设备 */
static InputDevice g_tNetinputDev ={.name = "netinput",.GetInputEvent  = NetinputGetInputEvent,.DeviceInit     = NetinputDeviceInit,.DeviceExit     = NetinputDeviceExit,
};

 *3.20 RegisterInputDevice(PInputDevice ptInputDev)

input_manager.c 注册上面的输入设备,这一层要接受下一层传输上来的注册信息 ,所以要定义一个 g_InputDevs的链表头,g_InputDevs这个链表中存放设备。将所有设备放到一个链表中

/* 注册输入设备函数 */
void RegisterInputDevice(PInputDevice ptInputDev)
{ptInputDev->ptNext = g_InputDevs;g_InputDevs = ptInputDev;
}

*3.21 IntpuDeviceInit(void)

input_manager.c中向上提供一个IntpuDeviceInit(void)对于每个设备初始化它,并且创建线程

* 初始化输入设备 */
void IntpuDeviceInit(void)
{int ret;pthread_t tid;/* 遍历所有输入设备,进行初始化和创建接收线程 */PInputDevice ptTmp = g_InputDevs;while (ptTmp){/* 初始化设备 */ret = ptTmp->DeviceInit();/* 创建接收线程 */if (!ret){ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);}ptTmp = ptTmp->ptNext;}
}

 3.22 *input_recv_thread_func(void *data)

input_manager.c 中创建*input_recv_thread_func(void *data)输入接收线程函数,等待数据,保存数据,唤醒线程


/* 输入接收线程函数 */
static void *input_recv_thread_func(void *data)
{PInputDevice ptInputDev = (PInputDevice)data;InputEvent tEvent;int ret;while (1){/* 从输入设备读取数据 */ret = ptInputDev->GetInputEvent(&tEvent);if (!ret){/* 保存数据到环形缓冲区 */pthread_mutex_lock(&g_tMutex);PutInputEventToBuffer(&tEvent);/* 唤醒等待数据的线程 */pthread_cond_signal(&g_tConVar);pthread_mutex_unlock(&g_tMutex);}}return NULL;
}

*3.23 GetInputEvent(PT_InputEvent ptInputEvent)

input_manager.c 中定义一个最重要的一个函数 ,最上层的代码只要调用这个函数就可以得到这些设备的数据,得到线程数据则返回数据,无数据则休眠

/* 获取输入事件的函数 */
int GetInputEvent(PInputEvent ptInputEvent)
{InputEvent tEvent; // 临时变量,用于存储从缓冲区读取的事件int ret; // 返回值,用于表示函数执行的结果/* 加锁以保证线程安全 */pthread_mutex_lock(&g_tMutex); // 获取互斥锁,保护共享资源/* 尝试从缓冲区获取事件 */if (GetInputEventFromBuffer(&tEvent)) // 如果成功从缓冲区获取事件{*ptInputEvent = tEvent; // 将获取的事件复制到传入的指针所指向的变量pthread_mutex_unlock(&g_tMutex); // 释放互斥锁return 0; // 返回0表示成功获取事件}else // 如果缓冲区为空,没有事件可获取{/* 休眠等待新事件的到来 */pthread_cond_wait(&g_tConVar, &g_tMutex); // 在条件变量上等待,同时释放互斥锁,直到被唤醒/* 再次尝试从缓冲区获取事件 */if (GetInputEventFromBuffer(&tEvent)) // 如果被唤醒后成功获取事件{*ptInputEvent = tEvent; // 将获取的事件复制到传入的指针所指向的变量ret = 0; // 设置返回值为0,表示成功获取事件}else // 如果仍然没有事件可获取{ret = -1; // 设置返回值为-1,表示获取事件失败}pthread_mutex_unlock(&g_tMutex); // 释放互斥锁}return ret; // 返回函数执行的结果
}

3.24 环形缓冲区

在input_manager.c中编写环形缓冲区也是一个一维数组,并不是一个环形的数组,用于保存各个输入设备得到的数据

/* 定义环形缓冲区的大小 */
#define BUFFER_LEN 20/* 定义环形缓冲区的读写指针 */
static int g_iRead = 0; // 读指针,指向缓冲区中下一个将被读取的元素位置
static int g_iWrite = 0; // 写指针,指向缓冲区中下一个将被写入的元素位置/* 定义环形缓冲区存储的数据类型,这里假设为InputEvent */
static InputEvent g_atInputEvents[BUFFER_LEN]; // 环形缓冲区,用于存储InputEvent类型的数据/* 检查环形缓冲区是否已满 */
static int isInputBufferFull(void)
{// 如果写指针的下一个位置是读指针,则缓冲区已满return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN));
}/* 检查环形缓冲区是否为空 */
static int isInputBufferEmpty(void)
{// 如果读指针和写指针相同,则缓冲区为空return (g_iRead == g_iWrite);
}/* 将输入事件放入环形缓冲区 */
static void PutInputEventToBuffer(PInputEvent ptInputEvent)
{// 如果缓冲区未满,则将输入事件写入缓冲区,并更新写指针if (!isInputBufferFull()){g_atInputEvents[g_iWrite] = *ptInputEvent;g_iWrite = (g_iWrite + 1) % BUFFER_LEN;}
}/* 从环形缓冲区获取输入事件 */
static int GetInputEventFromBuffer(PInputEvent ptInputEvent)
{// 如果缓冲区不为空,则从缓冲区读取输入事件,并更新读指针if (!isInputBufferEmpty()){*ptInputEvent = g_atInputEvents[g_iRead];g_iRead = (g_iRead + 1) % BUFFER_LEN;return 1; // 表示成功获取事件}else{return 0; // 表示缓冲区为空,无法获取事件}
}

要想访问环形缓冲区,需要加个互斥锁

/* 互斥锁和条件变量,用于线程同步 */
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER;

3.25 测试代码

#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> // IO控制设备的函数定义#include <input_manager.h> // 自定义输入管理的头文件int main(int argc, char **argv)
{int ret; // 用于函数返回值InputEvent event; // 定义一个输入事件的结构体变量InputInit(); // 初始化输入系统IntpuDeviceInit(); // 初始化输入设备,注意这里应该是 InputDeviceInitwhile (1) // 主循环{// 打印当前文件名、函数名和行号printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);ret = GetInputEvent(&event); // 从输入设备获取一个事件// 再次打印,并显示GetInputEvent函数的返回值printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret);if (ret) { // 如果返回值不是0,表示获取事件时出错printf("GetInputEvent err!\n");return -1; // 出错返回-1}else // 如果成功获取事件{// 打印事件类型printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType );if (event.iType == INPUT_TYPE_TOUCH) // 如果是触摸屏事件{// 打印触摸屏事件的详细信息printf("Type      : %d\n", event.iType);printf("iX        : %d\n", event.iX);printf("iY        : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}else if (event.iType == INPUT_TYPE_NET) // 如果是网络事件{// 打印网络事件的详细信息printf("Type      : %d\n", event.iType);printf("str       : %s\n", event.str);}}}return 0; // 正常退出程序	
}

详细介绍:Linux基础项目开发1:量产工具——输入系统(三)-CSDN博客

四、 文字系统:

*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;
}

详细介绍:Linux基础项目开发1:量产工具——文字系统(四)-CSDN博客

五、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;	
}

详细介绍:Linux基础项目开发1:量产工具——UI系统(五)_linux ui界面开发-CSDN博客

六、页面系统:

*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;}// 如果没有找到匹配的名称,返回NULLreturn 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);// 程序正常退出,返回0return 0;
}

详细介绍:Linux基础项目开发1:量产工具——页面系统(六)-CSDN博客

七、业务系统

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);// 程序正常退出,返回0return 0;
}
  1. 程序开始运行,进入main函数,首先检查命令行参数的数量。如果参数数量不等于2(一个是程序自身的路径,另一个是字体文件的路径),则打印使用说明并返回错误码,程序结束。

  2. 如果命令行参数数量正确,程序会进行显示系统的初始化(DisplayInit)。这可能包括分配内存、设置显示分辨率等操作。

  3. 然后,程序会选择默认的显示设备(SelectDefaultDisplay),在这个例子中是帧缓冲设备(frame buffer),并初始化该显示设备(InitDefaultDisplay)。帧缓冲设备是一种可以直接在内存中操作像素来绘制图像的设备。

  4. 接下来,程序会初始化输入系统(InputInitIntpuDeviceInit)。输入设备的初始化。

  5. 然后,程序会注册并初始化字体系统(FontsRegisterSelectAndInitFont)。字体文件的路径从命令行参数中获取。如果字体选择和初始化失败,程序会打印错误信息并返回错误码,结束运行。

  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]; // 返回指向对应条目的指针elsereturn 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) // 如果按钮总数无法被整除,则行数+1rows++;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 函数来比较传入
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
}

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

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

  • 函数首先初始化按钮的颜色为默认颜色,并准备用于存储名称和状态的缓冲区。
  • 然后,根据输入事件的类型(触摸屏事件或网络事件),执行不同的逻辑。
  • 对于触摸屏事件,函数检查按钮是否可以被点击,如果可以,则切换按钮的状态并相应地修改按钮的颜色。
  • 对于网络事件,函数从输入事件中解析出名称和状态,并根据状态的不同设置按钮的颜色。如果状态以数字开头,则将按钮名称更新为该数字,并设置为百分比颜色。
  • 如果事件类型不是触摸屏或网络事件,或者状态不符合任何条件,函数返回-1。
  • 最后,函数绘制按钮的底色,在按钮区域中央绘制文字,并将更新后的按钮区域刷新到显示缓冲区,然后返回0表示成功处理事件。
static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{unsigned int dwColor = BUTTON_DEFAULT_COLOR; // 初始化按钮颜色为默认颜色char name[100]; // 用于存储名称的缓冲区char status[100]; // 用于存储状态的缓冲区char *strButton; // 指向按钮名称的指针strButton = ptButton->name; // 获取按钮的名称/* 1. 处理触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCH){/* 1.1 检查按钮是否可以被点击 */if (GetItemCfgByName(ptButton->name)->bCanBeTouched == 0)return -1; // 如果按钮不可点击,返回-1/* 1.2 修改按钮状态和颜色 */ptButton->status = !ptButton->status; // 切换按钮状态if (ptButton->status)dwColor = BUTTON_PRESSED_COLOR; // 如果按钮被按下,设置为按下时的颜色}else if (ptInputEvent->iType == INPUT_TYPE_NET){/* 2. 处理网络事件 *//* 根据传入的字符串修改颜色 */sscanf(ptInputEvent->str, "%s %s", name, status); // 从输入事件中解析名称和状态if (strcmp(status, "ok") == 0)dwColor = BUTTON_PRESSED_COLOR; // 如果状态为"ok",设置为按下时的颜色else if (strcmp(status, "err") == 0)dwColor = BUTTON_DEFAULT_COLOR; // 如果状态为"err",设置为默认颜色else if (status[0] >= '0' && status[0] <= '9'){dwColor = BUTTON_PERCENT_COLOR; // 如果状态以数字开头,设置为百分比颜色strButton = status; // 更新按钮名称}elsereturn -1; // 如果状态不符合任何条件,返回-1}else{return -1; // 如果事件类型不是触摸屏或网络事件,返回-1}/* 绘制按钮底色 */DrawRegion(&ptButton->tRegion, dwColor); // 根据当前颜色绘制按钮区域/* 在按钮区域中央绘制文字 */DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR); // 在按钮中央绘制文字,颜色为按钮文字颜色/* 刷新显示到LCD或Web界面 */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff); // 刷新按钮区域到显示缓冲区return 0; // 返回0表示成功处理事件
}

 详细介绍:Linux基础项目开发1:量产工具——业务系统(七)_量产工具开发-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/325829.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库系统理论——关系数据库标准语言SQL

文章目录 一、数据定义1、基本表的定义、删除与修改2、索引的建立于删除&#xff08;了解&#xff09; 二、数据查询&#xff08;会其中一种&#xff09;1、单表查询&#xff08;1&#xff09;这里出现重复元组&#xff0c;怎么处理&#xff1f;&#xff1f;&#xff08;2&…

渗透测试-信息收集

网络安全信息收集是网络安全领域中至关重要的一环&#xff0c;它涉及到对目标系统、网络或应用进行全面而细致的信息搜集和分析。这一过程不仅有助于理解目标网络的结构、配置和潜在的安全风险&#xff0c;还能为后续的渗透测试、风险评估和安全加固提供有力的支持。 在网络安…

单调栈问题

原理 单调栈的核心原理是&#xff1a;在栈内保持元素的单调性&#xff08;递增或递减&#xff09; 单调递增栈&#xff1a; 用于处理“下一个更小的元素”问题。当新元素比栈顶元素小或等于时&#xff0c;直接入栈&#xff1b;否则&#xff0c;一直从栈顶弹出元素&#xff0c…

信息系统项目管理师0102:可行性研究的内容(7项目立项管理—7.2项目可行性研究—7.2.1可行性研究的内容)

点击查看专栏目录 文章目录 7.2项目可行性研究7.2.1可行性研究的内容1.技术可行性分析2.经济可行性分析3.社会效益可行性分析4.运行环境可行性分析5.其他方面的可行性分析记忆要点总结7.2项目可行性研究 可行性研究是在项目建议书被批准后,从技术、经济、社会和人员等方面的条…

在STM32中用寄存器方式点亮流水灯

文章目录 实验资料一、对寄存器的理解1.通俗认识寄存器2.深入了解寄存器&#xff08;1&#xff09;端口配置低寄存器&#xff08;配置0到7引脚的寄存器&#xff09;&#xff08;2&#xff09;端口配置高寄存器&#xff08;配置8到15引脚&#xff09; 3.GPIO口的功能描述 二、配…

【网络】网络基础

目录 一、前言 1.计算机网络背景 2.认识协议 二、网络协议初识 1.OSI七层模型 2.TCP/IP五层(或四层)模型 3.网络传输基本流程 4.数据包封装和分用 5.网络中的地址管理 1.IP地址 2.MAC地址 一、前言 1.计算机网络背景 网络之前&#xff0c;我们所有在电脑上的操作都是…

LeetCode题练习与总结:二叉树的中序遍历--94

一、题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;roo…

Github学习

1.Git与Github 区别: Git是一个分布式版本控制系统&#xff0c;简单的说就是一个软件&#xff0c;用于记录一个或若干个文件内容变化&#xff0c;以便将来查阅特点版本修订情况的软件。 Github是一个为用户提高Git服务的网站&#xff0c;简单说就是一个可以放代码的地方。Gi…

韩顺平0基础学Java——第10天

p202-233 类与对象&#xff08;第七章&#xff09; 成员方法 person类中的speak方法&#xff1a; 1.public表示方法是公开的 2.void表示方法没有返回值 3.speak&#xff08;&#xff09;中&#xff0c;speak表示方法名&#xff0c;括号是形参列表。 4.大括号为方法体&am…

重塑数据架构:云器Lakehouse如何简化组装式架构实现性能与成本的精益平衡

导读本文将介绍云器科技自研的 Lakehouse 产品。通过本次分享&#xff0c;您将了解云器 Lakehouse 产品特性&#xff0c;了解一体化数据平台如何提升数据处理和数据分析的效率&#xff0c;使之更轻松、更简洁、更高效&#xff0c;了解增量计算如何做到平衡数据新鲜度、查询性能…

DE2-115串口通信

目录 一、 内容概要二、 Hello Nios-II2.1 Nios-II编程2.1.1 硬件Ⅰ 搭建环境Ⅱ 编写代码 2.1.2 软件2.1.3 烧录Ⅰ硬件Ⅱ 软件 2.2 verilog编程 三、 心得体会 一、 内容概要 分别用Verilog和Nios软件编程, 实现DE2-115开发板串口输出“Hello Nios-II”字符到笔记本电脑串口助…

【Shell】shell编程之循环语句

目录 1.for循环 例题 2.while循环 例题 3.until循环 1.for循环 读取不同的变量值&#xff0c;用来逐个执行同一组命令 for 变量 in 取值列表 do 命令序列 done [rootlocalhost ~]# for i in 1 2 3 > do > echo "第 $i 次跳舞" > done 第 1 次跳舞 第 …

使用Pycharm编写Python程序时对基本类结构中方法的重写的两种初步操作方式

使用Pycharm编写Python程序时对基本类结构中方法的重写的两种初步操作方式 Python和其他一些高级面向对象的编程语言中&#xff0c;子类可继承父类中的方法&#xff0c;而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法&#xff0c;而是想作一定的修改&…

闲来装个虚拟机Ubuntu24.04和硬盘分区及挂载

简述 最近ubuntu出新版本了&#xff0c;ubuntu24.04&#xff0c; 俗称高贵食蚁兽。5年前进行Android或者linux开发基本是在windows下的虚拟机中进行。目前&#xff0c;虽然物质基础提高了&#xff0c;功能有独立进行编译、代码管理的服务器了。可以通过ssh登录&#xff0c;但是…

【C++11】C++11类与模板语法的完善

目录 一&#xff0c;新的类功能 1-1&#xff0c;默认成员函数 1-2&#xff0c;强制生成关键字 二&#xff0c;可变参数模板 2-1&#xff0c;模板参数包 2-2&#xff0c;STL容器empalce的相关接口 一&#xff0c;新的类功能 1-1&#xff0c;默认成员函数 C11之前的类中有…

Tomcat添加服务以及设置开机自启

下载地址连接 Index of /dist/tomcat&#x1f453; 注意点&#xff1a;不要出现中文路径 #环境变量 CATALINA_HOMED:\apache-tomcat-7.0.62 TOMCAT_HOMED:\apache-tomcat-7.0.62 JAVA_HOMED:\tool\jdk1.8.0_111 PATH%CATALINA_HOME%\bin;%CATALINA_HOME%\lib;%CATALINA_HOME%\…

对称加密介绍

一、什么是对称加密 对称密钥算法(Symmetric-key algorithm)&#xff0c;又称为对称加密、私钥加密、共享密钥加密&#xff0c;是密码学中的一类加密算法。 对称加密的特点是&#xff0c;在加密和解密时使用相同的密钥&#xff0c;或是使用两个可以简单地相互推算的密钥。 这…

超越传统游戏:生成式人工智能对游戏的变革性影响

人工智能&#xff08;AI&#xff09;在游戏中的应用 游戏产业是一个充满活力、不断发展的领域&#xff0c;人工智能&#xff08;AI&#xff09;的融入对其产生了重大影响。这一技术进步彻底改变了游戏的开发、玩法和体验方式。本文分析的重点是传统人工智能和生成式人工智能在游…

网络安全之弱口令与命令爆破(下篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;九头蛇&#xff08;hydra&#xff09;弱口令爆破工具 1&#xff0c;破解ssh登录密码 2&#xff0c;破解windows登录密码 3&#xf…

java项目之相亲网站的设计与实现源码(springboot+mysql+vue)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的相亲网站的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 相亲网站的设计与实…