feat(nescope): the box's output follows the client's window size

A new input event, INPUT_OUTPUT_SIZE [4][w u16][h u16], rides the input
path to nescope, which resizes its output and every window on it. The
size is clamped to the launch size, so the box's encode budget holds,
and kept even for 4:2:0. apply_resize existed but nothing called it.

Old hubs stop a batch at an unknown type, so clients send it alone.
This commit is contained in:
Wanjohi
2026-10-17 12:10:10 +03:00
parent 07e504f4c3
commit 176dd2dc2b
5 changed files with 72 additions and 5 deletions
+2
View File
@@ -338,6 +338,8 @@ pub fn decode_wire_event(data: &[u8]) -> Option<InputEvent> {
DecodedInput::MouseWheel { dx: _dx, dy } => {
Some(InputEvent::ScrollVertical { amount: dy as f64 })
}
// Not input: the IPC source hands it to the output before this.
DecodedInput::OutputSize { .. } => None,
}
}
+5 -1
View File
@@ -404,7 +404,11 @@ fn main() {
}
loop_handle
.insert_source(source, move |payload, _, data| {
if let Some(event) = decode_wire_event(&payload) {
if let Some(nesprotocol::input::DecodedInput::OutputSize { width, height }) =
nesprotocol::input::decode_input_event(&payload)
{
data.state.request_output_size(width, height);
} else if let Some(event) = decode_wire_event(&payload) {
process_input(event, &mut data.state);
}
})
+19 -1
View File
@@ -59,6 +59,10 @@ use crate::input::InputEvent;
// CalloopData — threaded through the event loop
// ---------------------------------------------------------------------------
/// The smallest side a client's window can make the output. Below it, games
/// lay out menus they cannot fit, and nothing is gained.
const MIN_OUTPUT: u32 = 320;
pub struct CalloopData {
pub state: NescopeState,
pub display: Display<NescopeState>,
@@ -195,6 +199,10 @@ pub struct NescopeState {
// ── Dimensions + frame rate ───────────────────────────────────────────
pub width: u32,
pub height: u32,
/// The size the box was started at, which is what its encode budget was
/// counted for, ref(d-0036): a client's window can shrink the output
/// below it, never grow it past.
pub launch_size: (u32, u32),
pub fps: u32,
// ── Lifecycle ─────────────────────────────────────────────────────────
@@ -328,6 +336,7 @@ impl NescopeState {
presentation_seq: 0,
width,
height,
launch_size: (width, height),
fps,
loop_handle,
game_launched: false,
@@ -462,8 +471,17 @@ impl NescopeState {
// Resize
// -----------------------------------------------------------------------
/// The client's window is `width`x`height`: follow it, inside the launch
/// size and on even numbers, which every encoder needs for 4:2:0.
pub fn request_output_size(&mut self, width: u16, height: u16) {
let fit = |want: u16, max: u32| (u32::from(want).clamp(MIN_OUTPUT, max) & !1).max(2);
let (w, h) = (fit(width, self.launch_size.0), fit(height, self.launch_size.1));
if (w, h) != (self.width, self.height) {
self.apply_resize(w, h);
}
}
/// Apply a new output resolution (e.g. from a runtime resize request).
#[allow(unused)]
pub fn apply_resize(&mut self, width: u32, height: u32) {
tracing::info!("Applying resize: {width}x{height}");
self.width = width;
+5 -3
View File
@@ -6,7 +6,9 @@ use tokio::sync::Mutex;
use tracing::{debug, info, warn};
use nesprotocol::datagram::{DGRAM_AUDIO, DGRAM_BUFFER_BYTES, DGRAM_VIDEO};
use nesprotocol::input::{INPUT_KEY, INPUT_MOUSE_BUTTON, INPUT_MOUSE_MOVE, INPUT_MOUSE_WHEEL};
use nesprotocol::input::{
INPUT_KEY, INPUT_MOUSE_BUTTON, INPUT_MOUSE_MOVE, INPUT_MOUSE_WHEEL, INPUT_OUTPUT_SIZE,
};
use nesprotocol::{BIDI_CONTROL, BIDI_INPUT, Carrier, STREAM_CURSOR, STREAM_STATS};
use nesprotocol::{ControlMode, ReceiverReport, decode_control_mode, decode_receiver_report};
use nesprotocol::{FRAME_HDR_LEN, STREAM_VERSION, encode_frame};
@@ -521,8 +523,8 @@ fn split_input_events(payload: &[u8]) -> Vec<Vec<u8>> {
let len = match payload[offset] {
// [type][up/down][keycode u16 LE]
INPUT_KEY => 4,
// [type][dx i16 LE][dy i16 LE]
INPUT_MOUSE_MOVE | INPUT_MOUSE_WHEEL => 5,
// [type][dx i16 LE][dy i16 LE], and [type][w u16 LE][h u16 LE]
INPUT_MOUSE_MOVE | INPUT_MOUSE_WHEEL | INPUT_OUTPUT_SIZE => 5,
// [type][button][up/down]
INPUT_MOUSE_BUTTON => 3,
other => {