Skip to content
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6ba08c3
squash and resolve conflicts
oscartbeaumont Aug 13, 2025
480f9ae
format + build
oscartbeaumont Aug 13, 2025
d36f764
prevent target overlay ending up in recordings
oscartbeaumont Aug 13, 2025
6fc48b4
revert mistakr
oscartbeaumont Aug 13, 2025
1f341c6
fix area selection + remove wrong multi-monitor calc
oscartbeaumont Aug 13, 2025
cf754fb
Merge branch 'main' into new-recording-flow2
oscartbeaumont Aug 13, 2025
7003fd7
format
oscartbeaumont Aug 13, 2025
388b82b
fix multiple monitors
oscartbeaumont Aug 13, 2025
464c20c
enable new recording flow in dev by default
oscartbeaumont Aug 13, 2025
d44b129
cargo fmt
oscartbeaumont Aug 13, 2025
4c4ec72
Merge branch 'main' into new-recording-flow2
oscartbeaumont Aug 13, 2025
6dfd7a5
remove unused import
oscartbeaumont Aug 13, 2025
04b7b2f
minor fixes
oscartbeaumont Aug 13, 2025
4caff81
format
oscartbeaumont Aug 13, 2025
85b0284
screen working
oscartbeaumont Aug 13, 2025
81a0d09
i think this commit is bad
oscartbeaumont Aug 13, 2025
8d4cf51
Merge branch 'main' into new-recording-flow2
oscartbeaumont Aug 18, 2025
591bff4
Merge branch 'main' into new-recording-flow2
oscartbeaumont Aug 19, 2025
7032bde
hide target select overlay on launch
oscartbeaumont Aug 19, 2025
d475dfe
only a single monitor can have an area selection at once
oscartbeaumont Aug 19, 2025
fbe7f97
Merge branch 'main' into new-recording-flow2
oscartbeaumont Aug 22, 2025
f17ff8a
wip
oscartbeaumont Aug 22, 2025
6760846
fixes
oscartbeaumont Aug 22, 2025
4e2a9d3
fix macos screen select
oscartbeaumont Aug 22, 2025
e957f5d
fixes
oscartbeaumont Aug 22, 2025
062de55
cleanup win
oscartbeaumont Aug 22, 2025
df944f8
cleanup
oscartbeaumont Aug 22, 2025
e2a8fad
cleanup window deps
oscartbeaumont Aug 22, 2025
d4f26d0
format
oscartbeaumont Aug 22, 2025
43d3b81
rebase Brendan's changes back on
oscartbeaumont Aug 22, 2025
570038c
fix
oscartbeaumont Aug 22, 2025
45a6191
format
oscartbeaumont Aug 22, 2025
9a0f8d3
implement countdown
oscartbeaumont Aug 22, 2025
8b3c380
format
oscartbeaumont Aug 22, 2025
8e0d218
fix target select overlay bounds
Brendonovich Aug 22, 2025
2cb6554
refine design
Brendonovich Aug 22, 2025
edf1210
scap-targets
Brendonovich Aug 23, 2025
d028c9c
Merge branch 'main' into new-recording-flow3
Brendonovich Aug 25, 2025
e7fb69b
instant mode sign in
Brendonovich Aug 25, 2025
db4a7ab
microphone levels
Brendonovich Aug 25, 2025
30aef8e
checkbox menu items
Brendonovich Aug 25, 2025
aa9ebf9
re-enable content protected
Brendonovich Aug 25, 2025
aacbb31
Merge branch 'main' into new-recording-flow3
Brendonovich Aug 25, 2025
3cd60fe
rustfmt
oscartbeaumont Aug 25, 2025
58219b3
reopen overlay after settings is closed
oscartbeaumont Aug 25, 2025
69f567b
compile on windows plz
oscartbeaumont Aug 25, 2025
66659c4
wip: fix windows window selection
oscartbeaumont Aug 25, 2025
e8f0f43
wip: async icon loading
oscartbeaumont Aug 25, 2025
df93111
format + close target select overflow for countdown
oscartbeaumont Aug 25, 2025
181dc90
icon fallback state
oscartbeaumont Aug 25, 2025
ec2d38b
optimize windows icon loading
oscartbeaumont Aug 25, 2025
63ff653
cleanup exe filtering
oscartbeaumont Aug 25, 2025
6d7582a
fix
oscartbeaumont Aug 25, 2025
0980e85
format + remove log
oscartbeaumont Aug 25, 2025
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
3 changes: 1 addition & 2 deletions apps/desktop/src-tauri/src/general_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ fn default_enable_native_camera_preview() -> bool {
}

fn default_enable_new_recording_flow() -> bool {
false
// cfg!(debug_assertions)
cfg!(debug_assertions)
}

fn no(_: &bool) -> bool {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
captions::export_captions_srt,
target_select_overlay::open_target_select_overlays,
target_select_overlay::close_target_select_overlays,
target_select_overlay::display_information,
])
.events(tauri_specta::collect_events![
RecordingOptionsChanged,
Expand Down
36 changes: 20 additions & 16 deletions apps/desktop/src-tauri/src/target_select_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use base64::prelude::*;

use crate::windows::{CapWindowId, ShowCapWindow};
use cap_displays::{
DisplayId, WindowId,
Display, DisplayId, WindowId,
bounds::{LogicalBounds, PhysicalSize},
};
use serde::Serialize;
Expand All @@ -24,7 +24,6 @@ use tracing::error;
pub struct TargetUnderCursor {
display_id: Option<DisplayId>,
window: Option<WindowUnderCursor>,
screen: Option<ScreenUnderCursor>,
}

#[derive(Serialize, Type, Clone)]
Expand All @@ -36,9 +35,9 @@ pub struct WindowUnderCursor {
}

#[derive(Serialize, Type, Clone)]
pub struct ScreenUnderCursor {
name: String,
physical_size: PhysicalSize,
pub struct DisplayInformation {
name: Option<String>,
physical_size: Option<PhysicalSize>,
refresh_rate: String,
}

Expand Down Expand Up @@ -77,13 +76,6 @@ pub async fn open_target_select_overlays(
}),
})
}),
screen: display.and_then(|d| {
Some(ScreenUnderCursor {
name: d.name().unwrap_or_default(),
physical_size: d.physical_size()?,
refresh_rate: d.refresh_rate().to_string(),
})
}),
}
.emit(&app);

Expand Down Expand Up @@ -112,10 +104,7 @@ pub async fn open_target_select_overlays(

#[specta::specta]
#[tauri::command]
pub async fn close_target_select_overlays(
app: AppHandle,
// state: tauri::State<'_, WindowFocusManager>,
) -> Result<(), String> {
pub async fn close_target_select_overlays(app: AppHandle) -> Result<(), String> {
for (id, window) in app.webview_windows() {
if let Ok(CapWindowId::TargetSelectOverlay { .. }) = CapWindowId::from_str(&id) {
let _ = window.close();
Expand All @@ -125,6 +114,21 @@ pub async fn close_target_select_overlays(
Ok(())
}

#[specta::specta]
#[tauri::command]
pub async fn display_information(display_id: &str) -> Result<DisplayInformation, String> {
let display_id = display_id
.parse::<DisplayId>()
.map_err(|err| format!("Invalid display ID: {}", err))?;
let display = Display::from_id(&display_id).ok_or("Display not found")?;

Ok(DisplayInformation {
name: display.name(),
physical_size: display.physical_size(),
refresh_rate: display.refresh_rate().to_string(),
})
}

// Windows doesn't have a proper concept of window z-index's so we implement them in userspace :(
#[derive(Default)]
pub struct WindowFocusManager {
Expand Down
53 changes: 40 additions & 13 deletions apps/desktop/src-tauri/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,6 @@ impl ShowCapWindow {
return Err(tauri::Error::WindowNotFound);
};

#[cfg(target_os = "macos")]
let position = display.raw_handle().logical_position();
#[cfg(target_os = "macos")]
let size = display.logical_size().unwrap();

#[cfg(windows)]
let position = display.raw_handle().physical_position().unwrap();
#[cfg(windows)]
let size = display.physical_size().unwrap();

let mut window_builder = self
.window_builder(
app,
Expand All @@ -275,15 +265,52 @@ impl ShowCapWindow {
.resizable(false)
.fullscreen(false)
.shadow(false)
.always_on_top(cfg!(target_os = "macos"))
.content_protected(true)
.always_on_top(true)
.visible_on_all_workspaces(true)
.skip_taskbar(true)
.inner_size(size.width(), size.height())
.position(position.x(), position.y())
.transparent(true);

#[cfg(target_os = "macos")]
{
let position = display.raw_handle().logical_position();
let size = display.logical_size().unwrap();

window_builder = window_builder
.inner_size(size.width(), size.height())
.position(position.x(), position.y());
}

#[cfg(windows)]
{
window_builder = window_builder.inner_size(100.0, 100.0).position(0.0, 0.0);
}

let window = window_builder.build()?;

#[cfg(windows)]
{
let position = display.raw_handle().physical_position().unwrap();
let logical_size = display.logical_size().unwrap();
let physical_size = display.physical_size().unwrap();
use tauri::{LogicalSize, PhysicalPosition, PhysicalSize};
let _ = window.set_size(LogicalSize::new(
logical_size.width(),
logical_size.height(),
));
let _ = window.set_position(PhysicalPosition::new(position.x(), position.y()));
tokio::time::sleep(std::time::Duration::from_millis(5)).await;

let actual_physical_size = window.inner_size().unwrap();
// this third time makes it work when the resulting size is wrong, god knows why
if physical_size.width() != actual_physical_size.width as f64 {
let _ = window.set_size(LogicalSize::new(
logical_size.width(),
logical_size.height(),
));
}
}
Comment on lines +292 to +313
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Windows post-build: unwraps can panic; also check height and use a tolerance. Add debug to help the DPI bug.

logical_size/physical_size/physical_position are Option and may be None (e.g., transient modes, hotplug). Unwraps here will crash the app, which aligns with “overlay isn’t showing” reports. Also, only width is checked; height should be too, with tolerance for rounding.

-                #[cfg(windows)]
-                {
-                    let position = display.raw_handle().physical_position().unwrap();
-                    let logical_size = display.logical_size().unwrap();
-                    let physical_size = display.physical_size().unwrap();
-                    use tauri::{LogicalSize, PhysicalPosition, PhysicalSize};
-                    let _ = window.set_size(LogicalSize::new(
-                        logical_size.width(),
-                        logical_size.height(),
-                    ));
-                    let _ = window.set_position(PhysicalPosition::new(position.x(), position.y()));
-                    tokio::time::sleep(std::time::Duration::from_millis(5)).await;
-
-                    let actual_physical_size = window.inner_size().unwrap();
-                    // this third time makes it work when the resulting size is wrong, god knows why
-                    if physical_size.width() != actual_physical_size.width as f64 {
-                        let _ = window.set_size(LogicalSize::new(
-                            logical_size.width(),
-                            logical_size.height(),
-                        ));
-                    }
-                }
+                #[cfg(windows)]
+                {
+                    use tauri::{LogicalSize as LS, PhysicalPosition as PP};
+                    if let (Some(position), Some(logical_size), Some(physical_size)) = (
+                        display.raw_handle().physical_position(),
+                        display.logical_size(),
+                        display.physical_size(),
+                    ) {
+                        let _ = window.set_position(PP::new(position.x(), position.y()));
+                        let _ = window.set_size(LS::new(logical_size.width(), logical_size.height()));
+                        // Give the compositor one frame at 60Hz rather than a magic 5ms.
+                        tokio::time::sleep(std::time::Duration::from_millis(16)).await;
+                        if let Ok(actual) = window.inner_size() {
+                            let w_mismatch = (physical_size.width() - actual.width as f64).abs() >= 1.0;
+                            let h_mismatch = (physical_size.height() - actual.height as f64).abs() >= 1.0;
+                            if w_mismatch || h_mismatch {
+                                // Retry with logical size; this has proven to stick across DPIs.
+                                let _ = window.set_size(LS::new(logical_size.width(), logical_size.height()));
+                            }
+                            tracing::debug!(
+                                "TargetSelectOverlay: expected_physical=({}x{}), actual_physical=({}x{}), dpi_scale={:?}",
+                                physical_size.width(),
+                                physical_size.height(),
+                                actual.width,
+                                actual.height,
+                                window.scale_factor().ok()
+                            );
+                        }
+                    } else {
+                        tracing::debug!("TargetSelectOverlay: missing display metrics; skipping size/pos fix-up.");
+                    }
+                }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[cfg(windows)]
{
let position = display.raw_handle().physical_position().unwrap();
let logical_size = display.logical_size().unwrap();
let physical_size = display.physical_size().unwrap();
use tauri::{LogicalSize, PhysicalPosition, PhysicalSize};
let _ = window.set_size(LogicalSize::new(
logical_size.width(),
logical_size.height(),
));
let _ = window.set_position(PhysicalPosition::new(position.x(), position.y()));
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
let actual_physical_size = window.inner_size().unwrap();
// this third time makes it work when the resulting size is wrong, god knows why
if physical_size.width() != actual_physical_size.width as f64 {
let _ = window.set_size(LogicalSize::new(
logical_size.width(),
logical_size.height(),
));
}
}
#[cfg(windows)]
{
use tauri::{LogicalSize as LS, PhysicalPosition as PP};
if let (Some(position), Some(logical_size), Some(physical_size)) = (
display.raw_handle().physical_position(),
display.logical_size(),
display.physical_size(),
) {
// First position, then size; fallible metric access is safely handled
let _ = window.set_position(PP::new(position.x(), position.y()));
let _ = window.set_size(LS::new(
logical_size.width(),
logical_size.height(),
));
// Give the compositor one frame at 60Hz rather than a magic 5ms.
tokio::time::sleep(std::time::Duration::from_millis(16)).await;
if let Ok(actual) = window.inner_size() {
// Check both dimensions with a 1px tolerance for DPI rounding
let w_mismatch =
(physical_size.width() - actual.width as f64).abs() >= 1.0;
let h_mismatch =
(physical_size.height() - actual.height as f64).abs() >= 1.0;
if w_mismatch || h_mismatch {
// Retry setting logical size if the first attempt didn't stick
let _ = window.set_size(LS::new(
logical_size.width(),
logical_size.height(),
));
}
tracing::debug!(
"TargetSelectOverlay: expected_physical=({}x{}), actual_physical=({}x{}), dpi_scale={:?}",
physical_size.width(),
physical_size.height(),
actual.width,
actual.height,
window.scale_factor().ok()
);
}
} else {
tracing::debug!(
"TargetSelectOverlay: missing display metrics; skipping size/pos fix-up."
);
}
}
🤖 Prompt for AI Agents
In apps/desktop/src-tauri/src/windows.rs around lines 291-312, replace the
unwraps on display.raw_handle().physical_position(), display.logical_size(),
display.physical_size(), and window.inner_size() with safe handling: return or
skip the Windows-specific sizing block if any of those Options are None (or
provide a sensible fallback), and log a debug message containing the display
DPI/handle, logical_size, physical_size and the actual_physical_size to aid
diagnosing the DPI bug; when comparing sizes, compare both width and height and
use a small tolerance (e.g., <=1.0 device pixels) to account for rounding before
re-applying set_size; keep the existing sleep and set_size calls but only call
them when sizes differ beyond the tolerance and ensure all set_* results are
handled or logged rather than silently ignored.


app.state::<WindowFocusManager>()
.spawn(display_id, window.clone());

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion apps/desktop/src/routes/(window-chrome).tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function Header() {
<header
class={cx(
"flex items-center space-x-1 h-9 select-none shrink-0 bg-gray-2",
isWindows ? "flex-row" : "flex-row-reverse pl-[5rem]",
isWindows ? "flex-row" : "flex-row-reverse pl-[4.2rem]",
)}
data-tauri-drag-region
>
Expand Down
59 changes: 0 additions & 59 deletions apps/desktop/src/routes/(window-chrome)/callback.template.ts

This file was deleted.

Loading
Loading