#[cfg(target_os = "linux")]
use std::io::{self, Write};
#[cfg(target_os = "linux")]
use std::os::unix::net::UnixStream;
#[cfg(target_os = "linux")]
const GPM_SOCKET: &str = "/dev/gpmctl";
#[cfg(target_os = "linux")]
pub fn is_gpm_running() -> bool {
std::path::Path::new(GPM_SOCKET).exists()
}
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
pub fn is_gpm_running() -> bool {
false
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))]
pub fn is_gpm_running() -> bool {
false
}
#[cfg(target_os = "linux")]
pub struct GpmConnection {
_stream: UnixStream,
}
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
pub struct GpmConnection;
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))]
pub struct GpmConnection;
#[cfg(target_os = "linux")]
impl GpmConnection {
pub fn disable_cursor() -> io::Result<Self> {
let mut stream = UnixStream::connect(GPM_SOCKET)?;
let vc = get_current_vc().unwrap_or(0);
let mut connect_msg = [0u8; 16];
connect_msg[0..2].copy_from_slice(&(vc as u16).to_le_bytes());
connect_msg[2..4].copy_from_slice(&0u16.to_le_bytes());
connect_msg[4..6].copy_from_slice(&0u16.to_le_bytes());
connect_msg[6..8].copy_from_slice(&0u16.to_le_bytes());
connect_msg[8..10].copy_from_slice(&0u16.to_le_bytes());
let pid = std::process::id();
connect_msg[10..14].copy_from_slice(&pid.to_le_bytes());
stream.write_all(&connect_msg)?;
Ok(GpmConnection { _stream: stream })
}
}
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
impl GpmConnection {
pub fn disable_cursor() -> std::io::Result<Self> {
Ok(GpmConnection)
}
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))]
impl GpmConnection {
pub fn disable_cursor() -> std::io::Result<Self> {
Ok(GpmConnection)
}
}
#[cfg(target_os = "linux")]
fn get_current_vc() -> Option<i32> {
if let Ok(vt) = std::env::var("XDG_VTNR") {
if let Ok(num) = vt.parse::<i32>() {
return Some(num);
}
}
if let Ok(active) = std::fs::read_to_string("/sys/class/tty/tty0/active") {
let active = active.trim();
if let Some(num_str) = active.strip_prefix("tty") {
if let Ok(num) = num_str.parse::<i32>() {
return Some(num);
}
}
}
Some(0)
}
pub fn try_disable_gpm() -> Option<GpmConnection> {
if !is_gpm_running() {
return None;
}
match GpmConnection::disable_cursor() {
Ok(conn) => {
eprintln!("GPM detected and cursor disabled");
Some(conn)
}
Err(e) => {
eprintln!(
"Warning: GPM is running but could not disable cursor: {}",
e
);
eprintln!(
"You may see duplicate cursors. Consider stopping GPM: sudo systemctl stop gpm"
);
None
}
}
}