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:
csd4ni3l
2026-03-30 16:24:42 +02:00
parent 91828daae2
commit aa5a2a6da6
7 changed files with 235 additions and 207 deletions

View File

@@ -1,12 +1,18 @@
use crate::arch::x86_64::mouse::mouse_interrupt; use crate::{
use crate::driver::mouse::MOUSE; arch::x86_64::{gdt, mouse::mouse_interrupt},
use crate::driver::timer::TIMER; driver::{
use crate::{arch::x86_64::gdt, driver::keyboard::keyboard_interrupt_handler, println}; keyboard::{KEYBOARD_STATE, KeyboardEvent},
mouse::MOUSE,
timer::TIMER,
},
println,
};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pc_keyboard::DecodedKey;
use pic8259::ChainedPics; use pic8259::ChainedPics;
use spin::Mutex; use spin::Mutex;
pub use x86_64::instructions::interrupts::without_interrupts;
use x86_64::{ use x86_64::{
instructions::port::Port,
registers::control::Cr2, registers::control::Cr2,
structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}, 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()); .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());
}
}

View File

@@ -4,7 +4,7 @@ use limine::framebuffer::Framebuffer as LimineFramebuffer;
use spin::Mutex; use spin::Mutex;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::interrupts::without_interrupts; use x86_64::instructions::interrupts::without_interrupts;
pub struct Framebuffer { pub struct Framebuffer {
addr: *mut u32, addr: *mut u32,

View File

@@ -1,37 +1,35 @@
use crate::{ use alloc::collections::VecDeque;
arch::x86_64::interrupts::{InterruptIndex, PICS},
print,
};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts}; use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts};
use spin::Mutex; use spin::mutex::Mutex;
use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame};
lazy_static! { #[derive(Debug, Clone)]
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = pub enum KeyboardEvent {
Mutex::new(Keyboard::new( 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(), ScancodeSet1::new(),
layouts::Us104Key, layouts::Us104Key,
HandleControl::Ignore HandleControl::Ignore,
)); ),
event_queue: VecDeque::new(),
}
}
} }
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { lazy_static! {
let mut port = Port::new(0x60); pub static ref KEYBOARD_STATE: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
let scancode: u8 = unsafe { port.read() }; }
let mut keyboard = KEYBOARD.lock();
pub fn pop_event() -> Option<KeyboardEvent> {
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { KEYBOARD_STATE.lock().event_queue.pop_front()
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => print!("{}", character),
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
} }

View File

@@ -1,6 +1,4 @@
use core::sync::atomic::{AtomicI16, AtomicU8, AtomicUsize, Ordering}; use core::sync::atomic::{AtomicI16, AtomicU8, Ordering};
use crate::driver::graphics::{base::rgb, framebuffer::Framebuffer};
pub struct Mouse { pub struct Mouse {
left_button_pressed: AtomicU8, left_button_pressed: AtomicU8,
@@ -9,15 +7,8 @@ pub struct Mouse {
x_delta: AtomicI16, x_delta: AtomicI16,
y_delta: AtomicI16, y_delta: AtomicI16,
status: AtomicU8, 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 { impl Mouse {
const fn new() -> Mouse { const fn new() -> Mouse {
Mouse { Mouse {
@@ -27,8 +18,6 @@ impl Mouse {
x_delta: AtomicI16::new(0), x_delta: AtomicI16::new(0),
y_delta: AtomicI16::new(0), y_delta: AtomicI16::new(0),
status: AtomicU8::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); self.y_delta.fetch_add(y_delta, Ordering::Relaxed);
} }
pub fn is_left_button_pressed(&self) -> u8 { pub fn button_state(&self) -> (u8, u8, u8) {
self.left_button_pressed.load(Ordering::Relaxed) (
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 { pub fn take_motion(&self) -> (i16, i16) {
self.right_button_pressed.load(Ordering::Relaxed) (
} self.x_delta.swap(0, Ordering::Relaxed),
pub fn is_middle_button_pressed(&self) -> u8 { self.y_delta.swap(0, Ordering::Relaxed),
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 set_status(&self, status: u8) { pub fn set_status(&self, status: u8) {
self.status.store(status, Ordering::Relaxed); self.status.store(status, Ordering::Relaxed);
@@ -71,61 +58,6 @@ impl Mouse {
pub fn get_status(&self) -> u8 { pub fn get_status(&self) -> u8 {
self.status.load(Ordering::Relaxed) 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(); pub static MOUSE: Mouse = Mouse::new();

View File

@@ -5,7 +5,7 @@ use core::fmt::{self, Write};
use spin::Mutex; use spin::Mutex;
#[cfg(target_arch = "x86_64")] #[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 struct ConsoleWriter<'a> {
pub fb: &'a mut Framebuffer, pub fb: &'a mut Framebuffer,

View File

@@ -10,9 +10,9 @@ use limine::request::{
DateAtBootRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, MpRequest, DateAtBootRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, MpRequest,
RequestsEndMarker, RequestsStartMarker, RequestsEndMarker, RequestsStartMarker,
}; };
use x86_64::instructions::interrupts::without_interrupts;
pub mod arch; pub mod arch;
pub mod driver; pub mod driver;
pub mod userspace_stub;
pub mod util; pub mod util;
use crate::arch::arch::{infinite_idle, init, kernel_crash, sleep}; 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::mouse::MOUSE;
use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console}; use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console};
use crate::driver::timer::TIMER; use crate::driver::timer::TIMER;
use crate::util::test_performance; use crate::userspace_stub::userspace_init;
use alloc::{boxed::Box, string::ToString, vec::Vec};
/// Sets the base revision to the latest revision supported by the crate. /// Sets the base revision to the latest revision supported by the crate.
/// See specification for further info. /// See specification for further info.
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler. /// 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.") println!("Could not get date at boot. Will default to 0.")
} }
boot_animation(); userspace_init()
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)
} }
#[panic_handler] #[panic_handler]

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