0x40A3 · fnInversionMultiHost
每主机 Fn 键翻转——读取或写入选定 Easy-Switch 主机槽位及其报告的默认状态。
0x40a3 控制报告这个 feature 的多主机键盘的选定主机槽位的 Fn 键翻转。Off 使 F 行按键默认发送 F1–F12。On 使这些按键默认执行印在键帽上的媒体或快捷功能。每个响应都包含所选主机、默认状态和能力位。
**规格:**Logitech HID++ 2.0——fnInversionForMultiHostDevices。**交叉核对:**Logi Options+ 2.8(静态分析)、Solaar。用于:键盘。
函数参考
FnInversionMultiHostFeature 从 feature version 0 开始实现 feature 0x40a3。
方法
| 函数 | HID++ fn | 签名 | 返回值 |
|---|---|---|---|
get_global_fn_inversion | 0 | (host: HostIndex) | FnInversionInfo |
set_global_fn_inversion | 1 | (host: HostIndex, state: FnInversionState) | FnInversionInfo |
is_state_broadcast | 任意,广播 | (message: &v20::Message) | bool |
get 和 set 方法都是 async,并返回 Result<…, Hidpp20Error>。is_state_broadcast 是同步识别辅助方法,不发送 I/O。
类型
FnInversionInfo
| 字段 | 类型 | 说明 |
|---|---|---|
host_index | HostIndex | 设备返回的主机选择器。 |
state | FnInversionState | 当前翻转状态。 |
default_state | FnInversionState | 报告的默认翻转状态。 |
capabilities | FnInversionCapabilities | 报告的能力位。 |
FnInversionState
这个不可穷举的状态枚举定义了以下值:
| 变体 | 值 | 说明 |
|---|---|---|
Off | 0 | F 行按键默认发送 F1–F12。 |
On | 1 | F 行按键默认执行印在键帽上的媒体或快捷功能。 |
FnInversionCapabilities
| 标志 | 位 | 说明 |
|---|---|---|
MANUAL_FN_LOCK | 0 | 设备报告支持手动 Fn 锁。 |
| 未知位 | 1–7 | OpenLogi 在 bitflags 值中保留这些位,但不命名或用它们作门控。 |
OpenLogi 也不使用 MANUAL_FN_LOCK 作为写入门控。
HostIndex
这个不可穷举的主机选择器有以下已定义形式:
| 变体 | 线值 | 说明 |
|---|---|---|
Current | 0xff | 键盘当前选择的槽位。 |
Slot(u8) | 0x00–0xfe | 从零开始的主机槽位。 |
事件
crate 没有提供类型化事件。每个 HID++ 长消息都会使 is_state_broadcast 返回 true。它不检查 function ID,也不解码有效载荷。
线格式
所有请求有效载荷都有 3 个字节。这个 feature 没有多字节字段。响应必须是 HID++ 长消息;OpenLogi 会拒绝短响应。
get_global_fn_inversion(fn 0)
| 字节 | 值 | 备注 |
|---|---|---|
| 0 | u8::from(host) | 0xff 选择当前槽位;0x00–0xfe 选择从零开始的槽位。 |
| 1–2 | 0x00 | OpenLogi 写入固定的零填充。 |
| 响应字节 | 字段 | 备注 |
|---|---|---|
| 0 | host_index | 0xff = Current;其他值 = Slot(value)。 |
| 1 | state | 0 = Off;1 = On。 |
| 2 | default_state | 0 = Off;1 = On。 |
| 3 | capabilities | 位 0 = MANUAL_FN_LOCK。未知位会保留。 |
| 4–15 | / | 没有定义字段。OpenLogi 忽略这些字节。 |
未知的 state 或 default_state 值会返回 Hidpp20Error::UnsupportedResponse。
set_global_fn_inversion(fn 1)
| 字节 | 值 | 备注 |
|---|---|---|
| 0 | u8::from(host) | 当前槽位或从零开始的槽位选择器。 |
| 1 | u8::from(state) | 0 = Off;1 = On。 |
| 2 | 0x00 | OpenLogi 写入固定的零填充。 |
响应使用与 function 0 相同的 4 个解码字段。OpenLogi 也要求这个函数的响应是长消息。
状态变更广播
Logi Options+ 2.8 会解码每个非请求 0x40a3 报告的 4 个字段,且不筛选 function ID。OpenLogi 把这个 feature 的每个长消息识别为状态变更信号。它不解码广播有效载荷。
清单监听器只考虑 software ID 为 0 且目标为这个 feature 索引的消息。它会增加设备设置版本号,然后桌面应用重新读取 Fn 锁。
OpenLogi 的用法
OpenLogi 优先使用 0x40a3,而不是两个旧变体。读取时,它使用 HostIndex::Current,并在请求中写入 0xff。面向用户的设置只读取和写入当前 Easy-Switch 槽位。
写入时,OpenLogi 先读取 0x1815 HostsInfo,再显式寻址当前槽位。这避开了 MX Keys S 固件忽略 0xff 写入的已知情况。如果没有 HostsInfo,OpenLogi 使用 0xff。如果 feature 存在但不能读取,OpenLogi 会让写入失败,而不是猜测。
请求写入完整状态值,因此 OpenLogi 不使用读-改-写。它会将返回的 Fn 锁状态与请求状态比较。从 v0.8.14 开始,PR #1782 拒绝短响应,而不是把缺失的能力字节视为零。
从 v0.8.14 开始,PR #1791 会在识别到设备端状态变更后重新读取设置。这个变更不会更新 config.toml。OpenLogi 会在重连后重放已配置的 fn_lock 值,而不会在会话结束时恢复先前的值。
包装器可以寻址任意 HostIndex。OpenLogi 的设备层和桌面层没有提供读取全部槽位或写入其他槽位的操作。这与 Options+ 可见的 Fn 锁控件一致,后者只使用当前槽位。
未确认的细节
- 硬件抓包尚未确认非请求报告的准确触发条件或 function ID。Options+ 2.8 静态分析不筛选 function ID。因此,OpenLogi 会在每个匹配的长广播后重新读取。
- 硬件抓包尚未确认 feature version 高于基础布局时增加的字段。OpenLogi 从 version 0 开始注册这个包装器,并忽略响应的字节 4–15。
Rust 用法
use hidpp::{
device::Device,
feature::{
fn_inversion::{FnInversionMultiHostFeature, FnInversionState},
hosts_info::HostIndex,
},
};
async fn inspect(device: &mut Device) -> Result<(), hidpp::protocol::v20::Hidpp20Error> {
let _ = device.enumerate_features().await?;
let Some(feature) = device.get_feature::<FnInversionMultiHostFeature>() else {
return Ok(());
};
let info = feature
.get_global_fn_inversion(HostIndex::Current)
.await?;
println!("state={:?}, default={:?}", info.state, info.default_state);
let updated = feature
.set_global_fn_inversion(HostIndex::Slot(0), FnInversionState::Off)
.await?;
println!("slot {:?} now has {:?}", updated.host_index, updated.state);
Ok(())
}