HIDAPI 子系统(CategoryHIDAPI)
SDL HIDAPI 功能的头文件封装模块,适配了 Alan Ott 开发的原生 HIDAPI 接口,其源代码遵循以下许可协议:
HIDAPI - 用于与 HID 设备通信的跨平台库
版权所有 2009,Alan Ott,Signal 11 Software。
保留所有权利。
只要源文件中的版权声明保持完整,任何人可出于任何原因使用本软件。
注:该许可协议与 SDL zlib 许可协议的第三条内容一致,不会为使用者增加任何新的约束条件。
若你希望使用不含此模块的 SDL 版本,可在编译时将 SDL_HIDAPI_DISABLED 定义为 1。例如在 iOS 或 tvOS 平台上,关闭该模块可避免依赖 CoreBluetooth 框架。
函数
- SDL_hid_ble_scan:扫描蓝牙低功耗(BLE)HID 设备(指定扫描时长,发现可用的 BLE HID 设备)
- SDL_hid_close:关闭已打开的 HID 设备句柄(释放设备资源,终止与设备的通信)
- SDL_hid_device_change_count:获取 HID 设备变更计数(用于检测设备的插拔事件)
- SDL_hid_enumerate:枚举系统中所有 HID 设备(按厂商ID/产品ID筛选,返回设备信息链表)
- SDL_hid_exit:退出 HIDAPI 子系统(释放全局资源,与 SDL_hid_init 配对使用)
- SDL_hid_free_enumeration:释放枚举 HID 设备时分配的内存(避免内存泄漏)
- SDL_hid_get_device_info:获取已打开 HID 设备的详细信息(返回 SDL_hid_device_info 结构体)
- SDL_hid_get_feature_report:从 HID 设备读取特征报告(用于获取设备配置/状态信息)
- SDL_hid_get_indexed_string:读取 HID 设备的索引字符串(获取设备的自定义描述字符串)
- SDL_hid_get_input_report:从 HID 设备读取输入报告(获取设备的实时输入数据)
- SDL_hid_get_manufacturer_string:读取 HID 设备的厂商名称字符串
- SDL_hid_get_product_string:读取 HID 设备的产品名称字符串
- SDL_hid_get_properties:获取 HID 设备的属性集合(如支持的报告类型、传输速率等)
- SDL_hid_get_report_descriptor:获取 HID 设备的报告描述符(解析设备的输入/输出报告格式)
- SDL_hid_get_serial_number_string:读取 HID 设备的序列号字符串
- SDL_hid_init:初始化 HIDAPI 子系统(必须先调用,否则无法使用其他 HIDAPI 函数)
- SDL_hid_open:通过厂商ID和产品ID打开 HID 设备(支持指定设备序列号区分同型号设备)
- SDL_hid_open_path:通过设备路径打开 HID 设备(更精准的设备定位方式)
- SDL_hid_read:从 HID 设备读取数据(阻塞式读取,直到有数据可用)
- SDL_hid_read_timeout:带超时的 HID 设备数据读取(指定超时时间,避免永久阻塞)
- SDL_hid_send_feature_report:向 HID 设备发送特征报告(用于配置设备参数)
- SDL_hid_set_nonblocking:设置 HID 设备的非阻塞模式(影响 read/write 操作的阻塞行为)
- SDL_hid_write:向 HID 设备写入数据(发送输出报告,控制设备行为)
数据类型
- SDL_hid_device:HID 设备句柄类型(标识已打开的 HID 设备,用于后续读写操作)
结构体
- SDL_hid_device_info:HID 设备信息结构体(包含厂商ID、产品ID、设备路径、名称、总线类型等)
枚举
- SDL_hid_bus_type:HID 设备总线类型枚举(如 USB、蓝牙、I2C、SPI 等)
宏
- (无)
FreeBASIC 示例代码
' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库,且启用 HIDAPI)
#Include "SDL.bi"
' 定义常用常量(示例:通用 USB HID 设备的厂商/产品ID,可替换为实际设备值)
Const VENDOR_ID As UShort = &H1234 ' 厂商ID(示例值,需替换为实际设备ID)
Const PRODUCT_ID As UShort = &H5678 ' 产品ID(示例值,需替换为实际设备ID)
Const REPORT_SIZE As Integer = 64 ' HID 报告长度(根据设备协议调整)
' 枚举并打印所有 HID 设备信息
Sub EnumerateAndPrintHIDDevices()
' 枚举所有 HID 设备(vendor_id=0, product_id=0 表示枚举全部)
Dim As SDL_hid_device_info Ptr devs = SDL_hid_enumerate(0, 0)
If (devs = NULL) Then
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "枚举 HID 设备失败:%s", SDL_GetError())
Return
End If
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "==== 系统中所有 HID 设备 ====")
' 遍历设备链表
Dim As SDL_hid_device_info Ptr dev = devs
Dim As Integer devCount = 0
While (dev <> NULL)
devCount += 1
' 获取总线类型描述
Dim As String busType = ""
Select Case dev->bus_type
Case SDL_HID_BUS_USB: busType = "USB"
Case SDL_HID_BUS_BLUETOOTH: busType = "蓝牙(BLE)"
Case SDL_HID_BUS_I2C: busType = "I2C"
Case SDL_HID_BUS_SPI: busType = "SPI"
Case Else: busType = "未知总线"
End Select
' 打印设备信息
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "设备 %d:", devCount)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 厂商ID: 0x%04X", dev->vendor_id)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 产品ID: 0x%04X", dev->product_id)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 设备路径: %s", dev->path)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 厂商名称: %s", IIf(dev->manufacturer_string, dev->manufacturer_string, "未知"))
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 产品名称: %s", IIf(dev->product_string, dev->product_string, "未知"))
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 序列号: %s", IIf(dev->serial_number, dev->serial_number, "无"))
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 总线类型: %s", busType)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "------------------------")
dev = dev->next
Wend
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "共发现 %d 个 HID 设备", devCount)
' 释放枚举内存
SDL_hid_free_enumeration(devs)
End Sub
' 打开指定 HID 设备并测试读写
Function OpenAndTestHIDDevice(ByVal vendorID As UShort, ByVal productID As UShort) As Boolean
' 初始化 HIDAPI
Dim As Integer initResult = SDL_hid_init()
If (initResult < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "初始化 HIDAPI 失败:%s", SDL_GetError())
Return False
End If
' 打开 HID 设备
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "尝试打开 HID 设备 (VID:0x%04X, PID:0x%04X)...", vendorID, productID)
Dim As SDL_hid_device Ptr dev = SDL_hid_open(vendorID, productID, NULL)
If (dev = NULL) Then
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "打开 HID 设备失败:%s", SDL_GetError())
SDL_hid_exit()
Return False
End If
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "成功打开 HID 设备")
' 获取设备信息
Dim As ZString * 256 manufacturer, product, serial
If (SDL_hid_get_manufacturer_string(dev, @manufacturer, 256) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "厂商名称:%s", manufacturer)
End If
If (SDL_hid_get_product_string(dev, @product, 256) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "产品名称:%s", product)
End If
If (SDL_hid_get_serial_number_string(dev, @serial, 256) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "序列号:%s", serial)
End If
' 设置非阻塞模式
SDL_hid_set_nonblocking(dev, 1)
' 测试:发送输出报告(示例数据,需根据设备协议调整)
Dim As UByte outputReport(0 To REPORT_SIZE-1) = {0}
outputReport(0) = 0x01 ' 报告ID(根据设备协议)
outputReport(1) = 0x02 ' 数据1
outputReport(2) = 0x03 ' 数据2
Dim As Integer writeResult = SDL_hid_write(dev, @outputReport(0), REPORT_SIZE)
If (writeResult < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "写入 HID 设备失败:%s", SDL_GetError())
Else
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "成功写入 %d 字节到 HID 设备", writeResult)
End If
' 测试:读取输入报告(超时 1000ms)
Dim As UByte inputReport(0 To REPORT_SIZE-1) = {0}
Dim As Integer readResult = SDL_hid_read_timeout(dev, @inputReport(0), REPORT_SIZE, 1000)
If (readResult < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "读取 HID 设备失败:%s", SDL_GetError())
ElseIf (readResult = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "读取超时(1000ms),无数据返回")
Else
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "成功读取 %d 字节:", readResult)
' 打印读取到的数据(十六进制)
Dim As String hexStr = ""
For i As Integer = 0 To readResult-1
hexStr += SDL_Format("0x%02X ", inputReport(i))
Next
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, " 数据:%s", hexStr)
End If
' 关闭设备
SDL_hid_close(dev)
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "已关闭 HID 设备")
' 退出 HIDAPI
SDL_hid_exit()
Return True
End Function
' 主程序
Sub Main()
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "=== SDL HIDAPI 示例程序 ===")
' 1. 枚举并打印所有 HID 设备
EnumerateAndPrintHIDDevices()
' 2. 尝试打开指定 HID 设备(替换为实际设备的 VID/PID)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "==== 测试指定 HID 设备 ====")
Dim As Boolean testResult = OpenAndTestHIDDevice(VENDOR_ID, PRODUCT_ID)
If (testResult) Then
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "HID 设备测试成功")
Else
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "HID 设备测试失败(请检查 VID/PID 是否正确)")
End If
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序结束")
End Sub
' 运行主程序
Main()
' 辅助函数:格式化字符串(模拟 SDL 格式化输出)
Function SDL_Format(ByVal fmt As String, ...) As String
Dim As Any Ptr args = va_start()
Dim As ZString * 1024 buffer
vsprintf(@buffer, fmt, args)
va_end(args)
Return buffer
End Function
核心知识点补充
-
HID 设备基础概念:
- VID/PID:厂商ID(Vendor ID)和产品ID(Product ID)是识别 USB/HID 设备的唯一标识,由 USB-IF 分配;
- 报告(Report):HID 设备通过报告传输数据,分为输入报告(设备→主机)、输出报告(主机→设备)、特征报告(配置/状态);
- 报告描述符:定义设备支持的报告格式、数据长度、用途,是解析 HID 数据的关键。
-
开发注意事项:
- 权限问题:Linux/macOS 平台访问 HID 设备可能需要 root 权限或自定义 udev 规则;
- 跨平台兼容:Windows 需注意设备路径格式,macOS/iOS 需处理蓝牙 HID 设备的扫描逻辑;
- 非阻塞模式:设置非阻塞模式后,
SDL_hid_read会立即返回,避免主线程阻塞。
-
调试技巧:
- 使用
SDL_hid_enumerate(0, 0)枚举所有设备,确认目标设备的 VID/PID 和路径; - 通过
SDL_hid_get_report_descriptor获取报告描述符,解析设备的数据格式; - 移动平台(Android/iOS)需配置权限(如蓝牙权限)才能访问 BLE HID 设备。
- 使用
总结
- 核心优势:
- SDL HIDAPI 封装了原生 HIDAPI,提供跨平台的 HID 设备访问接口,无需关注底层系统差异;
- 支持 USB、蓝牙等多种总线类型的 HID 设备,涵盖键鼠、手柄、传感器、自定义 HID 设备;
- 许可协议宽松,可自由使用且无额外约束;
- 使用建议:
- 必须先调用
SDL_hid_init初始化,使用完调用SDL_hid_exit释放资源; - 枚举设备后务必调用
SDL_hid_free_enumeration释放内存,避免泄漏; - 实际开发中需根据设备的 HID 协议解析读写数据,示例中的报告格式仅为参考;
- 必须先调用
- 关键接口:
- 设备枚举:
SDL_hid_enumerate/SDL_hid_free_enumeration; - 设备操作:
SDL_hid_open/SDL_hid_close/SDL_hid_init/SDL_hid_exit; - 数据读写:
SDL_hid_read/SDL_hid_write/SDL_hid_read_timeout。
- 设备枚举:
评论一下?