SDL 开发实战(四): SDL 事件处理
在前面学习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 事件处理的更多相关文章
- SDL 开发实战(二):SDL 2.0 核心 API 解析
在上一篇文章 SDL 开发实战(一):SDL介绍及开发环境配置 中,我们配置好了SDL的开发环境,并成功运行了SDL的Hello World 代码.但是可能大部分人还是读不太明白具体Hello Wol ...
- SDL 开发实战(三):使用 SDL 绘制基本图形
在上文 SDL 开发实战(二):SDL 2.0 核心 API 解析 我们讲解了SDL最核心的API,并结合Hello World代码了解了SDL渲染画面的基本原理. 本文我们来讲一下,如何使用SDL的 ...
- SDL 开发实战(五): SDL 纹理渲染
本文我们讲一下如何使用SDL_Texture将视频纹理渲染出来. 1. SDL 视频渲染相关对象 SDL 视频渲染主要涉及到四个对象:SDL_Window.SDL_Render.SDL_Texture ...
- SDL 开发实战(七): SDL 多线程与锁机制
为什么要用多线程?在音视频领域主要是实现音视频同步.实现了音视频同步,我们的播放器就基本上合格了. 这里我们将讲解一下SDL的多线程与锁机制. 多线程的好处主要是能使程序更加充分利用硬件(主要是CPU ...
- SDL 开发实战(一):SDL介绍及开发环境配置
一.什么是SDL? SDL是 “Simple DirectMedia Layer”的缩写,SDL是一个开源的跨平台的多媒体库,封装了复杂的音视频底层操作,简化了音视频处理的难度. SDL使用C语言写成 ...
- SDL 开发实战(七): 使用 SDL 实现 PCM播放器
在上文,我们做了YUV播放器,这样我们就入门了SDL播放视频.下面我们来做一个PCM播放,即使用SDL播放PCM数据. 下面说明一下使用SDL播放PCM音频的基本流程,主要分为两大部分:初始化SDL. ...
- SDL 开发实战(六): 使用 SDL 实现 YUV 播放器
前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器. 下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分: ...
- gRPC学习之四:实战四类服务方法
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 【C语言探索之旅】 第三部分第一课:SDL开发游戏之安装SDL
内容简介 1.课程大纲 2.第三部分第一课: SDL开发游戏之安装SDL 3.第三部分第二课预告: SDL开发游戏之创建窗口和画布 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会 ...
随机推荐
- Java软件工程的弹幕调试原则
日期:2019.4.25 博客期:054 星期四 今天是把很久之前的那个相关程序——一维数组的最大和的子数组的求取信息,我们今天的任务就是把每一步的信息都要进行输出查看! 如下图: package p ...
- 3D 散点图的绘制
一般情况下,我们用到最多的是axes3d() 中的axes3d.Axes3D()类,AxesD() 类下面存在散点图,线性图,柱状图,曲线图等各种制图方式. 采用matplotlib 生成散点图. 一 ...
- nginx 学习 不断更新
nginx 常用全局变量 $uri: 当前请求的uri,不带参数 $request_uri: 请求的uri,带完整参数 $host: http请求报文中host首部,如果没有则以处理此请求的虚拟主机的 ...
- Linux安装Discuz
安装lamp环境 安装参考 安装Discuz 1.进入官网 2.进入Discuz! 程序发布 3.选择最新版本 4.进入git地址 5.克隆下载 5. 确认Apache中的DocumentRoot配置 ...
- 解决 DBMS_AW_EXP: BIN$*****==$0 not AW$
在Oracle 11.2.0.4 版本的数据库中,使用数据泵导出数据时,有可能会遇到这样的提示: 示例1 Connected to: Oracle Database 11g Enterprise Ed ...
- C++—模板(1)模板与函数模板
1.引入 如何编写一个通用加法函数?第一个方法是使用函数重载, 针对每个所需相同行为的不同类型重新实现这个函数.C++的这种编程机制给编程者极大的方便,不需要为功能相似.参数不同的函数选用不同的函数名 ...
- Python网络编程之黏包问题
二.解决黏包问题 2.1 解决黏包方法1 计算消息实体的大小 服务端接受两次,一次时消息大小,二次是消息实体,解决消息实体黏包 客户端发送两次,一次是消息大小,一次是消息实体 在两次收发之间加入一次多 ...
- C# 编译运行原理
- VIM懒人配置
VIM懒人配置 VIM配置起来,是很折腾人的.所以为了方便,直接使用前人的配置.重在用不在折腾. 1 VIM安装 一行命令. # sudo apt-get install vim 2 配置 vim的个 ...
- tp5 mkdir(): Permission denied 问题
今天使用tp5 线上上传图片的时候遇到了一个问题 mkdir(): Permission denied 如图 百度了一下 发现大家都说 chmod -R 777 runtime 能解决问题 尝试了一下 ...