OpenLogi

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_inversion0(host: HostIndex)FnInversionInfo
set_global_fn_inversion1(host: HostIndex, state: FnInversionState)FnInversionInfo
is_state_broadcast任意,广播(message: &v20::Message)bool

get 和 set 方法都是 async,并返回 Result<…, Hidpp20Error>。is_state_broadcast 是同步识别辅助方法,不发送 I/O。

类型

FnInversionInfo

字段类型说明
host_indexHostIndex设备返回的主机选择器。
stateFnInversionState当前翻转状态。
default_stateFnInversionState报告的默认翻转状态。
capabilitiesFnInversionCapabilities报告的能力位。

FnInversionState

这个不可穷举的状态枚举定义了以下值:

变体值说明
Off0F 行按键默认发送 F1–F12。
On1F 行按键默认执行印在键帽上的媒体或快捷功能。

FnInversionCapabilities

标志位说明
MANUAL_FN_LOCK0设备报告支持手动 Fn 锁。
未知位1–7OpenLogi 在 bitflags 值中保留这些位,但不命名或用它们作门控。

OpenLogi 也不使用 MANUAL_FN_LOCK 作为写入门控。

HostIndex

这个不可穷举的主机选择器有以下已定义形式:

变体线值说明
Current0xff键盘当前选择的槽位。
Slot(u8)0x00–0xfe从零开始的主机槽位。

事件

crate 没有提供类型化事件。每个 HID++ 长消息都会使 is_state_broadcast 返回 true。它不检查 function ID,也不解码有效载荷。

线格式

所有请求有效载荷都有 3 个字节。这个 feature 没有多字节字段。响应必须是 HID++ 长消息;OpenLogi 会拒绝短响应。

get_global_fn_inversion(fn 0)

字节值备注
0u8::from(host)0xff 选择当前槽位;0x00–0xfe 选择从零开始的槽位。
1–20x00OpenLogi 写入固定的零填充。
响应字节字段备注
0host_index0xff = Current;其他值 = Slot(value)。
1state0 = Off;1 = On。
2default_state0 = Off;1 = On。
3capabilities位 0 = MANUAL_FN_LOCK。未知位会保留。
4–15/没有定义字段。OpenLogi 忽略这些字节。

未知的 state 或 default_state 值会返回 Hidpp20Error::UnsupportedResponse。

set_global_fn_inversion(fn 1)

字节值备注
0u8::from(host)当前槽位或从零开始的槽位选择器。
1u8::from(state)0 = Off;1 = On。
20x00OpenLogi 写入固定的零填充。

响应使用与 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(())
}

本页目录