SDL2事件机制与操作系统内核结构分析


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

简介

    事件处理: 一个完整的GUI程序,需要处理各种事件,如按键,鼠标,窗口操作等。

    一般这种程序会设计成一个与底层交互的事件驱动模型。

即操作系统底层不断发送事件【内核层】,

而在程序用一个循环不断处理各种事件【应用层】。 

image.png



事件处理流程

各个GUI都是采用这样模型来实现,SDL抽象这个模型,采用SDL_event来抽象表示具体的事件。

事件处理函数最终还是使用SDL库

首先定义一个SDL_Event类型的数据,通过SDL_PollEvent函数来监听消息,即从底层来提取事件。


事件处理允许程序响应来自用户的输入,而等待相应的时候,程序就会阻塞,从而将图形窗口定格在屏幕当中。如果有用户输入,则从阻塞状态中返回,同时SDL_Event也被赋予了代表某个事件的特定值。 


SDL_PollEvents()函数的功能是事件轮询

首先通过SDL_PumpEvents函数来处理硬件独立的事件后,再通过SDL_PeepEvents从队列中提取事件。


另外还有两个事件处理函数:

SDL_WaitEvent()必须等到有一个事件才返回,而SDL_PollEvent 没有事件也立即返回,这样提高系统反应速度。

SDL_PeepEvents()是提出查看事件,但事件本身仍然在事件队列中。

image.png




操作系统内核原理与流程分析

image.png


在操作中,我们按下按键时,键盘芯片会检测到这个动作,将这个信号发送给计算机。对于每个按键,都会有一个与之对应的键盘扫描码,按下按键时,传递给计算机消息队列的就是键盘扫描码,将按键的键盘扫描码传递给电脑,就达到模拟按键的功能。


键盘扫描码是跟具体的硬件相关的,同一个键在不同键盘上的扫描码有可能不同。

操作系统需要得到的信息的并不是键盘扫描码。

键盘控制器将这个扫描码传给计算机,然后交给键盘驱动程序。

键盘驱动程序会完成相关的工作,并把这个扫描码转换为键盘虚拟码。

键盘虚拟码是针对键盘扫描码的非通用性所提出。


尽管出于硬件原因,同一个按键可能有不同的扫描码,但是无论什么键盘,同一个按键的虚拟码总是相同的,这样程序就可以识别了。

当键盘驱动程序把扫描码转换为虚拟码后,会把这个键盘操作的扫描码和虚拟码还有其它信息一起传递给操作系统。


操作系统在得到这个信息后,会对消息进行封装,封装之后,就成了一个包含完整信息的事件,然后把这个键盘事件消息插入到事件消息队列。

最后,这个键盘消息最终会被送到当前的活动窗口那里,活动窗口会使用事件监听函数从事件队列中提取事件,所在的应用程序接收到这个事件消息后,就知道键盘上哪个键被按下,也就可以根据按键决定该作出什么响应返回给用户。





键盘事件案例

案例参考:sdl2keymouseevent.cpp

案例参考:sdl2demo3.cpp


点击窗口的关闭按钮(X)关闭程序仅仅是SDL能处理的事件的一种。

另一种被游戏广泛使用的输入就是键盘输入。

在这篇教程中我们将通过按下不同的按键来让不同的图像展示在屏幕上。


//函数功能:初始化SDL并创建窗口

bool init();


//函数功能:加载多媒体文件

bool loadMedia();


//函数功能:释放多媒体文件并关闭SDL

void close();


//加载不同的图像

SDL_Surface* loadSurface( std::string path );


//将要渲染的窗口

SDL_Window* gWindow = NULL;


//窗口包含的surface

SDL_Surface* gScreenSurface = NULL;


//用以存储不同按键对应的不同图像的surface指针数组

SDL_Surface* gKeyPressSurfaces[ KEY_PRESS_SURFACE_TOTAL ];


//当前屏幕上显示的图像

SDL_Surface* gCurrentSurface = NULL;


这里就是我们的事件循环,你可以看到之前的教程中讲过的窗口关闭事件的处理,然后我们处理了SDL_KEYDOWN事件。这个事件在你按下键盘上的按键时产生。


SDL事件内部是SDL键盘事件,其中包含按键事件的信息。

SDL键盘事件中包含一个SDL Keysym,其中包含有关按键的信息。 

Keysym包含按下的键的对应的SDL键码。


这段代码的作用就是为相应的按键设置相应的surface。如果你想知道其他按键的键码请参阅SDL的文件。


                //应用当前指向图像

                SDL_BlitSurface( gCurrentSurface, NULL, gScreenSurface, NULL );


                //更新surface

                SDL_UpdateWindowSurface( gWindow );

在按键事件处理完并设置完相应的surface之后,我们将其显示到屏幕上。



代码分析

#include <QCoreApplication>
#include <qdebug.h>
#include <stdio.h>
#include <string.h>
extern "C"
{
    #include "sdl2/SDL.h"
};
//#define main main
static int g__quit = 0;
int getMouseKeyEven(void *opaque) {
    SDL_Event ev;
    int quit = 0;
    //while (!quit)
    {
        while (SDL_PollEvent(&ev))//轮询消息队列中,如果有则取出一个消息
        {
            if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
            {
                if (SDLK_DOWN == ev.key.keysym.sym)
                {
                    printf("SDLK_DOWN ...............\n");
                }else if (SDLK_UP == ev.key.keysym.sym)
                {
                    printf("SDLK_UP ...............\n");
                }else if (SDLK_LEFT == ev.key.keysym.sym)
                {
                    printf("SDLK_LEFT ...............\n");
                }else if (SDLK_RIGHT == ev.key.keysym.sym)
                {
                    printf("SDLK_RIGHT ...............\n");
                }
                else if (SDLK_q == ev.key.keysym.sym)
                {
                    printf("bye ...............\n");
                    g__quit = 1;
                }
            }
            else if (SDL_MOUSEBUTTONDOWN == ev.type)
            {
                if(SDL_BUTTON_LEFT == ev.button.button)
                {
                    int px = ev.button.x;
                    int py = ev.button.y;
                    printf("left:x, y %d %d ...............\n", px, py);
                }
                else if(SDL_BUTTON_RIGHT == ev.button.button)
                {
                    int px = ev.button.x;
                    int py = ev.button.y;
                    printf("right:x, y %d %d ...............\n", px, py);
                }
            }
            else if (SDL_MOUSEMOTION == ev.type)
            {
                int px = ev.motion.x;
                int py = ev.motion.y;
                printf("motion:x, y %d %d ...............\n", px, py);
            }
            else if (SDL_QUIT == ev.type)
            {
                printf("SDL_QUIT ...............\n");
                return 0;
            }
        }
    }
    return 0;
}
int main234sxsd(int argc, char* argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<< "Hello,sdl" ;
    if(SDL_Init(SDL_INIT_VIDEO)) {
        printf( "Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }
    SDL_Window *pscreen;
    //SDL 2.0 Support for multiple windows
    pscreen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        800, 600,SDL_WINDOW_OPENGL);
    if(!pscreen) {
        printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
        return -1;
    }
    while (1) {
        SDL_Delay(10);
        getMouseKeyEven(NULL) ;
        if(g__quit == 1){
            /// 销毁window
            SDL_DestroyWindow(pscreen);
            break;
        }
    }
    return a.exec();
}


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***