1. 事件处理
一个完整的GUI程序,需要处理各种事件,如按键,鼠标,窗口操作等。一般这种程序会设计成一个与底层交互的事件驱动模型。即底层不断发送事件,而在程序用一个循环不断处理各种事件。 各个GUI都是采用这样模型来实现,SDL抽象这个模型,采用SDL_event来抽象表示具体的事件。 在agar中,事件处理函数最终还是使用SDL库。
在AG_SetEvent()中设定事件,通过定义一个AG_Event型的结构体来描述事件信息。
在AG_Eventloop()函数内,首先定义一个SDL_Event类型的数据,通过SDL_PollEvent函数来监听消息,即从底层来提取事件。事件处理允许程序响应来自用户的输入,而等待相应的时候,程序就会阻塞,从而将图形窗口定格在屏幕当中。如果有用户输入,则从阻塞状态中返回,同时SDL_Event也被赋予了代表某个事件的特定值。 SDL_PollEvents()函数的功能是事件轮询。首先通过SDL_PumpEvents函数来处理硬件独立的事件后,再通过SDL_PeepEvents从队列中提取事件。
另外还有两个事件处理函数:
SDL_WaitEvent()必须等到有一个事件才返回,而SDL_PollEvent 没有事件也立即返回,这样提高系统反应速度。
SDL_PeepEvents()是提出查看事件,但事件本身仍然在事件队列中。
2. 键盘事件
在操作中,我们按下按键时,键盘芯片会检测到这个动作,将这个信号发送给计算机。对于每个按键,都会有一个与之对应的键盘扫描码,按下按键时,传递给计算机消息队列的就是键盘扫描码,将按键的键盘扫描码传递给电脑,就达到模拟按键的功能。
键盘扫描码是跟具体的硬件相关的,同一个键在不同键盘上的扫描码有可能不同。操作系统需要得到的信息的并不是键盘扫描码。键盘控制器将这个扫描码传给计算机,然后交给键盘驱动程序。键盘驱动程序会完成相关的工作,并把这个扫描码转换为键盘虚拟码。键盘虚拟码是针对键盘扫描码的非通用性所提出。尽管出于硬件原因,同一个按键可能有不同的扫描码,但是无论什么键盘,同一个按键的虚拟码总是相同的,这样程序就可以识别了。当键盘驱动程序把扫描码转换为虚拟码后,会把这个键盘操作的扫描码和虚拟码还有其它信息一起传递给操作系统。
操作系统在得到这个信息后,会对消息进行封装,封装之后,就成了一个包含完整信息的事件,然后把这个键盘事件消息插入到事件消息队列。最后,这个键盘消息最终会被送到当前的活动窗口那里,活动窗口会使用事件监听函数从事件队列中提取事件,所在的应用程序接收到这个事件消息后,就知道键盘上哪个键被按下,也就可以根据按键决定该作出什么响应返回给用户。
3. 单按键
在SDL中,当事件等待函数监听到事件后,判断事件类型,如果event.type == SDL_KEYDOWN,表明用户按下键盘,保存在event.key.keysym.sym是相应的键值。而根据键值,调用函数SDL_GetKeyName(event.key.keysym.sym)),即可得到按下的按键键名。
SDL_Event event;
while(SDL_PollEvent(&event)){
switch (event.type) {
case SDL_KEYDOWN:
printf("key %s down!\n", SDL_GetKeyName(event.key.keysym.sym));
break;
case SDL_QUIT:
exit(0);
break;
}
}
4. 组合按键
有时候我们需要按下组合按键,key.keysym.mod 保存组合键状态,如检测是否按下 ALT:
if(event.key.keysym.mod & KMOD_ALT) 使用按键控制可执行程序运行,这里,举一个按键的使用加以说明。
#define KBD_DEV "/dev/SOC3210_74HC165_button"
static int kbd_fd = -1;
unsigned char data1 = 0;
void key3210()
{
int key = 0;
int mod = 0;
SDL_Event event;
if((kbd_fd = open(KBD_DEV,O_RDONLY)) < 0){ //打开按键设备
printf("Error opening %s deivce\n",KBD_DEV);
}
while(1)
{
usleep(200000);
read(kbd_fd,&data1,sizeof(data1)); //读取按键键值
if(data1)
{
usleep(1000);
printf("button is 0x%x\n",data1);
}
switch(data1)
{
case 0x80: //判断是否为欲按键
key = SDLK_8;
mod = 0;
break;
default:
break;
}
if(data1)
{
if(key == SDLK_8 )
{
event.type = SDL_KEYDOWN; //定义事件类型
event.key.keysym.sym = key; //定义键值为SDLK_8
event.key.keysym.mod = mod; //组合按键
SDL_PushEvent(&event); //将事件消息插入事件队列
}
}
data1 = 0;
key = 0;
}
}
上述DEV3210按键处理流程为:
首先打开DEV3210设备,当按下按键后,使用read函数读取按键键值。如果得到的键值是我们所需要按键的键值0x80,说明对应的按键被按下。这里我们封装一个事件消息,事件消息包括事件类型,键值为SDLK_8,组合按键。 SDL_PushEvent(&event)将该事件消息插入事件队列中,这样就相当于按下键值为SDLK_8的按键,当前窗口接收到该事件消息后,会根据按键作出相应响应返回给用户。
实际上,这里相当于按了两次键,一次为DEV3210开发板上按键,一次为键盘上数字8按键。