Created comprehensive VPN setup tooling for Peaceful Spirit L2TP/IPsec connection and enhanced agent documentation framework. VPN Configuration (PST-NW-VPN): - Setup-PST-L2TP-VPN.ps1: Automated L2TP/IPsec setup with split-tunnel and DNS - Connect-PST-VPN.ps1: Connection helper with PPP adapter detection, DNS (192.168.0.2), and route config (192.168.0.0/24) - Connect-PST-VPN-Standalone.ps1: Self-contained connection script for remote deployment - Fix-PST-VPN-Auth.ps1: Authentication troubleshooting for CHAP/MSChapv2 - Diagnose-VPN-Interface.ps1: Comprehensive VPN interface and routing diagnostic - Quick-Test-VPN.ps1: Fast connectivity verification (DNS/router/routes) - Add-PST-VPN-Route-Manual.ps1: Manual route configuration helper - vpn-connect.bat, vpn-disconnect.bat: Simple batch file shortcuts - OpenVPN config files (Windows-compatible, abandoned for L2TP) Key VPN Implementation Details: - L2TP creates PPP adapter with connection name as interface description - UniFi auto-configures DNS (192.168.0.2) but requires manual route to 192.168.0.0/24 - Split-tunnel enabled (only remote traffic through VPN) - All-user connection for pre-login auto-connect via scheduled task - Authentication: CHAP + MSChapv2 for UniFi compatibility Agent Documentation: - AGENT_QUICK_REFERENCE.md: Quick reference for all specialized agents - documentation-squire.md: Documentation and task management specialist agent - Updated all agent markdown files with standardized formatting Project Organization: - Moved conversation logs to dedicated directories (guru-connect-conversation-logs, guru-rmm-conversation-logs) - Cleaned up old session JSONL files from projects/msp-tools/ - Added guru-connect infrastructure (agent, dashboard, proto, scripts, .gitea workflows) - Added guru-rmm server components and deployment configs Technical Notes: - VPN IP pool: 192.168.4.x (client gets 192.168.4.6) - Remote network: 192.168.0.0/24 (router at 192.168.0.10) - PSK: rrClvnmUeXEFo90Ol+z7tfsAZHeSK6w7 - Credentials: pst-admin / 24Hearts$ Files: 15 VPN scripts, 2 agent docs, conversation log reorganization, guru-connect/guru-rmm infrastructure additions Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
160 lines
4.2 KiB
Rust
160 lines
4.2 KiB
Rust
//! Display enumeration and information
|
|
|
|
use anyhow::Result;
|
|
|
|
/// Information about a display/monitor
|
|
#[derive(Debug, Clone)]
|
|
pub struct Display {
|
|
/// Unique display ID
|
|
pub id: u32,
|
|
|
|
/// Display name (e.g., "\\\\.\\DISPLAY1")
|
|
pub name: String,
|
|
|
|
/// X position in virtual screen coordinates
|
|
pub x: i32,
|
|
|
|
/// Y position in virtual screen coordinates
|
|
pub y: i32,
|
|
|
|
/// Width in pixels
|
|
pub width: u32,
|
|
|
|
/// Height in pixels
|
|
pub height: u32,
|
|
|
|
/// Whether this is the primary display
|
|
pub is_primary: bool,
|
|
|
|
/// Platform-specific handle (HMONITOR on Windows)
|
|
#[cfg(windows)]
|
|
pub handle: isize,
|
|
}
|
|
|
|
/// Display info for protocol messages
|
|
#[derive(Debug, Clone)]
|
|
pub struct DisplayInfo {
|
|
pub displays: Vec<Display>,
|
|
pub primary_id: u32,
|
|
}
|
|
|
|
impl Display {
|
|
/// Total pixels in the display
|
|
pub fn pixel_count(&self) -> u32 {
|
|
self.width * self.height
|
|
}
|
|
|
|
/// Bytes needed for BGRA frame buffer
|
|
pub fn buffer_size(&self) -> usize {
|
|
(self.width * self.height * 4) as usize
|
|
}
|
|
}
|
|
|
|
/// Enumerate all connected displays
|
|
#[cfg(windows)]
|
|
pub fn enumerate_displays() -> Result<Vec<Display>> {
|
|
use windows::Win32::Graphics::Gdi::{
|
|
EnumDisplayMonitors, GetMonitorInfoW, HMONITOR, MONITORINFOEXW,
|
|
};
|
|
use windows::Win32::Foundation::{BOOL, LPARAM, RECT};
|
|
use std::mem;
|
|
|
|
let mut displays = Vec::new();
|
|
let mut display_id = 0u32;
|
|
|
|
// Callback for EnumDisplayMonitors
|
|
unsafe extern "system" fn enum_callback(
|
|
hmonitor: HMONITOR,
|
|
_hdc: windows::Win32::Graphics::Gdi::HDC,
|
|
_rect: *mut RECT,
|
|
lparam: LPARAM,
|
|
) -> BOOL {
|
|
let displays = &mut *(lparam.0 as *mut Vec<(HMONITOR, u32)>);
|
|
let id = displays.len() as u32;
|
|
displays.push((hmonitor, id));
|
|
BOOL(1) // Continue enumeration
|
|
}
|
|
|
|
// Collect all monitor handles
|
|
let mut monitors: Vec<(windows::Win32::Graphics::Gdi::HMONITOR, u32)> = Vec::new();
|
|
unsafe {
|
|
let result = EnumDisplayMonitors(
|
|
None,
|
|
None,
|
|
Some(enum_callback),
|
|
LPARAM(&mut monitors as *mut _ as isize),
|
|
);
|
|
if !result.as_bool() {
|
|
anyhow::bail!("EnumDisplayMonitors failed");
|
|
}
|
|
}
|
|
|
|
// Get detailed info for each monitor
|
|
for (hmonitor, id) in monitors {
|
|
let mut info: MONITORINFOEXW = unsafe { mem::zeroed() };
|
|
info.monitorInfo.cbSize = mem::size_of::<MONITORINFOEXW>() as u32;
|
|
|
|
unsafe {
|
|
if GetMonitorInfoW(hmonitor, &mut info.monitorInfo as *mut _ as *mut _).as_bool() {
|
|
let rect = info.monitorInfo.rcMonitor;
|
|
let name = String::from_utf16_lossy(
|
|
&info.szDevice[..info.szDevice.iter().position(|&c| c == 0).unwrap_or(info.szDevice.len())]
|
|
);
|
|
|
|
let is_primary = (info.monitorInfo.dwFlags & 1) != 0; // MONITORINFOF_PRIMARY
|
|
|
|
displays.push(Display {
|
|
id,
|
|
name,
|
|
x: rect.left,
|
|
y: rect.top,
|
|
width: (rect.right - rect.left) as u32,
|
|
height: (rect.bottom - rect.top) as u32,
|
|
is_primary,
|
|
handle: hmonitor.0 as isize,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort by position (left to right, top to bottom)
|
|
displays.sort_by(|a, b| {
|
|
if a.y != b.y {
|
|
a.y.cmp(&b.y)
|
|
} else {
|
|
a.x.cmp(&b.x)
|
|
}
|
|
});
|
|
|
|
// Reassign IDs after sorting
|
|
for (i, display) in displays.iter_mut().enumerate() {
|
|
display.id = i as u32;
|
|
}
|
|
|
|
if displays.is_empty() {
|
|
anyhow::bail!("No displays found");
|
|
}
|
|
|
|
Ok(displays)
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
pub fn enumerate_displays() -> Result<Vec<Display>> {
|
|
anyhow::bail!("Display enumeration only supported on Windows")
|
|
}
|
|
|
|
/// Get display info for protocol
|
|
pub fn get_display_info() -> Result<DisplayInfo> {
|
|
let displays = enumerate_displays()?;
|
|
let primary_id = displays
|
|
.iter()
|
|
.find(|d| d.is_primary)
|
|
.map(|d| d.id)
|
|
.unwrap_or(0);
|
|
|
|
Ok(DisplayInfo {
|
|
displays,
|
|
primary_id,
|
|
})
|
|
}
|