You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bevy中实现全局输入?第三方库使用遇阻求助

如何在Bevy中实现全局播放/暂停按键检测(窗口失焦时仍有效)

我正在用Bevy和Rust开发音乐播放器,需要实现全局输入检测——即使窗口未获得焦点,也能响应播放/暂停按键的按下操作。我尝试了三款第三方库,但都遇到了问题:

  • bevy_global_input插件添加失败
    添加插件时触发语法错误,错误信息如下:

    error[E0277]: the trait bound `GlobalInputPlugins: Plugins<_>` is not satisfied
       --> src\gui\mod.rs:20:18
        |
    20  |     .add_plugins(GlobalInputPlugins)
        |      ----------- ^^^^^^^^^^^^^^^^^^ the trait `bevy::bevy_app::plugin::sealed::Plugins<_>` is not implemented for `GlobalInputPlugins`, which is required by `GlobalInputPlugins: Plugins<_>`
        |      |
        |      required by a bound introduced by this call
        |
    = help: the following other types implement trait `bevy::bevy_app::plugin::sealed::Plugins<Marker>`:
              `()` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker,)>`
              `(S0, S1)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1)>`
              `(S0, S1, S2)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2)>`
              `(S0, S1, S2, S3)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3)>`
              `(S0, S1, S2, S3, S4)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4)>`
              `(S0, S1, S2, S3, S4, S5)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5)>`
              `(S0, S1, S2, S3, S4, S5, S6)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5, P6)>`
              `(S0, S1, S2, S3, S4, S5, S6, S7)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5, P6, P7)>`
            and 8 others
    = note: required for `GlobalInputPlugins` to implement `Plugins<_>`
    note: required by a bound in `bevy::prelude::App::add_plugins`
       --> C:\Users\samsj\.cargo\registry\src\index.crates.io-6f17d22bba15001f\bevy_app-0.14.2\src\app.rs:543:52
        |
    543 |     pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
        |                                                    ^^^^^^^^^^ required by this bound in `App::add_plugins`
    
  • inputbot库无法适配Bevy帧逻辑
    该库仅支持绑定按键按下的回调函数,无法直接检测按键是否在上一帧被按下。我尝试将其放入独立线程,通过通道传递数据到Bevy资源,但通道的Receiver导致结构体不满足Sync trait,无法作为Bevy资源使用。

  • hookmap-core库同样存在Sync问题
    与inputbot类似,需要在资源中持有Receiver,同样触发结构体不满足Sync trait的错误。


解决方案

1. 修复bevy_global_input的插件添加问题

这个错误通常是版本不兼容或调用方式错误导致的:

  • 先检查Cargo.toml中bevy_global_input的版本是否与你使用的Bevy版本匹配(比如Bevy 0.14需要对应支持该版本的插件)。
  • 若版本没问题,尝试修改插件添加方式为实例化调用:
    .add_plugins(GlobalInputPlugins::default())
    
    多数Bevy插件需要通过default()创建实例,而非直接传递类型。

2. 用线程安全封装适配inputbot/hookmap-core

针对通道Receiver的Sync问题,可以用Arc<Mutex<>>包裹接收器,让结构体满足Send + Sync要求。以inputbot为例:

use bevy::prelude::*;
use inputbot::{KeybdKey};
use std::sync::{Arc, Mutex};
use std::thread;

// 全局输入资源:用Arc<Mutex>包裹触发标记
#[derive(Resource)]
struct GlobalInput {
    play_pause_triggered: Arc<Mutex<bool>>,
}

fn setup_global_input(mut commands: Commands) {
    let trigger = Arc::new(Mutex::new(false));
    let trigger_clone = trigger.clone();

    // 启动独立线程监听全局按键
    thread::spawn(move || {
        KeybdKey::MediaPlayPauseKey.bind(move || {
            *trigger_clone.lock().unwrap() = true;
        });
        inputbot::handle_input_events();
    });

    commands.insert_resource(GlobalInput { play_pause_triggered: trigger });
}

// 在Bevy系统中检测并处理触发事件
fn check_play_pause(mut input: ResMut<GlobalInput>, mut audio_state: ResMut<AudioPlaybackState>) {
    let mut triggered = input.play_pause_triggered.lock().unwrap();
    if *triggered {
        audio_state.toggle();
        *triggered = false; // 重置标记避免重复触发
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup_global_input)
        .add_system(check_play_pause)
        .run();
}

3. 替代方案:使用global-hotkey库

如果上述库适配麻烦,可以试试global-hotkey,它支持跨平台全局快捷键,且适配Bevy逻辑更顺畅:

use bevy::prelude::*;
use global_hotkey::{GlobalHotKeyManager, HotKey, Key};
use std::sync::{Arc, Mutex};

#[derive(Resource)]
struct HotkeyTrigger(Arc<Mutex<bool>>);

fn setup_hotkeys(mut commands: Commands) {
    let trigger = Arc::new(Mutex::new(false));
    let trigger_clone = trigger.clone();

    let mut manager = GlobalHotKeyManager::new().unwrap();
    let play_pause_hotkey = HotKey::new(None, Key::MediaPlayPause);
    
    manager.register(play_pause_hotkey, move || {
        *trigger_clone.lock().unwrap() = true;
    }).unwrap();

    commands.insert_resource(HotkeyTrigger(trigger));
}

fn handle_hotkey_trigger(mut hotkey: ResMut<HotkeyTrigger>, mut audio_state: ResMut<AudioPlaybackState>) {
    let mut triggered = hotkey.0.lock().unwrap();
    if *triggered {
        audio_state.toggle();
        *triggered = false;
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup_hotkeys)
        .add_system(handle_hotkey_trigger)
        .run();
}

内容的提问来源于stack exchange,提问作者Mr. Sja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:24:50