Add native self-hosted instance connection to fluxer_desktop

Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
This commit is contained in:
2026-07-01 18:22:43 -04:00
commit 682afacd30
1763 changed files with 613720 additions and 0 deletions
@@ -0,0 +1,366 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use crate::{MAX_FRAME_HEIGHT, MAX_FRAME_WIDTH, nv12_byte_size};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureFormat {
Nv12,
P010,
Bgra8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendError {
DimensionsOutOfRange { width: u32, height: u32 },
UnsupportedFormat { format: TextureFormat },
PlatformUnsupported { reason: &'static str },
KeyMismatch { expected: u64, observed: u64 },
AcquireWhileWriting { slot_index: u32 },
ReleaseWithoutAcquire { slot_index: u32 },
WouldBlock { slot_index: u32 },
}
impl std::fmt::Display for BackendError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DimensionsOutOfRange { width, height } => {
write!(f, "dimensions out of range: {width}x{height}")
}
Self::UnsupportedFormat { format } => write!(f, "unsupported format: {format:?}"),
Self::PlatformUnsupported { reason } => write!(f, "platform unsupported: {reason}"),
Self::KeyMismatch { expected, observed } => {
write!(
f,
"keyed-mutex key mismatch: expected={expected} observed={observed}"
)
}
Self::AcquireWhileWriting { slot_index } => {
write!(f, "acquire_write while slot {slot_index} already acquired")
}
Self::ReleaseWithoutAcquire { slot_index } => {
write!(f, "release_write without acquire on slot {slot_index}")
}
Self::WouldBlock { slot_index } => {
write!(
f,
"keyed mutex busy on slot {slot_index}; skipped without blocking"
)
}
}
}
}
impl std::error::Error for BackendError {}
pub const NUM_SLOTS_DEFAULT: usize = 8;
pub trait KeyedMutexBackend: Send {
type SlotHandle: Send + Clone;
const NUM_SLOTS: usize;
fn create_slots(
&mut self,
width: u32,
height: u32,
format: TextureFormat,
) -> Result<Vec<Self::SlotHandle>, BackendError>;
fn acquire_write(&mut self, slot: &Self::SlotHandle, key: u64) -> Result<(), BackendError>;
fn release_write(&mut self, slot: &Self::SlotHandle, next_key: u64)
-> Result<(), BackendError>;
fn poll_complete(&mut self, slot: &Self::SlotHandle) -> bool;
fn mark_consumed(&mut self, slot: &Self::SlotHandle);
fn fill_test_pattern(&mut self, _slot: &Self::SlotHandle, _value: u8) {}
}
#[derive(Clone)]
pub struct CpuSlotHandle {
inner: Arc<CpuSlotInner>,
}
impl CpuSlotHandle {
pub fn slot_index(&self) -> u32 {
let idx = self.inner.slot_index;
assert!((idx as usize) < NUM_SLOTS_DEFAULT, "slot_index in range");
assert!(
self.inner.buffer.len() == self.inner.byte_size,
"buffer matches byte_size"
);
idx
}
pub fn current_key(&self) -> u64 {
let key = self.inner.current_key.load(Ordering::Acquire);
assert!(
self.inner.buffer.len() == self.inner.byte_size,
"buffer intact"
);
assert!(
(self.inner.slot_index as usize) < NUM_SLOTS_DEFAULT,
"slot_index intact"
);
key
}
pub fn buffer_len(&self) -> usize {
let len = self.inner.byte_size;
assert!(len > 0, "byte_size positive");
assert!(self.inner.buffer.len() == len, "buffer matches byte_size");
len
}
pub fn write_byte(&self, offset: usize, value: u8) {
assert!(offset < self.inner.byte_size, "offset within buffer");
assert!(
self.inner.acquired.load(Ordering::Acquire),
"writes only while acquired",
);
unsafe {
let ptr = self.inner.buffer.as_ptr().add(offset) as *mut u8;
ptr.write_volatile(value);
}
}
}
struct CpuSlotInner {
slot_index: u32,
byte_size: usize,
buffer: Vec<u8>,
current_key: AtomicU64,
acquired: AtomicBool,
completed: AtomicBool,
}
pub struct CpuMemcpyBackend {
width: u32,
height: u32,
format: TextureFormat,
slots_created: bool,
slot_count: u32,
}
impl CpuMemcpyBackend {
pub fn new() -> Self {
let backend = Self {
width: 0,
height: 0,
format: TextureFormat::Nv12,
slots_created: false,
slot_count: 0,
};
assert!(!backend.slots_created, "fresh backend has no slots");
assert_eq!(backend.slot_count, 0, "fresh slot_count zero");
backend
}
pub fn width(&self) -> u32 {
assert!(self.width <= MAX_FRAME_WIDTH, "width within cap");
self.width
}
pub fn height(&self) -> u32 {
assert!(self.height <= MAX_FRAME_HEIGHT, "height within cap");
self.height
}
}
impl Default for CpuMemcpyBackend {
fn default() -> Self {
Self::new()
}
}
impl KeyedMutexBackend for CpuMemcpyBackend {
type SlotHandle = CpuSlotHandle;
const NUM_SLOTS: usize = NUM_SLOTS_DEFAULT;
fn create_slots(
&mut self,
width: u32,
height: u32,
format: TextureFormat,
) -> Result<Vec<CpuSlotHandle>, BackendError> {
if width == 0 || height == 0 || width > MAX_FRAME_WIDTH || height > MAX_FRAME_HEIGHT {
return Err(BackendError::DimensionsOutOfRange { width, height });
}
if !matches!(format, TextureFormat::Nv12) {
return Err(BackendError::UnsupportedFormat { format });
}
assert!(!self.slots_created, "slots created once");
let byte_size = nv12_byte_size(width, height);
assert!(byte_size > 0, "byte_size positive");
let mut out: Vec<CpuSlotHandle> = Vec::with_capacity(Self::NUM_SLOTS);
for idx in 0..Self::NUM_SLOTS {
let inner = CpuSlotInner {
slot_index: idx as u32,
byte_size,
buffer: vec![0u8; byte_size],
current_key: AtomicU64::new(0),
acquired: AtomicBool::new(false),
completed: AtomicBool::new(false),
};
out.push(CpuSlotHandle {
inner: Arc::new(inner),
});
}
self.width = width;
self.height = height;
self.format = format;
self.slots_created = true;
self.slot_count = Self::NUM_SLOTS as u32;
assert_eq!(out.len(), Self::NUM_SLOTS, "slot vector length");
assert!(self.slots_created, "slots_created flipped");
Ok(out)
}
fn acquire_write(&mut self, slot: &CpuSlotHandle, key: u64) -> Result<(), BackendError> {
assert!(self.slots_created, "slots must exist before acquire");
let current = slot.inner.current_key.load(Ordering::Acquire);
if current != key {
return Err(BackendError::KeyMismatch {
expected: key,
observed: current,
});
}
let was_acquired = slot.inner.acquired.swap(true, Ordering::AcqRel);
if was_acquired {
return Err(BackendError::AcquireWhileWriting {
slot_index: slot.inner.slot_index,
});
}
slot.inner.completed.store(false, Ordering::Release);
assert!(
slot.inner.acquired.load(Ordering::Acquire),
"acquired flag set"
);
assert!(
!slot.inner.completed.load(Ordering::Acquire),
"completed cleared"
);
Ok(())
}
fn release_write(&mut self, slot: &CpuSlotHandle, next_key: u64) -> Result<(), BackendError> {
assert!(self.slots_created, "slots must exist before release");
let was_acquired = slot.inner.acquired.swap(false, Ordering::AcqRel);
if !was_acquired {
return Err(BackendError::ReleaseWithoutAcquire {
slot_index: slot.inner.slot_index,
});
}
slot.inner.current_key.store(next_key, Ordering::Release);
slot.inner.completed.store(true, Ordering::Release);
assert!(
!slot.inner.acquired.load(Ordering::Acquire),
"acquired cleared"
);
assert!(
slot.inner.completed.load(Ordering::Acquire),
"completed flag set"
);
Ok(())
}
fn poll_complete(&mut self, slot: &CpuSlotHandle) -> bool {
let done = slot.inner.completed.load(Ordering::Acquire);
assert!(self.slots_created, "slots exist for poll");
assert!(
(slot.inner.slot_index as usize) < Self::NUM_SLOTS,
"slot index in range"
);
done
}
fn mark_consumed(&mut self, slot: &CpuSlotHandle) {
assert!(self.slots_created, "slots exist for mark_consumed");
assert!(
(slot.inner.slot_index as usize) < Self::NUM_SLOTS,
"slot index in range"
);
slot.inner.completed.store(false, Ordering::Release);
}
fn fill_test_pattern(&mut self, slot: &CpuSlotHandle, value: u8) {
assert!(self.slots_created, "slots exist for fill");
assert!(
slot.inner.acquired.load(Ordering::Acquire),
"fill only while acquired"
);
let len = slot.inner.byte_size;
for offset in 0..len {
slot.write_byte(offset, value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_slots_for_1080p_nv12_yields_eight_slots() {
let mut backend = CpuMemcpyBackend::new();
let slots = backend
.create_slots(1920, 1080, TextureFormat::Nv12)
.expect("creation succeeds");
assert_eq!(slots.len(), CpuMemcpyBackend::NUM_SLOTS);
assert_eq!(slots.len(), 8);
for (idx, slot) in slots.iter().enumerate() {
assert_eq!(slot.slot_index(), idx as u32);
assert_eq!(slot.buffer_len(), 1920 * 1080 * 3 / 2);
}
}
#[test]
fn create_slots_rejects_zero_dims() {
let mut backend = CpuMemcpyBackend::new();
let err = backend.create_slots(0, 1080, TextureFormat::Nv12).err();
assert!(matches!(
err,
Some(BackendError::DimensionsOutOfRange { .. })
));
}
#[test]
fn create_slots_rejects_unsupported_format() {
let mut backend = CpuMemcpyBackend::new();
let err = backend.create_slots(1920, 1080, TextureFormat::P010).err();
assert!(matches!(err, Some(BackendError::UnsupportedFormat { .. })));
}
#[test]
fn acquire_release_round_trip_marks_complete() {
let mut backend = CpuMemcpyBackend::new();
let slots = backend
.create_slots(64, 64, TextureFormat::Nv12)
.expect("create");
let slot = slots[0].clone();
backend.acquire_write(&slot, 0).expect("acquire");
backend.release_write(&slot, 1).expect("release");
assert!(backend.poll_complete(&slot));
backend.mark_consumed(&slot);
assert!(!backend.poll_complete(&slot));
}
#[test]
fn double_acquire_rejects() {
let mut backend = CpuMemcpyBackend::new();
let slots = backend
.create_slots(64, 64, TextureFormat::Nv12)
.expect("create");
let slot = slots[0].clone();
backend.acquire_write(&slot, 0).expect("first acquire");
let err = backend.acquire_write(&slot, 0).err();
assert!(matches!(
err,
Some(BackendError::AcquireWhileWriting { .. })
));
}
}