markdown · 3469 bytes Raw Blame History

Sprint 8: Multi-Monitor Support

Goal: Properly handle multi-monitor setups by detecting all monitors via RandR and positioning the UI appropriately.

Objectives

  1. Detect all connected monitors using XRandR
  2. Create window spanning entire virtual screen
  3. Render background on all monitors
  4. Center login UI on primary monitor
  5. Handle monitor hotplug events (optional)

Implementation

Monitor Detection

// Using x11rb's randr extension
use x11rb::protocol::randr::{self, ConnectionExt as _};

struct Monitor {
    x: i16,
    y: i16,
    width: u16,
    height: u16,
    primary: bool,
    name: String,
}

fn get_monitors(conn: &RustConnection, root: Window) -> Result<Vec<Monitor>> {
    let resources = conn.randr_get_screen_resources(root)?.reply()?;
    let mut monitors = Vec::new();

    for output in resources.outputs {
        let output_info = conn.randr_get_output_info(output, 0)?.reply()?;
        if output_info.connection == randr::Connection::CONNECTED {
            if let Some(crtc) = output_info.crtc {
                let crtc_info = conn.randr_get_crtc_info(crtc, 0)?.reply()?;
                monitors.push(Monitor {
                    x: crtc_info.x,
                    y: crtc_info.y,
                    width: crtc_info.width,
                    height: crtc_info.height,
                    primary: false, // Check via randr_get_output_primary
                    name: String::from_utf8_lossy(&output_info.name).to_string(),
                });
            }
        }
    }

    // Mark primary
    let primary = conn.randr_get_output_primary(root)?.reply()?;
    // ... mark the matching monitor as primary

    Ok(monitors)
}

Window Spanning

  • Window covers entire virtual screen (union of all monitors)
  • Use root window dimensions for total size
  • Background rendered per-monitor with proper offsets

UI Centering

  • Find primary monitor (or largest if no primary set)
  • Calculate center point of primary monitor
  • Offset all UI element positions relative to primary center
  • Login form, user list, session selector all centered on primary

Background Rendering

  • Load background image once
  • For each monitor:
    • Scale/crop background to monitor dimensions
    • Apply blur and brightness
    • Render at monitor's x,y offset in the virtual screen

Example Layout

┌─────────────────┬─────────────────────────┐
│                 │                         │
│   Monitor 2     │      Monitor 1          │
│   1920x1080     │      2560x1440          │
│   (secondary)   │      (primary)          │
│                 │                         │
│   [background]  │   [background + UI]     │
│                 │                         │
└─────────────────┴─────────────────────────┘

Testing

# Test with Xephyr multi-head (limited)
# Better: test on actual multi-monitor setup

# Check detected monitors
xrandr --query

# Verify greeter spans all monitors
DISPLAY=:0 ./target/release/gardm-greeter

Files to Modify

  • gardm-greeter/src/window.rs - Add RandR detection
  • gardm-greeter/src/main.rs - Pass monitor info to widgets
  • gardm-greeter/src/background.rs - Per-monitor rendering
  • Widget files - Accept center offset parameter
View source
1 # Sprint 8: Multi-Monitor Support
2
3 **Goal:** Properly handle multi-monitor setups by detecting all monitors via RandR and positioning the UI appropriately.
4
5 ## Objectives
6
7 1. Detect all connected monitors using XRandR
8 2. Create window spanning entire virtual screen
9 3. Render background on all monitors
10 4. Center login UI on primary monitor
11 5. Handle monitor hotplug events (optional)
12
13 ## Implementation
14
15 ### Monitor Detection
16 ```rust
17 // Using x11rb's randr extension
18 use x11rb::protocol::randr::{self, ConnectionExt as _};
19
20 struct Monitor {
21 x: i16,
22 y: i16,
23 width: u16,
24 height: u16,
25 primary: bool,
26 name: String,
27 }
28
29 fn get_monitors(conn: &RustConnection, root: Window) -> Result<Vec<Monitor>> {
30 let resources = conn.randr_get_screen_resources(root)?.reply()?;
31 let mut monitors = Vec::new();
32
33 for output in resources.outputs {
34 let output_info = conn.randr_get_output_info(output, 0)?.reply()?;
35 if output_info.connection == randr::Connection::CONNECTED {
36 if let Some(crtc) = output_info.crtc {
37 let crtc_info = conn.randr_get_crtc_info(crtc, 0)?.reply()?;
38 monitors.push(Monitor {
39 x: crtc_info.x,
40 y: crtc_info.y,
41 width: crtc_info.width,
42 height: crtc_info.height,
43 primary: false, // Check via randr_get_output_primary
44 name: String::from_utf8_lossy(&output_info.name).to_string(),
45 });
46 }
47 }
48 }
49
50 // Mark primary
51 let primary = conn.randr_get_output_primary(root)?.reply()?;
52 // ... mark the matching monitor as primary
53
54 Ok(monitors)
55 }
56 ```
57
58 ### Window Spanning
59 - Window covers entire virtual screen (union of all monitors)
60 - Use root window dimensions for total size
61 - Background rendered per-monitor with proper offsets
62
63 ### UI Centering
64 - Find primary monitor (or largest if no primary set)
65 - Calculate center point of primary monitor
66 - Offset all UI element positions relative to primary center
67 - Login form, user list, session selector all centered on primary
68
69 ### Background Rendering
70 - Load background image once
71 - For each monitor:
72 - Scale/crop background to monitor dimensions
73 - Apply blur and brightness
74 - Render at monitor's x,y offset in the virtual screen
75
76 ### Example Layout
77 ```
78 ┌─────────────────┬─────────────────────────┐
79 │ │ │
80 │ Monitor 2 │ Monitor 1 │
81 │ 1920x1080 │ 2560x1440 │
82 │ (secondary) │ (primary) │
83 │ │ │
84 │ [background] │ [background + UI] │
85 │ │ │
86 └─────────────────┴─────────────────────────┘
87 ```
88
89 ## Testing
90 ```bash
91 # Test with Xephyr multi-head (limited)
92 # Better: test on actual multi-monitor setup
93
94 # Check detected monitors
95 xrandr --query
96
97 # Verify greeter spans all monitors
98 DISPLAY=:0 ./target/release/gardm-greeter
99 ```
100
101 ## Files to Modify
102 - `gardm-greeter/src/window.rs` - Add RandR detection
103 - `gardm-greeter/src/main.rs` - Pass monitor info to widgets
104 - `gardm-greeter/src/background.rs` - Per-monitor rendering
105 - Widget files - Accept center offset parameter