Skip to content
Merged
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
34 changes: 30 additions & 4 deletions apps/desktop/src-tauri/src/target_select_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
};

use base64::prelude::*;
use cap_recording::screen_capture::ScreenCaptureTarget;

use crate::windows::{CapWindowId, ShowCapWindow};
use scap_targets::{
Expand Down Expand Up @@ -46,6 +47,7 @@ pub struct DisplayInformation {
pub async fn open_target_select_overlays(
app: AppHandle,
state: tauri::State<'_, WindowFocusManager>,
focused_target: Option<ScreenCaptureTarget>,
) -> Result<(), String> {
let displays = scap_targets::Display::list()
.into_iter()
Expand All @@ -59,11 +61,18 @@ pub async fn open_target_select_overlays(

let handle = tokio::spawn({
let app = app.clone();

async move {
loop {
{
let display = scap_targets::Display::get_containing_cursor();
let window = scap_targets::Window::get_topmost_at_cursor();
let display = focused_target
.as_ref()
.map(|v| v.display())
.unwrap_or_else(|| scap_targets::Display::get_containing_cursor());
let window = focused_target
.as_ref()
.map(|v| v.window().and_then(|id| scap_targets::Window::from_id(&id)))
.unwrap_or_else(|| scap_targets::Window::get_topmost_at_cursor());

let _ = TargetUnderCursor {
display_id: display.map(|d| d.id()),
Expand Down Expand Up @@ -171,13 +180,30 @@ pub async fn focus_window(window_id: WindowId) -> Result<(), String> {
#[cfg(target_os = "windows")]
{
use windows::Win32::UI::WindowsAndMessaging::{
SW_RESTORE, SetForegroundWindow, ShowWindow,
GetWindowPlacement, IsIconic, SW_RESTORE, SetForegroundWindow, SetWindowPlacement,
ShowWindow, WINDOWPLACEMENT,
};

let hwnd = window.raw_handle().inner();

unsafe {
ShowWindow(hwnd, SW_RESTORE);
// Only restore if the window is actually minimized
if IsIconic(hwnd).as_bool() {
// Get current window placement to preserve size/position
let mut wp = WINDOWPLACEMENT::default();
wp.length = std::mem::size_of::<WINDOWPLACEMENT>() as u32;

if GetWindowPlacement(hwnd, &mut wp).is_ok() {
// Restore using the previous placement to avoid resizing
wp.showCmd = SW_RESTORE.0 as u32;
SetWindowPlacement(hwnd, &wp);
} else {
// Fallback to simple restore if placement fails
ShowWindow(hwnd, SW_RESTORE);
}
}

// Always try to bring to foreground
SetForegroundWindow(hwnd);
}
}
Expand Down
25 changes: 17 additions & 8 deletions apps/desktop/src/routes/(window-chrome)/new-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ function TargetMenuPanel(props: TargetMenuPanelProps & SharedTargetMenuProps) {
<div class="flex gap-3 justify-between items-center mt-3">
<div
onClick={() => props.onBack()}
class="flex gap-1 items-center rounded-md px-1.5 text-xs
text-gray-11 transition-opacity hover:opacity-70 hover:text-gray-12
class="flex gap-1 items-center rounded-md px-1.5 text-xs
text-gray-11 transition-opacity hover:opacity-70 hover:text-gray-12
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-9 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-1"
>
<IconLucideArrowLeft class="size-3 text-gray-11" />
Expand Down Expand Up @@ -383,6 +383,7 @@ function Page() {
reconcile({ variant: "display", id: target.id }),
);
setOptions("targetMode", "display");
commands.openTargetSelectOverlays(rawOptions.captureTarget);
setDisplayMenuOpen(false);
displayTriggerRef?.focus();
};
Expand All @@ -393,6 +394,7 @@ function Page() {
reconcile({ variant: "window", id: target.id }),
);
setOptions("targetMode", "window");
commands.openTargetSelectOverlays(rawOptions.captureTarget);
setWindowMenuOpen(false);
windowTriggerRef?.focus();

Expand All @@ -412,7 +414,10 @@ function Page() {
createUpdateCheck();

onMount(async () => {
setOptions({ targetMode: (window as any).__CAP__.initialTargetMode });
const targetMode = (window as any).__CAP__.initialTargetMode;
setOptions({ targetMode });
if (rawOptions.targetMode) commands.openTargetSelectOverlays(null);
else commands.closeTargetSelectOverlays();

const currentWindow = getCurrentWindow();

Expand Down Expand Up @@ -444,11 +449,6 @@ function Page() {
if (!monitor) return;
});

createEffect(() => {
if (rawOptions.targetMode) commands.openTargetSelectOverlays();
else commands.closeTargetSelectOverlays();
});

const cameras = useQuery(() => listVideoDevices);
const mics = useQuery(() => listAudioDevices);

Expand Down Expand Up @@ -649,6 +649,9 @@ function Page() {
setOptions("targetMode", (v) =>
v === "display" ? null : "display",
);
if (rawOptions.targetMode)
commands.openTargetSelectOverlays(null);
else commands.closeTargetSelectOverlays();
}}
name="Display"
class="flex-1 rounded-none focus-visible:ring-0 focus-visible:ring-offset-0"
Expand Down Expand Up @@ -691,6 +694,9 @@ function Page() {
setOptions("targetMode", (v) =>
v === "window" ? null : "window",
);
if (rawOptions.targetMode)
commands.openTargetSelectOverlays(null);
else commands.closeTargetSelectOverlays();
}}
name="Window"
class="flex-1 rounded-none focus-visible:ring-0 focus-visible:ring-offset-0"
Expand Down Expand Up @@ -724,6 +730,9 @@ function Page() {
onClick={() => {
if (isRecording()) return;
setOptions("targetMode", (v) => (v === "area" ? null : "area"));
if (rawOptions.targetMode)
commands.openTargetSelectOverlays(null);
else commands.closeTargetSelectOverlays();
}}
name="Area"
/>
Expand Down
60 changes: 16 additions & 44 deletions apps/desktop/src/routes/target-select-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { useSearchParams } from "@solidjs/router";
import { createQuery, useMutation } from "@tanstack/solid-query";
import { emit } from "@tauri-apps/api/event";
import { CheckMenuItem, Menu, Submenu } from "@tauri-apps/api/menu";
import * as dialog from "@tauri-apps/plugin-dialog";
import { cx } from "cva";
import {
type ComponentProps,
Expand Down Expand Up @@ -66,48 +65,16 @@ function Inner() {
});
onCleanup(() => unsubTargetUnderCursor.then((unsub) => unsub()));

const selectedWindow = createQuery(() => ({
queryKey: ["selectedWindow", rawOptions.captureTarget],
queryFn: async () => {
if (rawOptions.captureTarget.variant !== "window") return null;
const windowId = rawOptions.captureTarget.id;

const windows = await commands.listCaptureWindows();
const window = windows.find((w) => w.id === windowId);

if (!window) return null;

return {
id: window.id,
app_name: window.owner_name || window.name || "Unknown",
bounds: window.bounds,
};
},
enabled:
rawOptions.captureTarget.variant === "window" &&
rawOptions.targetMode === "window",
staleTime: 5 * 1000,
}));

const windowToShow = () => {
const hoveredWindow = targetUnderCursor.window;
if (hoveredWindow) return hoveredWindow;
if (rawOptions.captureTarget.variant === "window") {
const selected = selectedWindow.data;
if (selected) return selected;
}
return hoveredWindow;
};

const windowIcon = createQuery(() => ({
queryKey: ["windowIcon", windowToShow()?.id],
queryKey: ["windowIcon", targetUnderCursor.window?.id],
queryFn: async () => {
const window = windowToShow();
if (!window?.id) return null;
return await commands.getWindowIcon(window.id.toString());
if (!targetUnderCursor.window?.id) return null;
return await commands.getWindowIcon(
targetUnderCursor.window.id.toString(),
);
},
enabled: !!windowToShow()?.id,
staleTime: 5 * 60 * 1000,
enabled: !!targetUnderCursor.window?.id,
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
}));

const displayInformation = createQuery(() => ({
Expand Down Expand Up @@ -159,9 +126,10 @@ function Inner() {
};

// We do this so any Cap window, (or external in the case of a bug) that are focused can trigger the close shortcut
const unsubOnEscapePress = events.onEscapePress.listen(() =>
setOptions("targetMode", null),
);
const unsubOnEscapePress = events.onEscapePress.listen(() => {
setOptions("targetMode", null);
commands.closeTargetSelectOverlays();
});
onCleanup(() => unsubOnEscapePress.then((f) => f()));

// This prevents browser keyboard shortcuts from firing.
Expand Down Expand Up @@ -273,6 +241,7 @@ function Inner() {
setOptions({
targetMode: "area",
});
commands.openTargetSelectOverlays(null);
}}
>
Adjust recording area
Expand Down Expand Up @@ -809,7 +778,10 @@ function RecordingControls(props: {
<>
<div class="flex gap-2.5 items-center p-2.5 my-2.5 rounded-xl border min-w-fit w-fit bg-gray-2 border-gray-4">
<div
onClick={() => setOptions("targetMode", null)}
onClick={() => {
setOptions("targetMode", null);
commands.closeTargetSelectOverlays();
}}
class="flex justify-center items-center rounded-full transition-opacity bg-gray-12 size-9 hover:opacity-80"
>
<IconCapX class="invert will-change-transform size-3 dark:invert-0" />
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/utils/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ async deleteWhisperModel(modelPath: string) : Promise<null> {
async exportCaptionsSrt(videoId: string) : Promise<string | null> {
return await TAURI_INVOKE("export_captions_srt", { videoId });
},
async openTargetSelectOverlays() : Promise<null> {
return await TAURI_INVOKE("open_target_select_overlays");
async openTargetSelectOverlays(focusedTarget: ScreenCaptureTarget | null) : Promise<null> {
return await TAURI_INVOKE("open_target_select_overlays", { focusedTarget });
},
async closeTargetSelectOverlays() : Promise<null> {
return await TAURI_INVOKE("close_target_select_overlays");
Expand Down
6 changes: 3 additions & 3 deletions crates/recording/src/sources/audio_mixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,9 @@ impl AudioMixer {

frame.set_rate(source.info.rate() as u32);

let silence_duration =
Duration::from_secs_f64(silence_samples_count as f64 / rate as f64);
let silence_duration = Duration::from_secs_f64(
silence_samples_count as f64 / rate as f64,
);
let timestamp = buffer_last_timestamp + buffer_last_duration;
source.buffer_last = Some((timestamp, silence_duration));
source.buffer.push_back(AudioFrame::new(frame, timestamp));
Expand Down Expand Up @@ -687,4 +688,3 @@ mod test {
}
}
}

7 changes: 7 additions & 0 deletions crates/recording/src/sources/screen_capture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ impl ScreenCaptureTarget {
}
}

pub fn window(&self) -> Option<WindowId> {
match self {
Self::Window { id } => Some(id.clone()),
_ => None,
}
}

pub fn cursor_crop(&self) -> Option<CursorCropBounds> {
match self {
Self::Display { .. } => {
Expand Down
Loading