IT 인터넷/일반

[Rust] 화면 캡쳐

Banjubu 2024. 10. 28. 17:53
반응형

 

macos 기준이에요.

 

Cargo.toml

[package]
name = "screen_capture"
version = "0.1.0"
edition = "2021"
description = "Screen Capture Application for macOS"

[package.metadata.bundle]
name = "Screen Capture"
identifier = "com.example.screen-capture"
icon = ["32x32.png", "128x128.png", "128x128@2x.png"]
version = "0.1.0"
copyright = "Copyright (c) Example Company 2024. All rights reserved."
category = "Developer Tool"
short_description = "Screen capture utility for macOS"

[dependencies]
anyhow = "1.0"
dirs = "5.0"
core-graphics = "0.22.3"

 

src/main.rs

use std::fs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use std::process::Command;
use anyhow::{Result, Context};
use core_graphics::display::CGDisplay;

struct ScreenCaptureApp {
    save_path: PathBuf,
}

impl ScreenCaptureApp {
    fn new() -> Result<Self> {
        let mut save_path = dirs::picture_dir()
            .context("Failed to get Pictures directory")?;
        save_path.push("ScreenCaptures");
        
        if !save_path.exists() {
            fs::create_dir_all(&save_path)?;
        }

        Ok(Self { save_path })
    }

    fn request_screen_capture_permission() -> Result<()> {
        let script = r#"
            tell application "System Settings"
                activate
                reveal pane id "com.apple.settings.Security-Privacy.Privacy"
                delay 1
                tell application "System Events"
                    tell process "System Settings"
                        click radio button "Screen & System Audio Recording" of scroll area 1 of group 1 of window 1
                    end tell
                end tell
            end tell
        "#;

        Command::new("osascript")
            .arg("-e")
            .arg(script)
            .output()?;

        println!("시스템 설정에서 화면 녹화 권한을 허용해 주세요.");
        println!("권한을 허용한 후, Enter 키를 눌러 계속하세요...");
        
        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;
        
        Ok(())
    }

    fn capture_all_displays(&self) -> Result<Vec<PathBuf>> {
        let mut captured_files = Vec::new();
        let display_count = self.get_display_count()?;

        println!("발견된 디스플레이 수: {}", display_count);
        
        for display_id in 1..=display_count {
            let timestamp = SystemTime::now()
                .duration_since(UNIX_EPOCH)?
                .as_secs();
                
            let filename = format!("screenshot_display_{}_at_{}.png", display_id, timestamp);
            let file_path = self.save_path.join(&filename);

            println!("디스플레이 {} 캡처 시도 중...", display_id);
            let result = Command::new("screencapture")
                .args(["-D", &display_id.to_string(), "-x"])  // 각 디스플레이 캡처
                .arg(&file_path)
                .output();
                
            match result {
                Ok(output) => {
                    if output.status.success() {
                        println!("디스플레이 {} 캡처 성공: {}", display_id, filename);
                        if let Ok(metadata) = fs::metadata(&file_path) {
                            println!("파일 크기: {} bytes", metadata.len());
                        }
                        captured_files.push(file_path);
                    } else {
                        println!("디스플레이 {} 캡처 실패. 권한을 확인하세요.", display_id);
                        Self::request_screen_capture_permission()?;
                    }
                },
                Err(e) => {
                    println!("디스플레이 {} 캡처 중 오류 발생: {}", display_id, e);
                    Self::request_screen_capture_permission()?;
                }
            }
        }

        Ok(captured_files)
    }

    fn get_display_count(&self) -> Result<u32> {
        let display_ids = CGDisplay::active_displays().expect("Failed to get active displays");
        Ok(display_ids.len() as u32)
    }

    fn open_in_finder(&self, paths: &[PathBuf]) -> Result<()> {
        if !paths.is_empty() {
            if let Some(path) = paths[0].parent() {
                Command::new("open")
                    .arg(path)
                    .output()?;
            }
        }
        Ok(())
    }
}

fn main() -> Result<()> {
    let app = ScreenCaptureApp::new()?;
    
    let captured_files = app.capture_all_displays()?;
    
    if !captured_files.is_empty() {
        println!("\n모든 디스플레이의 스크린샷이 성공적으로 저장되었습니다!");
        println!("저장 위치: {}", app.save_path.display());
        
        app.open_in_finder(&captured_files)?;
    }
    
    Ok(())
}

 

 

빌드 및 번들링

cargo clean
cargo build
cargo bundle

 

실행 후 '시스템 설정 > 개인정보 보호 및 보안 > 화면 및 시스템 오디오 녹음'에서 해당 앱을 허용해주세요.

 

이미지는 시스템의 '그림 > ScreenCaptures' 폴더에 생성됩니다.

 

 

 

반응형
LIST