在前面学习SDL的例子运行时,我们发现我们的窗口只停留了几秒,但是如果设置更长时间显然也有其他的弊端。

那么有没有一种好的办法可以解决这个问题呢?例如:能不能让窗口一直显示,直到检测到用户用鼠标点击关闭按钮后才关闭呢?

答:显然可以! 下面就来介绍一下SDL的事件处理机制。

1. SDL 事件处理机制原理

SDL事件就是键盘事件,鼠标事件,窗口事件等。SDL将所有事件都存放在一个队列中。所有对事件的操作,其实就是对队列的操作。

而SDL对这些事件都做了封装,提供了统一的API,下面我们就来详细的看一下。

2. SDL 操作事件队列的API

  • SDL_PollEvent: 将队列头中的事件抛出来。
  • SDL_WaitEvent: 当队列中有事件时,抛出事件。否则处于阻塞状态,释放 CPU。
  • SDL_WaitEventTimeout: 与SDL_WaitEvent的区别时,当到达超时时间后,退出阻塞状态。
  • SDL_PeekEvent: 从队列中取出事件,但该事件不从队列中删除。
  • SDL_PushEvent: 向队列中插入事件。

3. SDL 处理事件的API

  • SDL_WindowEvent : Window窗口相关的事件。
  • SDL_KeyboardEvent : 键盘相关的事件。
  • SDL_MouseMotionEvent : 鼠标移动相关的事件。
  • SDL_QuitEvent : 退出事件。
  • SDL_UserEvent : 用户自定义事件。

实战

在上面我们也说过了,如果不做SDL窗口的关闭事件的处理,我们是不能够通过点击关闭按钮,关闭SDL显示的窗口的。这样对用户是非常不友好的。

下面我们对SDL的Hello World代码做一下优化,其实就是在程序的末尾增加SDL_Event的事件处理,本例做的事情是检测用户是否按下了退出按钮。如果检测到了,则直接退出,否则保持显示状态。

代码实例:

// SDL.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include "pch.h"
#include <iostream> extern "C" {
#include "SDL.h"
} int main(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_VIDEO)) {
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return ;
} SDL_Window *win = SDL_CreateWindow("Hello World!", , , , , SDL_WINDOW_SHOWN); if (win == nullptr) {
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
return ;
} SDL_Renderer *ren = SDL_CreateRenderer(win, -, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (ren == nullptr) {
SDL_DestroyWindow(win);
std::cout << "SDL_CreateRender Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return ;
} std::string imagePath = "1.bmp";
SDL_Surface *bmp = SDL_LoadBMP(imagePath.c_str());
if (bmp == nullptr) {
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return ;
} SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp); SDL_FreeSurface(bmp);
if (tex == nullptr) {
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return ;
} for (int i = ; i < ; ++i) {
SDL_RenderClear(ren);
SDL_RenderCopy(ren, tex, NULL, NULL);
SDL_RenderPresent(ren);
SDL_Delay();
} int quit = ;
do {
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT:
SDL_Log("Event type is %d", event.type);
quit = ;
default:
SDL_Log("Event type is %d", event.type);
break;
}
} while (quit); SDL_DestroyTexture(tex);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit(); // Return
return ;
}

运行效果:

我们能看到同HelloWorld一样的界面输出,此时如果我们对SDL窗口不做任何处理的话,界面是不会消失的,当我们点击窗口的关闭按钮后,界面关闭了。

4. SDL_PollEvent 与 SDL_WaitEvent

细心的人会发现,使用 SDL_PollEvent 和使用 SDL_WaitEvent 两个方法都能处理SDL的事件队列。如果我们简单的将程序中的SDL_WaitEvent 替换为SDL_PollEvent ,运行时发现也没什么问题。但是当我们打开任务管理器时,发现我们的程序居然跑满了CPU。是什么原因造成的呢?我们来仔细看一下我们增加的代码吧。它由两层 while 循环组成,最里面的while循环的意思是,当队列中一直能取出事件,那就让他一直做下去,直到事件队列为空。外面的while循环的意思是,当队列为空的时候,重新执行内部的while循环。也就是说代码一直在工作,从不休息。所以导致CPU很快就跑满了。 而使用SDL_WaitEvent方法,CPU就不会出现这个问题,因为当它发现队列为空时,它会阻塞在那里,并将CPU占用释放掉。

SDL_WaitEvent和SDL_PollEvent这两个方法使用的场景不同:

  • 对于游戏来说,它要求事件的实时处理,我们最好使用SDL_PollEvent方法
  • 对于一些其它实时性不高的case来说,则可以使用 SDL_WaitEvent了

SDL 开发实战(四): SDL 事件处理的更多相关文章

  1. SDL 开发实战(二):SDL 2.0 核心 API 解析

    在上一篇文章 SDL 开发实战(一):SDL介绍及开发环境配置 中,我们配置好了SDL的开发环境,并成功运行了SDL的Hello World 代码.但是可能大部分人还是读不太明白具体Hello Wol ...

  2. SDL 开发实战(三):使用 SDL 绘制基本图形

    在上文 SDL 开发实战(二):SDL 2.0 核心 API 解析 我们讲解了SDL最核心的API,并结合Hello World代码了解了SDL渲染画面的基本原理. 本文我们来讲一下,如何使用SDL的 ...

  3. SDL 开发实战(五): SDL 纹理渲染

    本文我们讲一下如何使用SDL_Texture将视频纹理渲染出来. 1. SDL 视频渲染相关对象 SDL 视频渲染主要涉及到四个对象:SDL_Window.SDL_Render.SDL_Texture ...

  4. SDL 开发实战(七): SDL 多线程与锁机制

    为什么要用多线程?在音视频领域主要是实现音视频同步.实现了音视频同步,我们的播放器就基本上合格了. 这里我们将讲解一下SDL的多线程与锁机制. 多线程的好处主要是能使程序更加充分利用硬件(主要是CPU ...

  5. SDL 开发实战(一):SDL介绍及开发环境配置

    一.什么是SDL? SDL是 “Simple DirectMedia Layer”的缩写,SDL是一个开源的跨平台的多媒体库,封装了复杂的音视频底层操作,简化了音视频处理的难度. SDL使用C语言写成 ...

  6. SDL 开发实战(七): 使用 SDL 实现 PCM播放器

    在上文,我们做了YUV播放器,这样我们就入门了SDL播放视频.下面我们来做一个PCM播放,即使用SDL播放PCM数据. 下面说明一下使用SDL播放PCM音频的基本流程,主要分为两大部分:初始化SDL. ...

  7. SDL 开发实战(六): 使用 SDL 实现 YUV 播放器

    前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器. 下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分: ...

  8. gRPC学习之四:实战四类服务方法

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. 【C语言探索之旅】 第三部分第一课:SDL开发游戏之安装SDL

    内容简介 1.课程大纲 2.第三部分第一课: SDL开发游戏之安装SDL 3.第三部分第二课预告: SDL开发游戏之创建窗口和画布 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会 ...

随机推荐

  1. Android6.0以上系统动态获取权限

    动态权限的申请方法: 1.首先,需要在AndroidManifest.xml静态申请权限,否则无法动态申请权限: <uses-permission android:name="andr ...

  2. 注册中心(Eureka)

    1. pom.xml依赖 <dependencies> <dependency> <groupId>org.springframework.cloud</gr ...

  3. Spring Bean装配

    1. Bean注入三种方式: A. 包扫描 + 组件标注注解(@Controller/@Service/@Repository/@Component),适用场景:自己写的类: B. @Bean或xml ...

  4. Topshelf的使用

    一.简介 Topshelf可用于创建和管理Windows服务.其优势在于不需要创建windows服务,创建控制台程序就可以.便于调试. 二.官方地址: 1.官网:http://topshelf-pro ...

  5. 2018年冬季寒假作业4--PTA 抓老鼠啊~亏了还是赚了?

    1. 实验代码; #include<stdio.h> ; void search(char a,int *p){ if(a=='X'){ ) *p=; ; printf("U&q ...

  6. JavaScript模块化CommonJS/AMD/CMD/UMD/ES6Module的区别

    目录 JS-模块化进程 原始的开发方式 CommonJS && node.js AMD && Require.js CMD && Sea.js UMD ...

  7. pip包导出

    用pip对已有的环境做导出准备 1. 使用pip导出当前环境所有依赖包信息文件 pip freeze > requirements.txt 2. 下载所有依赖包到本地 pip install - ...

  8. .NET英文技术文章导读(2017-03-23)

    关键字:VS2017.扩展.Service Fabric.Unit Test.ELMAH Web开发人员必装的5个VS2017扩展 作者:Jeffrey T. Fritz 链接:https://blo ...

  9. ImCash:币圈英文术语大全

    近年来随着数字货币的火热,在全世界范围内涌现出了一群数字货币的“发烧友”和忠实投资者,他们形成了自己的圈子“币圈”,并且有了自己的文化和语言,今天就让我们一起来了解一下外国币圈有哪些有意思的英文“专用 ...

  10. flask 中使用 socket 遇到的坑

    很久没用博客园了,最近涉及到一个问题,需要向前端推送日志 考虑的方案两个,一个是定时ajax 但是这样效率太慢了,二是用socket 那看看有没有轮子咯,面向百度编程,有两个库可以使用 1.flask ...