多线程支持多输入

	      main.c
		    |
      Input_manager.c
     |				 |
Touchscreen.c     Stdin.c

输入模块分为三层,Input_manager.c通过链表管理底层各种输入设备,每个设备向上注册InputOpr结构体,提供该设备的各种操作函数和数据。
其中Input_manager.c实现了几个函数供main.c调用。

//调用所有"输入模块"的设备相关的初始化函数并创建用于读取输入数据的子线程
int AllInputDevicesInit(void);	
//获得输入数据,它会使得当前线程休眠,当各输入模块的子线程读到数据后会把它
//唤醒(与Touchscreen.c等提供的GetInputEvent函数不同)供下层调用
int GetInputEvent(PT_InputEvent ptInputEvent);	
//注册"输入模块"调用下层
int RegisterInputOpr(PT_InputOpr ptInputOpr);	
//调用各个输入模块的初始化函数,就是注册各个输入模块	
int InputInit(void);	
输入操作
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;

输入事件结构体
typedef struct InputEvent {
	struct timeval tTime;   /* 发生这个输入事件时的时间 */
	int iType;  /* 类别: stdin, touchsceen */
	int iX;     /* X/Y座标 */
	int iY;
	int iKey;   /* 按键值 */
	int iPressure; /* 压力值 */
}T_InputEvent, *PT_InputEvent;

main.c调用Input_manager.c的GetInputEvent函数获取输入事件进入休眠,AllInputDevicesInit函数中初始化各个输入设备,并创建子线程,其中线程函数传递的参数就是该设备的GetInputEvent函数,各个设备都循环调用read函数并以阻塞方式打开,所以都会进入休眠,当获得输入事件后,子线程唤醒主线程

参考 Unix_Linux_Windows_OpenMP多线程编程.pdf P18 3.3.2 条件变量
在这里插入图片描述
前面两种方式都是单线程实现的,第一种轮询方式while死循环,不断调用GetInputEvent函数,CPU占用率高。第二种也会调用GetInputEvent函数,执行到select会休眠,由内核唤醒,不断循环。第三种多线程,主线程休眠,子线程也在休眠,一旦有数据就唤醒子线程,子线程得到数据再来唤醒主线程,传递输入事件结构体InputEvent,注意使用互斥锁保护临界资源(全局变量)。
互斥锁和条件变量

main函数

iError = AllInputDevicesInit();		//调用所有输入设备初始化

while (1){
	if (0 == GetInputEvent(&tInputEvent)){		//input_manager.c的获得输入事件函数GetInputEvent
		if (tInputEvent.iVal == INPUT_VALUE_DOWN){
			ShowNextPage();
		}
		else if (tInputEvent.iVal == INPUT_VALUE_UP){
			ShowPrePage();			
		}
		else if (tInputEvent.iVal == INPUT_VALUE_EXIT){
			return 0;
		}
	}
}

input_manager.c函数

static T_InputEvent g_tInputEvent;  //全局变量,临界资源
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;	//互斥量
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;	//条件变量

static void *InputEventTreadFunction(void *pVoid){ //输入事件子线程函数
	T_InputEvent tInputEvent;
	
	int (*GetInputEvent)(PT_InputEvent ptInputEvent);	//定义函数指针,指向pVoid参数
	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 AllInputDevicesInit(void){	//所有输入设备初始化函数,并创建子线程
	PT_InputOpr ptTmp = g_ptInputOprHead;
	int iError = -1;
	while (ptTmp){
		if (0 == ptTmp->DeviceInit()){	   //调用设备初始化函数成功
			/* 创建子线程  参数:保存线程ID结构体,线程属性对象,线程开始执行时调用函数,传给函数的参数 */
			pthread_create(&ptTmp->tTreadID, NULL, InputEventTreadFunction, ptTmp->GetInputEvent);			
			iError = 0;
		}
		ptTmp = ptTmp->ptNext;
	}
	return iError;
}

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

main函数先调用AllInputDevicesInit()函数,调用各输入设备的初始化函数,还会创建子线程。子线程的InputEventTreadFunction函数根据传入参数不同,调用touchscreen或stdin的GetInputEvent(&tInputEvent)函数来获得输入事件。
main函数中,循环调用input_manager.c的GetInputEvent函数来获得输入事件,获得互斥量,等待条件变量pthread_cond_wait休眠。
若touchscreen或stdin中获得输入事件,获得互斥量,把值赋给全局变量g_tInputEvent,唤醒主线程pthread_cond_signal。
input_manager.c的GetInputEvent函数被唤醒后,此时全局变量g_tInputEvent的值为touchscreen或stdin传来的输入事件,将该值返回给main函数,释放互斥量。

stdin.c函数

static int StdinGetInputEvent(PT_InputEvent ptInputEvent){
	/* 如果有数据就读取、处理、返回
	 * 如果没有数据, 立刻返回, 不等待*/
 	char c;	
	/* 处理数据 */
	ptInputEvent->iType = INPUT_TYPE_STDIN;
	
	c = fgetc(stdin);  /* 没有数据会休眠直到有输入 */
	gettimeofday(&ptInputEvent->tTime, NULL);	//记录时间值	
	if (c == 'u')
		ptInputEvent->iVal = INPUT_VALUE_UP;
	else if (c == 'n')
		ptInputEvent->iVal = INPUT_VALUE_DOWN;
	else if (c == 'q')
		ptInputEvent->iVal = INPUT_VALUE_EXIT;
	else
		ptInputEvent->iVal = INPUT_VALUE_UNKNOWN;		
	return 0;
 }

touchscreen.c函数

static int TouchScreenGetInputEvent(PT_InputEvent ptInputEvent){
	struct ts_sample tSamp;
	struct ts_sample tSampPressed;		//压下值
	struct ts_sample tSampReleased;		//松开值
	int iRet;
	int bStart = 0;
	int iDelta;	     // x差值
	static struct timeval tPreTime;
	
	while (1){
		iRet = ts_read(g_tTSDev, &tSamp, 1);  /* 以阻塞方式打开的,如果无数据则休眠 */
		if (iRet == 1){			//若读到数据
			if ((tSamp.pressure > 0) && (bStart == 0)){  // 刚按下				
				tSampPressed = tSamp;					 // 记录刚开始压下的点
				bStart = 1;								 //标记位:已按下
			}			
			if (tSamp.pressure <= 0){	/* 松开 */
				tSampReleased = tSamp;			
				if (!bStart){			/* 处理数据 */
					return -1;
				}
				else{
					iDelta = tSampReleased.x - tSampPressed.x;		//  x差值
					ptInputEvent->tTime = tSampReleased.tv;			// 记录时间
					ptInputEvent->iType = INPUT_TYPE_TOUCHSCREEN;	// 记录类型				
					if (iDelta > giXres/5){
						/* 翻到上一页 */
						ptInputEvent->iVal = INPUT_VALUE_UP;		// 记录值
					}
					else if (iDelta < 0 - giXres/5){
						/* 翻到下一页 */
						ptInputEvent->iVal = INPUT_VALUE_DOWN;
					}
					else{
						ptInputEvent->iVal = INPUT_VALUE_UNKNOWN;
					}
					return 0;
				}
			}
		}
		else{
			return -1;
		}
	}
	return 0;
}

其中

触摸屏read得到的数据,参考ts_print和tslib.h
struct ts_sample {
	int		x;
	int		y;
	unsigned int	pressure;
	struct timeval	tv;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值