gardesk/garlock / f745691

Browse files

Add screenshot capture from root window

- XGetImage-based screen capture
- BGRA pixel format for X11 compatibility
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
f745691022abcf00bfd6bce77f4fc289a7b0b0e7
Parents
4e4bf68
Tree
6d8a209

1 changed file

StatusFile+-
A garlock/src/screenshot.rs 96 0
garlock/src/screenshot.rsadded
@@ -0,0 +1,96 @@
1
+//! Screenshot capture for garlock
2
+//!
3
+//! Captures the root window contents before displaying the locker.
4
+
5
+use anyhow::{Context, Result};
6
+use x11rb::connection::Connection;
7
+use x11rb::protocol::xproto::{ConnectionExt, ImageFormat};
8
+
9
+/// Captured screenshot data
10
+pub struct Screenshot {
11
+    /// Raw pixel data in BGRA format (X11 native)
12
+    pub data: Vec<u8>,
13
+    /// Image width
14
+    pub width: u32,
15
+    /// Image height
16
+    pub height: u32,
17
+    /// Bits per pixel (typically 24 or 32)
18
+    pub depth: u8,
19
+}
20
+
21
+impl Screenshot {
22
+    /// Capture the root window contents
23
+    ///
24
+    /// This should be called BEFORE creating the locker window to capture
25
+    /// the current desktop state.
26
+    pub fn capture() -> Result<Self> {
27
+        let (conn, screen_num) =
28
+            x11rb::connect(None).context("Failed to connect to X server for screenshot")?;
29
+
30
+        let screen = &conn.setup().roots[screen_num];
31
+        let root = screen.root;
32
+        let width = screen.width_in_pixels;
33
+        let height = screen.height_in_pixels;
34
+        let depth = screen.root_depth;
35
+
36
+        tracing::debug!(width, height, depth, "Capturing root window");
37
+
38
+        // Get image from root window
39
+        let reply = conn
40
+            .get_image(
41
+                ImageFormat::Z_PIXMAP,
42
+                root,
43
+                0,
44
+                0,
45
+                width,
46
+                height,
47
+                !0, // plane_mask - all planes
48
+            )?
49
+            .reply()
50
+            .context("Failed to get root window image")?;
51
+
52
+        tracing::debug!(
53
+            data_len = reply.data.len(),
54
+            depth = reply.depth,
55
+            "Screenshot captured"
56
+        );
57
+
58
+        // Verify we got the expected amount of data
59
+        let expected_size = width as usize * height as usize * 4; // 4 bytes per pixel for 32-bit
60
+        if reply.data.len() < expected_size {
61
+            tracing::warn!(
62
+                "Screenshot data smaller than expected: {} < {}",
63
+                reply.data.len(),
64
+                expected_size
65
+            );
66
+        }
67
+
68
+        Ok(Self {
69
+            data: reply.data,
70
+            width: width as u32,
71
+            height: height as u32,
72
+            depth: reply.depth,
73
+        })
74
+    }
75
+
76
+    /// Convert BGRA data to RGBA for image processing
77
+    pub fn to_rgba(&self) -> Vec<u8> {
78
+        let mut rgba = self.data.clone();
79
+        bgra_to_rgba(&mut rgba);
80
+        rgba
81
+    }
82
+}
83
+
84
+/// Convert BGRA pixel data to RGBA in-place
85
+pub fn bgra_to_rgba(data: &mut [u8]) {
86
+    for chunk in data.chunks_exact_mut(4) {
87
+        chunk.swap(0, 2); // B <-> R
88
+    }
89
+}
90
+
91
+/// Convert RGBA pixel data to BGRA in-place
92
+pub fn rgba_to_bgra(data: &mut [u8]) {
93
+    for chunk in data.chunks_exact_mut(4) {
94
+        chunk.swap(0, 2); // R <-> B
95
+    }
96
+}