Part of #41: the comments of `benilla-world`, Rust and WGSL, are rewritten to the rule in `docs/METHOD.md`, which is what the code does and the reference fact behind it, in a line. 24,226 comment lines become 8,863. - **The mechanical strip.** The citations that were only a private record number go first, and each edit is shown to be a pure removal. - **Wrong facts corrected.** The rewrite corrects the comments that stated wrong facts. Among them: - `0x6b62e0` is the WMO group's liquid renderer, not a doodad drawer. - The reference creates a WMO's props once, when a group enters its streaming window (`0x698720` → `0x695aa0`). It then admits them each frame the portal walk visits the group (`0x685d70` → `0x6838f0`). The old text had them created per visible group. - The liquid query's comments now state the reference's order: the map-object leg first, then the ADT (`0x69b6d0`). They no longer call the Outdoors/Inside split a match. - The interior probe runs from the character, not the camera. - The reference lights ground clutter. The old comment said it draws unlit. - The model-particle "pool order" deviation was wrong. Each instance is its own mesh, depth-sorted every frame. - The reference's vertical field of view follows the aspect ratio (44.1° at 16:9). The comment now gives it beside our fixed 45°. - The effect shader's far wall is the live `farclip`, 177 to 777 yd, not a fixed 777 yd. - Dozens more turned up on the way: renamed or deleted items, docs sitting on the wrong item, stale counts and links. - **Deviation labels.** "Deviation:" now marks only a choice with a reason. An unbuilt behaviour says so plainly. - **Verification.** Every file strips to identical code, and `cargo fmt --check` is clean. Five second readers compared every file old against new, restored the facts the rewrite had dropped, and checked each new citation against the reference. Defects the rewrite surfaced outside the comments are filed as their own issues (#106, #114, #115, #116). The comments here state what the code does. String literals are untouched; they are #92. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
191 lines
7.1 KiB
Rust
191 lines
7.1 KiB
Rust
//! The modifier keys: the dev-overlay chord every instrument is bound on, and the stuck-modifier
|
|
//! reconciliation beneath it.
|
|
//!
|
|
//! A macOS shortcut that grabs the keyboard without de-focusing the window (the ⇧⌘5 screenshot
|
|
//! overlay) swallows the modifiers' release events: no focus loss arrives, and winit synthesizes
|
|
//! modifier events only from `flagsChanged`, which the grab withholds. Shift or Cmd then stays
|
|
//! held and every bare-key binding goes dead. Each frame after Bevy's input collection, any
|
|
//! modifier the OS's live state (`+[NSEvent modifierFlags]`) reports up is released, both sides of
|
|
//! its family. Release only: synthesized presses could race in-flight release events.
|
|
|
|
use bevy::prelude::*;
|
|
|
|
/// How the dev plane is written on screen; every surface that names the chord reads this.
|
|
pub const DEV_CHORD: &str = "Ctrl+Shift";
|
|
|
|
/// Whether the dev-overlay chord, [`DEV_CHORD`] + `key`, just fired. Exactly Ctrl and Shift, either
|
|
/// side: Alt and Super block it, since AltGr is Ctrl+Alt and AltGr+Shift+key typed into chat must
|
|
/// not fire. Of the reference's 152 default bindings only `CTRL-SHIFT-TAB` and
|
|
/// `CTRL-SHIFT-PAGEDOWN` use this plane, and no letter does. Not gated on
|
|
/// `ui_script::UiKeyboardCapture`: a chord is never typed text.
|
|
///
|
|
/// Deviation: the reference (`0x4b7990`) retries a binding miss once without the leftmost
|
|
/// modifier, so `CTRL-SHIFT-P` would reach `SHIFT-P` (`TOGGLECHARACTER3`, the pet paper doll); in
|
|
/// a run with dev affordances `bindings::BindingDispatch::resolve` skips that retry on this plane,
|
|
/// because a dev chord must open nothing in game. An exact `CTRL-SHIFT-` binding still dispatches.
|
|
pub fn dev_chord(keys: &ButtonInput<KeyCode>, key: KeyCode) -> bool {
|
|
dev_plane(keys) && keys.just_pressed(key)
|
|
}
|
|
|
|
/// The modifier half of [`dev_chord`].
|
|
fn dev_plane(keys: &ButtonInput<KeyCode>) -> bool {
|
|
let ctrl = keys.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]);
|
|
let shift = keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]);
|
|
let blocked = keys.any_pressed([
|
|
KeyCode::AltLeft,
|
|
KeyCode::AltRight,
|
|
KeyCode::SuperLeft,
|
|
KeyCode::SuperRight,
|
|
]);
|
|
ctrl && shift && !blocked
|
|
}
|
|
|
|
/// Keys a synthetic input source (the probe's `WOW_PROBE_KEY` taps) holds this frame, written by
|
|
/// whatever synthesizes input on every platform: the reconciler's hardware poll reads them as up
|
|
/// and would release them a frame after the press.
|
|
#[derive(Resource, Default)]
|
|
pub struct SyntheticHold(pub Vec<KeyCode>);
|
|
|
|
/// Whether Bevy holds `code` down with no synthetic source holding it: stuck, if the OS says up.
|
|
#[cfg(any(target_os = "macos", test))]
|
|
fn is_stuck(code: KeyCode, pressed: &ButtonInput<KeyCode>, hold: &SyntheticHold) -> bool {
|
|
pressed.pressed(code) && !hold.0.contains(&code)
|
|
}
|
|
|
|
pub struct ModKeysPlugin;
|
|
|
|
impl Plugin for ModKeysPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.init_resource::<SyntheticHold>();
|
|
#[cfg(target_os = "macos")]
|
|
app.add_systems(
|
|
PreUpdate,
|
|
mac::reconcile_modifiers.after(bevy::input::InputSystems),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
mod mac {
|
|
use bevy::ecs::system::NonSendMarker;
|
|
use bevy::input::keyboard::Key;
|
|
use bevy::prelude::*;
|
|
use objc2_app_kit::{NSEvent, NSEventModifierFlags};
|
|
|
|
/// Release any modifier key Bevy holds pressed that the OS's live flag state says is up.
|
|
/// `NonSendMarker` pins the system to the main thread, which AppKit requires.
|
|
pub(super) fn reconcile_modifiers(
|
|
_main_thread: NonSendMarker,
|
|
mut codes: ResMut<ButtonInput<KeyCode>>,
|
|
mut logical: ResMut<ButtonInput<Key>>,
|
|
hold: Res<super::SyntheticHold>,
|
|
) {
|
|
let flags = unsafe { NSEvent::modifierFlags_class() };
|
|
let families = [
|
|
(
|
|
NSEventModifierFlags::NSEventModifierFlagShift,
|
|
[KeyCode::ShiftLeft, KeyCode::ShiftRight],
|
|
Key::Shift,
|
|
),
|
|
(
|
|
NSEventModifierFlags::NSEventModifierFlagControl,
|
|
[KeyCode::ControlLeft, KeyCode::ControlRight],
|
|
Key::Control,
|
|
),
|
|
(
|
|
NSEventModifierFlags::NSEventModifierFlagOption,
|
|
[KeyCode::AltLeft, KeyCode::AltRight],
|
|
Key::Alt,
|
|
),
|
|
(
|
|
NSEventModifierFlags::NSEventModifierFlagCommand,
|
|
[KeyCode::SuperLeft, KeyCode::SuperRight],
|
|
Key::Super,
|
|
),
|
|
];
|
|
for (flag, keys, key) in families {
|
|
if flags.contains(flag) {
|
|
continue;
|
|
}
|
|
// Guarded so an in-sync frame never marks the resources changed; a synthetic hold is
|
|
// never stuck, since the hardware poll cannot see it.
|
|
let mut synthetic = false;
|
|
for code in keys {
|
|
if super::is_stuck(code, &codes, &hold) {
|
|
warn!("modkeys: releasing stuck {code:?} (OS reports it up)");
|
|
codes.release(code);
|
|
}
|
|
synthetic |= hold.0.contains(&code);
|
|
}
|
|
if !synthetic && logical.pressed(key.clone()) {
|
|
logical.release(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use bevy::input::ButtonInput;
|
|
use bevy::prelude::KeyCode;
|
|
|
|
fn keys(held: &[KeyCode]) -> ButtonInput<KeyCode> {
|
|
let mut k = ButtonInput::default();
|
|
for &c in held {
|
|
k.press(c);
|
|
}
|
|
k
|
|
}
|
|
|
|
/// The plane fires on its two modifiers, either side of each.
|
|
#[test]
|
|
fn ctrl_shift_is_the_plane() {
|
|
for pair in [
|
|
[KeyCode::ControlLeft, KeyCode::ShiftRight],
|
|
[KeyCode::ControlRight, KeyCode::ShiftLeft],
|
|
] {
|
|
assert!(dev_plane(&keys(&pair)), "{pair:?} fires");
|
|
}
|
|
}
|
|
|
|
/// AltGr is Ctrl+Alt, so AltGr+Shift+key is typed text, never the chord.
|
|
#[test]
|
|
fn a_lone_or_extra_modifier_is_not_the_chord() {
|
|
for held in [
|
|
vec![KeyCode::ControlLeft],
|
|
vec![KeyCode::ShiftLeft],
|
|
vec![KeyCode::ControlLeft, KeyCode::SuperLeft],
|
|
vec![KeyCode::ControlLeft, KeyCode::ShiftLeft, KeyCode::AltLeft],
|
|
vec![KeyCode::ControlLeft, KeyCode::ShiftLeft, KeyCode::SuperLeft],
|
|
] {
|
|
assert!(!dev_plane(&keys(&held)), "{held:?} is not the chord");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_synthesized_modifier_is_not_stuck() {
|
|
let held = keys(&[KeyCode::ShiftLeft]);
|
|
assert!(is_stuck(
|
|
KeyCode::ShiftLeft,
|
|
&held,
|
|
&SyntheticHold::default()
|
|
));
|
|
let hold = SyntheticHold(vec![KeyCode::ShiftLeft]);
|
|
assert!(!is_stuck(KeyCode::ShiftLeft, &held, &hold));
|
|
assert!(
|
|
!is_stuck(KeyCode::ControlLeft, &held, &hold),
|
|
"an unheld key is nothing to release either way"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_label_matches_the_plane() {
|
|
assert_eq!(DEV_CHORD, "Ctrl+Shift");
|
|
assert!(dev_plane(&keys(&[
|
|
KeyCode::ControlLeft,
|
|
KeyCode::ShiftLeft
|
|
])));
|
|
}
|
|
}
|