Skip to content

Commit

Permalink
Cap for Windows, Part 2 (#163)
Browse files Browse the repository at this point in the history
* Improved Titlebars

* remove unused file

* Update (window-chrome).tsx

* (WIP) update windows-rs

* (macOS) Fix traffic lights resetting and shadows not appearing

* Update windows-rs features

* Fix window_names and monitor_bounds for Windows

* Transparent scrollbar background for prev-recordings view

* Only (re)position traffic lights on main

* use get_next_frame in AVFrameCapture

* show placeholder if only one target option

* create scap options inside run()

* attempt windows pipe implementation

* Titlebar for setup view

* comment-out `get_cursor_image_data`

* fix: take_screenshot function

* fix windows recording, encoding, display names, decorations

* only use windows deps on windows

* update selected target if not found

* No debug login, Editor header draggable, Caption buttons transition

* Use native decorations/shadow for InProgressRecording window

* reduce titlebar height

* fix mjpeg cameras + mic capture

mic encoding still needs work

* windows named pipes for exporting

* fix unix

* format + typecheck

* format

---------

Co-authored-by: Brendan Allan <[email protected]>
Co-authored-by: Richie McIlroy <[email protected]>
  • Loading branch information
3 people authored Nov 13, 2024
1 parent 32b784f commit 5ded4dd
Show file tree
Hide file tree
Showing 33 changed files with 1,170 additions and 946 deletions.
129 changes: 66 additions & 63 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ tokio = { version = "1.39.3", features = [
] }
tauri = { version = "2.0.0" }
specta = { version = "=2.0.0-rc.20" }
scap = { git = "https://github.com/CapSoftware/scap", rev = "b1e140a3fe90" }
nokhwa = { git = "https://github.com/CapSoftware/nokhwa", rev = "c5c7e2298764", features = [
scap = { git = "https://github.com/CapSoftware/scap", rev = "b1e140a3fe905c19b845dfea66b3b1aea02f0472" }
nokhwa = { git = "https://github.com/CapSoftware/nokhwa", rev = "0d3d1f30a78b", features = [
"input-native",
"serialize",
] }
nokhwa-bindings-macos = { git = "https://github.com/CapSoftware/nokhwa", rev = "c5c7e2298764" }
nokhwa-bindings-macos = { git = "https://github.com/CapSoftware/nokhwa", rev = "0d3d1f30a78b" }
wgpu = "22.1.0"

windows = "0.58.0"
windows-sys = "0.59.0"
windows-capture = "=1.3.6"
3 changes: 2 additions & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ swift-rs = "1.0.6"
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }

[target.'cfg(target_os= "windows")'.dependencies]
windows = { version = "0.52.0", features = [
windows = { workspace = true, features = [
"Win32_Foundation",
"Win32_System",
"Win32_UI_WindowsAndMessaging",
"Win32_Graphics_Gdi",
] }
windows-sys = { workspace = true }

[target.'cfg(unix)'.dependencies]
nix = { version = "0.29.0", features = ["fs"] }
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-set-size",
"core:window:allow-set-focus",
"core:window:allow-start-dragging",
Expand Down
255 changes: 128 additions & 127 deletions apps/desktop/src-tauri/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,131 +185,132 @@ fn get_cursor_image_data() -> Option<Vec<u8>> {

#[cfg(windows)]
fn get_cursor_image_data() -> Option<Vec<u8>> {
use windows::Win32::Foundation::{BOOL, HWND, POINT};
use windows::Win32::Graphics::Gdi::{
BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, CreateDIBSection, DeleteDC,
DeleteObject, GetDC, GetObjectA, ReleaseDC, SelectObject, BITMAP, BITMAPINFO,
BITMAPINFOHEADER, DIB_RGB_COLORS, SRCCOPY,
};
use windows::Win32::UI::WindowsAndMessaging::{GetCursorInfo, CURSORINFO, CURSORINFO_FLAGS};
use windows::Win32::UI::WindowsAndMessaging::{GetIconInfo, ICONINFO};

unsafe {
// Get cursor info
let mut cursor_info = CURSORINFO {
cbSize: std::mem::size_of::<CURSORINFO>() as u32,
flags: CURSORINFO_FLAGS(0),
hCursor: Default::default(),
ptScreenPos: POINT::default(),
};

// Handle Result return type
if GetCursorInfo(&mut cursor_info).is_err() {
return None;
}

// If no cursor, return None
if cursor_info.hCursor.is_invalid() {
return None;
}

// Get icon info
let mut icon_info = ICONINFO::default();
// Handle Result return type
if GetIconInfo(cursor_info.hCursor, &mut icon_info).is_err() {
return None;
}

// Get bitmap info
let mut bitmap = BITMAP::default();
if GetObjectA(
icon_info.hbmColor,
std::mem::size_of::<BITMAP>() as i32,
Some(&mut bitmap as *mut _ as *mut _),
) == 0
{
return None;
}

// Create compatible DC
let screen_dc = GetDC(HWND(0));
let mem_dc = CreateCompatibleDC(screen_dc);

// Create bitmap info header
let bi = BITMAPINFOHEADER {
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
biWidth: bitmap.bmWidth,
biHeight: -bitmap.bmHeight, // Negative height for top-down bitmap
biPlanes: 1,
biBitCount: 32,
biCompression: 0,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
};

let bitmap_info = BITMAPINFO {
bmiHeader: bi,
bmiColors: [Default::default()],
};

// Create DIB section
let mut bits: *mut std::ffi::c_void = std::ptr::null_mut();
let dib = CreateDIBSection(mem_dc, &bitmap_info, DIB_RGB_COLORS, &mut bits, None, 0);

if dib.is_err() {
return None;
}

let dib = dib.unwrap();

// Select DIB into DC
let old_bitmap = SelectObject(mem_dc, dib);

// Copy cursor image
if BitBlt(
mem_dc,
0,
0,
bitmap.bmWidth,
bitmap.bmHeight,
screen_dc,
cursor_info.ptScreenPos.x,
cursor_info.ptScreenPos.y,
SRCCOPY,
)
.is_err()
{
return None;
}

// Get image data
let size = (bitmap.bmWidth * bitmap.bmHeight * 4) as usize;
let mut image_data = vec![0u8; size];
std::ptr::copy_nonoverlapping(bits, image_data.as_mut_ptr() as *mut _, size);

// Cleanup
SelectObject(mem_dc, old_bitmap);
DeleteObject(dib);
DeleteDC(mem_dc);
ReleaseDC(HWND(0), screen_dc);
DeleteObject(icon_info.hbmColor);
DeleteObject(icon_info.hbmMask);

// Convert to PNG format
let image =
image::RgbaImage::from_raw(bitmap.bmWidth as u32, bitmap.bmHeight as u32, image_data)?;

let mut png_data = Vec::new();
image
.write_to(
&mut std::io::Cursor::new(&mut png_data),
image::ImageFormat::Png,
)
.ok()?;

Some(png_data)
}
return None;
// use windows::Win32::Foundation::{BOOL, HWND, POINT};
// use windows::Win32::Graphics::Gdi::{
// BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, CreateDIBSection, DeleteDC,
// DeleteObject, GetDC, GetObjectA, ReleaseDC, SelectObject, BITMAP, BITMAPINFO,
// BITMAPINFOHEADER, DIB_RGB_COLORS, SRCCOPY,
// };
// use windows::Win32::UI::WindowsAndMessaging::{GetCursorInfo, CURSORINFO, CURSORINFO_FLAGS};
// use windows::Win32::UI::WindowsAndMessaging::{GetIconInfo, ICONINFO};

// unsafe {
// // Get cursor info
// let mut cursor_info = CURSORINFO {
// cbSize: std::mem::size_of::<CURSORINFO>() as u32,
// flags: CURSORINFO_FLAGS(0),
// hCursor: Default::default(),
// ptScreenPos: POINT::default(),
// };

// // Handle Result return type
// if GetCursorInfo(&mut cursor_info).is_err() {
// return None;
// }

// // If no cursor, return None
// if cursor_info.hCursor.is_invalid() {
// return None;
// }

// // Get icon info
// let mut icon_info = ICONINFO::default();
// // Handle Result return type
// if GetIconInfo(cursor_info.hCursor, &mut icon_info).is_err() {
// return None;
// }

// // Get bitmap info
// let mut bitmap = BITMAP::default();
// if GetObjectA(
// icon_info.hbmColor,
// std::mem::size_of::<BITMAP>() as i32,
// Some(&mut bitmap as *mut _ as *mut _),
// ) == 0
// {
// return None;
// }

// // Create compatible DC
// let screen_dc = GetDC(HWND(0));
// let mem_dc = CreateCompatibleDC(screen_dc);

// // Create bitmap info header
// let bi = BITMAPINFOHEADER {
// biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
// biWidth: bitmap.bmWidth,
// biHeight: -bitmap.bmHeight, // Negative height for top-down bitmap
// biPlanes: 1,
// biBitCount: 32,
// biCompression: 0,
// biSizeImage: 0,
// biXPelsPerMeter: 0,
// biYPelsPerMeter: 0,
// biClrUsed: 0,
// biClrImportant: 0,
// };

// let bitmap_info = BITMAPINFO {
// bmiHeader: bi,
// bmiColors: [Default::default()],
// };

// // Create DIB section
// let mut bits: *mut std::ffi::c_void = std::ptr::null_mut();
// let dib = CreateDIBSection(mem_dc, &bitmap_info, DIB_RGB_COLORS, &mut bits, None, 0);

// if dib.is_err() {
// return None;
// }

// let dib = dib.unwrap();

// // Select DIB into DC
// let old_bitmap = SelectObject(mem_dc, dib);

// // Copy cursor image
// if BitBlt(
// mem_dc,
// 0,
// 0,
// bitmap.bmWidth,
// bitmap.bmHeight,
// screen_dc,
// cursor_info.ptScreenPos.x,
// cursor_info.ptScreenPos.y,
// SRCCOPY,
// )
// .is_err()
// {
// return None;
// }

// // Get image data
// let size = (bitmap.bmWidth * bitmap.bmHeight * 4) as usize;
// let mut image_data = vec![0u8; size];
// std::ptr::copy_nonoverlapping(bits, image_data.as_mut_ptr() as *mut _, size);

// // Cleanup
// SelectObject(mem_dc, old_bitmap);
// DeleteObject(dib);
// DeleteDC(mem_dc);
// ReleaseDC(HWND(0), screen_dc);
// DeleteObject(icon_info.hbmColor);
// DeleteObject(icon_info.hbmMask);

// // Convert to PNG format
// let image =
// image::RgbaImage::from_raw(bitmap.bmWidth as u32, bitmap.bmHeight as u32, image_data)?;

// let mut png_data = Vec::new();
// image
// .write_to(
// &mut std::io::Cursor::new(&mut png_data),
// image::ImageFormat::Png,
// )
// .ok()?;

// Some(png_data)
// }
}
Loading

1 comment on commit 5ded4dd

@vercel
Copy link

@vercel vercel bot commented on 5ded4dd Nov 13, 2024

Choose a reason for hiding this comment

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

Please sign in to comment.