Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions i18n/en/camera.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ settings-camera = Camera
settings-video = Video
settings-device = Device
settings-format = Format
settings-backend = Backend
settings-backend-description = Camera backend to use. PipeWire is recommended for most systems. Libcamera is for mobile Linux devices.
settings-microphone = Microphone
settings-encoder = Encoder
settings-quality = Quality
Expand All @@ -40,6 +42,10 @@ settings-bug-reports = Bug reports
settings-report-bug = Report bug
settings-show-report = Show Report
settings-resolution = Resolution

# Backend options
backend-pipewire = PipeWire
backend-libcamera = libcamera
settings-version = Version { $version }
settings-version-flatpak = Version { $version } (Flatpak)

Expand Down
6 changes: 3 additions & 3 deletions src/app/camera_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl AppModel {
let camera_path = &camera.path;

// Get formats for the new mode
let backend = crate::backends::camera::get_backend();
let backend = crate::backends::camera::get_backend(self.config.backend);
let device = crate::backends::camera::types::CameraDevice {
name: camera.name.clone(),
path: camera_path.clone(),
Expand Down Expand Up @@ -230,8 +230,8 @@ impl AppModel {
};
let camera_path = camera.path.clone();

// Get formats for this camera using PipeWire backend
let backend = crate::backends::camera::get_backend();
// Get formats for this camera using the configured backend
let backend = crate::backends::camera::get_backend(self.config.backend);
let device = crate::backends::camera::types::CameraDevice {
name: camera.name.clone(),
path: camera_path.clone(),
Expand Down
22 changes: 21 additions & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,26 @@ impl cosmic::Application for AppModel {
.iter()
.map(|p| p.display_name().to_string())
.collect(),
backend_dropdown_options: {
let mut options = Vec::new();
if crate::backends::camera::pipewire::is_pipewire_available() {
options.push(fl!("backend-pipewire"));
}
if crate::backends::camera::libcamera::is_libcamera_available() {
options.push(fl!("backend-libcamera"));
}
options
},
available_backends: {
let mut backends = Vec::new();
if crate::backends::camera::pipewire::is_pipewire_available() {
backends.push(crate::backends::camera::CameraBackendType::PipeWire);
}
if crate::backends::camera::libcamera::is_libcamera_available() {
backends.push(crate::backends::camera::CameraBackendType::Libcamera);
}
backends
},
bitrate_info_visible: false,
transition_state: crate::app::state::TransitionState::default(),
// QR detection enabled by default
Expand All @@ -269,7 +289,7 @@ impl cosmic::Application for AppModel {

// Enumerate cameras (can be slow, especially with multiple devices)
info!(backend = %backend_type, "Enumerating cameras asynchronously");
let backend = crate::backends::camera::get_backend();
let backend = crate::backends::camera::get_backend(backend_type);
let cameras = backend.enumerate_cameras();
info!(count = cameras.len(), backend = %backend_type, "Found camera(s)");

Expand Down
24 changes: 22 additions & 2 deletions src/app/settings/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ impl AppModel {
.position(|p| *p == self.config.bitrate_preset)
.unwrap_or(1); // Default to Medium (index 1)

// Camera section
let camera_section = widget::settings::section()
// Current backend index for dropdown (find in available backends list)
let current_backend_index = self
.available_backends
.iter()
.position(|b| *b == self.config.backend)
.unwrap_or(0);

// Camera section - conditionally add backend dropdown only if multiple backends available
let mut camera_section = widget::settings::section()
.title(fl!("settings-camera"))
.add(
widget::settings::item::builder(fl!("settings-device")).control(widget::dropdown(
Expand All @@ -51,6 +58,19 @@ impl AppModel {
)),
);

// Only show backend dropdown if multiple backends are available
if self.available_backends.len() > 1 {
camera_section = camera_section.add(
widget::settings::item::builder(fl!("settings-backend"))
.description(fl!("settings-backend-description"))
.control(widget::dropdown(
&self.backend_dropdown_options,
Some(current_backend_index),
Message::SelectBackend,
)),
);
}

// Video section
let video_section = widget::settings::section()
.title(fl!("settings-video"))
Expand Down
6 changes: 6 additions & 0 deletions src/app/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ pub struct AppModel {
pub codec_dropdown_options: Vec<String>,
/// Bitrate preset dropdown options
pub bitrate_preset_dropdown_options: Vec<String>,
/// Backend dropdown options
pub backend_dropdown_options: Vec<String>,
/// Available backend types (corresponding to dropdown options)
pub available_backends: Vec<crate::backends::camera::CameraBackendType>,
/// Whether the bitrate info matrix is visible
pub bitrate_info_visible: bool,

Expand Down Expand Up @@ -599,6 +603,8 @@ pub enum Message {
SelectAudioDevice(usize),
/// Select video encoder
SelectVideoEncoder(usize),
/// Select camera backend
SelectBackend(usize),
/// Toggle virtual camera feature enabled
ToggleVirtualCameraEnabled,

Expand Down
69 changes: 69 additions & 0 deletions src/app/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl AppModel {
Message::UpdateConfig(config) => self.handle_update_config(config),
Message::SelectAudioDevice(index) => self.handle_select_audio_device(index),
Message::SelectVideoEncoder(index) => self.handle_select_video_encoder(index),
Message::SelectBackend(index) => self.handle_select_backend(index),

// ===== System & Recovery =====
Message::CameraRecoveryStarted {
Expand Down Expand Up @@ -2133,6 +2134,74 @@ impl AppModel {
Task::none()
}

fn handle_select_backend(&mut self, index: usize) -> Task<cosmic::Action<Message>> {
// Get the backend type from the available backends list
let new_backend = match self.available_backends.get(index) {
Some(backend) => *backend,
None => return Task::none(),
};

// Don't do anything if the backend is the same
if new_backend == self.config.backend {
return Task::none();
}

info!(old = %self.config.backend, new = %new_backend, "Switching camera backend");

// Update config
self.config.backend = new_backend;
if let Some(handler) = self.config_handler.as_ref() {
if let Err(err) = self.config.write_entry(handler) {
error!(?err, "Failed to save backend selection");
}
}

// Change the backend in the manager
if let Some(ref manager) = self.backend_manager {
if let Err(err) = manager.change_backend(new_backend) {
error!(?err, "Failed to change backend");
return Task::none();
}
}

// Clear current state and re-enumerate cameras with the new backend
self.available_cameras.clear();
self.available_formats.clear();
self.active_format = None;
self.current_frame = None;

// Re-enumerate cameras with the new backend
let backend = crate::backends::camera::get_backend(new_backend);
let cameras = backend.enumerate_cameras();
info!(count = cameras.len(), backend = %new_backend, "Re-enumerated cameras with new backend");

if !cameras.is_empty() {
self.available_cameras = cameras;
self.current_camera_index = 0;

// Update camera dropdown
self.camera_dropdown_options = self
.available_cameras
.iter()
.map(|c| c.name.clone())
.collect();

// Get formats for the first camera
let device = &self.available_cameras[0];
self.available_formats = backend.get_formats(device, false);

// Select the first format if available
if !self.available_formats.is_empty() {
self.active_format = self.available_formats.first().cloned();
}

// Update all dropdowns
self.update_all_dropdowns();
}

Task::none()
}

// =========================================================================
// System & Recovery Handlers
// =========================================================================
Expand Down
Loading
Loading