事件子系统(CategoryEvents)
SDL 事件队列的管理核心模块,是应用与「外部交互」的核心通道——几乎所有和现实世界的交互信息(用户操作、硬件插拔、系统状态变化等)都会流经事件队列。
核心使用方式
应用通常在「新帧开始」时处理事件队列:
- 最常用:循环调用
SDL_PollEvent()直到返回 false(无事件),逐个处理或忽略事件; - 回调模式:若使用主回调函数,事件会在
SDL_AppIterate()调用前通过SDL_AppEvent()逐个传入,此时无需调用SDL_PollEvent(); - 其他控制方式:
SDL_PeepEvents():功能更丰富,但使用复杂度更高;SDL_WaitEvent():阻塞进程直到有事件发生(低功耗硬件场景更友好);SDL_AddEventWatch():注册回调,有新事件时触发。
自定义事件
应用也可主动生成事件:
SDL_PushEvent():将自定义事件推入队列,供后续读取;SDL_RegisterEvents():注册自定义事件类型,确保其 ID 不与系统内置类型冲突。
函数
- SDL_AddEventWatch:注册事件监听回调(有新事件加入队列时触发该回调)
- SDL_EventEnabled:检查指定类型的事件是否被启用(返回布尔值)
- SDL_FilterEvents:对事件队列中的事件批量应用过滤器(筛选/修改事件)
- SDL_FlushEvent:清空事件队列中指定类型的单个事件
- SDL_FlushEvents:清空事件队列中指定类型范围的所有事件
- SDL_GetEventDescription:获取指定事件类型的文本描述(如 "SDL_QUIT" 对应 "Quit requested")
- SDL_GetEventFilter:获取当前设置的全局事件过滤器
- SDL_GetWindowFromEvent:从事件对象中提取关联的 SDL_Window 指针
- SDL_HasEvent:检查事件队列中是否存在指定类型的单个事件(返回布尔值)
- SDL_HasEvents:检查事件队列中是否存在指定类型范围的任意事件(返回布尔值)
- SDL_PeepEvents:从事件队列中读取/移除事件(支持批量操作,比 PollEvent 更灵活)
- SDL_PollEvent:从事件队列中取出一个事件(非阻塞,无事件时返回 false)
- SDL_PumpEvents:强制更新事件队列(收集系统输入事件,SDL 内部自动调用,一般无需手动调用)
- SDL_PushEvent:将自定义事件推入事件队列(返回是否成功)
- SDL_RegisterEvents:注册自定义事件类型(指定数量,返回起始事件 ID,避免类型冲突)
- SDL_RemoveEventWatch:移除已注册的事件监听回调
- SDL_SetEventEnabled:启用/禁用指定类型的事件(禁用后该事件不会进入队列)
- SDL_SetEventFilter:设置全局事件过滤器(可拦截/修改所有事件)
- SDL_WaitEvent:阻塞等待事件队列中有事件(直到有事件时取出并返回)
- SDL_WaitEventTimeout:带超时的阻塞等待事件(超时后返回 false,避免永久阻塞)
数据类型
- SDL_EventFilter:事件过滤器回调类型(用于拦截/修改事件,返回布尔值表示是否保留事件)
结构体
- SDL_AudioDeviceEvent:音频设备事件(设备添加/移除)
- SDL_CameraDeviceEvent:摄像头设备事件(设备添加/移除/授权)
- SDL_ClipboardEvent:剪贴板事件(内容变化)
- SDL_CommonEvent:所有事件的公共基结构体(包含时间戳、事件类型等通用字段)
- SDL_DisplayEvent:显示设备事件(分辨率变化、显示连接/断开)
- SDL_DropEvent:拖放事件(文件拖入窗口)
- SDL_Event:核心事件联合体(包含所有类型的事件结构体)
- SDL_GamepadAxisEvent:游戏手柄轴事件(摇杆/扳机移动)
- SDL_GamepadButtonEvent:游戏手柄按键事件(按下/释放)
- SDL_GamepadDeviceEvent:游戏手柄设备事件(连接/断开)
- SDL_GamepadSensorEvent:游戏手柄传感器事件(陀螺仪/加速度计数据)
- SDL_GamepadTouchpadEvent:游戏手柄触摸板事件
- SDL_JoyAxisEvent:摇杆轴事件(传统摇杆设备)
- SDL_JoyBallEvent:摇杆轨迹球事件
- SDL_JoyBatteryEvent:摇杆电池事件(电量变化)
- SDL_JoyButtonEvent:摇杆按键事件
- SDL_JoyDeviceEvent:摇杆设备事件(连接/断开)
- SDL_JoyHatEvent:摇杆方向键事件
- SDL_KeyboardDeviceEvent:键盘设备事件(连接/断开)
- SDL_KeyboardEvent:键盘事件(按键按下/释放)
- SDL_MouseButtonEvent:鼠标按键事件(按下/释放)
- SDL_MouseDeviceEvent:鼠标设备事件(连接/断开)
- SDL_MouseMotionEvent:鼠标移动事件
- SDL_MouseWheelEvent:鼠标滚轮事件
- SDL_PenAxisEvent:手写笔轴事件(压力/倾斜变化)
- SDL_PenButtonEvent:手写笔按键事件
- SDL_PenMotionEvent:手写笔移动事件
- SDL_PenProximityEvent:手写笔接近/离开事件
- SDL_PenTouchEvent:手写笔触摸事件
- SDL_PinchFingerEvent:捏合手势事件(多点触控)
- SDL_QuitEvent:退出事件(用户请求关闭应用)
- SDL_RenderEvent:渲染事件(渲染目标刷新)
- SDL_SensorEvent:传感器事件(加速度计/陀螺仪等)
- SDL_TextEditingCandidatesEvent:文本编辑候选词事件(输入法候选词)
- SDL_TextEditingEvent:文本编辑事件(输入法正在输入)
- SDL_TextInputEvent:文本输入事件(完成的文本输入)
- SDL_TouchFingerEvent:触摸手指事件(按下/移动/抬起)
- SDL_UserEvent:自定义用户事件(可携带自定义数据)
- SDL_WindowEvent:窗口事件(大小变化、移动、最小化、关闭等)
枚举
- SDL_EventAction:事件操作枚举(用于 SDL_PeepEvents,如 SDL_ADDEVENT/SDL_PEEKEVENT/SDL_GETEVENT)
- SDL_EventType:事件类型枚举(所有内置事件的类型 ID,如 SDL_QUIT/SDL_KEYDOWN 等)
宏
- (无)
FreeBASIC 示例代码
' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"
' 自定义事件类型 ID
Dim As Uint32 CUSTOM_EVENT_ID
' 事件过滤器回调:拦截并打印键盘事件
Function EventFilter Cdecl (ByVal userdata As Any Ptr, ByVal evt As SDL_Event Ptr) As Integer
Select Case evt->type
Case SDL_KEYDOWN
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "过滤器拦截:按键 %d 按下", evt->key.keysym.sym)
' 返回 1 保留事件,返回 0 丢弃事件
Return 1
Case SDL_KEYUP
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "过滤器拦截:按键 %d 释放", evt->key.keysym.sym)
Return 1
Case Else
Return 1 ' 保留其他事件
End Select
End Function
' 事件监听回调:打印所有新事件
Sub EventWatchCallback Cdecl (ByVal userdata As Any Ptr, ByVal evt As SDL_Event Ptr)
Dim As ZString Ptr evtDesc = SDL_GetEventDescription(evt->type)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "事件监听:%s (类型 ID: %d)", evtDesc, evt->type)
End Sub
' 发送自定义事件的线程函数
Sub SendCustomEventThread Cdecl (ByVal data As Any Ptr)
Dim As Integer i = 0
While (i < 5)
' 构造自定义事件
Dim As SDL_Event customEvt
customEvt.type = CUSTOM_EVENT_ID
customEvt.user.code = 100 + i
customEvt.user.data1 = Cast(Any Ptr, i)
customEvt.user.data2 = NULL
' 推入事件队列
If (SDL_PushEvent(@customEvt) > 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "线程:发送自定义事件 %d", i)
End If
SDL_Delay(1000)
i += 1
Wend
End Sub
' 主程序
Dim As SDL_Window Ptr window = NULL
Dim As SDL_Event evt
Dim As SDL_Thread Ptr thread = NULL
Dim As Integer quit = 0
' 1. 初始化 SDL
If (SDL_Init(SDL_INIT_VIDEO) < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
End 1
End If
' 2. 注册自定义事件类型(1 个自定义类型)
CUSTOM_EVENT_ID = SDL_RegisterEvents(1)
If (CUSTOM_EVENT_ID = SDL_INVALID_EVENT) Then
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "注册自定义事件失败")
SDL_Quit()
End 1
End If
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "自定义事件 ID:%d", CUSTOM_EVENT_ID)
' 3. 创建窗口
window = SDL_CreateWindow("SDL 事件系统示例", _
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _
600, 400, SDL_WINDOW_SHOWN)
If (window = NULL) Then
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建窗口失败:%s", SDL_GetError())
SDL_Quit()
End 1
End If
' 4. 设置事件过滤器和监听回调
SDL_SetEventFilter(@EventFilter, NULL)
SDL_AddEventWatch(@EventWatchCallback, NULL)
' 5. 创建线程发送自定义事件
thread = SDL_CreateThread(@SendCustomEventThread, "CustomEventThread", NULL)
If (thread = NULL) Then
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "创建线程失败:%s", SDL_GetError())
End If
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "=== 事件处理示例 ===")
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "按 ESC 退出 | 按任意键触发键盘事件 | 等待自定义事件...")
' 6. 主事件循环
While (quit = 0)
' 非阻塞读取事件
While (SDL_PollEvent(@evt))
Select Case evt.type
' 窗口关闭事件
Case SDL_QUITEVENT
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "收到退出事件")
quit = 1
' 键盘事件
Case SDL_KEYDOWN
If (evt.key.keysym.sym = SDLK_ESCAPE) Then
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "ESC 键按下,退出程序")
quit = 1
End If
' 窗口事件
Case SDL_WINDOWEVENT
Select Case evt.window.event
Case SDL_WINDOWEVENT_RESIZED
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "窗口大小变化:%dx%d", _
evt.window.data1, evt.window.data2)
End Select
' 自定义事件
Case CUSTOM_EVENT_ID
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "收到自定义事件:code=%d, data1=%d", _
evt.user.code, Cast(Integer, evt.user.data1))
' 其他事件
Case Else
' 忽略
End Select
Wend
SDL_Delay(10) ' 降低 CPU 占用
Wend
' 7. 清理资源
SDL_RemoveEventWatch(@EventWatchCallback, NULL)
If (thread <> NULL) Then
SDL_WaitThread(thread, NULL)
End If
SDL_DestroyWindow(window)
SDL_Quit()
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出")
End 0
总结
- SDL 事件子系统是应用交互的核心,
SDL_PollEvent是最常用的事件读取接口(非阻塞),SDL_WaitEvent适用于低功耗场景(阻塞); - FreeBASIC 中使用时需注意:
- 自定义事件需通过
SDL_RegisterEvents注册 ID,避免冲突; - 事件过滤器(
SDL_SetEventFilter)可全局拦截事件,监听回调(SDL_AddEventWatch)仅监听不拦截; SDL_PushEvent可跨线程发送事件,是线程间通信的常用方式;
- 自定义事件需通过
- 核心流程:初始化 → 注册自定义事件(可选)→ 设置过滤器/监听(可选)→ 事件循环处理 → 清理资源。
评论一下?