mirror of
https://github.com/XunilGroup/XunilOS.git
synced 2026-04-25 11:49:03 +02:00
Make keyboards use events, move all userspace-like code (from mouse as
well) to a stub file instead of being inside the kernel.
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
use crate::arch::x86_64::mouse::mouse_interrupt;
|
||||
use crate::driver::mouse::MOUSE;
|
||||
use crate::driver::timer::TIMER;
|
||||
use crate::{arch::x86_64::gdt, driver::keyboard::keyboard_interrupt_handler, println};
|
||||
use crate::{
|
||||
arch::x86_64::{gdt, mouse::mouse_interrupt},
|
||||
driver::{
|
||||
keyboard::{KEYBOARD_STATE, KeyboardEvent},
|
||||
mouse::MOUSE,
|
||||
timer::TIMER,
|
||||
},
|
||||
println,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use pc_keyboard::DecodedKey;
|
||||
use pic8259::ChainedPics;
|
||||
use spin::Mutex;
|
||||
pub use x86_64::instructions::interrupts::without_interrupts;
|
||||
use x86_64::{
|
||||
instructions::port::Port,
|
||||
registers::control::Cr2,
|
||||
structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode},
|
||||
};
|
||||
@@ -113,3 +119,27 @@ extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: InterruptStackFr
|
||||
.notify_end_of_interrupt(InterruptIndex::Mouse.as_u8());
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
let mut port = Port::new(0x60);
|
||||
let scancode: u8 = unsafe { port.read() };
|
||||
let mut keyboard_state = KEYBOARD_STATE.lock();
|
||||
|
||||
if let Ok(Some(key_event)) = keyboard_state.keyboard.add_byte(scancode) {
|
||||
if let Some(key) = keyboard_state.keyboard.process_keyevent(key_event) {
|
||||
match key {
|
||||
DecodedKey::Unicode(character) => keyboard_state
|
||||
.event_queue
|
||||
.push_back(KeyboardEvent::Unicode(character)),
|
||||
DecodedKey::RawKey(key) => keyboard_state
|
||||
.event_queue
|
||||
.push_back(KeyboardEvent::RawKey(key)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use limine::framebuffer::Framebuffer as LimineFramebuffer;
|
||||
use spin::Mutex;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86_64::interrupts::without_interrupts;
|
||||
use x86_64::instructions::interrupts::without_interrupts;
|
||||
|
||||
pub struct Framebuffer {
|
||||
addr: *mut u32,
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
use crate::{
|
||||
arch::x86_64::interrupts::{InterruptIndex, PICS},
|
||||
print,
|
||||
};
|
||||
use alloc::collections::VecDeque;
|
||||
use lazy_static::lazy_static;
|
||||
use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts};
|
||||
use spin::Mutex;
|
||||
use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame};
|
||||
use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts};
|
||||
use spin::mutex::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> =
|
||||
Mutex::new(Keyboard::new(
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum KeyboardEvent {
|
||||
Unicode(char),
|
||||
RawKey(pc_keyboard::KeyCode),
|
||||
}
|
||||
|
||||
pub struct KeyboardState {
|
||||
pub keyboard: Keyboard<layouts::Us104Key, ScancodeSet1>,
|
||||
pub event_queue: VecDeque<KeyboardEvent>,
|
||||
}
|
||||
impl KeyboardState {
|
||||
pub fn new() -> KeyboardState {
|
||||
KeyboardState {
|
||||
keyboard: Keyboard::new(
|
||||
ScancodeSet1::new(),
|
||||
layouts::Us104Key,
|
||||
HandleControl::Ignore
|
||||
));
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
let mut port = Port::new(0x60);
|
||||
let scancode: u8 = unsafe { port.read() };
|
||||
let mut keyboard = KEYBOARD.lock();
|
||||
|
||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||
if let Some(key) = keyboard.process_keyevent(key_event) {
|
||||
match key {
|
||||
DecodedKey::Unicode(character) => print!("{}", character),
|
||||
DecodedKey::RawKey(key) => print!("{:?}", key),
|
||||
HandleControl::Ignore,
|
||||
),
|
||||
event_queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
|
||||
lazy_static! {
|
||||
pub static ref KEYBOARD_STATE: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
|
||||
}
|
||||
|
||||
pub fn pop_event() -> Option<KeyboardEvent> {
|
||||
KEYBOARD_STATE.lock().event_queue.pop_front()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use core::sync::atomic::{AtomicI16, AtomicU8, AtomicUsize, Ordering};
|
||||
|
||||
use crate::driver::graphics::{base::rgb, framebuffer::Framebuffer};
|
||||
use core::sync::atomic::{AtomicI16, AtomicU8, Ordering};
|
||||
|
||||
pub struct Mouse {
|
||||
left_button_pressed: AtomicU8,
|
||||
@@ -9,15 +7,8 @@ pub struct Mouse {
|
||||
x_delta: AtomicI16,
|
||||
y_delta: AtomicI16,
|
||||
status: AtomicU8,
|
||||
mouse_x: AtomicUsize,
|
||||
mouse_y: AtomicUsize,
|
||||
}
|
||||
|
||||
static CURSOR_BYTES: &[u8] = include_bytes!("../../../assets/cursors/default.bmp");
|
||||
const BMP_HEADER_SIZE: usize = 138;
|
||||
const CURSOR_W: usize = 24;
|
||||
const CURSOR_H: usize = 24;
|
||||
|
||||
impl Mouse {
|
||||
const fn new() -> Mouse {
|
||||
Mouse {
|
||||
@@ -27,8 +18,6 @@ impl Mouse {
|
||||
x_delta: AtomicI16::new(0),
|
||||
y_delta: AtomicI16::new(0),
|
||||
status: AtomicU8::new(0),
|
||||
mouse_x: AtomicUsize::new(100),
|
||||
mouse_y: AtomicUsize::new(100),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,20 +39,18 @@ impl Mouse {
|
||||
self.y_delta.fetch_add(y_delta, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_left_button_pressed(&self) -> u8 {
|
||||
self.left_button_pressed.load(Ordering::Relaxed)
|
||||
pub fn button_state(&self) -> (u8, u8, u8) {
|
||||
(
|
||||
self.left_button_pressed.load(Ordering::Relaxed),
|
||||
self.right_button_pressed.load(Ordering::Relaxed),
|
||||
self.middle_button_pressed.load(Ordering::Relaxed),
|
||||
)
|
||||
}
|
||||
pub fn is_right_button_pressed(&self) -> u8 {
|
||||
self.right_button_pressed.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn is_middle_button_pressed(&self) -> u8 {
|
||||
self.middle_button_pressed.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn get_x_delta(&self) -> i16 {
|
||||
self.x_delta.swap(0, Ordering::Relaxed)
|
||||
}
|
||||
pub fn get_y_delta(&self) -> i16 {
|
||||
self.y_delta.swap(0, Ordering::Relaxed)
|
||||
pub fn take_motion(&self) -> (i16, i16) {
|
||||
(
|
||||
self.x_delta.swap(0, Ordering::Relaxed),
|
||||
self.y_delta.swap(0, Ordering::Relaxed),
|
||||
)
|
||||
}
|
||||
pub fn set_status(&self, status: u8) {
|
||||
self.status.store(status, Ordering::Relaxed);
|
||||
@@ -71,61 +58,6 @@ impl Mouse {
|
||||
pub fn get_status(&self) -> u8 {
|
||||
self.status.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn update(&self, width: usize, height: usize) {
|
||||
let x_delta = self.get_x_delta() / 5;
|
||||
let y_delta = self.get_y_delta() / 5;
|
||||
|
||||
if x_delta != 0 {
|
||||
self.mouse_x.store(
|
||||
(self.mouse_x.load(Ordering::Relaxed) as isize + x_delta as isize).max(0) as usize,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
if y_delta != 0 {
|
||||
self.mouse_y.store(
|
||||
(self.mouse_y.load(Ordering::Relaxed) as isize + y_delta as isize).max(0) as usize,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
if self.mouse_x.load(Ordering::Relaxed) > width {
|
||||
self.mouse_x.store(width - CURSOR_W, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if self.mouse_y.load(Ordering::Relaxed) > height {
|
||||
self.mouse_y.store(height - CURSOR_H, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, fb: &mut Framebuffer) {
|
||||
let pixels = &CURSOR_BYTES[BMP_HEADER_SIZE..]; // remove header
|
||||
|
||||
for row in 0..CURSOR_H {
|
||||
let src_row = (CURSOR_H - 1 - row) * CURSOR_W * 4;
|
||||
for col in 0..CURSOR_W {
|
||||
let i = src_row + col * 4; // 4 because rgba
|
||||
|
||||
let b = pixels[i];
|
||||
let g = pixels[i + 1];
|
||||
let r = pixels[i + 2];
|
||||
let a = pixels[i + 3];
|
||||
|
||||
if a < 255 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let color = rgb(r, g, b);
|
||||
|
||||
fb.put_pixel(
|
||||
(self.mouse_x.load(Ordering::Relaxed) + col) as usize,
|
||||
(self.mouse_y.load(Ordering::Relaxed) + row) as usize,
|
||||
color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static MOUSE: Mouse = Mouse::new();
|
||||
|
||||
@@ -5,7 +5,7 @@ use core::fmt::{self, Write};
|
||||
use spin::Mutex;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86_64::interrupts::without_interrupts;
|
||||
use x86_64::instructions::interrupts::without_interrupts;
|
||||
|
||||
pub struct ConsoleWriter<'a> {
|
||||
pub fb: &'a mut Framebuffer,
|
||||
|
||||
@@ -10,9 +10,9 @@ use limine::request::{
|
||||
DateAtBootRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, MpRequest,
|
||||
RequestsEndMarker, RequestsStartMarker,
|
||||
};
|
||||
use x86_64::instructions::interrupts::without_interrupts;
|
||||
pub mod arch;
|
||||
pub mod driver;
|
||||
pub mod userspace_stub;
|
||||
pub mod util;
|
||||
|
||||
use crate::arch::arch::{infinite_idle, init, kernel_crash, sleep};
|
||||
@@ -24,9 +24,7 @@ use crate::driver::graphics::primitives::{
|
||||
use crate::driver::mouse::MOUSE;
|
||||
use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console};
|
||||
use crate::driver::timer::TIMER;
|
||||
use crate::util::test_performance;
|
||||
use alloc::{boxed::Box, string::ToString, vec::Vec};
|
||||
|
||||
use crate::userspace_stub::userspace_init;
|
||||
/// Sets the base revision to the latest revision supported by the crate.
|
||||
/// See specification for further info.
|
||||
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
||||
@@ -127,92 +125,7 @@ unsafe extern "C" fn kmain() -> ! {
|
||||
println!("Could not get date at boot. Will default to 0.")
|
||||
}
|
||||
|
||||
boot_animation();
|
||||
|
||||
let mut mouse_status = 0;
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
|
||||
loop {
|
||||
with_serial_console(|serial_console| serial_console.clear(0, 0));
|
||||
with_framebuffer(|fb| fb.clear(rgb(253, 129, 0)));
|
||||
test_performance(|| {
|
||||
mouse_status = MOUSE.get_status();
|
||||
with_framebuffer(|mut fb| {
|
||||
width = fb.width;
|
||||
height = fb.height;
|
||||
MOUSE.update(width, height);
|
||||
|
||||
rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0));
|
||||
rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0));
|
||||
circle_filled(&mut fb, 200, 200, 100, rgb(0, 0, 0));
|
||||
circle_outline(&mut fb, 400, 200, 100, rgb(0, 0, 0));
|
||||
triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0));
|
||||
|
||||
MOUSE.draw(fb);
|
||||
});
|
||||
|
||||
let (hours, minutes, seconds) =
|
||||
unix_to_hms(TIMER.get_date_at_boot() + (TIMER.now().elapsed()) / 1000);
|
||||
|
||||
print!(
|
||||
"{:?}:{:?}:{:?}\nMouse status: {:?}",
|
||||
hours, minutes, seconds, mouse_status
|
||||
);
|
||||
});
|
||||
with_framebuffer(|fb| {
|
||||
fb.swap();
|
||||
});
|
||||
sleep(16);
|
||||
}
|
||||
}
|
||||
|
||||
fn boot_animation() {
|
||||
let mut i = 1;
|
||||
|
||||
while i < 10 {
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
|
||||
with_framebuffer(|fb| {
|
||||
fb.clear(rgb(253, 129, 0));
|
||||
width = fb.width;
|
||||
height = fb.height;
|
||||
});
|
||||
|
||||
let text_width = ("XunilOS Loading".len() + ".".repeat(i).len()) * 4 * 2;
|
||||
|
||||
with_serial_console(|serial_console| {
|
||||
serial_console.clear(width / 2 - text_width / 2, height / 2)
|
||||
});
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
"XunilOS Loading".to_string() + &".".repeat(i).as_str()
|
||||
);
|
||||
|
||||
i += 1;
|
||||
|
||||
with_framebuffer(|fb| fb.swap());
|
||||
|
||||
sleep(200);
|
||||
}
|
||||
|
||||
with_serial_console(|serial_console| {
|
||||
serial_console.clear(0, 0);
|
||||
});
|
||||
|
||||
with_framebuffer(|fb| {
|
||||
fb.clear(rgb(253, 129, 0));
|
||||
});
|
||||
}
|
||||
|
||||
fn unix_to_hms(timestamp: u64) -> (u64, u64, u64) {
|
||||
let seconds = timestamp % 86400;
|
||||
let h = seconds / 3600;
|
||||
let m = (seconds % 3600) / 60;
|
||||
let s = seconds % 60;
|
||||
(h, m, s)
|
||||
userspace_init()
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
|
||||
155
kernel/src/userspace_stub.rs
Normal file
155
kernel/src/userspace_stub.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
use alloc::string::ToString;
|
||||
|
||||
use crate::{
|
||||
arch::arch::sleep,
|
||||
driver::{
|
||||
graphics::{
|
||||
base::rgb,
|
||||
framebuffer::with_framebuffer,
|
||||
primitives::{
|
||||
circle_filled, circle_outline, rectangle_filled, rectangle_outline,
|
||||
triangle_outline,
|
||||
},
|
||||
},
|
||||
mouse::MOUSE,
|
||||
serial::with_serial_console,
|
||||
timer::TIMER,
|
||||
},
|
||||
print, println,
|
||||
util::test_performance,
|
||||
};
|
||||
|
||||
static CURSOR_BYTES: &[u8] = include_bytes!("../../assets/cursors/default.bmp");
|
||||
const BMP_HEADER_SIZE: usize = 138;
|
||||
pub const CURSOR_W: usize = 24;
|
||||
pub const CURSOR_H: usize = 24;
|
||||
|
||||
fn unix_to_hms(timestamp: u64) -> (u64, u64, u64) {
|
||||
let seconds = timestamp % 86400;
|
||||
let h = seconds / 3600;
|
||||
let m = (seconds % 3600) / 60;
|
||||
let s = seconds % 60;
|
||||
(h, m, s)
|
||||
}
|
||||
|
||||
fn boot_animation() {
|
||||
let mut i = 1;
|
||||
|
||||
while i < 10 {
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
|
||||
with_framebuffer(|fb| {
|
||||
fb.clear(rgb(253, 129, 0));
|
||||
width = fb.width;
|
||||
height = fb.height;
|
||||
});
|
||||
|
||||
let text_width = ("XunilOS Loading".len() + ".".repeat(i).len()) * 4 * 2;
|
||||
|
||||
with_serial_console(|serial_console| {
|
||||
serial_console.clear(width / 2 - text_width / 2, height / 2)
|
||||
});
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
"XunilOS Loading".to_string() + &".".repeat(i).as_str()
|
||||
);
|
||||
|
||||
i += 1;
|
||||
|
||||
with_framebuffer(|fb| fb.swap());
|
||||
|
||||
sleep(200);
|
||||
}
|
||||
|
||||
with_serial_console(|serial_console| {
|
||||
serial_console.clear(0, 0);
|
||||
});
|
||||
|
||||
with_framebuffer(|fb| {
|
||||
fb.clear(rgb(253, 129, 0));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn userspace_init() -> ! {
|
||||
// this is just a stub
|
||||
boot_animation();
|
||||
|
||||
let mut mouse_status = 0;
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
let mut mouse_x = 100;
|
||||
let mut mouse_y = 100;
|
||||
|
||||
loop {
|
||||
with_serial_console(|serial_console| serial_console.clear(0, 0));
|
||||
with_framebuffer(|fb| fb.clear(rgb(253, 129, 0)));
|
||||
test_performance(|| {
|
||||
mouse_status = MOUSE.get_status();
|
||||
with_framebuffer(|mut fb| {
|
||||
width = fb.width;
|
||||
height = fb.height;
|
||||
|
||||
let (x_delta, y_delta) = MOUSE.take_motion();
|
||||
|
||||
if x_delta != 0 {
|
||||
mouse_x = (mouse_x as isize + x_delta as isize).max(0) as usize;
|
||||
}
|
||||
if y_delta != 0 {
|
||||
mouse_y = (mouse_y as isize + y_delta as isize).max(0) as usize;
|
||||
}
|
||||
if mouse_x > width {
|
||||
mouse_x = width - CURSOR_W;
|
||||
}
|
||||
if mouse_y > height {
|
||||
mouse_y = height - CURSOR_H;
|
||||
}
|
||||
|
||||
rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0));
|
||||
rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0));
|
||||
circle_filled(&mut fb, 200, 200, 100, rgb(0, 0, 0));
|
||||
circle_outline(&mut fb, 400, 200, 100, rgb(0, 0, 0));
|
||||
triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0));
|
||||
|
||||
let pixels = &CURSOR_BYTES[BMP_HEADER_SIZE..]; // remove header
|
||||
|
||||
for row in 0..CURSOR_H {
|
||||
let src_row = (CURSOR_H - 1 - row) * CURSOR_W * 4;
|
||||
for col in 0..CURSOR_W {
|
||||
let i = src_row + col * 4; // 4 because rgba
|
||||
|
||||
let b = pixels[i];
|
||||
let g = pixels[i + 1];
|
||||
let r = pixels[i + 2];
|
||||
let a = pixels[i + 3];
|
||||
|
||||
if a < 255 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let color = rgb(r, g, b);
|
||||
|
||||
fb.put_pixel((mouse_x + col) as usize, (mouse_y + row) as usize, color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (hours, minutes, seconds) =
|
||||
unix_to_hms(TIMER.get_date_at_boot() + (TIMER.now().elapsed()) / 1000);
|
||||
|
||||
print!(
|
||||
"{:?}:{:?}:{:?}\nMouse status: {:?}\nDesktop Size: {:?}",
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
mouse_status,
|
||||
(width, height)
|
||||
);
|
||||
});
|
||||
with_framebuffer(|fb| {
|
||||
fb.swap();
|
||||
});
|
||||
sleep(1000 / 165);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user