Skip to content
Merged

tired #1481

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
16 changes: 13 additions & 3 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2931,9 +2931,19 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {

match event {
WindowEvent::CloseRequested { .. } => {
if let Ok(CapWindowId::Camera) = CapWindowId::from_str(label) {
tracing::warn!("Camera window CloseRequested event received!");
tokio::spawn(cleanup_camera_window(app.clone()));
if let Ok(window_id) = CapWindowId::from_str(label) {
match window_id {
CapWindowId::Camera => {
tracing::warn!("Camera window CloseRequested event received!");
tokio::spawn(cleanup_camera_window(app.clone()));
}
CapWindowId::Main => {
if let Some(camera_window) = CapWindowId::Camera.get(app) {
let _ = camera_window.close();
}
}
_ => {}
}
}
}
WindowEvent::Destroyed => {
Expand Down
21 changes: 21 additions & 0 deletions apps/desktop/src-tauri/src/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,14 @@ fn handle_previous_item_click(app: &AppHandle, path_str: &str) {
}
}

pub fn get_tray_icon() -> &'static [u8] {
include_bytes!("../icons/tray-default-icon.png")
}

pub fn get_mode_icon(mode: RecordingMode) -> &'static [u8] {
if cfg!(target_os = "windows") {
return get_tray_icon();
}
match mode {
RecordingMode::Studio => include_bytes!("../icons/tray-default-icon-studio.png"),
RecordingMode::Instant => include_bytes!("../icons/tray-default-icon-instant.png"),
Expand All @@ -536,6 +543,10 @@ pub fn get_mode_icon(mode: RecordingMode) -> &'static [u8] {
}

pub fn update_tray_icon_for_mode(app: &AppHandle, mode: RecordingMode) {
if cfg!(target_os = "windows") {
return;
}

let Some(tray) = app.tray_by_id("tray") else {
return;
};
Expand Down Expand Up @@ -734,6 +745,11 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
let is_recording = is_recording.clone();
move |_| {
is_recording.store(true, Ordering::Relaxed);

if cfg!(target_os = "windows") {
return;
}

let Some(tray) = app.tray_by_id("tray") else {
return;
};
Expand All @@ -749,6 +765,11 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
let is_recording = is_recording.clone();
move |_| {
is_recording.store(false, Ordering::Relaxed);

if cfg!(target_os = "windows") {
return;
}

let Some(tray) = app_handle.tray_by_id("tray") else {
return;
};
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/routes/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,18 @@ function Inner() {
}),
);

const updateConfigAndRender = throttle(async (time: number) => {
const doConfigUpdate = async (time: number) => {
const config = serializeProjectConfiguration(project);
await commands.updateProjectConfigInMemory(config);
canvasControls()?.resetFrameState();
renderFrame(time);
}, 1000 / FPS);
};
const throttledConfigUpdate = throttle(doConfigUpdate, 1000 / FPS);
const trailingConfigUpdate = debounce(doConfigUpdate, 1000 / FPS + 16);
const updateConfigAndRender = (time: number) => {
throttledConfigUpdate(time);
trailingConfigUpdate(time);
};
createEffect(
on(
() => trackDeep(project),
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/routes/editor/PresetsDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function PresetsDropdown() {
const normalizedConfig = normalizeProject({
...preset.config,
timeline: project.timeline,
clips: project.clips,
});
setProject(reconcile(normalizedConfig));
}
Expand Down
15 changes: 13 additions & 2 deletions apps/desktop/src/routes/screenshot-editor/context.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContextProvider } from "@solid-primitives/context";
import { trackStore } from "@solid-primitives/deep";
import { debounce } from "@solid-primitives/scheduled";
import { debounce, throttle } from "@solid-primitives/scheduled";
import { makePersisted } from "@solid-primitives/storage";
import { convertFileSrc } from "@tauri-apps/api/core";
import {
Expand Down Expand Up @@ -293,6 +293,16 @@ function createScreenshotEditorContext() {
}
});

const FPS = 60;
const FRAME_TIME = 1000 / FPS;

const doRenderUpdate = (config: ProjectConfiguration) => {
commands.updateScreenshotConfig(config, false);
};

const throttledRenderUpdate = throttle(doRenderUpdate, FRAME_TIME);
const trailingRenderUpdate = debounce(doRenderUpdate, FRAME_TIME + 16);

const saveConfig = debounce((config: ProjectConfiguration) => {
commands.updateScreenshotConfig(config, true);
}, 1000);
Expand All @@ -312,7 +322,8 @@ function createScreenshotEditorContext() {
annotations: unwrap(annotations),
};

commands.updateScreenshotConfig(config, false);
throttledRenderUpdate(config);
trailingRenderUpdate(config);
saveConfig(config);
},
),
Expand Down
7 changes: 2 additions & 5 deletions apps/desktop/src/utils/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ uploadProgressEvent: "upload-progress-event"

/** user-defined types **/

export type AllGpusInfo = { gpus: GpuInfoDiag[]; primaryGpuIndex: number | null; isMultiGpuSystem: boolean; hasDiscreteGpu: boolean }
export type Annotation = { id: string; type: AnnotationType; x: number; y: number; width: number; height: number; strokeColor: string; strokeWidth: number; fillColor: string; opacity: number; rotation: number; text: string | null; maskType?: MaskType | null; maskLevel?: number | null }
export type AnnotationType = "arrow" | "circle" | "rectangle" | "text" | "mask"
export type AppTheme = "system" | "light" | "dark"
Expand Down Expand Up @@ -438,7 +437,6 @@ quality: number | null;
* Whether to prioritize speed over quality (default: false)
*/
fast: boolean | null }
export type GpuInfoDiag = { vendor: string; description: string; dedicatedVideoMemoryMb: number; adapterIndex: number; isSoftwareAdapter: boolean; isBasicRenderDriver: boolean; supportsHardwareEncoding: boolean }
export type HapticPattern = "alignment" | "levelChange" | "generic"
export type HapticPerformanceTime = "default" | "now" | "drawCompleted"
export type Hotkey = { code: string; meta: boolean; ctrl: boolean; alt: boolean; shift: boolean }
Expand All @@ -451,6 +449,7 @@ export type JsonValue<T> = [T]
export type LogicalBounds = { position: LogicalPosition; size: LogicalSize }
export type LogicalPosition = { x: number; y: number }
export type LogicalSize = { width: number; height: number }
export type MacOSVersionInfo = { displayName: string }
export type MainWindowRecordingStartBehaviour = "close" | "minimise"
export type MaskKeyframes = { position?: MaskVectorKeyframe[]; size?: MaskVectorKeyframe[]; intensity?: MaskScalarKeyframe[] }
export type MaskKind = "sensitive" | "highlight"
Expand Down Expand Up @@ -493,7 +492,6 @@ export type RecordingStatus = "pending" | "recording"
export type RecordingStopped = null
export type RecordingTargetMode = "display" | "window" | "area"
export type RenderFrameEvent = { frame_number: number; fps: number; resolution_base: XY<number> }
export type RenderingStatus = { isUsingSoftwareRendering: boolean; isUsingBasicRenderDriver: boolean; hardwareEncodingAvailable: boolean; warningMessage: string | null }
export type RequestOpenRecordingPicker = { target_mode: RecordingTargetMode | null }
export type RequestOpenSettings = { page: string }
export type RequestScreenCapturePrewarm = { force?: boolean }
Expand All @@ -514,7 +512,7 @@ export type StartRecordingInputs = { capture_target: ScreenCaptureTarget; captur
export type StereoMode = "stereo" | "monoL" | "monoR"
export type StudioRecordingMeta = { segment: SingleSegment } | { inner: MultipleSegments }
export type StudioRecordingStatus = { status: "InProgress" } | { status: "NeedsRemux" } | { status: "Failed"; error: string } | { status: "Complete" }
export type SystemDiagnostics = { windowsVersion: WindowsVersionInfo | null; gpuInfo: GpuInfoDiag | null; allGpus: AllGpusInfo | null; renderingStatus: RenderingStatus; availableEncoders: string[]; graphicsCaptureSupported: boolean; d3D11VideoProcessorAvailable: boolean }
export type SystemDiagnostics = { macosVersion: MacOSVersionInfo | null; availableEncoders: string[]; screenCaptureSupported: boolean }
export type TargetUnderCursor = { display_id: DisplayId | null; window: WindowUnderCursor | null }
export type TextSegment = { start: number; end: number; enabled?: boolean; content?: string; center?: XY<number>; size?: XY<number>; fontFamily?: string; fontSize?: number; fontWeight?: number; italic?: boolean; color?: string; fadeDuration?: number }
export type TimelineConfiguration = { segments: TimelineSegment[]; zoomSegments: ZoomSegment[]; sceneSegments?: SceneSegment[]; maskSegments?: MaskSegment[]; textSegments?: TextSegment[] }
Expand All @@ -531,7 +529,6 @@ export type VideoUploadInfo = { id: string; link: string; config: S3UploadMeta }
export type WindowExclusion = { bundleIdentifier?: string | null; ownerName?: string | null; windowTitle?: string | null }
export type WindowId = string
export type WindowUnderCursor = { id: WindowId; app_name: string; bounds: LogicalBounds }
export type WindowsVersionInfo = { major: number; minor: number; build: number; displayName: string; meetsRequirements: boolean; isWindows11: boolean }
export type XY<T> = { x: T; y: T }
export type ZoomMode = "auto" | { manual: { x: number; y: number } }
export type ZoomSegment = { start: number; end: number; amount: number; mode: ZoomMode }
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/76.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Editor playback stability & bug fixes
app: Cap Desktop
publishedAt: 2025-10-19
publishedAt: "2025-10-19"
version: 0.3.75
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/77.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Video upload improvements
app: Cap Desktop
publishedAt: 2025-10-21
publishedAt: "2025-10-21"
version: 0.3.76
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/78.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Instant Mode resolution picker + Improvements to audio and uploader
app: Cap Desktop
publishedAt: 2025-10-23
publishedAt: "2025-10-23"
version: 0.3.77
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/79.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Recording fixes + Editor enhancements
app: Cap Desktop
publishedAt: 2025-10-23
publishedAt: "2025-10-23"
version: 0.3.78
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/80.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Audio recording improvements + Editor enhancements
app: Cap Desktop
publishedAt: 2025-10-23
publishedAt: "2025-10-23"
version: 0.3.80
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/81.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Speed modification in editor, New Crop UI, Bug fixes
app: Cap Desktop
publishedAt: 2025-11-5
publishedAt: "2025-11-5"
version: 0.3.82
image:
---
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/changelog/82.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Studio Mode overhaul, mic downmixing, capture reliability
app: Cap Desktop
publishedAt: 2025-11-11
publishedAt: "2025-11-11"
version: 0.3.83
image:
---
Expand Down
37 changes: 23 additions & 14 deletions crates/editor/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,14 @@ impl AudioRenderer {
let channels: usize = 2;

if self.cursor.timescale != 1.0 {
self.elapsed_samples += samples;
return None;
};

let tracks = &self.data[self.cursor.clip_index as usize].tracks;

if tracks.is_empty() {
self.elapsed_samples += samples;
return None;
}

Expand Down Expand Up @@ -444,15 +446,13 @@ impl AudioResampler {
}
}

#[cfg(target_os = "windows")]
pub struct PrerenderedAudioBuffer<T: FromSampleBytes> {
samples: Vec<T>,
read_position: usize,
sample_rate: u32,
channels: usize,
}

#[cfg(target_os = "windows")]
impl<T: FromSampleBytes> PrerenderedAudioBuffer<T> {
pub fn new(
segments: Vec<AudioSegment>,
Expand All @@ -464,7 +464,7 @@ impl<T: FromSampleBytes> PrerenderedAudioBuffer<T> {
duration_secs = duration_secs,
sample_rate = output_info.sample_rate,
channels = output_info.channels,
"Pre-rendering audio for Windows playback"
"Pre-rendering audio for playback"
);

let mut renderer = AudioRenderer::new(segments);
Expand All @@ -481,20 +481,29 @@ impl<T: FromSampleBytes> PrerenderedAudioBuffer<T> {
renderer.set_playhead(0.0, project);

let mut rendered_source_samples = 0usize;
let output_chunk_samples = (chunk_size as f64 * output_info.sample_rate as f64
/ AudioData::SAMPLE_RATE as f64) as usize
* output_info.channels;

while rendered_source_samples < total_source_samples {
let frame_opt = renderer.render_frame(chunk_size, project);

let resampled = match frame_opt {
Some(frame) => resampler.queue_and_process_frame(&frame),
None => match resampler.flush_frame() {
Some(data) => data,
None => break,
},
};

if !resampled.is_empty() {
for chunk in resampled.chunks(bytes_per_sample) {
samples.push(T::from_bytes(chunk));
match frame_opt {
Some(frame) => {
let resampled = resampler.queue_and_process_frame(&frame);
for chunk in resampled.chunks(bytes_per_sample) {
samples.push(T::from_bytes(chunk));
}
}
None => {
if let Some(flushed) = resampler.flush_frame() {
for chunk in flushed.chunks(bytes_per_sample) {
samples.push(T::from_bytes(chunk));
}
}
for _ in 0..output_chunk_samples {
samples.push(T::EQUILIBRIUM);
}
}
}

Expand Down
Loading