首页 > 其他分享 >100ASK_IMX6ULL-PRO 数码相框扩展项目:支持鼠标输入

100ASK_IMX6ULL-PRO 数码相框扩展项目:支持鼠标输入

时间:2024-11-25 10:11:29浏览次数:7  
标签:iX ptInputEvent 鼠标 PT int 100ASK ptOldRegion IMX6ULL PRO

1. 鼠标输入事件

驱动已支持,测试 Hexdump /dev/input/eventX确定设备节点

项目中的输入模块

输入事件的获取

项目启动时初始化输入设备 InputDeviceInit,通过链表进行设备管理,对于每一个注册的输入设备创建一个线程阻塞式读取输入数据:

static void *InputEventThreadFunction(void *pVoid)
{
	T_InputEvent tInputEvent;

	/* 定义函数指针 */
	int (*GetInputEvent)(PT_InputEvent ptInputEvent);
	GetInputEvent = (int (*)(PT_InputEvent))pVoid;

	while (1)
	{
		if(0 == GetInputEvent(&tInputEvent))
		{
			/* 唤醒主线程, 把tInputEvent的值赋给一个全局变量 */
			/* 访问临界资源前,先获得互斥量 */
			pthread_mutex_lock(&g_tMutex);
			g_tInputEvent = tInputEvent;

			/*  唤醒主线程 */
			pthread_cond_signal(&g_tConVar);

			/* 释放互斥量 */
			pthread_mutex_unlock(&g_tMutex);
		}
	}

	return NULL;
}

对外接口:

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

输入事件的应用

对于每一个界面,结合界面的UI布局对原始输入事件进行解析:

  • 确定响应的UI元素,主要是按钮,从而执行对应的业务逻辑
  • 将输入事件数据通过参数进行传递,以供进一步的解析应用(按下松开,移动的距离等)
/**********************************************************************
 * 函数名称: GenericGetInputEvent
 * 功能描述: 读取输入数据,并判断它位于哪一个图标上
 * 输入参数: ptPageLayout - 内含多个图标的显示区域
 * 输出参数: ptInputEvent - 内含得到的输入数据
 * 返 回 值: -1     - 输入数据不位于任何一个图标之上
 *            其他值 - 输入数据所落在的图标(PageLayout->atLayout数组的哪一项)
 ***********************************************************************/
int GenericGetInputEvent(PT_PageLayout ptPageLayout, PT_InputEvent ptInputEvent)
{
	T_InputEvent tInputEvent;
	int iRet;
	int i = 0;
	PT_Layout atLayout = ptPageLayout->atLayout;

	/* 获得原始的触摸屏数据 
	 * 它是调用input_manager.c的函数,此函数会让当前线否休眠
	 * 当触摸屏线程获得数据后,会把它唤醒
	 */
	iRet = GetInputEvent(&tInputEvent);
	if (iRet)
	{
		return -1;
	}

	if (tInputEvent.iType == INPUT_TYPE_STDIN)	// 仅响应触摸点击和鼠标点击
	{
		return -1;
	}

	*ptInputEvent = tInputEvent;


	/* 处理鼠标事件 */
	if ((ptInputEvent->iType == INPUT_TYPE_MOUSE) && (ptInputEvent->iPressure == MOUSE_MOVE)){
		// 仅在鼠标移动时修改坐标,按下时要反转像素
		SetMousePos(ptInputEvent->iX, ptInputEvent->iY);
		SwitchRegionInDev(ptInputEvent->iX, ptInputEvent->iY, GetMouseIconPixelDatas(), GetMouseBox());
	}

	/* 处理数据 */
	/* 确定触点位于哪一个按钮上 */
	while (atLayout[i].strIconName)
	{
		if ((tInputEvent.iX >= atLayout[i].iTopLeftX) && (tInputEvent.iX <= atLayout[i].iBotRightX) && \
			 (tInputEvent.iY >= atLayout[i].iTopLeftY) && (tInputEvent.iY <= atLayout[i].iBotRightY))
		{
			/* 找到了被点中的按钮 */
			return i;
		}
		else
		{
			i++;
		}
	}

	/* 触点没有落在按钮上 */
	return -1;
}

以主界面为例:

/**********************************************************************
 * 函数名称: MainPageGetInputEvent
 * 功能描述: 为"主页面"获得输入数据,判断输入事件位于哪一个图标上
 * 输入参数: ptPageLayout - 内含多个图标的显示区域
 * 输出参数: ptInputEvent - 内含得到的输入数据
 * 返 回 值: -1     - 输入数据不位于任何一个图标之上
 *            其他值 - 输入数据所落在的图标(PageLayout->atLayout数组的哪一项)
 ***********************************************************************/
static int MainPageGetInputEvent(PT_PageLayout ptPageLayout, PT_InputEvent ptInputEvent)
{
	return GenericGetInputEvent(ptPageLayout, ptInputEvent);
}

鼠标事件的实现

static T_InputOpr g_tMouseOpr = {
	.name          = "Mouse",
	.DeviceInit    = MouseDevInit,
	.DeviceExit    = MouseDevExit,
	.GetInputEvent = MouseGetInputEvent,
};
  • 出入口函数实现:

调用open, close接口对鼠标输入文件进行打开和关闭操作

  • 输入事件获取函数实现:

调用read接口读取数据,无数据则休眠。

主要关注两种数据类型:鼠标左键操作EV_KEY(按下和松开事件的解析)和鼠标的相对位移操作EV_REL(移动事件的解析)

static int MouseGetInputEvent(PT_InputEvent ptInputEvent)
{
	struct input_event tEvent;
	int iLen;

	while (1)
	{
		iLen = read(giFd, &tEvent, sizeof(tEvent)); /* 如果无数据则休眠 */
		if (iLen != sizeof(tEvent))
			DBG_PRINTF("read input_event form mouse failed.\n");
		if (tEvent.type == EV_KEY)
		{
			if (tEvent.code == BTN_LEFT)
				giPressure = tEvent.value;
			else
				continue;
		}
		else if (tEvent.type == EV_REL)
		{
			if (tEvent.code == REL_X ){
				giPosX += tEvent.value;
				giPosX = MAX(giPosX, 0);
				giPosX = MIN(giPosX, giPosXres);
				giPressure = MOUSE_MOVE;
			}
			else if ( tEvent.code == REL_Y){
				giPosY += tEvent.value;
				giPosY = MAX(giPosY, 0);
				giPosY = MIN(giPosY, giPosYres);
				giPressure = MOUSE_MOVE;
			}
			else 
				continue;
		}
		else 
			continue;

		ptInputEvent->tTime     = tEvent.time;
		ptInputEvent->iType     = INPUT_TYPE_MOUSE;
		ptInputEvent->iX        = giPosX;
		ptInputEvent->iY        = giPosY;
		ptInputEvent->iPressure = giPressure;
		return 0;
	}

	return 0;
}

2. 鼠标的显示

鼠标图标的数据来源

  • 直接生成像素数据
  • ** 使用图片:更加方便,且项目功能支持。采用该方案。**

刷新策略

刷新的时机

  • 鼠标移动操作,变更显示位置
  • 鼠标点击操作,发生页面的切换需要重新显示鼠标,包括主界面和浏览界面等之间的切换,还有浏览界面中不同层级的切换

刷新的策略

单独控制鼠标区域刷新,因为相比刷新整个页面效率更高

实现的思路:显示页面的局部交换

  • 交换区域:界面中以鼠标的位置作为左上角,与鼠标图标大小相同的区域

  • 交换流程:

    • step 1:还原

      • 将记录的内容还原到界面中,此处为实际显示界面 GetDevVideoMem
    • step 2:扣取

      • 重新记录界面中指定的区域,此处为设计显示界面 GetCurVideoMem
    • step 3:填充

      • 将鼠标图标填充到界面的指定区域进行显示。此处为实际显示界面 GetDevVideoMem

image

鼠标与界面的隔离保证了交换效果的实现,避免残留污染

  • GetDevVideoMem:实际的LCD显示效果页面,包含鼠标图标
  • GetCurVideoMem:当前的界面设计显示效果页面,不包含鼠标图标
/**********************************************************************
 * 函数名称: SwitchRegionInDev
 * 功能描述: 交换在显示设备上的指定区域像素数据
 * 输入参数: iX 		  - 起始点X坐标
 * 			 iY 		 - 起始点Y坐标
 * 			 ptNewRegion - 更新显示的象素数据
 * 输出参数: ptOldRegion - 更新前显示的像素数据
 * 返 回 值: 0 - 成功, 其他值 - 失败
 ***********************************************************************/
int SwitchRegionInDev(int iX, int iY, PT_PixelDatas ptNewRegion, PT_PixelDatas ptOldRegion){
	int i;
	int iBoxX, iBoxY;
	int iMaxLineBytes;
	unsigned char *pucOld;
	unsigned char *pucPage;

	PT_PixelDatas ptDevPixelDatas = &(GetDevVideoMem()->tPixelDatas);
	PT_PixelDatas ptCurPixelDatas;
	PT_VideoMem ptCurVideoMem = GetCurVideoMem();
	// 交换基于页面而不是显示设备当前的像素数据, 隔离页面和鼠标的显示
	if (ptCurVideoMem)
		ptCurPixelDatas = &(ptCurVideoMem->tPixelDatas);
	else
		// ptCurPixelDatas = ptDevPixelDatas;
		return -1;

	ptOldRegion->iWidth = ptNewRegion->iWidth;
	ptOldRegion->iHeight = ptNewRegion->iHeight;
	ptOldRegion->iBpp = ptNewRegion->iBpp;
	ptOldRegion->iLineBytes = ptOldRegion->iWidth * ptOldRegion->iBpp / 8;
	ptOldRegion->iTotalBytes = ptOldRegion->iLineBytes * ptOldRegion->iHeight;

	// step 1 还原:将 ptOldRegion 刷新到显示设备
	if (!ptOldRegion->aucPixelDatas){ // 第一次交换之前没有旧数据
		ptOldRegion->aucPixelDatas = (unsigned char *)malloc(ptNewRegion->iTotalBytes);
		if (!ptOldRegion->aucPixelDatas){
			DBG_PRINTF("%s malloc error!\n", __FUNCTION__);
			return -1;
		}
	}
	else {
		GetMouseBoxPos(&iBoxX, &iBoxY);
		if ((iBoxX != iX) || (iBoxY != iY)){	// 原地交换时无需刷新
			// FlushRegionToDev(iBoxX, iBoxY, ptOldRegion);
			PicMerge(iBoxX, iBoxY, ptOldRegion, ptDevPixelDatas);
		}
	}

	// step 2 扣取:从页面上复制对应区域的像素数据保存到 ptOldRegion
	pucOld = ptOldRegion->aucPixelDatas;
	pucPage = ptCurPixelDatas->aucPixelDatas + iY * ptCurPixelDatas->iLineBytes + iX * ptCurPixelDatas->iBpp / 8;
	iMaxLineBytes = MIN(ptOldRegion->iLineBytes, (ptCurPixelDatas->iWidth - iX) * ptCurPixelDatas->iBpp / 8); // 保证不越出右边界的最大长度
	for (i = 0; i < ptOldRegion->iHeight; i++)
	{
		if (iY + i >= ptCurPixelDatas->iHeight) // 底部越界,结束
			break;
		memcpy(pucOld, pucPage, iMaxLineBytes);
		pucPage += ptCurPixelDatas->iLineBytes;
		pucOld += ptOldRegion->iLineBytes;
	}
	SetMouseBoxPos(iX, iY);

	// step 3 填充:将 ptNewRegion 像素数据刷新至显示设备上
	FlushRegionToDev(iX, iY, ptNewRegion);

	return 0;
}

一些细节问题的处理

透明背景的实现

PNG_parser只读取RGB数据,并且ConvertOneLine仅支持24bpp

LCD 仅支持 RGB,利用alpha 控制显示与隐藏 ShowPixel: FlushRegionToDev

  • png图片解析与转换

    • 调用libpng库转换时保留alpha信息

          // 透明度处理
          if (iRGBMode == 3)
              png_set_strip_alpha(ptPNG);           // 丢弃透明度和背景
          else {
              if (iColorType!= PNG_COLOR_TYPE_RGBA) { // 保留透明度
                  png_set_expand(ptPNG);
              }
              if (iBitDepth!= 8) {
                  png_set_strip_16(ptPNG);
              }
              png_set_packing(ptPNG);
              if (iColorType == PNG_COLOR_TYPE_GRAY || iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
                  png_set_gray_to_rgb(ptPNG);
              }
          } // iRGBMode = 4;
      
    • 支持32位的 bpp 格式转换:注意ARGB的格式

      /**********************************************************************
       * 函数名称: CovertOneLine
       * 功能描述: 根据BPP进行像素数据转换
       * 输入参数: iWidth      - 像素个数
       *           iSrcBpp     - 源数据BPP
       *           iDstBpp     - 目的数据BPP
       *           pudSrcDatas - 源像素数据,注意 png, bmp, jpg 传入像素数据格式不同,暂时无法通用
       * 输出参数: pudDstDatas - 目的像素数据
       * 返 回 值: 0 - 成功, 其他值 - 失败
       ***********************************************************************/
      static int CovertOneLine(int iWidth, int iSrcBpp, int iDstBpp, unsigned char *pudSrcDatas, unsigned char *pudDstDatas)
      {
          unsigned int dwRed;
      	unsigned int dwGreen;
      	unsigned int dwBlue;
      	unsigned int dwColor;
      
      	unsigned short *pwDstDatas16bpp = (unsigned short *)pudDstDatas;
      	unsigned int   *pwDstDatas32bpp = (unsigned int *)pudDstDatas;
      
      	int i;
      
          /*
              BPP格式转换说明: from 32/24 to 32/24/16
          */
      
      	if (iSrcBpp != 24 && iSrcBpp != 32)
      	{
      		return -1;
      	}
      
      	if (iDstBpp == iSrcBpp)
      	{
              if (iSrcBpp == 32){     // from 32 to 32: RGBA --> ARGB
      	        unsigned int dwAlpha;
                  for (i = 0; i < iWidth; i++){
                      dwRed    = pudSrcDatas[i * iSrcBpp/8 + 0];
                      dwGreen  = pudSrcDatas[i * iSrcBpp/8 + 1];
                      dwBlue   = pudSrcDatas[i * iSrcBpp/8 + 2];
      			    dwAlpha  = pudSrcDatas[i * iSrcBpp/8 + 3];
                      dwColor = (dwAlpha << 24) | (dwRed << 16) | (dwGreen << 8) | dwBlue;
      				*pwDstDatas32bpp = dwColor;
      				pwDstDatas32bpp++;
                  }
              }
              else // from 24 to 24: RGb --> RGB
          		memcpy(pudDstDatas, pudSrcDatas, iWidth * iDstBpp / 8);
      	}
      	else // iDstBpp == iSrcBpp,不考虑alpha
      	{
      		for (i = 0; i < iWidth; i++)
      		{
      			dwRed   = pudSrcDatas[i * iSrcBpp/8 + 0];
      			dwGreen = pudSrcDatas[i * iSrcBpp/8 + 1];
      			dwBlue  = pudSrcDatas[i * iSrcBpp/8 + 2];
      			if (iDstBpp == 32)
      			{
      				dwColor = (dwRed << 16) | (dwGreen << 8) | dwBlue;
      				*pwDstDatas32bpp = dwColor;
      				pwDstDatas32bpp++;
      			}
      			else if (iDstBpp == 24){
      				*pudDstDatas = dwRed;
      				pudDstDatas++;
      				*pudDstDatas = dwGreen;
      				pudDstDatas++;
      				*pudDstDatas = dwBlue;
      				pudDstDatas++;
      			}
      			else if (iDstBpp == 16)
      			{
      				/* 565 */
      				dwRed   = dwRed >> 3;
      				dwGreen = dwGreen >> 2;
      				dwBlue  = dwBlue >> 3;
      				dwColor = (dwRed << 11) | (dwGreen << 5) | (dwBlue);
      				*pwDstDatas16bpp = dwColor;
      				pwDstDatas16bpp++;
      			}
      		}
      	}
          return 0;
      }
      
    • 显示刷新接口针对alpha数据处理:注意字节序问题

      /**********************************************************************
       * 函数名称: FlushRegionToDev
       * 功能描述: 在显示设备上显示指定像素数据中的图像, 支持透明度
       * 输入参数: iX - 起始点X坐标
       * 			 iY - 起始点Y坐标
       * 			  ptPixelDatas - 象素数据
       * 输出参数: 无
       * 返 回 值: 0 - 成功, 其他值 - 失败
       ***********************************************************************/
      void FlushRegionToDev(int iX, int iY, PT_PixelDatas ptPixelDatas){
      	// PicMerge(iX, iY, ptPixelDatas, &(GetDevVideoMem()->tPixelDatas)); // 考虑边界问题,透明度问题
      	int iBytes;
      	unsigned char *pucSrc;
      	unsigned char *pucDst;
      	PT_PixelDatas ptDevPixelDatas = &(GetDevVideoMem()->tPixelDatas);
      
      	if ((ptPixelDatas->iWidth > ptDevPixelDatas->iWidth)  ||
      		(ptPixelDatas->iHeight > ptDevPixelDatas->iHeight) ||
      		(ptPixelDatas->iBpp != ptDevPixelDatas->iBpp))
      	{
      		return ;
      	}
      
      	iBytes = ptDevPixelDatas->iBpp / 8;
      	pucSrc = ptPixelDatas->aucPixelDatas;
      	pucDst = ptDevPixelDatas->aucPixelDatas + iY * ptDevPixelDatas->iLineBytes + iX * iBytes;
      	int i, j, k;
      	for (i = 0; i < ptPixelDatas->iHeight; i++)
      	{
      		// 底部越界,结束当前区域显示
      		if (iY + i >= ptDevPixelDatas->iHeight)
      			break;
      
      		for(j=0; j< ptPixelDatas->iWidth; j++){
      			// 右边越界,结束当前行显示
      			if (j + iX >= ptDevPixelDatas->iWidth)
      				break;
      
      			//LCD格式0x00RRGGBB, 小字节序保存 BBGGRRAA, alpha为最后一个字节
      			if (*(pucSrc + (i *  ptPixelDatas->iWidth + j) * iBytes + iBytes - 1) == 0){	// 像素点alpha = 0, 则不显示, 
      				continue;
      			}
      			else 
      				for (k = 0; k < iBytes; k++){	// 像素点alpha != 0, 刷新显示
      					*(pucDst + (i * ptDevPixelDatas->iWidth + j) * iBytes + k) = *(pucSrc + (i *  ptPixelDatas->iWidth + j) * iBytes + k);
      				}
      		}
      	}
      }
      

鼠标显示控制问题

  1. 按钮点击特效:显示设备按钮区域的像素反转会影响鼠标,另外按钮之间的图标也会影响

页面和鼠标显示逻辑分离,按钮特效不再使用显示设备像素数据 GetDevVideoMem 而是使用页面缓冲 GetCurVideoMem

  1. 页面切换时鼠标残留和偏移
  • 清除交换区域缓存,不同页面之间数据不通用
  • 将鼠标移动事件作为鼠标位置更新的唯一条件,以免发生位置偏移
  1. 按钮点击的有效性:点击事件按照按下和松开时是否在按钮区域
  • press时一定在按钮中,刷新鼠标显示
  • release时不一定在按钮中,仅当鼠标停留在按钮中才认定为点击有效,再刷新鼠标显示

标签:iX,ptInputEvent,鼠标,PT,int,100ASK,ptOldRegion,IMX6ULL,PRO
From: https://www.cnblogs.com/libq8/p/18567053

相关文章

  • 韦东山百问网 IMX6ULL_Pro 开发板 数码相框扩展项目
    说明本篇为韦东山嵌入式Linux课程的实践项目的学习笔记,主要结合课程讲解学习项目的设计和实现,并尝试增加支持一些新功能。开发环境:开发板:100ASK_IMX6ULL-ProLinuxversion4.9.88交叉工具链(gccversion7.5.0(Buildroot2020.02-gee85cab)本次开发主要基于以下百问网......
  • CF2038A - Bonus Project 题解
    题目传送门https://codeforces.com/contest/2038/problem/A先大致捋一下题目的含义一共有n个工程师,每个工程师完成相应的工作都有一定的奖金a,但同时也会消耗成本b,目前一共有k个工作需要做这些工程师对他们的同事很友好,他们能接受自己的总收益为0来增长经验,但不能接受自己为负......
  • Promise、async、await
    PromisePromise是一个js的内置对象,用于处理异步等待的对象写法如下:returnnewPromise((resolve,reject)=>{resolve(数据内容)})async当我们将一个函数声明为async时,该函数会返回一个Promise。如果函数返回的是非Promise值,async函数会自动将其包装成一个已经......
  • QListView 使用QSortFilterProxyModel 过滤后的Item 无法拖动改变顺序
    当使用QSortFilterProxyModel对QListView进行过滤时,拖动顺序的改变通常不会生效,因为QSortFilterProxyModel是只读的,不支持修改模型中的数据顺序。要解决这个问题,可以通过以下方法实现:操作源模型:在拖动和放置时,将操作应用于QSortFilterProxyModel的源模型(sourceModel)......
  • Toyota Programming Contest 2024#11(AtCoder Beginner Contest 379)
    A-Cyclic链接:A-Cyclic代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){ stringss; cin>>ss; cout<<ss[1]<<ss[2]<<ss[0]<<""<<ss[2]<<ss[0]<<ss[1]; return0;}B-Strawberri......
  • Metasploit Pro 4.22.5-2024111401 发布下载,新增功能概览
    MetasploitPro4.22.5-2024111401发布下载,新增功能概览MetasploitPro4.22.5-2024111401(Linux,Windows)-专业渗透测试框架Rapid7Penetrationtesting,releasedNov14,2024请访问原文链接:https://sysin.org/blog/metasploit-pro-4/查看最新版。原创作品,转载......
  • 猫头虎分享:18种写AI 提示词 Prompt Engineering方法大集合
    猫头虎分享:18种写AI提示词PromptEngineering方法大集合大家好,我是猫头虎!今天为大家整理了PromptEngineering(提示工程)的18种方法,并为每种方法提供了实际案例及对应论文参考链接。希望大家能从中受益!目录Top1猫头虎提示词Top2Zero-shotPromptTop3Few-shotPro......
  • k8s阶段03 持久卷, PV和PVC, CSI存储方案示例csi-driver-nfs, OpenEBS, ConfigMap, Se
    2持久卷PV和PVC在Pod级别定义存储卷有两个弊端卷对象的生命周期无法独立于Pod而存在用户必须要足够熟悉可用的存储及其详情才能在Pod上配置和使用卷PV和PVC可用于降低这种耦合关系PV(PersistentVolume)是集群级别的资源,负责将存储空间引入到集群中,通常由管理员定义......
  • 使用 BeanPostProcessor 进行 bean 初始化增强
    简介BeanPostProcessor是Spring框架中的一个接口,用于在Spring容器初始化bean之前和之后进行一些自定义操作。它允许开发者对bean的实例化过程进行干预,通常用于修改或替换bean的属性、执行特定的初始化逻辑等。下面是接口定义:publicinterfaceBeanPostProcessor{ @......
  • CF1763C Another Array Problem
    CF1763CAnotherArrayProblem题目大意:有一个数列\(a\),每次操作可以选择两个位置\(i,j(1\lei<j\len)\),然后把所有位置\(k(i\lek\lej)\)的值\(a_k\)变成\(|a_i-a_j|\)。问若干次操作后,序列的和的最大值为多少?思路:序列的和的最大值可以在所有数都等于原序......