Stable Point - AV/K/M working
This commit is contained in:
parent
981f1d1fa3
commit
f60736990b
@ -149,7 +149,7 @@ impl LesavkaClientApp {
|
||||
/*──────────────── keyboard stream ───────────────*/
|
||||
async fn stream_loop_keyboard(&self, ep: Channel) {
|
||||
loop {
|
||||
info!("⌨️ dial {}", self.server_addr); // LESAVKA-client
|
||||
info!("⌨️🤙 dial {}", self.server_addr); // LESAVKA-client
|
||||
let mut cli = RelayClient::new(ep.clone());
|
||||
|
||||
// ✅ use kbd_tx here - fixes E0271
|
||||
@ -171,7 +171,7 @@ impl LesavkaClientApp {
|
||||
/*──────────────── mouse stream ──────────────────*/
|
||||
async fn stream_loop_mouse(&self, ep: Channel) {
|
||||
loop {
|
||||
info!("🖱️ dial {}", self.server_addr);
|
||||
info!("🖱️🤙 dial {}", self.server_addr);
|
||||
let mut cli = RelayClient::new(ep.clone());
|
||||
|
||||
let outbound = BroadcastStream::new(self.mou_tx.subscribe())
|
||||
|
||||
@ -64,7 +64,7 @@ impl InputAggregator {
|
||||
match classify_device(&dev) {
|
||||
DeviceKind::Keyboard => {
|
||||
dev.grab().with_context(|| format!("grabbing keyboard {path:?}"))?;
|
||||
info!("Grabbed keyboard {:?}", dev.name().unwrap_or("UNKNOWN"));
|
||||
info!("🤏🖱️ Grabbed keyboard {:?}", dev.name().unwrap_or("UNKNOWN"));
|
||||
|
||||
// pass dev_mode to aggregator
|
||||
// let kbd_agg = KeyboardAggregator::new(dev, self.dev_mode);
|
||||
@ -75,7 +75,7 @@ impl InputAggregator {
|
||||
}
|
||||
DeviceKind::Mouse => {
|
||||
dev.grab().with_context(|| format!("grabbing mouse {path:?}"))?;
|
||||
info!("Grabbed mouse {:?}", dev.name().unwrap_or("UNKNOWN"));
|
||||
info!("🤏⌨️ Grabbed mouse {:?}", dev.name().unwrap_or("UNKNOWN"));
|
||||
|
||||
// let mouse_agg = MouseAggregator::new(dev);
|
||||
let mouse_agg = MouseAggregator::new(dev, self.dev_mode, self.mou_tx.clone());
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// client/src/layout.rs – Wayland-only window placement utilities
|
||||
// client/src/layout.rs - Wayland-only window placement utilities
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
@ -69,7 +69,7 @@ async fn main() -> Result<()> {
|
||||
.with(file_layer)
|
||||
.init();
|
||||
|
||||
tracing::info!("lesavka-client running in DEV mode → {}", log_path.display());
|
||||
tracing::info!("📜 lesavka-client running in DEV mode → {}", log_path.display());
|
||||
} else {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
|
||||
@ -4,6 +4,7 @@ use anyhow::{Context, Result};
|
||||
use gstreamer as gst;
|
||||
use gstreamer_app as gst_app;
|
||||
use gst::prelude::*;
|
||||
use gst::MessageView::*;
|
||||
use tracing::{error, info, warn, debug};
|
||||
|
||||
use lesavka_common::lesavka::AudioPacket;
|
||||
@ -69,7 +70,6 @@ impl AudioOut {
|
||||
// ── 4. Log *all* warnings/errors from the bus ──────────────────────
|
||||
let bus = pipeline.bus().unwrap();
|
||||
std::thread::spawn(move || {
|
||||
use gst::MessageView::*;
|
||||
for msg in bus.iter_timed(gst::ClockTime::NONE) {
|
||||
match msg.view() {
|
||||
Error(e) => error!("💥 gst error from {:?}: {} ({})",
|
||||
@ -82,8 +82,18 @@ impl AudioOut {
|
||||
.structure()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default()),
|
||||
StateChanged(s) if s.current() == gst::State::Playing =>
|
||||
info!("🔊 audio pipeline PLAYING (sink='{}')", sink),
|
||||
StateChanged(s) if s.current() == gst::State::Playing => {
|
||||
if msg
|
||||
.src()
|
||||
.map(|s| s.is::<gst::Pipeline>())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
info!("🔊 audio pipeline PLAYING (sink='{}')", sink);
|
||||
} else {
|
||||
debug!("🔊 element {} now PLAYING",
|
||||
msg.src().map(|s| s.name()).unwrap_or_default());
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -116,12 +126,11 @@ impl Drop for AudioOut {
|
||||
fn pick_sink_element() -> Result<String> {
|
||||
// 1. Operator override
|
||||
if let Ok(s) = std::env::var("LESAVKA_AUDIO_SINK") {
|
||||
info!("🎛️ sink overridden via LESAVKA_AUDIO_SINK={}", s);
|
||||
info!("💪 sink overridden via LESAVKA_AUDIO_SINK={}", s);
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
// 2. Query PipeWire for default & running sinks
|
||||
// (works even if PulseAudio is present because PipeWire mimics it)
|
||||
let sinks = list_pw_sinks(); // Vec<(name,state)>
|
||||
for (n, st) in &sinks {
|
||||
if *st == "RUNNING" {
|
||||
@ -132,45 +141,23 @@ fn pick_sink_element() -> Result<String> {
|
||||
|
||||
// 3. First RUNNING sink
|
||||
if let Some((n, _)) = sinks.iter().find(|(_, st)| *st == "RUNNING") {
|
||||
warn!("🪄 picking first RUNNING sink '{}'", n);
|
||||
return Ok(format!("pulsesink device={}", n));
|
||||
}
|
||||
// 4. Anything
|
||||
if let Some((n, _)) = sinks.first() {
|
||||
warn!("🪄 picking first sink '{}'", n);
|
||||
warn!("🏃 picking first RUNNING sink '{}'", n);
|
||||
return Ok(format!("pulsesink device={}", n));
|
||||
}
|
||||
|
||||
// Fallback – let autoaudiosink try its luck
|
||||
warn!("😬 no PipeWire sinks readable – falling back to autoaudiosink");
|
||||
// 4. Anything
|
||||
if let Some((n, _)) = sinks.first() {
|
||||
warn!("🎲 picking first sink '{}'", n);
|
||||
return Ok(format!("pulsesink device={}", n));
|
||||
}
|
||||
|
||||
// Fallback - let autoaudiosink try its luck
|
||||
warn!("🫣 no PipeWire sinks readable - falling back to autoaudiosink");
|
||||
Ok("autoaudiosink".to_string())
|
||||
}
|
||||
|
||||
/// Minimal PipeWire sink enumerator (no extra crate required).
|
||||
fn list_pw_sinks() -> Vec<(String, String)> {
|
||||
let mut out = Vec::new();
|
||||
// if let Ok(lines) = std::process::Command::new("pw-cli")
|
||||
// .args(["ls", "Node"])
|
||||
// .output()
|
||||
// .map(|o| String::from_utf8_lossy(&o.stdout).to_string())
|
||||
// {
|
||||
// for l in lines.lines() {
|
||||
// // Example: " 36 │ node.alive = true │ alsa_output.pci-0000_2f_00.4.iec958-stereo │ state: SUSPENDED ..."
|
||||
// if let Some(pos) = l.find("│") {
|
||||
// let parts: Vec<_> = l[pos..].split('│').map(|s| s.trim()).collect();
|
||||
// if parts.len() >= 3 && parts[2].starts_with("alsa_output.") {
|
||||
// let name = parts[2].to_string();
|
||||
// // try to parse state, else UNKNOWN
|
||||
// let state = parts.get(3)
|
||||
// .and_then(|s| s.split_whitespace().nth(1))
|
||||
// .unwrap_or("UNKNOWN")
|
||||
// .to_string();
|
||||
// out.push((name, state));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if out.is_empty() {
|
||||
// ── PulseAudio / pactl fallback ────────────────────────────────
|
||||
if let Ok(info) = std::process::Command::new("pactl")
|
||||
|
||||
@ -15,7 +15,7 @@ pub struct MonitorInfo {
|
||||
/// Enumerate monitors sorted by our desired priority.
|
||||
pub fn enumerate_monitors() -> Vec<MonitorInfo> {
|
||||
let Some(display) = gdk::Display::default() else {
|
||||
tracing::warn!("⚠️ no GDK display – falling back to single-monitor 0,0");
|
||||
tracing::warn!("⚠️ no GDK display - falling back to single-monitor 0,0");
|
||||
return vec![MonitorInfo {
|
||||
geometry: gdk::Rectangle::new(0, 0, 1920, 1080),
|
||||
scale_factor: 1,
|
||||
|
||||
@ -124,7 +124,7 @@ impl MonitorWindow {
|
||||
y = r.y,
|
||||
);
|
||||
|
||||
// Retry in a detached thread – avoids blocking GStreamer
|
||||
// Retry in a detached thread - avoids blocking GStreamer
|
||||
let placename = placer.name;
|
||||
let runner = placer.run.clone();
|
||||
thread::spawn(move || {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# lesavka‑core.sh – one‑shot USB‑gadget bring‑up (Pi‑5 / Arch‑ARM)
|
||||
# lesavka‑core.sh - one‑shot USB‑gadget bring‑up (Pi‑5 / Arch‑ARM)
|
||||
# Presents: • Boot‑protocol keyboard (hidg0)
|
||||
# • Boot‑protocol mouse (hidg1)
|
||||
# • Stereo UAC2 speaker + microphone
|
||||
@ -98,7 +98,7 @@ printf '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00'\
|
||||
'\x05\x01\x09\x30\x09\x31\x09\x38\x15\x81\x25\x7f\x75\x08\x95\x03\x81\x06'\
|
||||
'\xc0\xc0' >"$G/functions/hid.usb1/report_desc"
|
||||
|
||||
# ---------- UAC2 function – speaker + mic, 2×48 kHz stereo ---------
|
||||
# ---------- UAC2 function - speaker + mic, 2×48 kHz stereo ---------
|
||||
mkdir -p "$G/functions/uac2.usb0"
|
||||
U="$G/functions/uac2.usb0"
|
||||
# Playback (speaker)
|
||||
|
||||
@ -122,8 +122,6 @@ ExecStart=/usr/local/bin/lesavka-server
|
||||
Restart=always
|
||||
Environment=RUST_LOG=lesavka_server=info,lesavka_server::audio=info,lesavka_server::video=info,lesavka_server::gadget=info
|
||||
Environment=RUST_BACKTRACE=1
|
||||
Environment=GST_DEBUG=3,v4l2src:6,tsdemux:6,parsebin:5
|
||||
Environment=GST_DEBUG_DUMP_DOT_DIR=/tmp
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardError=append:/tmp/lesavka-server.stderr
|
||||
|
||||
@ -39,7 +39,7 @@ impl Drop for AudioStream {
|
||||
}
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────*/
|
||||
/* ear() – capture from ALSA (“speaker”) and push AAC AUs via gRPC */
|
||||
/* ear() - capture from ALSA (“speaker”) and push AAC AUs via gRPC */
|
||||
/*───────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
pub async fn ear(alsa_dev: &str, id: u32) -> anyhow::Result<AudioStream> {
|
||||
@ -151,7 +151,6 @@ fn build_pipeline_desc(dev: &str) -> anyhow::Result<String> {
|
||||
})
|
||||
.ok_or_else(|| anyhow!("no AAC encoder plugin available"))?;
|
||||
|
||||
// one long literal assembled with `concat!` so Rust sees *one* string
|
||||
Ok(format!(
|
||||
concat!(
|
||||
"alsasrc device=\"{dev}\" do-timestamp=true ! ",
|
||||
|
||||
@ -32,7 +32,7 @@ impl Stream for VideoStream {
|
||||
|
||||
impl Drop for VideoStream {
|
||||
fn drop(&mut self) {
|
||||
// shut down nicely – avoids the “dispose element … READY/PLAYING …” spam
|
||||
// shut down nicely - avoids the “dispose element … READY/PLAYING …” spam
|
||||
let _ = self._pipeline.set_state(gst::State::Null);
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ pub async fn eye_ball(
|
||||
} else {
|
||||
warn!(target:"lesavka_server::video",
|
||||
eye = %eye,
|
||||
"🍪 cam_{eye} not found – skipping pad-probe");
|
||||
"🍪 cam_{eye} not found - skipping pad-probe");
|
||||
}
|
||||
|
||||
let eye_clone = eye.to_owned();
|
||||
@ -191,7 +191,7 @@ pub async fn eye_ball(
|
||||
debug!(target:"lesavka_server::video",
|
||||
eye = %eye,
|
||||
dropped = c,
|
||||
"⏳ channel full – dropping frames");
|
||||
"⏳ channel full - dropping frames");
|
||||
}
|
||||
}
|
||||
Err(e) => error!("mpsc send err: {e}"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user