前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【FFmpeg】SDL 音视频开发 ③ ( SDL 事件处理 | SDL_Event 数据结构分析 | 事件处理流程 | SDL 事件获取函数 | SDL_WaitEvent 函数 )

【FFmpeg】SDL 音视频开发 ③ ( SDL 事件处理 | SDL_Event 数据结构分析 | 事件处理流程 | SDL 事件获取函数 | SDL_WaitEvent 函数 )

作者头像
韩曙亮
发布2024-06-21 13:44:08
790
发布2024-06-21 13:44:08
举报

博客源码下载 : https://download.csdn.net/download/han1202012/89432451

SDL 事件处理代码执行效果如下 :

一、SDL 事件处理简介

1、SDL 事件处理引入

SDL , Simple DirectMedia Layer , 是 跨平台的多媒体开发库 , 用于开发 跨平台 的 多媒体应用程序 , 可开发 WIndows / Linux / MacOS 多媒体应用 , 编写一次代码 , 可以在多个平台运行 ;

使用 SDL 多媒体库开发的 应用 有如下功能 :

  • 窗口创建
  • 图像绘制
  • 视频播放
  • 音频播放
  • 事件处理

其中的 事件处理 , 就是处理 外设 ( 键盘 / 鼠标 ) 传入的事件 , 如 鼠标移动 , 鼠标点击 , 键盘按键 等事件 ;

2、SDL 事件处理步骤

SDL 事件处理步骤 :

  • 首先 , 调用 SDL_Init 函数 , 初始化 SDL 环境 , 设置要开发的子系统 ;
代码语言:javascript
复制
// 初始化 SDL 环境
SDL_Init(SDL_INIT_VIDEO);
  • 然后 , 调用 SDL_CreateWindow 函数创建窗口 ;
代码语言:javascript
复制
// 创建 SDL_Window 窗口
SDL_Window* window = SDL_CreateWindow(
	"SDL Event Handling", 
	SDL_WINDOWPOS_UNDEFINED, 
	SDL_WINDOWPOS_UNDEFINED, 
	800, 600, SDL_WINDOW_SHOWN);
  • 再后 , 处理事件 , 开启一个事件循环 , 不断调用 SDL_PollEvent 函数 , 获取是否有事件发生 , 如果获取到了键盘/鼠标事件 , 则执行对应的操作 ;
代码语言:javascript
复制
    // 事件循环
    while (1) {
        // 检查是否有事件发生
        if (SDL_PollEvent(&event)) {
            // 检测事件类型 , 执行不同的操作 
            // event.type
        }
    }
  • 最后 , 用户触发退出操作 , 则退出事件循环 , 并 调用 SDL_Quit 函数 , 释放 SDL 资源并退出 ;
代码语言:javascript
复制
    // 退出SDL
    SDL_DestroyWindow(window);
    SDL_Quit();

代码示例 :

代码语言:javascript
复制
#include <stdio.h>

#include <SDL.h>

#undef main
int main(int argc, char* argv[])
{
    printf("Hello World!\n");

    // SDL 窗口
    SDL_Window* window = NULL;
    // SDL 事件
    SDL_Event event;

    // 1. 初始化SDL
    SDL_Init(SDL_INIT_VIDEO);

    // 2. 创建窗口
    window = SDL_CreateWindow("SDL 事件处理",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              800, 600, SDL_WINDOW_SHOWN);

    // 3. 事件循环
    while (1) {
        // 检查是否有事件发生
        if (SDL_PollEvent(&event)) {
            // 退出事件 : 鼠标点击右上角的 x 关闭按钮
            if (event.type == SDL_QUIT) {
                printf("SDL_QUIT Event\n");
                break;
            }
        }
    }

    // 4. 退出SDL
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;

    return 0;
}

执行结果 : 运行程序后 , 弹出窗口 ,

此时开启循环 , 监听事件 , 每次循环时有事件发生 , 就会进入 if (event.type == SDL_QUIT) 分支语句 ,

点击窗口中右上角的 x 关闭按钮 , 会触发 SDL_QUIT 事件 , 此时退出事件循环 ,

继续执行代码就会关闭窗口 , 释放 SDL 资源 , 程序运行结束 ;

运行程序后弹出事件窗口 :

点击 窗口中的 关闭按钮 , 就会触发 SDL_QUIT 事件 ;

二、SDL 事件数据结构分析

1、SDL_Event 事件 - union 联合体

SDL_Event 用于存储各种事件 , 事件有如下类型 :

  • 键盘事件
  • 鼠标事件
  • 手柄事件
  • 窗口事件
  • 手势事件
  • 传感器事件

具体类型 , 在下面的 SDL_Event 联合体的代码注释中有详细说明 ;

SDL_Event 是一个联合体 定义在 SDL_event.h 头文件中 , 该 SDL_Event 联合体是一种特殊的数据结构 , 允许在相同的内存位置存储不同类型的数据 ;

完整的 SDL_Event 联合体 union 内容如下 :

代码语言:javascript
复制
typedef union SDL_Event {
    Uint32 type;                    /**< 事件类型,与所有事件共享 */
    SDL_CommonEvent common;         /**< 通用事件数据 */
    SDL_DisplayEvent display;       /**< 显示事件数据 */
    SDL_WindowEvent window;         /**< 窗口事件数据 */
    SDL_KeyboardEvent key;          /**< 键盘事件数据 */
    SDL_TextEditingEvent edit;      /**< 文本编辑事件数据 */
    SDL_TextInputEvent text;        /**< 文本输入事件数据 */
    SDL_MouseMotionEvent motion;    /**< 鼠标移动事件数据 */
    SDL_MouseButtonEvent button;    /**< 鼠标按钮事件数据 */
    SDL_MouseWheelEvent wheel;      /**< 鼠标滚轮事件数据 */
    SDL_JoyAxisEvent jaxis;         /**< 游戏手柄轴事件数据 */
    SDL_JoyBallEvent jball;         /**< 游戏手柄球事件数据 */
    SDL_JoyHatEvent jhat;           /**< 游戏手柄帽事件数据 */
    SDL_JoyButtonEvent jbutton;     /**< 游戏手柄按钮事件数据 */
    SDL_JoyDeviceEvent jdevice;     /**< 游戏手柄设备变更事件数据 */
    SDL_ControllerAxisEvent caxis;  /**< 游戏控制器轴事件数据 */
    SDL_ControllerButtonEvent cbutton; /**< 游戏控制器按钮事件数据 */
    SDL_ControllerDeviceEvent cdevice; /**< 游戏控制器设备事件数据 */
    SDL_AudioDeviceEvent adevice;   /**< 音频设备事件数据 */
    SDL_TouchFingerEvent tfinger;   /**< 触摸手指事件数据 */
    SDL_MultiGestureEvent mgesture; /**< 多指手势事件数据 */
    SDL_DollarGestureEvent dgesture; /**< 手势事件数据 */
    SDL_DropEvent drop;             /**< 拖放事件数据 */
    SDL_SensorEvent sensor;         /**< 传感器事件数据 */
    SDL_QuitEvent quit;             /**< 退出请求事件数据 */
    SDL_OSEvent user;               /**< 用户自定义事件数据 */
    Uint8 padding[56];
} SDL_Event;

代码位置 :

2、SDL_Event 事件处理流程

SDL_Event 联合体设计很巧妙 , 其第一个类型成员 Uint32 type 与 其它 25 个结构体类型成员 共同占用同一个内存空间 , 这 25 个结构体 的第一个参数也都是 Uint32 type 成员 ;

因此 , 不管是什么类型的 事件 , 都可以通过 获取 Uint32 type 成员 , 获得该成员的类型 , 然后根据该类型 , 就知道这是什么事件 , 直接调用该事件对应的结构体即可 ;

举例说明 : 获取的 SDL_Event 事件对象 的 Uint32 type 类型是键盘事件 SDL_KEYDOWN , 那么就使用 SDL_KeyboardEvent 结构体的 数据格式 访问该 SDL_Event 事件 ;

SDL_KeyboardEvent 结构体内容如下 :

代码语言:javascript
复制
/**
 *  \brief 键盘按键事件结构体 (event.key.*)
 */
typedef struct SDL_KeyboardEvent
{
    Uint32 type;        /**< ::SDL_KEYDOWN or ::SDL_KEYUP */
    Uint32 timestamp;   /**< 毫秒级时间戳,使用 SDL_GetTicks() 填充 */
    Uint32 windowID;    /**< 拥有键盘焦点的窗口,如果有的话 */
    Uint8 state;        /**< ::SDL_PRESSED 或 ::SDL_RELEASED */
    Uint8 repeat;       /**< 如果这是一个键重复事件,则非零 */
    Uint8 padding2;
    Uint8 padding3;
    SDL_Keysym keysym;  /**< 被按下或释放的按键 */
} SDL_KeyboardEvent;

解析键盘事件的代码示例如下 : 调用 SDL_WaitEvent 函数 , 阻塞等待事件到来 , 事件到来后 ,

  • 首先 , 获取事件的类型 , event.type , 先看是哪种类型的事件 , 选择使用哪种结构体数据格式访问改事件数据 ;
  • 然后 , 如果获取的事件是 SDL_KEYDOWN 键盘事件 , 则使用 SDL_KeyboardEvent 结构体的数据格式访问 SDL_Event 事件对象 ;
  • 最后 , 通过访问 SDL_KeyboardEvent 结构体的 event.key.keysym.sym 成员 , 获取按下的是哪个按键 , 根据不同的按键 , 进行不同的操作 ;
代码语言:javascript
复制
// 3. 事件循环
    while (1) {
        SDL_WaitEvent(&event);
                // 获取事件的类型
                switch (event.type)
                {
                case SDL_KEYDOWN: // 事件类型为键盘事件
                    // 获取键盘按键
                    switch (event.key.keysym.sym)
                    {
                    case SDLK_a:    // 按下 A 键
                        printf("Press A\n");
                        break;
                    case SDLK_s:    // 按下 S 键
                        printf("Press S\n");
                        break;
                    case SDLK_d:    // 按下 D 键
                        printf("Press D\n");
                        break;
                    case SDLK_w:    // 按下 W 键
                        printf("Press W\n");
                        break;
                    default:
                        printf("Press 0x%x\n", event.key.keysym.sym);
                        break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:   // 鼠标点击事件
                    break;
                case SDL_MOUSEMOTION:	// 鼠标移动事件
                    break;
                }
    }

三、SDL 事件函数

1、SDL_PollEvent 函数

在上面的示例中 , 使用了 SDL_PollEvent 函数 , 检测是否有事件发生 ,

  • 如果有事件发生 , 则通过参数中的 SDL_Event 指针返回该事件 , 函数返回值 1 ;
  • 如果没有事件发生 , 函数返回 0 , SDL_Event 指针参数不变 ;

SDL_PollEvent 函数 是 非阻塞函数 , 如果没有事件发生 , 则立刻返回 , 不会阻塞程序执行 ; 与之相对的是 SDL_WaitEvent 函数 , 该函数会阻塞程序 , 等待事件发生 ;

SDL_PollEvent 函数原型如下 :

代码语言:javascript
复制
int SDL_PollEvent(SDL_Event *event);
  • event 参数 : 指向 SDL_Event 对象的指针 , 如果有事件 , 则通过该参数获取事件 ;
  • int 返回值 : 如果获取事件成功 , 则返回 1 ; 如果获取事件失败 , 返回 0 ;

代码示例 : 使用 SDL_PollEvent 函数时 ,

  • 首先 , 声明 SDL_Event 变量 ;
  • 然后 , 将 SDL_Event 变量地址传入 SDL_PollEvent 函数 ;
  • 最后 , 根据 SDL_PollEvent 函数的返回值 , 决定是否处理事件 ;
    • 如果返回 1 , 则处理事件 ;
    • 如果返回 0 , 则不处理事件 ;
代码语言:javascript
复制
// 声明 SDL_Event 变量
SDL_Event event;
// 将 SDL_Event 变量地址传入 SDL_PollEvent 函数
// 	如果返回 1 , 则处理事件 
//	如果返回 0 , 则不处理事件
if (SDL_PollEvent(&event)) {
    // 处理事件
}

2、SDL_WaitEvent 函数

SDL_WaitEvent 函数 的作用是 阻塞等待事件发生 , 如果没有事件发生 , 则程序会一直阻塞等待下去 , 当有事件发生时 , 再继续向下执行 ;

SDL_WaitEvent 函数原型如下 :

代码语言:javascript
复制
int SDL_WaitEvent(SDL_Event *event);
  • event 参数 : 指向 SDL_Event 对象的指针 , 如果有事件 , 则通过该参数获取事件 ;
  • int 返回值 : 如果获取事件成功 , 则返回 1 ;

如果没有事件发生 , 则程序会一直阻塞下去 ;

代码示例 : 使用 SDL_WaitEvent 函数的流程如下 :

  • 首先 , 定义 SDL_Event 事件变量 ;
  • 然后 , 调用 SDL_WaitEvent 函数 , 传入 SDL_Event 变量的地址 , 使用 取地址符 & 获取变量地址 ;

调用了 SDL_WaitEvent 函数后 , 会阻塞当前的程序 , 直到 事件发生 , 解除阻塞继续执行 ;

代码语言:javascript
复制
SDL_Event event;
// 调用后会阻塞等待事件到来
SDL_WaitEvent(&event);
// 事件到来 , 解除阻塞 , 开始处理事件 
// ...

四、完整代码示例

博客源码下载 : https://download.csdn.net/download/han1202012/89432451

1、代码示例

代码示例 :

代码语言:javascript
复制
#include <stdio.h>
#include <SDL.h>
#define MY_QUIT_EVENT (SDL_USEREVENT + 2) // 用户自定义事件

#undef main
int main(int argc, char* argv[])
{
    printf("Hello World!\n");

    // SDL 窗口
    SDL_Window* window = NULL;
    // SDL 事件
    SDL_Event event;
    // 事件循环退出标志位
    int event_exit = 0;

    // 1. 初始化SDL
    SDL_Init(SDL_INIT_VIDEO);

    // 2. 创建窗口
    window = SDL_CreateWindow("SDL 事件处理",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              800, 600, SDL_WINDOW_SHOWN);

    // 3. 事件循环
    while (1) {
        SDL_WaitEvent(&event);
                // 获取事件的类型
                switch (event.type)
                {
                case SDL_KEYDOWN: // 事件类型为键盘事件
                    // 获取键盘按键
                    switch (event.key.keysym.sym)
                    {
                    case SDLK_a:    // 按下 A 键
                        printf("Press A\n");
                        break;
                    case SDLK_s:    // 按下 S 键
                        printf("Press S\n");
                        break;
                    case SDLK_d:    // 按下 D 键
                        printf("Press D\n");
                        break;
                    case SDLK_w:    // 按下 W 键
                        printf("Press W\n");
                        break;
                    case SDLK_q:
                        printf("Press Q\n");

                        // 生成自定义事件
                        SDL_Event event_q;
                        event_q.type = MY_QUIT_EVENT;
                        SDL_PushEvent(&event_q);
                        break;
                    default:
                        printf("Press 0x%x\n", event.key.keysym.sym);
                        break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:   // 鼠标事件
                    if (event.button.button == SDL_BUTTON_LEFT)
                    {
                        // 按下鼠标左键
                        printf("SDL_BUTTON_LEFT\n");
                    }
                    else if(event.button.button == SDL_BUTTON_RIGHT)
                    {
                        printf("SDL_BUTTON_RIGHT\n");
                    }
                    else
                    {
                        printf("Press Mouse Button %d\n", event.button.button);
                    }
                    break;
                case SDL_MOUSEMOTION:   // 鼠标点击事件
                    printf("Mouse Moved To (%d,%d)\n", event.button.x, event.button.y);
                    break;
                case MY_QUIT_EVENT:
                    printf("MY_QUIT_EVENT\n");
                    event_exit = 1;
                    break;
                }
                if(event_exit)
                    break;
    }

    // 4. 退出SDL
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;

    return 0;
}

2、执行结果

执行结果 :

鼠标移动 , 触发的事件 :

键盘按键 , 触发的事件 :

鼠标点击触发的事件 :

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、SDL 事件处理简介
    • 1、SDL 事件处理引入
      • 2、SDL 事件处理步骤
      • 二、SDL 事件数据结构分析
        • 1、SDL_Event 事件 - union 联合体
          • 2、SDL_Event 事件处理流程
          • 三、SDL 事件函数
            • 1、SDL_PollEvent 函数
              • 2、SDL_WaitEvent 函数
              • 四、完整代码示例
                • 1、代码示例
                  • 2、执行结果
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档