ui: enhance notification countdown bar visibility and optimize daemon memory

This commit is contained in:
katR 2026-09-12 10:45:45 +05:30
parent 0e620e2658
commit 5a1e503508
5 changed files with 71 additions and 21 deletions

View file

@ -58,7 +58,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
scythe::hyprland_binds::register_hyprland_binds(&initial_config);
scythe::hyprland_binds::spawn_hyprland_reload_watcher();
let (frame_tx, frame_rx) = bounded::<Frame>(5);
let (frame_tx, frame_rx) = bounded::<Frame>(2);
let (cmd_tx, cmd_rx) = bounded::<Command>(32);
let (mux_tx, mux_rx) = bounded::<Vec<Packet>>(4);
let (audio_tx, audio_rx) = bounded::<Vec<f32>>(500);
@ -372,7 +372,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
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::<Packet>::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::<Packet>::new(ring_capacity);
let mut normal_muxer: Option<Muxer> = None;
let mut normal_recording = false;
let mut normal_waiting_keyframe = false;
@ -474,6 +479,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
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<dyn std::error::Error + Send + Sync>> {
let mut latest_frame: Option<Frame> = 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<dyn std::error::Error + Send + Sync>> {
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::<Packet>::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<dyn std::error::Error + Send + Sync>> {
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<dyn std::error::Error + Send + Sync>> {
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<dyn std::error::Error + Send + Sync>> {
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;

View file

@ -16,7 +16,7 @@ pub struct PipeWireStream {
impl PipeWireStream {
pub fn new(node_id: u32, fd: OwnedFd) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let (tx, rx) = bounded(5);
let (tx, rx) = bounded(2);
std::thread::spawn(move || {
pw::init();

View file

@ -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);
}

View file

@ -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
});

View file

@ -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);