1.encoding
用到的结构体
typedef struct EncodingOpr {
char *name; /* 编码模块的名字 */
int iHeadLen; /* 文件头的长度: 一般在文件的开始用几个字节来表示它的编码方式 */
PT_FontOpr ptFontOprSupportedHead; /* 把能支持这种编码的"字体模块", 放在这个链表里 */
int (*isSupport)(unsigned char *pucBufHead); /* 用这个函数来判断是否支持某个文件 */
int (*GetCodeFrmBuf)(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode); /* 取出一个字符的编码值 */
struct EncodingOpr *ptNext; /* 链表 */
}T_EncodingOpr, *PT_EncodingOpr;
encoding_manager.c
static PT_EncodingOpr g_ptEncodingOprHead; //编码的头
int RegisterEncodingOpr(PT_EncodingOpr ptEncodingOpr);
void ShowEncodingOpr(void);
PT_EncodingOpr SelectEncodingOprForFile(unsigned char *pucFileBufHead); //给文件选一种编码方式,就是一一调用编码链表成员里的isSupport函数判断
/* 这个函数本质就是ascii.c中的AsciiGetCodeFrmBuf
* 这个函数获取编码,只适用于ascii和gbk
* 传入一个起始一个结束地址,如果这个起始地址的值小于128,那么就说明这是一个单字节的ascii码
* 而如果这个值大于128,那么就说明这是一个双字节的GBK码
*/
int GetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode)
/* 传入一个编码结构体,一个字体结构体
* malloc一个字体结构体T_FontOpr内存,把传进来的结构体赋值给malloc的这块内存
* 编码结构体的ptFontOprSupportedHead,以头插法把刚malloc的内存插入链表
* 为什么这里要malloc一块内存,而不用传入的字体结构体直接插入?:
* 因为在字体结构体链表中,这个结构体已经插入那里了,next指针指向是那个链表的下一个
*/
int AddFontOprForEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr)
/* 从编码结构体的支持字体链表中删除和这个同名的字体结构体,并释放掉当初malloc的内存 */
int DelFontOprFrmEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr)
/* 调用各编码的Init,注册到encoding_manager.c文件中 */
int EncodingInit(void)
关于各编码方式
- ASCII和GBK(GB2312)
- ASCII是单字节的编码方式,GBK是双字节的编码方式,GBK兼容ASCII,是在ASCII的基础上扩展。
- ASCII编码方式只用到0-127,也就是一个字节的前7位,第八位未用到为0.
- GB2312首先对ASCII进行扩展,使用两个字节,第一个字节和第二个字节都是从0xA1,即其两个字节的最高位都是1,这样就可以很好的和ASCII区分开。
- GBK是在GB2312的基础上继续扩展,是对GB2312的扩展,其不要求第二个字节必须大于127,只要求第一个字节大于127,也就是说,只要第一个字节大于127,那么就代表这是一个汉字的开始,且占用两个字节
- https://blog.youkuaiyun.com/ldanduo/article/details/8203532/ 这篇文章写的还可以
- unicode和UTF8/UTF16LE/UTF16BE
- unicode是国际的一种同一编码标准,统一占用两个字节,和中国自己的GBK编码完全不一样,没有一个公式可以转换,只能查表
- UTF8等只是对unicode的不同的表现形式,UTF16LE/BE只是对UNICODE的一个字节序的改变,UTF8是可以将英文用一个字节表示,具体的编码方式,从代码里看
2.fonts
结构体
/* 用来存放一个“字”的位置信息 */
typedef struct FontBitMap {
int iXLeft; /* 位图左上角X座标 */
int iYTop; /* 位图左上角Y座标 */
int iXMax; /* 位图的最大X座标值 */
int iYMax; /* 位图的最大Y座标值 */
int iBpp; /* 位图中一个象素用多少位来表示 */
int iPitch; /* 对于单色位图, 两行象素之间的跨度, 即第N行、第N+1行象素数据之间的偏移值,注意这里是单色*/
int iCurOriginX; /* 位图的原点X座标(一般是左下角), 使用原点来确定相邻字符的位置 */
int iCurOriginY; /* 位图的原点Y座标 */
int iNextOriginX; /* 下一个字符即右边字符的原点X座标 */
int iNextOriginY; /* 下一个字符即右边字符的原点Y座标 */
unsigned char *pucBuffer; /* 存有字符的位图数据 */
}T_FontBitMap, *PT_FontBitMap;
/* 支持哪些字体的结构体 */
typedef struct FontOpr {
char *name; /* 字体模块的名字 */
int (*FontInit)(char *pcFontFile, unsigned int dwFontSize); /* 字体模块的初始化函数 */
int (*GetFontBitmap)(unsigned int dwCode, PT_FontBitMap ptFontBitMap); /* 根据编码值获得字符的位图 */
void (*SetFontSize)(unsigned int dwFontSize); /* 设置字体尺寸(单位:象素) */
struct FontOpr *ptNext; /* 链表 */
}T_FontOpr, *PT_FontOpr;
fonts_manager.c
int RegisterFontOpr(PT_FontOpr ptFontOpr);
void ShowFontOpr(void);
PT_FontOpr GetFontOpr(char *pcName);
void SetFontSize(unsigned int dwFontSize); // 调用每个字体结构体的SetFontSize来设置字体大小
unsigned int GetFontSize(void); // 获得字体的大小
/* 通过编码dwCode,调用每一个注册过的字体的GetFontBitmap函数,直到成功获取 */
int GetFontBitmap(unsigned int dwCode, PT_FontBitMap ptFontBitMap);
/* 设置字体的一些细节
* 根据字体的名字pcFontsName,调用这个名字对应结构体的Init函数
* 把这个字体可能用到的文件(gbk用到HZK16,ascii不用,freetype用.ttc文件)*/
int SetFontsDetail(char *pcFontsName, char *pcFontsFile, unsigned int dwFontSize);
关于字体的初始化和获取bitmap,直接阅读源码
3.debug文件夹
关于debug的网络打印部分,参看之前的
https://blog.youkuaiyun.com/qq_22655017/article/details/95459887
debug本地显示(串口stdout),直接阅读源码
4.input文件夹
宏和结构体
/* 输入事件类别 */
#define INPUT_TYPE_STDIN 0
#define INPUT_TYPE_TOUCHSCREEN 1
/* 标志一个事件,包括了xy的坐标,是按键还是触摸屏,按键的话键值是多少,触摸屏的话是按下还是松开 */
typedef struct InputEvent {
struct timeval tTime; /* 发生这个输入事件时的时间 */
int iType; /* 类别: stdin, touchsceen */
int iX; /* X/Y座标 */
int iY;
int iKey; /* 按键值 */
int iPressure; /* 压力值 */
}T_InputEvent, *PT_InputEvent;
/* 设备结构体,不同的设备需要开不同的子线程去阻塞(或者select)等待事件输入 */
typedef struct InputOpr {
char *name; /* 输入模块的名字 */
pthread_t tTreadID; /* 子线程ID */
int (*DeviceInit)(void); /* 设备初始化函数 */
int (*DeviceExit)(void); /* 设备退出函数 */
int (*GetInputEvent)(PT_InputEvent ptInputEvent); /* 获得输入数据 */
struct InputOpr *ptNext;
}T_InputOpr, *PT_InputOpr;
input_manager.c
int RegisterInputOpr(PT_InputOpr ptInputOpr)
void ShowInputOpr(void)
/* 下面三个函数 看下面的函数详解 */
static void *InputEventThreadFunction(void *pVoid)
int AllInputDevicesInit(void)
int GetInputEvent(PT_InputEvent ptInputEvent)
int InputInit(void)
- InputEventThreadFunction函数
/* 每个设备都需要去创建一个子线程,在子线程中运行自己特殊的等待事件输入函数
- 获得事件后需要反馈给上一层
- 那么就可以直接把这一层封装成一个函数,各子线程函数都来调用
- 并且在最后数据返回时,可以直接返回给这个文件,不需要考虑其他的数据走向问题*/
static void *InputEventThreadFunction(void *pVoid)
{
T_InputEvent tInputEvent; //这里不能是指针,只能是结构体,不然还得分配一块空间
/* 定义函数指针
* 这个指针指向传进来的参数
* 这个函数就是各设备自己用来阻塞(或select)检测输入事件的
*/
int (*GetInputEvent)(PT_InputEvent ptInputEvent);
GetInputEvent = (int (*)(PT_InputEvent))pVoid;
while (1)
{
if(0 == GetInputEvent(&tInputEvent)) //阻塞或者睡眠等待输入事件
{
/* 获得事件后,首先上锁 */
pthread_mutex_lock(&g_tMutex);
/* 把事件交给这个文件的全局变量 */
g_tInputEvent = tInputEvent;
/* 唤醒主线程 */
pthread_cond_signal(&g_tConVar);
/* 释放互斥量 */
pthread_mutex_unlock(&g_tMutex);
}
}
return NULL;
}
- AllInputDevicesInit函数
/* 所有输入设备的初始化,挨个调用每个设备的初始化函数,并为每个设备开一个子线程*/
int AllInputDevicesInit(void)
{
PT_InputOpr ptTmp = g_ptInputOprHead;
int iError = -1;
while (ptTmp)
{
if (0 == ptTmp->DeviceInit())
{
/* 创建子线程,最后一项传入参数就是函数指针,每个设备自己输入事件的函数 */
pthread_create(&ptTmp->tTreadID, NULL, InputEventThreadFunction, ptTmp->GetInputEvent);
iError = 0;
}
ptTmp = ptTmp->ptNext;
}
return iError;
}
- GetInputEvent函数
/* 和上层交接的一个函数,上层调用这个函数获得输入事件
* 调用这个函数后就进入睡眠
* 有子线程唤醒后把数据返回
*/
int GetInputEvent(PT_InputEvent ptInputEvent)
{
/* 休眠 */
pthread_mutex_lock(&g_tMutex);
pthread_cond_wait(&g_tConVar, &g_tMutex);
/* 被唤醒后,返回数据 */
*ptInputEvent = g_tInputEvent;
pthread_mutex_unlock(&g_tMutex);
return 0;
}
stdin.c(本工程中虽然写入了stdin的输入方式,但是并没有使用,在获取数据时,只获取了,并没有向上反馈)
以下几个函数是对输入设备(stdin)的初始化,主要是让输入终端接收到一个字符就返回,而不是遇到回车时返回,具体讲解见
https://blog.youkuaiyun.com/qq_22655017/article/details/95331554
static int StdinDevInit(void)
static int StdinDevExit(void)
int StdinInit(void)
- touchscreen.c
关于tslib库的使用(打开触摸屏,对触摸屏设置,取触摸屏的数据等),详见之前的博客
https://blog.youkuaiyun.com/qq_22655017/article/details/95331554
下面详解子线程调用的函数
static int TouchScreenGetInputEvent(PT_InputEvent ptInputEvent)
{
struct ts_sample tSamp;
int iRet;
while (1)
{
iRet = ts_read(g_tTSDev, &tSamp, 1); /* 打开触摸屏设备时是阻塞方式,所以这里如果无数据则休眠 */
if (iRet == 1)
{
ptInputEvent->tTime = tSamp.tv; /* 获得数据后,把数据放到传出参数ptInputEvent中,就可以返回了 */
ptInputEvent->iType = INPUT_TYPE_TOUCHSCREEN;
ptInputEvent->iX = tSamp.x;
ptInputEvent->iY = tSamp.y;
ptInputEvent->iPressure = tSamp.pressure;
return 0;
}
else
{
return -1;
}
}
return 0;
}