Commit e4a297b7 authored by Jeremy Soller's avatar Jeremy Soller

vmmouse support

parent 8e41cb9f
......@@ -22,6 +22,7 @@ use controller::Ps2;
mod controller;
mod keymap;
mod vm;
bitflags! {
flags MousePacketFlags: u8 {
......@@ -38,6 +39,7 @@ bitflags! {
struct Ps2d<F: Fn(u8,bool) -> char> {
ps2: Ps2,
vmmouse: bool,
input: File,
width: u32,
height: u32,
......@@ -60,6 +62,8 @@ impl<F: Fn(u8,bool) -> char> Ps2d<F> {
let mut ps2 = Ps2::new();
let extra_packet = ps2.init();
let vmmouse = vm::enable();
let mut width = 0;
let mut height = 0;
{
......@@ -74,6 +78,7 @@ impl<F: Fn(u8,bool) -> char> Ps2d<F> {
Ps2d {
ps2: ps2,
vmmouse: vmmouse,
input: input,
width: width,
height: height,
......@@ -116,6 +121,65 @@ impl<F: Fn(u8,bool) -> char> Ps2d<F> {
scancode: scancode,
pressed: pressed
}.to_event()).expect("ps2d: failed to write key event");
} else if self.vmmouse {
for _i in 0..256 {
let (status, _, _, _, _, _) = unsafe { vm::cmd(vm::ABSPOINTER_STATUS, 0) };
//TODO if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR)
let queue_length = status & 0xffff;
if queue_length == 0 {
break;
}
if queue_length % 4 != 0 {
println!("queue length not a multiple of 4: {}", queue_length);
break;
}
let (status, dx, dy, dz, _, _) = unsafe { vm::cmd(vm::ABSPOINTER_DATA, 4) };
let (x, y) = if status & vm::RELATIVE_PACKET == vm::RELATIVE_PACKET {
(
cmp::max(0, cmp::min(self.width as i32, self.mouse_x + dx as i32)),
cmp::max(0, cmp::min(self.height as i32, self.mouse_y - dy as i32))
)
} else {
(
dx as i32 * self.width as i32 / 0xFFFF,
dy as i32 * self.height as i32 / 0xFFFF
)
};
if x != self.mouse_x || y != self.mouse_y {
self.mouse_x = x;
self.mouse_y = y;
self.input.write(&MouseEvent {
x: x,
y: y,
}.to_event()).expect("ps2d: failed to write mouse event");
}
if dz != 0 {
self.input.write(&ScrollEvent {
x: 0,
y: -(dz as i32),
}.to_event()).expect("ps2d: failed to write scroll event");
}
let left = status & vm::LEFT_BUTTON == vm::LEFT_BUTTON;
let middle = status & vm::MIDDLE_BUTTON == vm::MIDDLE_BUTTON;
let right = status & vm::RIGHT_BUTTON == vm::RIGHT_BUTTON;
if left != self.mouse_left || middle != self.mouse_middle || right != self.mouse_right {
self.mouse_left = left;
self.mouse_middle = middle;
self.mouse_right = right;
self.input.write(&ButtonEvent {
left: left,
middle: middle,
right: right,
}.to_event()).expect("ps2d: failed to write button event");
}
}
} else {
self.packets[self.packet_i] = data;
self.packet_i += 1;
......@@ -158,6 +222,13 @@ impl<F: Fn(u8,bool) -> char> Ps2d<F> {
}.to_event()).expect("ps2d: failed to write mouse event");
}
if dz != 0 {
self.input.write(&ScrollEvent {
x: 0,
y: dz,
}.to_event()).expect("ps2d: failed to write scroll event");
}
let left = flags.contains(LEFT_BUTTON);
let middle = flags.contains(MIDDLE_BUTTON);
let right = flags.contains(RIGHT_BUTTON);
......@@ -171,13 +242,6 @@ impl<F: Fn(u8,bool) -> char> Ps2d<F> {
right: right,
}.to_event()).expect("ps2d: failed to write button event");
}
if dz != 0 {
self.input.write(&ScrollEvent {
x: 0,
y: dz,
}.to_event()).expect("ps2d: failed to write scroll event");
}
} else {
println!("ps2d: overflow {:X} {:X} {:X} {:X}", self.packets[0], self.packets[1], self.packets[2], self.packets[3]);
}
......
// This code is informed by the QEMU implementation found here:
// https://github.com/qemu/qemu/blob/master/hw/input/vmmouse.c
//
// As well as the Linux implementation here:
// http://elixir.free-electrons.com/linux/v4.1/source/drivers/input/mouse/vmmouse.c
const MAGIC: u32 = 0x564D5868;
const PORT: u16 = 0x5658;
pub const ABSPOINTER_DATA: u32 = 39;
pub const ABSPOINTER_STATUS: u32 = 40;
pub const ABSPOINTER_COMMAND: u32 = 41;
pub const CMD_ENABLE: u32 = 0x45414552;
pub const CMD_DISABLE: u32 = 0x000000f5;
pub const CMD_REQUEST_ABSOLUTE: u32 = 0x53424152;
const VERSION: u32 = 0x3442554a;
pub const RELATIVE_PACKET: u32 = 0x00010000;
pub const LEFT_BUTTON: u32 = 0x20;
pub const RIGHT_BUTTON: u32 = 0x10;
pub const MIDDLE_BUTTON: u32 = 0x08;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32, u32, u32) {
let a: u32;
let b: u32;
let c: u32;
let d: u32;
let si: u32;
let di: u32;
asm!(
"in eax, dx"
:
"={eax}"(a),
"={ebx}"(b),
"={ecx}"(c),
"={edx}"(d),
"={esi}"(si),
"={edi}"(di)
:
"{eax}"(MAGIC),
"{ebx}"(arg),
"{ecx}"(cmd),
"{dx}"(PORT)
:
"memory"
:
"intel", "volatile"
);
(a, b, c, d, si, di)
}
pub fn enable() -> bool {
println!("Enable");
unsafe {
let _ = cmd(ABSPOINTER_COMMAND, CMD_ENABLE);
let (status, _, _, _, _, _) = cmd(ABSPOINTER_STATUS, 0);
if (status & 0x0000ffff) == 0 {
println!("No vmmouse");
return false;
}
let (version, _, _, _, _, _) = cmd(ABSPOINTER_DATA, 1);
if version != VERSION {
println!("Invalid vmmouse version: {} instead of {}", version, VERSION);
let _ = cmd(ABSPOINTER_COMMAND, CMD_DISABLE);
return false;
}
cmd(ABSPOINTER_COMMAND, CMD_REQUEST_ABSOLUTE);
}
return true;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment