use libc::{c_int, c_short, c_ushort};
use std::ptr;
const GPM_MOVE: c_int = 1;
const GPM_DRAG: c_int = 2;
const GPM_DOWN: c_int = 4;
const GPM_UP: c_int = 8;
const GPM_B_LEFT: c_int = 1;
const GPM_B_MIDDLE: c_int = 2;
const GPM_B_RIGHT: c_int = 4;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GpmEvent {
pub buttons: c_uchar,
pub modifiers: c_uchar,
pub vc: c_ushort,
pub dx: c_short,
pub dy: c_short,
pub x: c_short,
pub y: c_short,
pub event_type: c_int,
pub clicks: c_int,
pub margin: c_int,
}
use libc::c_uchar;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct GpmConnect {
event_mask: c_ushort,
default_mask: c_ushort,
min_mod: c_ushort,
max_mod: c_ushort,
}
const GPM_MOVE_MODE: c_ushort = 1;
const GPM_DRAG_MODE: c_ushort = 2;
const GPM_DOWN_MODE: c_ushort = 4;
const GPM_UP_MODE: c_ushort = 8;
#[link(name = "gpm")]
unsafe extern "C" {
fn Gpm_Open(conn: *mut GpmConnect, flag: c_int) -> c_int;
fn Gpm_Close() -> c_int;
fn Gpm_GetEvent(event: *mut GpmEvent) -> c_int;
#[allow(dead_code)]
fn Gpm_Getc(f: *mut libc::FILE) -> c_int;
}
pub struct GpmConnection {
fd: c_int,
connected: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpmEventType {
Move,
Drag,
Down,
Up,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpmButton {
Left,
Middle,
Right,
}
#[derive(Debug, Clone, Copy)]
pub struct GpmMouseEvent {
pub x: u16,
pub y: u16,
pub event_type: GpmEventType,
pub button: Option<GpmButton>,
}
impl GpmConnection {
pub fn open() -> Option<Self> {
unsafe {
let mut conn = GpmConnect {
event_mask: (GPM_MOVE_MODE | GPM_DRAG_MODE | GPM_DOWN_MODE | GPM_UP_MODE),
default_mask: 0,
min_mod: 0,
max_mod: 0,
};
let fd = Gpm_Open(&mut conn as *mut GpmConnect, 0);
if fd < 0 {
None
} else {
Some(GpmConnection {
fd,
connected: true,
})
}
}
}
#[allow(dead_code)]
pub fn fd(&self) -> c_int {
self.fd
}
pub fn has_event(&self) -> bool {
if !self.connected {
return false;
}
unsafe {
let mut read_fds: libc::fd_set = std::mem::zeroed();
libc::FD_ZERO(&mut read_fds);
libc::FD_SET(self.fd, &mut read_fds);
let mut timeout = libc::timeval {
tv_sec: 0,
tv_usec: 0,
};
let result = libc::select(
self.fd + 1,
&mut read_fds,
ptr::null_mut(),
ptr::null_mut(),
&mut timeout,
);
result > 0
}
}
pub fn get_event(&self) -> Option<GpmMouseEvent> {
if !self.connected {
return None;
}
unsafe {
let mut event: GpmEvent = std::mem::zeroed();
let result = Gpm_GetEvent(&mut event as *mut GpmEvent);
if result <= 0 {
return None;
}
let event_type = if (event.event_type & GPM_DOWN) != 0 {
GpmEventType::Down
} else if (event.event_type & GPM_UP) != 0 {
GpmEventType::Up
} else if (event.event_type & GPM_DRAG) != 0 {
GpmEventType::Drag
} else if (event.event_type & GPM_MOVE) != 0 {
GpmEventType::Move
} else {
return None;
};
let button = if (event.buttons as c_int & GPM_B_LEFT) != 0 {
Some(GpmButton::Left)
} else if (event.buttons as c_int & GPM_B_MIDDLE) != 0 {
Some(GpmButton::Middle)
} else if (event.buttons as c_int & GPM_B_RIGHT) != 0 {
Some(GpmButton::Right)
} else {
None
};
let x = (event.x - 1).max(0) as u16;
let y = (event.y - 1).max(0) as u16;
Some(GpmMouseEvent {
x,
y,
event_type,
button,
})
}
}
pub fn close(&mut self) {
if self.connected {
unsafe {
Gpm_Close();
}
self.connected = false;
}
}
#[allow(dead_code)]
pub fn is_connected(&self) -> bool {
self.connected
}
}
impl Drop for GpmConnection {
fn drop(&mut self) {
self.close();
}
}
#[allow(dead_code)]
pub fn is_gpm_available() -> bool {
if let Some(mut conn) = GpmConnection::open() {
conn.close();
true
} else {
false
}
}