From b32f6daab610d8776f51a633ee4ddddca9de7da4 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 17 Dec 2025 10:03:09 -0500 Subject: [PATCH] Revert "wip" This reverts commit b5d0f5d4f89c02df151a2b017d8f46f0ada29ed3. --- Cargo.lock | 1 - crates/gpui/Cargo.toml | 9 - crates/gpui/examples/capture_zed.rs | 447 ---------------------------- crates/gpui/examples/screenshot.rs | 428 -------------------------- 4 files changed, 885 deletions(-) delete mode 100644 crates/gpui/examples/capture_zed.rs delete mode 100644 crates/gpui/examples/screenshot.rs diff --git a/Cargo.lock b/Cargo.lock index 866ec2edd3e348534d0ca249ea4345e5fd8eaa4d..de9cb227c6cfb799099abf446c1bdee61ec85bff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7328,7 +7328,6 @@ dependencies = [ "parking_lot", "pathfinder_geometry", "pin-project", - "png 0.17.16", "postage", "pretty_assertions", "profiling", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 9c6def6534055d43a89400705ba2e816eec38a6c..da7e660a0171f38b8dd61de1c9323773ded2589b 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -253,7 +253,6 @@ rand.workspace = true reqwest_client = { workspace = true, features = ["test-support"] } unicode-segmentation.workspace = true util = { workspace = true, features = ["test-support"] } -png = "0.17" [target.'cfg(target_os = "windows")'.build-dependencies] embed-resource = "3.0" @@ -332,14 +331,6 @@ path = "examples/window_shadow.rs" name = "grid_layout" path = "examples/grid_layout.rs" -[[example]] -name = "screenshot" -path = "examples/screenshot.rs" - -[[example]] -name = "capture_zed" -path = "examples/capture_zed.rs" - [[example]] name = "mouse_pressure" path = "examples/mouse_pressure.rs" diff --git a/crates/gpui/examples/capture_zed.rs b/crates/gpui/examples/capture_zed.rs deleted file mode 100644 index e23b82488f120cb51c48765a8a57db6474cb0085..0000000000000000000000000000000000000000 --- a/crates/gpui/examples/capture_zed.rs +++ /dev/null @@ -1,447 +0,0 @@ -//! Utility: Capture Screenshots of Running Zed Windows -//! -//! This utility finds running Zed windows and captures screenshots of them. -//! It can be used for debugging, documentation, or visual testing. -//! -//! Usage: -//! cargo run -p gpui --example capture_zed -//! -//! Options (via environment variables): -//! CAPTURE_OUTPUT_DIR - Directory to save screenshots (default: current directory) -//! CAPTURE_WINDOW_INDEX - Which Zed window to capture, 0-indexed (default: all) -//! -//! Note: This requires macOS and Screen Recording permissions. -//! The first time you run this, macOS will prompt you to grant permission. - -use std::path::PathBuf; - -fn main() { - #[cfg(target_os = "macos")] - { - macos::run(); - } - - #[cfg(not(target_os = "macos"))] - { - eprintln!("This utility only works on macOS"); - std::process::exit(1); - } -} - -#[cfg(target_os = "macos")] -mod macos { - use std::path::PathBuf; - - // FFI declarations for CoreGraphics window list - #[link(name = "CoreGraphics", kind = "framework")] - unsafe extern "C" { - fn CGWindowListCopyWindowInfo(option: u32, relativeToWindow: u32) -> CFArrayRef; - fn CGWindowListCreateImage( - rect: CGRect, - list_option: u32, - window_id: u32, - image_option: u32, - ) -> CGImageRef; - fn CGImageGetWidth(image: CGImageRef) -> usize; - fn CGImageGetHeight(image: CGImageRef) -> usize; - fn CGImageGetBytesPerRow(image: CGImageRef) -> usize; - fn CGImageGetDataProvider(image: CGImageRef) -> CGDataProviderRef; - fn CGImageRelease(image: CGImageRef); - fn CGDataProviderCopyData(provider: CGDataProviderRef) -> CFDataRef; - } - - #[link(name = "CoreFoundation", kind = "framework")] - unsafe extern "C" { - fn CFArrayGetCount(array: CFArrayRef) -> isize; - fn CFArrayGetValueAtIndex(array: CFArrayRef, idx: isize) -> *const std::ffi::c_void; - fn CFDictionaryGetValue( - dict: CFDictionaryRef, - key: *const std::ffi::c_void, - ) -> *const std::ffi::c_void; - fn CFStringCreateWithCString( - alloc: *const std::ffi::c_void, - cstr: *const i8, - encoding: u32, - ) -> CFStringRef; - fn CFStringGetCStringPtr(string: CFStringRef, encoding: u32) -> *const i8; - fn CFNumberGetValue( - number: CFNumberRef, - theType: i32, - valuePtr: *mut std::ffi::c_void, - ) -> bool; - fn CFDataGetLength(data: CFDataRef) -> isize; - fn CFDataGetBytePtr(data: CFDataRef) -> *const u8; - fn CFRelease(cf: *const std::ffi::c_void); - } - - type CFArrayRef = *const std::ffi::c_void; - type CFDictionaryRef = *const std::ffi::c_void; - type CFStringRef = *const std::ffi::c_void; - type CFNumberRef = *const std::ffi::c_void; - type CGImageRef = *mut std::ffi::c_void; - type CGDataProviderRef = *mut std::ffi::c_void; - type CFDataRef = *mut std::ffi::c_void; - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGPoint { - x: f64, - y: f64, - } - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGSize { - width: f64, - height: f64, - } - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGRect { - origin: CGPoint, - size: CGSize, - } - - impl CGRect { - fn null() -> Self { - CGRect { - origin: CGPoint { - x: f64::INFINITY, - y: f64::INFINITY, - }, - size: CGSize { - width: 0.0, - height: 0.0, - }, - } - } - } - - // Constants - #[allow(non_upper_case_globals)] - const kCGWindowListOptionOnScreenOnly: u32 = 1 << 0; - #[allow(non_upper_case_globals)] - const kCGWindowListExcludeDesktopElements: u32 = 1 << 4; - #[allow(non_upper_case_globals)] - const kCGWindowListOptionIncludingWindow: u32 = 1 << 3; - #[allow(non_upper_case_globals)] - const kCGWindowImageBoundsIgnoreFraming: u32 = 1 << 0; - #[allow(non_upper_case_globals)] - const kCFStringEncodingUTF8: u32 = 0x08000100; - #[allow(non_upper_case_globals)] - const kCFNumberSInt32Type: i32 = 3; - - #[derive(Debug)] - struct WindowInfo { - window_id: u32, - owner_name: String, - window_name: String, - bounds: (f64, f64, f64, f64), // x, y, width, height - } - - fn get_cf_string(key: &str) -> CFStringRef { - unsafe { - let cstr = std::ffi::CString::new(key).unwrap(); - CFStringCreateWithCString(std::ptr::null(), cstr.as_ptr(), kCFStringEncodingUTF8) - } - } - - fn cf_string_to_rust(cf_string: CFStringRef) -> Option { - if cf_string.is_null() { - return None; - } - unsafe { - let ptr = CFStringGetCStringPtr(cf_string, kCFStringEncodingUTF8); - if ptr.is_null() { - return None; - } - Some(std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned()) - } - } - - fn cf_number_to_i32(cf_number: CFNumberRef) -> Option { - if cf_number.is_null() { - return None; - } - unsafe { - let mut value: i32 = 0; - if CFNumberGetValue( - cf_number, - kCFNumberSInt32Type, - &mut value as *mut i32 as *mut std::ffi::c_void, - ) { - Some(value) - } else { - None - } - } - } - - fn get_zed_windows() -> Vec { - let mut windows = Vec::new(); - - unsafe { - let window_list = CGWindowListCopyWindowInfo( - kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, - 0, - ); - - if window_list.is_null() { - return windows; - } - - let count = CFArrayGetCount(window_list); - - let key_owner_name = get_cf_string("kCGWindowOwnerName"); - let key_window_name = get_cf_string("kCGWindowName"); - let key_window_number = get_cf_string("kCGWindowNumber"); - let key_bounds = get_cf_string("kCGWindowBounds"); - let key_x = get_cf_string("X"); - let key_y = get_cf_string("Y"); - let key_width = get_cf_string("Width"); - let key_height = get_cf_string("Height"); - - for i in 0..count { - let dict = CFArrayGetValueAtIndex(window_list, i) as CFDictionaryRef; - if dict.is_null() { - continue; - } - - // Get owner name - let owner_name_cf = CFDictionaryGetValue(dict, key_owner_name) as CFStringRef; - let owner_name = cf_string_to_rust(owner_name_cf).unwrap_or_default(); - - // Check if this is a Zed window - if !owner_name.contains("Zed") { - continue; - } - - // Get window name - let window_name_cf = CFDictionaryGetValue(dict, key_window_name) as CFStringRef; - let window_name = cf_string_to_rust(window_name_cf).unwrap_or_default(); - - // Get window ID - let window_number_cf = CFDictionaryGetValue(dict, key_window_number) as CFNumberRef; - let window_id = cf_number_to_i32(window_number_cf).unwrap_or(0) as u32; - - // Get bounds - let bounds_dict = CFDictionaryGetValue(dict, key_bounds) as CFDictionaryRef; - let (x, y, width, height) = if !bounds_dict.is_null() { - let x_cf = CFDictionaryGetValue(bounds_dict, key_x) as CFNumberRef; - let y_cf = CFDictionaryGetValue(bounds_dict, key_y) as CFNumberRef; - let w_cf = CFDictionaryGetValue(bounds_dict, key_width) as CFNumberRef; - let h_cf = CFDictionaryGetValue(bounds_dict, key_height) as CFNumberRef; - - ( - cf_number_to_i32(x_cf).unwrap_or(0) as f64, - cf_number_to_i32(y_cf).unwrap_or(0) as f64, - cf_number_to_i32(w_cf).unwrap_or(0) as f64, - cf_number_to_i32(h_cf).unwrap_or(0) as f64, - ) - } else { - (0.0, 0.0, 0.0, 0.0) - }; - - // Skip windows with zero size (like menu bar items) - if width < 100.0 || height < 100.0 { - continue; - } - - windows.push(WindowInfo { - window_id, - owner_name, - window_name, - bounds: (x, y, width, height), - }); - } - - // Clean up CF strings - CFRelease(key_owner_name); - CFRelease(key_window_name); - CFRelease(key_window_number); - CFRelease(key_bounds); - CFRelease(key_x); - CFRelease(key_y); - CFRelease(key_width); - CFRelease(key_height); - CFRelease(window_list); - } - - windows - } - - fn capture_window_to_png( - window_id: u32, - output_path: &std::path::Path, - ) -> Result<(usize, usize), Box> { - use std::fs::File; - use std::io::BufWriter; - - // Capture the window - let image = unsafe { - CGWindowListCreateImage( - CGRect::null(), - kCGWindowListOptionIncludingWindow, - window_id, - kCGWindowImageBoundsIgnoreFraming, - ) - }; - - if image.is_null() { - return Err("Failed to capture window - image is null. \ - Make sure Screen Recording permission is granted in \ - System Preferences > Privacy & Security > Screen Recording." - .into()); - } - - // Get image dimensions - let width = unsafe { CGImageGetWidth(image) }; - let height = unsafe { CGImageGetHeight(image) }; - - if width == 0 || height == 0 { - unsafe { CGImageRelease(image) }; - return Err("Captured image has zero dimensions".into()); - } - - // Get the image data - let data_provider = unsafe { CGImageGetDataProvider(image) }; - if data_provider.is_null() { - unsafe { CGImageRelease(image) }; - return Err("Failed to get image data provider".into()); - } - - let data = unsafe { CGDataProviderCopyData(data_provider) }; - if data.is_null() { - unsafe { CGImageRelease(image) }; - return Err("Failed to copy image data".into()); - } - - let length = unsafe { CFDataGetLength(data) } as usize; - let ptr = unsafe { CFDataGetBytePtr(data) }; - let bytes = unsafe { std::slice::from_raw_parts(ptr, length) }; - let bytes_per_row = unsafe { CGImageGetBytesPerRow(image) }; - - // The image is in BGRA format with potential row padding, convert to RGBA for PNG - let mut rgba_bytes = Vec::with_capacity(width * height * 4); - for row in 0..height { - let row_start = row * bytes_per_row; - for col in 0..width { - let pixel_start = row_start + col * 4; - if pixel_start + 3 < length { - rgba_bytes.push(bytes[pixel_start + 2]); // R (was B) - rgba_bytes.push(bytes[pixel_start + 1]); // G - rgba_bytes.push(bytes[pixel_start]); // B (was R) - rgba_bytes.push(bytes[pixel_start + 3]); // A - } - } - } - - // Write PNG file - let file = File::create(output_path)?; - let w = BufWriter::new(file); - let mut encoder = png::Encoder::new(w, width as u32, height as u32); - encoder.set_color(png::ColorType::Rgba); - encoder.set_depth(png::BitDepth::Eight); - let mut writer = encoder.write_header()?; - writer.write_image_data(&rgba_bytes)?; - - // Cleanup - unsafe { - CFRelease(data as *const _); - CGImageRelease(image); - } - - Ok((width, height)) - } - - pub fn run() { - println!("Looking for Zed windows...\n"); - - let windows = get_zed_windows(); - - if windows.is_empty() { - eprintln!("No Zed windows found!"); - eprintln!("\nMake sure Zed is running and visible on screen."); - eprintln!("Note: Minimized windows cannot be captured."); - std::process::exit(1); - } - - println!("Found {} Zed window(s):\n", windows.len()); - for (i, window) in windows.iter().enumerate() { - println!( - " [{}] Window ID: {}, Title: \"{}\", Size: {}x{}", - i, window.window_id, window.window_name, window.bounds.2, window.bounds.3 - ); - } - println!(); - - // Get output directory - let output_dir = std::env::var("CAPTURE_OUTPUT_DIR") - .map(PathBuf::from) - .unwrap_or_else(|_| std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))); - - // Get window index filter - let window_index_filter: Option = std::env::var("CAPTURE_WINDOW_INDEX") - .ok() - .and_then(|s| s.parse().ok()); - - // Capture windows - let windows_to_capture: Vec<_> = match window_index_filter { - Some(idx) => { - if idx < windows.len() { - vec![&windows[idx]] - } else { - eprintln!( - "Window index {} is out of range (0-{})", - idx, - windows.len() - 1 - ); - std::process::exit(1); - } - } - None => windows.iter().collect(), - }; - - println!("Capturing {} window(s)...\n", windows_to_capture.len()); - - for (i, window) in windows_to_capture.iter().enumerate() { - let filename = if window.window_name.is_empty() { - format!("zed_window_{}.png", i) - } else { - // Sanitize window name for filename - let safe_name: String = window - .window_name - .chars() - .map(|c| { - if c.is_alphanumeric() || c == '-' || c == '_' { - c - } else { - '_' - } - }) - .collect(); - format!("zed_{}.png", safe_name) - }; - - let output_path = output_dir.join(&filename); - - match capture_window_to_png(window.window_id, &output_path) { - Ok((width, height)) => { - println!( - "✓ Captured \"{}\" -> {} ({}x{})", - window.window_name, - output_path.display(), - width, - height - ); - } - Err(e) => { - eprintln!("✗ Failed to capture \"{}\": {}", window.window_name, e); - } - } - } - - println!("\nDone!"); - } -} diff --git a/crates/gpui/examples/screenshot.rs b/crates/gpui/examples/screenshot.rs deleted file mode 100644 index 3d5c0c76df56c0fb885ebd0bb20799feb6b16087..0000000000000000000000000000000000000000 --- a/crates/gpui/examples/screenshot.rs +++ /dev/null @@ -1,428 +0,0 @@ -//! Example: Off-screen Window Rendering with Screenshots -//! -//! This example demonstrates how to: -//! 1. Create a window positioned off-screen (so it's not visible to the user) -//! 2. Render real GPUI content using Metal -//! 3. Take screenshots of the window using CGWindowListCreateImage -//! 4. Save the screenshots as PNG files -//! -//! This is useful for automated visual testing where you want real rendering -//! but don't want windows appearing on screen. -//! -//! Usage: -//! cargo run -p gpui --example screenshot -//! -//! Note: This requires macOS and Screen Recording permissions. -//! The first time you run this, macOS will prompt you to grant permission. - -use gpui::{ - App, AppContext, Application, Bounds, Context, Entity, IntoElement, Render, SharedString, - Window, WindowBounds, WindowHandle, WindowOptions, div, point, prelude::*, px, rgb, size, -}; -use raw_window_handle::{HasWindowHandle, RawWindowHandle}; -use std::path::PathBuf; -use std::time::Duration; - -// ============================================================================ -// GPUI View to Render -// ============================================================================ - -struct ScreenshotDemo { - counter: u32, - message: SharedString, -} - -impl ScreenshotDemo { - fn new() -> Self { - Self { - counter: 0, - message: "Hello, Screenshot!".into(), - } - } - - fn increment(&mut self) { - self.counter += 1; - self.message = format!("Counter: {}", self.counter).into(); - } -} - -impl Render for ScreenshotDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_4() - .bg(rgb(0x1e1e2e)) // Dark background - .size_full() - .justify_center() - .items_center() - .child( - div() - .text_3xl() - .text_color(rgb(0xcdd6f4)) - .child(self.message.clone()), - ) - .child( - div() - .flex() - .gap_3() - .child(colored_box(rgb(0xf38ba8))) // Red - .child(colored_box(rgb(0xa6e3a1))) // Green - .child(colored_box(rgb(0x89b4fa))) // Blue - .child(colored_box(rgb(0xf9e2af))), // Yellow - ) - .child( - div() - .mt_4() - .px_4() - .py_2() - .bg(rgb(0x313244)) - .rounded_md() - .text_color(rgb(0xbac2de)) - .child(format!("Frame: {}", self.counter)), - ) - } -} - -fn colored_box(color: gpui::Rgba) -> impl IntoElement { - div() - .size_16() - .bg(color) - .rounded_lg() - .shadow_md() - .border_2() - .border_color(rgb(0x45475a)) -} - -// ============================================================================ -// Screenshot Capture (macOS-specific using CGWindowListCreateImage) -// ============================================================================ - -#[cfg(target_os = "macos")] -mod screenshot { - use std::path::Path; - - // FFI declarations for CoreGraphics - #[link(name = "CoreGraphics", kind = "framework")] - unsafe extern "C" { - fn CGWindowListCreateImage( - rect: CGRect, - list_option: u32, - window_id: u32, - image_option: u32, - ) -> CGImageRef; - - fn CGImageGetWidth(image: CGImageRef) -> usize; - fn CGImageGetHeight(image: CGImageRef) -> usize; - fn CGImageGetDataProvider(image: CGImageRef) -> CGDataProviderRef; - fn CGImageRelease(image: CGImageRef); - fn CGDataProviderCopyData(provider: CGDataProviderRef) -> CFDataRef; - } - - #[link(name = "CoreFoundation", kind = "framework")] - unsafe extern "C" { - fn CFDataGetLength(data: CFDataRef) -> isize; - fn CFDataGetBytePtr(data: CFDataRef) -> *const u8; - fn CFRelease(cf: *const std::ffi::c_void); - } - - type CGImageRef = *mut std::ffi::c_void; - type CGDataProviderRef = *mut std::ffi::c_void; - type CFDataRef = *mut std::ffi::c_void; - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGPoint { - x: f64, - y: f64, - } - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGSize { - width: f64, - height: f64, - } - - #[repr(C)] - #[derive(Copy, Clone)] - struct CGRect { - origin: CGPoint, - size: CGSize, - } - - impl CGRect { - fn null() -> Self { - CGRect { - origin: CGPoint { - x: f64::INFINITY, - y: f64::INFINITY, - }, - size: CGSize { - width: 0.0, - height: 0.0, - }, - } - } - } - - #[allow(non_upper_case_globals)] - const kCGWindowListOptionIncludingWindow: u32 = 1 << 3; - #[allow(non_upper_case_globals)] - const kCGWindowImageBoundsIgnoreFraming: u32 = 1 << 0; - - /// Captures a screenshot of the specified window and saves it as a PNG. - pub fn capture_window_to_png( - window_number: i64, - output_path: &Path, - ) -> Result<(), Box> { - use std::fs::File; - use std::io::BufWriter; - - // Capture the window - let image = unsafe { - CGWindowListCreateImage( - CGRect::null(), - kCGWindowListOptionIncludingWindow, - window_number as u32, - kCGWindowImageBoundsIgnoreFraming, - ) - }; - - if image.is_null() { - return Err("Failed to capture window - image is null. \ - Make sure Screen Recording permission is granted in \ - System Preferences > Privacy & Security > Screen Recording." - .into()); - } - - // Get image dimensions - let width = unsafe { CGImageGetWidth(image) }; - let height = unsafe { CGImageGetHeight(image) }; - - if width == 0 || height == 0 { - unsafe { CGImageRelease(image) }; - return Err("Captured image has zero dimensions".into()); - } - - // Get the image data - let data_provider = unsafe { CGImageGetDataProvider(image) }; - if data_provider.is_null() { - unsafe { CGImageRelease(image) }; - return Err("Failed to get image data provider".into()); - } - - let data = unsafe { CGDataProviderCopyData(data_provider) }; - if data.is_null() { - unsafe { CGImageRelease(image) }; - return Err("Failed to copy image data".into()); - } - - let length = unsafe { CFDataGetLength(data) } as usize; - let ptr = unsafe { CFDataGetBytePtr(data) }; - let bytes = unsafe { std::slice::from_raw_parts(ptr, length) }; - - // The image is in BGRA format, convert to RGBA for PNG - let mut rgba_bytes = Vec::with_capacity(length); - for chunk in bytes.chunks(4) { - if chunk.len() == 4 { - rgba_bytes.push(chunk[2]); // R (was B) - rgba_bytes.push(chunk[1]); // G - rgba_bytes.push(chunk[0]); // B (was R) - rgba_bytes.push(chunk[3]); // A - } - } - - // Write PNG file - let file = File::create(output_path)?; - let w = BufWriter::new(file); - let mut encoder = png::Encoder::new(w, width as u32, height as u32); - encoder.set_color(png::ColorType::Rgba); - encoder.set_depth(png::BitDepth::Eight); - let mut writer = encoder.write_header()?; - writer.write_image_data(&rgba_bytes)?; - - // Cleanup - unsafe { - CFRelease(data as *const _); - CGImageRelease(image); - } - - println!( - "Screenshot saved to {} ({}x{})", - output_path.display(), - width, - height - ); - Ok(()) - } -} - -#[cfg(not(target_os = "macos"))] -mod screenshot { - use std::path::Path; - - pub fn capture_window_to_png( - _window_number: i64, - _output_path: &Path, - ) -> Result<(), Box> { - Err("Screenshot capture is only supported on macOS".into()) - } -} - -// ============================================================================ -// Main Application -// ============================================================================ - -fn main() { - env_logger::init(); - - Application::new().run(|cx: &mut App| { - // Position the window FAR off-screen so it's not visible - // but macOS still renders it (unlike minimized/hidden windows) - let off_screen_origin = point(px(-10000.0), px(-10000.0)); - let window_size = size(px(800.0), px(600.0)); - - let bounds = Bounds { - origin: off_screen_origin, - size: window_size, - }; - - println!("Creating off-screen window at {:?}", bounds); - println!("(The window is positioned off-screen but is still being rendered by macOS)"); - - // Open the window - let window_handle: WindowHandle = cx - .open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - focus: false, // Don't steal focus - show: true, // Must be true for rendering to occur - ..Default::default() - }, - |_, cx| cx.new(|_| ScreenshotDemo::new()), - ) - .expect("Failed to open window"); - - // Get the entity for later updates - let view_entity: Entity = - window_handle.entity(cx).expect("Failed to get root entity"); - - // Get output directory - let output_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); - - // Schedule screenshot captures after allowing time for rendering - cx.spawn(async move |cx| { - // Wait for the window to fully render - smol::Timer::after(Duration::from_millis(500)).await; - - // Get the window number for screenshots - let window_number = cx - .update(|app: &mut App| get_window_number_from_handle(&window_handle, app)) - .ok() - .flatten(); - - let Some(window_number) = window_number else { - eprintln!("Could not get window number. Are you running on macOS?"); - let _ = cx.update(|app: &mut App| app.quit()); - return; - }; - - println!("Window number: {}", window_number); - - // Take screenshot 1 - let output_path = output_dir.join("screenshot_1.png"); - match screenshot::capture_window_to_png(window_number, &output_path) { - Ok(()) => println!("✓ Captured screenshot_1.png"), - Err(e) => eprintln!("✗ Failed to capture screenshot_1.png: {}", e), - } - - // Update the view (update the entity directly, not through window_handle.update) - let _ = cx.update_entity(&view_entity, |view: &mut ScreenshotDemo, ecx| { - view.increment(); - view.increment(); - view.increment(); - ecx.notify(); // Trigger a re-render - }); - - // Wait for re-render - smol::Timer::after(Duration::from_millis(200)).await; - - // Take screenshot 2 - let output_path = output_dir.join("screenshot_2.png"); - match screenshot::capture_window_to_png(window_number, &output_path) { - Ok(()) => println!("✓ Captured screenshot_2.png"), - Err(e) => eprintln!("✗ Failed to capture screenshot_2.png: {}", e), - } - - // Update again - let _ = cx.update_entity(&view_entity, |view: &mut ScreenshotDemo, ecx| { - for _ in 0..7 { - view.increment(); - } - ecx.notify(); // Trigger a re-render - }); - - // Wait for re-render - smol::Timer::after(Duration::from_millis(200)).await; - - // Take screenshot 3 - let output_path = output_dir.join("screenshot_3.png"); - match screenshot::capture_window_to_png(window_number, &output_path) { - Ok(()) => println!("✓ Captured screenshot_3.png"), - Err(e) => eprintln!("✗ Failed to capture screenshot_3.png: {}", e), - } - - println!("\nAll screenshots captured!"); - println!( - "Check {} for screenshot_1.png, screenshot_2.png, screenshot_3.png", - output_dir.display() - ); - - // Quit after screenshots are taken - smol::Timer::after(Duration::from_millis(500)).await; - let _ = cx.update(|app: &mut App| app.quit()); - }) - .detach(); - }); -} - -/// Extract the window number from a GPUI WindowHandle using raw_window_handle -#[cfg(target_os = "macos")] -fn get_window_number_from_handle( - window_handle: &WindowHandle, - cx: &mut App, -) -> Option { - use objc::{msg_send, sel, sel_impl}; - - window_handle - .update(cx, |_root: &mut V, window: &mut Window, _cx| { - let handle = window.window_handle().ok()?; - match handle.as_raw() { - RawWindowHandle::AppKit(appkit_handle) => { - let ns_view = appkit_handle.ns_view.as_ptr(); - unsafe { - let ns_window: *mut std::ffi::c_void = - msg_send![ns_view as cocoa::base::id, window]; - if ns_window.is_null() { - return None; - } - let window_number: i64 = - msg_send![ns_window as cocoa::base::id, windowNumber]; - Some(window_number) - } - } - _ => None, - } - }) - .ok() - .flatten() -} - -#[cfg(not(target_os = "macos"))] -fn get_window_number_from_handle( - _window_handle: &WindowHandle, - _cx: &mut App, -) -> Option { - None -}