From 5a1e50350886fd4e1db956605dda334a37590794 Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Sat, 12 Sep 2026 10:45:45 +0530 Subject: [PATCH] ui: enhance notification countdown bar visibility and optimize daemon memory --- src/bin/scythe-daemon.rs | 30 ++++++++++++++++++++++++++--- src/capture/wayland_stream.rs | 2 +- src/encoder.rs | 4 ++-- src/overlay.rs | 20 ++++++++++++++----- src/overlay_egui.rs | 36 +++++++++++++++++++++++++---------- 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/bin/scythe-daemon.rs b/src/bin/scythe-daemon.rs index c994d23..7f4e540 100644 --- a/src/bin/scythe-daemon.rs +++ b/src/bin/scythe-daemon.rs @@ -58,7 +58,7 @@ async fn main() -> Result<(), Box> { scythe::hyprland_binds::register_hyprland_binds(&initial_config); scythe::hyprland_binds::spawn_hyprland_reload_watcher(); - let (frame_tx, frame_rx) = bounded::(5); + let (frame_tx, frame_rx) = bounded::(2); let (cmd_tx, cmd_rx) = bounded::(32); let (mux_tx, mux_rx) = bounded::>(4); let (audio_tx, audio_rx) = bounded::>(500); @@ -372,7 +372,12 @@ async fn main() -> Result<(), Box> { let video_time_base = unsafe { (*(codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext)).time_base }; let audio_time_base = audio_codec_ctx_ptr.map(|p| unsafe { (*(p as *mut ffmpeg_next::ffi::AVCodecContext)).time_base }); - let mut ring = HeapRb::::new((config.replay_duration_sec as usize) * (config.fps as usize + 60) + 120); + let ring_capacity = if config.replay_enabled { + (config.replay_duration_sec as usize) * (config.fps as usize + 50) + 60 + } else { + 1 + }; + let mut ring = HeapRb::::new(ring_capacity); let mut normal_muxer: Option = None; let mut normal_recording = false; let mut normal_waiting_keyframe = false; @@ -474,6 +479,8 @@ async fn main() -> Result<(), Box> { eprintln!("Error finalizing replay muxer: {}", e); } else { println!("Replay saved to {}!", full_path); + #[cfg(target_os = "linux")] + unsafe { libc::malloc_trim(0); } } } Err(e) => { @@ -489,6 +496,7 @@ async fn main() -> Result<(), Box> { let mut latest_frame: Option = None; let mut has_new_frame = false; let mut last_encoded_raw_ptr: usize = 0; + let mut last_malloc_trim = std::time::Instant::now(); loop { crossbeam_channel::select! { @@ -521,10 +529,16 @@ async fn main() -> Result<(), Box> { replay_state_clone.store(config.replay_enabled, Ordering::SeqCst); audio_muted_clone.store(config.audio_mode == "muted", Ordering::SeqCst); println!("Daemon config reloaded! Audio mode: {}", config.audio_mode); - let new_capacity = (config.replay_duration_sec as usize) * (config.fps as usize + 60) + 120; + let new_capacity = if config.replay_enabled { + (config.replay_duration_sec as usize) * (config.fps as usize + 50) + 60 + } else { + 1 + }; if ring.capacity().get() != new_capacity { ring = HeapRb::::new(new_capacity); println!("Replay buffer resized to {} packets.", new_capacity); + #[cfg(target_os = "linux")] + unsafe { libc::malloc_trim(0); } } scythe::hyprland_binds::register_hyprland_binds(&config); audio_capture = match scythe::capture::audio::AudioCapture::new_with_device_mode_volumes_and_levels( @@ -581,6 +595,8 @@ async fn main() -> Result<(), Box> { rec_base_audio_pts = -1; rec_state_clone.store(false, Ordering::SeqCst); rec_start_clone.store(0, Ordering::SeqCst); + #[cfg(target_os = "linux")] + unsafe { libc::malloc_trim(0); } }, Command::ToggleRecording => { if normal_recording { @@ -594,6 +610,8 @@ async fn main() -> Result<(), Box> { rec_base_audio_pts = -1; rec_state_clone.store(false, Ordering::SeqCst); rec_start_clone.store(0, Ordering::SeqCst); + #[cfg(target_os = "linux")] + unsafe { libc::malloc_trim(0); } } else { normal_recording = true; normal_waiting_keyframe = true; @@ -672,6 +690,12 @@ async fn main() -> Result<(), Box> { recv(ticker) -> _ => { while ticker.try_recv().is_ok() {} + if last_malloc_trim.elapsed().as_secs() >= 5 { + last_malloc_trim = std::time::Instant::now(); + #[cfg(target_os = "linux")] + unsafe { libc::malloc_trim(0); } + } + if !normal_recording && !config.replay_enabled { has_new_frame = false; continue; diff --git a/src/capture/wayland_stream.rs b/src/capture/wayland_stream.rs index db9802c..430d024 100644 --- a/src/capture/wayland_stream.rs +++ b/src/capture/wayland_stream.rs @@ -16,7 +16,7 @@ pub struct PipeWireStream { impl PipeWireStream { pub fn new(node_id: u32, fd: OwnedFd) -> Result> { - let (tx, rx) = bounded(5); + let (tx, rx) = bounded(2); std::thread::spawn(move || { pw::init(); diff --git a/src/encoder.rs b/src/encoder.rs index 43625d3..1bff95c 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -99,7 +99,7 @@ impl VaapiEncoder { (*frames_ctx).sw_format = AVPixelFormat::AV_PIX_FMT_NV12; (*frames_ctx).width = width as i32; (*frames_ctx).height = height as i32; - (*frames_ctx).initial_pool_size = 20; + (*frames_ctx).initial_pool_size = 6; let ret = av_hwframe_ctx_init(hw_frames_ref); if ret < 0 { @@ -291,7 +291,7 @@ impl VaapiEncoder { 0, ); if p != libc::MAP_FAILED { - if self.dma_mmap_cache.len() >= 16 { + if self.dma_mmap_cache.len() >= 4 { for (_, (old_p, old_size)) in self.dma_mmap_cache.drain() { libc::munmap(old_p, old_size); } diff --git a/src/overlay.rs b/src/overlay.rs index 03ca3d0..34449b1 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -354,16 +354,26 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) { cr.rectangle(0.5, 0.5, w - 1.0, h - 1.0); let _ = cr.stroke(); - // 5. Animated countdown timer line on bottom edge + // 5. Animated countdown timer line on bottom edge (high visibility) let progress = 1.0 - (elapsed / total_dur).clamp(0.0, 1.0); - cr.set_source_rgba(1.0, 1.0, 1.0, 0.08 * alpha); - cr.rectangle(0.0, h - 2.0, w, 2.0); + let bar_h = 3.5; + cr.set_source_rgba(1.0, 1.0, 1.0, 0.15 * alpha); + cr.rectangle(0.0, h - bar_h, w, bar_h); let _ = cr.fill(); - cr.set_source_rgba(active_accent.0, active_accent.1, active_accent.2, 0.75 * alpha); - cr.rectangle(0.0, h - 2.0, w * progress, 2.0); + let bar_w = (w * progress).max(0.0); + cr.set_source_rgba(active_accent.0, active_accent.1, active_accent.2, 0.95 * alpha); + cr.rectangle(0.0, h - bar_h, bar_w, bar_h); let _ = cr.fill(); + if bar_w > 2.0 { + cr.set_source_rgba(1.0, 1.0, 1.0, 0.85 * alpha); + cr.set_line_width(1.5); + cr.move_to(bar_w, h - bar_h); + cr.line_to(bar_w, h); + let _ = cr.stroke(); + } + gtk::glib::Propagation::Proceed }); diff --git a/src/overlay_egui.rs b/src/overlay_egui.rs index 7de9fe7..73e8e83 100644 --- a/src/overlay_egui.rs +++ b/src/overlay_egui.rs @@ -2224,12 +2224,20 @@ impl ScytheOverlayApp { egui::StrokeKind::Inside, ); - // 5. Animated Countdown Timer Line (bottom edge) + // 5. Animated Countdown Timer Line (bottom edge - high visibility) let progress = 1.0 - (elapsed / total_dur).clamp(0.0, 1.0); - let bar_track = egui::Rect::from_min_size(egui::pos2(toast_rect.left(), toast_rect.bottom() - 2.0), egui::vec2(card_w, 2.0)); - painter.rect_filled(bar_track, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(255, 255, 255, (16.0 * anim_alpha) as u8)); - let bar_rect = egui::Rect::from_min_size(egui::pos2(toast_rect.left(), toast_rect.bottom() - 2.0), egui::vec2(card_w * progress, 2.0)); - painter.rect_filled(bar_rect, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(active_color.r(), active_color.g(), active_color.b(), (190.0 * anim_alpha) as u8)); + let bar_h = 3.5_f32; + let bar_track = egui::Rect::from_min_size(egui::pos2(toast_rect.left(), toast_rect.bottom() - bar_h), egui::vec2(card_w, bar_h)); + painter.rect_filled(bar_track, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(255, 255, 255, (30.0 * anim_alpha) as u8)); + let bar_w = (card_w * progress).max(0.0); + let bar_rect = egui::Rect::from_min_size(egui::pos2(toast_rect.left(), toast_rect.bottom() - bar_h), egui::vec2(bar_w, bar_h)); + painter.rect_filled(bar_rect, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(active_color.r(), active_color.g(), active_color.b(), (255.0 * anim_alpha) as u8)); + if bar_w > 2.0 { + painter.line_segment( + [egui::pos2(toast_rect.left() + bar_w, toast_rect.bottom() - bar_h), egui::pos2(toast_rect.left() + bar_w, toast_rect.bottom())], + Stroke::new(1.5_f32, Color32::from_rgba_unmultiplied(255, 255, 255, (220.0 * anim_alpha) as u8)), + ); + } // Left icon area (centered vertically at x = 24.0) let icon_center = egui::pos2(toast_rect.left() + 24.0, toast_rect.center().y); @@ -2768,12 +2776,20 @@ impl eframe::App for ShadowPlayToastApp { egui::StrokeKind::Inside, ); - // 5. Animated Countdown Timer Line (bottom edge) + // 5. Animated Countdown Timer Line (bottom edge - high visibility) let progress = 1.0 - (elapsed / total_dur).clamp(0.0, 1.0); - let bar_track = egui::Rect::from_min_size(egui::pos2(rect.left(), rect.bottom() - 2.0), egui::vec2(320.0, 2.0)); - painter.rect_filled(bar_track, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(255, 255, 255, (16.0 * anim_alpha) as u8)); - let bar_rect = egui::Rect::from_min_size(egui::pos2(rect.left(), rect.bottom() - 2.0), egui::vec2(320.0 * progress, 2.0)); - painter.rect_filled(bar_rect, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(active_color.r(), active_color.g(), active_color.b(), (190.0 * anim_alpha) as u8)); + let bar_h = 3.5_f32; + let bar_track = egui::Rect::from_min_size(egui::pos2(rect.left(), rect.bottom() - bar_h), egui::vec2(320.0, bar_h)); + painter.rect_filled(bar_track, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(255, 255, 255, (30.0 * anim_alpha) as u8)); + let bar_w = (320.0 * progress).max(0.0); + let bar_rect = egui::Rect::from_min_size(egui::pos2(rect.left(), rect.bottom() - bar_h), egui::vec2(bar_w, bar_h)); + painter.rect_filled(bar_rect, CornerRadius::ZERO, Color32::from_rgba_unmultiplied(active_color.r(), active_color.g(), active_color.b(), (255.0 * anim_alpha) as u8)); + if bar_w > 2.0 { + painter.line_segment( + [egui::pos2(rect.left() + bar_w, rect.bottom() - bar_h), egui::pos2(rect.left() + bar_w, rect.bottom())], + Stroke::new(1.5_f32, Color32::from_rgba_unmultiplied(255, 255, 255, (220.0 * anim_alpha) as u8)), + ); + } // Left icon area (centered vertically at x = 24.0) let icon_center = egui::pos2(rect.left() + 24.0, rect.center().y);