diff --git a/docs/native-player-inventory.md b/docs/native-player-inventory.md index c63846bc..1b51bba8 100644 --- a/docs/native-player-inventory.md +++ b/docs/native-player-inventory.md @@ -233,8 +233,11 @@ Unchanged Java Creative slot echoes preserve the existing native stack and netwo Installed direct and ViaProxy controls confirm these pointer, capacity, and reopening cases through successful server responses. Recipe families beyond the captured plank, cake, and honey controls, recipes with more than two outputs, and complete installed native parity remain unfinished. -Local Bedrock 1.26.60.5 controls retain Creative search through Overworld/Nether transitions and clear it after world exit and re-entry. -Multiplayer reconnects, server transfers, other world changes, and matching Java runtime comparisons remain unverified. +Bedrock 1.26.60.5 controls retain Creative search through Overworld/Nether transitions in local worlds and on an owned BDS 1.26.60.6 server. +Installed direct and ViaProxy controls match this multiplayer retention. +Native and both Java routes clear search after disconnecting and reconnecting in the same game process. Local Bedrock also clears it after world exit and re-entry. +The native multiplayer control uses a transport/authentication relay with blob caching disabled. Inventory and movement packets remain unchanged. The proxy control enters its query through normal UI paste. +Server transfers, other world changes, and complete search input parity remain unverified. Official Bedrock 1.26.60.5/BDS 1.26.60.6 controls delay the initial open acknowledgment by four seconds on protocol 2225. They establish visible admission, repeated input, movement, and cancellation through Escape or chat. Rebuilt direct and ViaProxy controls retain the world during that same delay in Survival, Creative, and workbench openings. diff --git a/native/capture-x11/src/x11.rs b/native/capture-x11/src/x11.rs index b2a27323..d97c0bb8 100644 --- a/native/capture-x11/src/x11.rs +++ b/native/capture-x11/src/x11.rs @@ -334,6 +334,10 @@ impl Display { fn focus(&self, window: Window) -> Result<()> { self.geometry(window)?; + if self.connection.get_input_focus()?.reply()?.focus == window { + // Repeated activation can make SDL rebuild a focused text editor. + return Ok(()); + } let time = self.server_time()?; self.connection .configure_window( diff --git a/native/capture-x11/tests/xvfb.rs b/native/capture-x11/tests/xvfb.rs index 1c91f2a3..fea23cc2 100644 --- a/native/capture-x11/tests/xvfb.rs +++ b/native/capture-x11/tests/xvfb.rs @@ -502,6 +502,58 @@ fn discovers_reparented_clients_and_delivers_their_focus_protocol_before_input() ); } +#[test] +#[ignore = "requires Xvfb; run cargo test -- --include-ignored"] +fn typing_in_the_focused_window_does_not_repeat_window_activation() { + let fixture = Fixture::new(24); + let protocols = fixture.atom(b"WM_PROTOCOLS"); + let take_focus = fixture.atom(b"WM_TAKE_FOCUS"); + fixture + .connection + .change_property32( + PropMode::REPLACE, + fixture.window, + protocols, + AtomEnum::ATOM, + &[take_focus], + ) + .unwrap() + .check() + .unwrap(); + fixture + .connection + .change_window_attributes( + fixture.window, + &xproto::ChangeWindowAttributesAux::new().event_mask( + EventMask::KEY_PRESS | EventMask::KEY_RELEASE | EventMask::STRUCTURE_NOTIFY, + ), + ) + .unwrap() + .check() + .unwrap(); + let id = fixture.window.to_string(); + fixture.success(&["focus", &id]); + fixture.events(); + + fixture.success(&["type", &id, "a"]); + let events = fixture.events(); + assert!( + events + .iter() + .any(|event| matches!(event, Event::KeyPress(_))) + ); + assert!( + events + .iter() + .any(|event| matches!(event, Event::KeyRelease(_))) + ); + assert!( + !events + .iter() + .any(|event| matches!(event, Event::ConfigureNotify(_) | Event::ClientMessage(_))) + ); +} + #[test] #[ignore = "requires Xvfb; run cargo test -- --include-ignored"] fn emits_ordered_input_and_releases_it_on_interrupts_and_window_failure() {