7a21573ebd
This will transparently release the mouse grab of the VM when leaving the screen edge, making it much easier to quickly switch between the VM and other apps running on the host.
362 lines
15 KiB
Rust
362 lines
15 KiB
Rust
use std::fs::File;
|
|
use std::io::Write;
|
|
use std::os::unix::io::AsRawFd;
|
|
use std::str;
|
|
|
|
use orbclient::{KeyEvent, MouseEvent, MouseRelativeEvent, ButtonEvent, ScrollEvent};
|
|
|
|
use crate::controller::Ps2;
|
|
use crate::vm;
|
|
|
|
bitflags! {
|
|
pub struct MousePacketFlags: u8 {
|
|
const LEFT_BUTTON = 1;
|
|
const RIGHT_BUTTON = 1 << 1;
|
|
const MIDDLE_BUTTON = 1 << 2;
|
|
const ALWAYS_ON = 1 << 3;
|
|
const X_SIGN = 1 << 4;
|
|
const Y_SIGN = 1 << 5;
|
|
const X_OVERFLOW = 1 << 6;
|
|
const Y_OVERFLOW = 1 << 7;
|
|
}
|
|
}
|
|
|
|
pub struct Ps2d<F: Fn(u8,bool) -> char> {
|
|
ps2: Ps2,
|
|
vmmouse: bool,
|
|
vmmouse_relative: bool,
|
|
input: File,
|
|
extended: bool,
|
|
lshift: bool,
|
|
rshift: bool,
|
|
mouse_x: i32,
|
|
mouse_y: i32,
|
|
mouse_left: bool,
|
|
mouse_middle: bool,
|
|
mouse_right: bool,
|
|
packets: [u8; 4],
|
|
packet_i: usize,
|
|
extra_packet: bool,
|
|
//Keymap function
|
|
get_char: F
|
|
}
|
|
|
|
impl<F: Fn(u8,bool) -> char> Ps2d<F> {
|
|
pub fn new(input: File, keymap: F) -> Self {
|
|
let mut ps2 = Ps2::new();
|
|
let extra_packet = ps2.init().expect("ps2d: failed to initialize");
|
|
|
|
// FIXME add an option for orbital to disable this when an app captures the mouse.
|
|
let vmmouse_relative = false;
|
|
let vmmouse = vm::enable(vmmouse_relative);
|
|
|
|
Ps2d {
|
|
ps2,
|
|
vmmouse,
|
|
vmmouse_relative,
|
|
input,
|
|
extended: false,
|
|
lshift: false,
|
|
rshift: false,
|
|
mouse_x: 0,
|
|
mouse_y: 0,
|
|
mouse_left: false,
|
|
mouse_middle: false,
|
|
mouse_right: false,
|
|
packets: [0; 4],
|
|
packet_i: 0,
|
|
extra_packet,
|
|
get_char: keymap
|
|
}
|
|
}
|
|
|
|
pub fn irq(&mut self) {
|
|
while let Some((keyboard, data)) = self.ps2.next() {
|
|
self.handle(keyboard, data);
|
|
}
|
|
}
|
|
|
|
pub fn handle(&mut self, keyboard: bool, data: u8) {
|
|
if keyboard {
|
|
if data == 0xE0 {
|
|
self.extended = true;
|
|
} else {
|
|
let (ps2_scancode, pressed) = if data >= 0x80 {
|
|
(data - 0x80, false)
|
|
} else {
|
|
(data, true)
|
|
};
|
|
|
|
let scancode = if self.extended {
|
|
self.extended = false;
|
|
match ps2_scancode {
|
|
//TODO: media keys
|
|
//TODO: 0x1C => orbclient::K_NUM_ENTER,
|
|
//TODO: 0x1D => orbclient::K_RIGHT_CTRL,
|
|
0x20 => 0x80 + 0x20, //TODO: orbclient::K_VOLUME_MUTE,
|
|
0x2E => 0x80 + 0x2E, //TODO: orbclient::K_VOLUME_DOWN,
|
|
0x30 => 0x80 + 0x30, //TODO: orbclient::K_VOLUME_UP,
|
|
//TODO: 0x35 => orbclient::K_NUM_SLASH,
|
|
0x38 => orbclient::K_ALT_GR,
|
|
0x47 => orbclient::K_HOME,
|
|
0x48 => orbclient::K_UP,
|
|
0x49 => orbclient::K_PGUP,
|
|
0x4B => orbclient::K_LEFT,
|
|
0x4D => orbclient::K_RIGHT,
|
|
0x4F => orbclient::K_END,
|
|
0x50 => orbclient::K_DOWN,
|
|
0x51 => orbclient::K_PGDN,
|
|
//TODO: 0x52 => orbclient::K_INSERT,
|
|
0x53 => orbclient::K_DEL,
|
|
0x5B => 0x5B, //TODO: orbclient::K_LEFT_SUPER,
|
|
//TODO: 0x5C => orbclient::K_RIGHT_SUPER,
|
|
//TODO: 0x5D => orbclient::K_APP,
|
|
//TODO power keys
|
|
/* 0x80 to 0xFF used for press/release detection */
|
|
_ => {
|
|
if pressed {
|
|
println!("ps2d: unknown extended scancode {:02X}", ps2_scancode);
|
|
}
|
|
0
|
|
}
|
|
}
|
|
} else {
|
|
match ps2_scancode {
|
|
/* 0x00 unused */
|
|
0x01 => orbclient::K_ESC,
|
|
0x02 => orbclient::K_1,
|
|
0x03 => orbclient::K_2,
|
|
0x04 => orbclient::K_3,
|
|
0x05 => orbclient::K_4,
|
|
0x06 => orbclient::K_5,
|
|
0x07 => orbclient::K_6,
|
|
0x08 => orbclient::K_7,
|
|
0x09 => orbclient::K_8,
|
|
0x0A => orbclient::K_9,
|
|
0x0B => orbclient::K_0,
|
|
0x0C => orbclient::K_MINUS,
|
|
0x0D => orbclient::K_EQUALS,
|
|
0x0E => orbclient::K_BKSP,
|
|
0x0F => orbclient::K_TAB,
|
|
0x10 => orbclient::K_Q,
|
|
0x11 => orbclient::K_W,
|
|
0x12 => orbclient::K_E,
|
|
0x13 => orbclient::K_R,
|
|
0x14 => orbclient::K_T,
|
|
0x15 => orbclient::K_Y,
|
|
0x16 => orbclient::K_U,
|
|
0x17 => orbclient::K_I,
|
|
0x18 => orbclient::K_O,
|
|
0x19 => orbclient::K_P,
|
|
0x1A => orbclient::K_BRACE_OPEN,
|
|
0x1B => orbclient::K_BRACE_CLOSE,
|
|
0x1C => orbclient::K_ENTER,
|
|
0x1D => orbclient::K_CTRL,
|
|
0x1E => orbclient::K_A,
|
|
0x1F => orbclient::K_S,
|
|
0x20 => orbclient::K_D,
|
|
0x21 => orbclient::K_F,
|
|
0x22 => orbclient::K_G,
|
|
0x23 => orbclient::K_H,
|
|
0x24 => orbclient::K_J,
|
|
0x25 => orbclient::K_K,
|
|
0x26 => orbclient::K_L,
|
|
0x27 => orbclient::K_SEMICOLON,
|
|
0x28 => orbclient::K_QUOTE,
|
|
0x29 => orbclient::K_TICK,
|
|
0x2A => orbclient::K_LEFT_SHIFT,
|
|
0x2B => orbclient::K_BACKSLASH,
|
|
0x2C => orbclient::K_Z,
|
|
0x2D => orbclient::K_X,
|
|
0x2E => orbclient::K_C,
|
|
0x2F => orbclient::K_V,
|
|
0x30 => orbclient::K_B,
|
|
0x31 => orbclient::K_N,
|
|
0x32 => orbclient::K_M,
|
|
0x33 => orbclient::K_COMMA,
|
|
0x34 => orbclient::K_PERIOD,
|
|
0x35 => orbclient::K_SLASH,
|
|
0x36 => orbclient::K_RIGHT_SHIFT,
|
|
//TODO: 0x37 => orbclient::K_NUM_ASTERISK,
|
|
0x38 => orbclient::K_ALT,
|
|
0x39 => orbclient::K_SPACE,
|
|
0x3A => orbclient::K_CAPS,
|
|
0x3B => orbclient::K_F1,
|
|
0x3C => orbclient::K_F2,
|
|
0x3D => orbclient::K_F3,
|
|
0x3E => orbclient::K_F4,
|
|
0x3F => orbclient::K_F5,
|
|
0x40 => orbclient::K_F6,
|
|
0x41 => orbclient::K_F7,
|
|
0x42 => orbclient::K_F8,
|
|
0x43 => orbclient::K_F9,
|
|
0x44 => orbclient::K_F10,
|
|
//TODO: 0x45 => orbclient::K_NUM_LOCK,
|
|
//TODO: 0x46 => orbclient::K_SCROLL_LOCK,
|
|
0x47 => orbclient::K_NUM_7,
|
|
0x48 => orbclient::K_NUM_8,
|
|
0x49 => orbclient::K_NUM_9,
|
|
//TODO: 0x4A => orbclient::K_NUM_MINUS,
|
|
0x4B => orbclient::K_NUM_4,
|
|
0x4C => orbclient::K_NUM_5,
|
|
0x4D => orbclient::K_NUM_6,
|
|
//TODO: 0x4E => orbclient::K_NUM_PLUS,
|
|
0x4F => orbclient::K_NUM_1,
|
|
0x50 => orbclient::K_NUM_2,
|
|
0x51 => orbclient::K_NUM_3,
|
|
0x52 => orbclient::K_NUM_0,
|
|
//TODO: 0x53 => orbclient::K_NUM_PERIOD,
|
|
/* 0x54 to 0x56 unused */
|
|
0x57 => orbclient::K_F11,
|
|
0x58 => orbclient::K_F12,
|
|
/* 0x59 to 0x7F unused */
|
|
/* 0x80 to 0xFF used for press/release detection */
|
|
_ => {
|
|
if pressed {
|
|
println!("ps2d: unknown scancode {:02X}", ps2_scancode);
|
|
}
|
|
0
|
|
}
|
|
}
|
|
};
|
|
|
|
if scancode == orbclient::K_LEFT_SHIFT {
|
|
self.lshift = pressed;
|
|
} else if scancode == orbclient::K_RIGHT_SHIFT {
|
|
self.rshift = pressed;
|
|
}
|
|
|
|
if scancode != 0 {
|
|
self.input.write(&KeyEvent {
|
|
character: (self.get_char)(ps2_scancode, self.lshift || self.rshift),
|
|
scancode,
|
|
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 {
|
|
eprintln!("ps2d: queue length not a multiple of 4: {}", queue_length);
|
|
break;
|
|
}
|
|
|
|
let (status, dx, dy, dz, _, _) = unsafe { vm::cmd(vm::ABSPOINTER_DATA, 4) };
|
|
|
|
if self.vmmouse_relative {
|
|
if dx != 0 || dy != 0 {
|
|
self.input.write(&MouseRelativeEvent {
|
|
dx: dx as i32,
|
|
dy: dy as i32,
|
|
}.to_event()).expect("ps2d: failed to write mouse event");
|
|
}
|
|
} else {
|
|
let x = dx as i32;
|
|
let y = dy as i32;
|
|
if x != self.mouse_x || y != self.mouse_y {
|
|
self.mouse_x = x;
|
|
self.mouse_y = y;
|
|
self.input
|
|
.write(&MouseEvent { x, 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;
|
|
|
|
let flags = MousePacketFlags::from_bits_truncate(self.packets[0]);
|
|
if ! flags.contains(MousePacketFlags::ALWAYS_ON) {
|
|
eprintln!("ps2d: mouse misalign {:X}", self.packets[0]);
|
|
|
|
self.packets = [0; 4];
|
|
self.packet_i = 0;
|
|
} else if self.packet_i >= self.packets.len() || (!self.extra_packet && self.packet_i >= 3) {
|
|
if ! flags.contains(MousePacketFlags::X_OVERFLOW) && ! flags.contains(MousePacketFlags::Y_OVERFLOW) {
|
|
let mut dx = self.packets[1] as i32;
|
|
if flags.contains(MousePacketFlags::X_SIGN) {
|
|
dx -= 0x100;
|
|
}
|
|
|
|
let mut dy = -(self.packets[2] as i32);
|
|
if flags.contains(MousePacketFlags::Y_SIGN) {
|
|
dy += 0x100;
|
|
}
|
|
|
|
let mut dz = 0;
|
|
if self.extra_packet {
|
|
let mut scroll = (self.packets[3] & 0xF) as i8;
|
|
if scroll & (1 << 3) == 1 << 3 {
|
|
scroll -= 16;
|
|
}
|
|
dz = -scroll as i32;
|
|
}
|
|
|
|
if dx != 0 || dy != 0 {
|
|
self.input.write(&MouseRelativeEvent {
|
|
dx: dx,
|
|
dy: dy,
|
|
}.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(MousePacketFlags::LEFT_BUTTON);
|
|
let middle = flags.contains(MousePacketFlags::MIDDLE_BUTTON);
|
|
let right = flags.contains(MousePacketFlags::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 {
|
|
eprintln!("ps2d: overflow {:X} {:X} {:X} {:X}", self.packets[0], self.packets[1], self.packets[2], self.packets[3]);
|
|
}
|
|
|
|
self.packets = [0; 4];
|
|
self.packet_i = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|