Files
RedBear-OS/ps2d/src/state.rs
T
bjorn3 7a21573ebd Switch to absolute mouse events by default when running in a VM
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.
2024-01-12 18:33:29 +01:00

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;
}
}
}
}