diff --git a/Cargo.lock b/Cargo.lock index 032afe6c97..545ddfbea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,12 +548,13 @@ version = "0.1.0" dependencies = [ "async_channel", "dreadnought", - "event_types", "futures", "graphics", "hashbrown", + "keyboard", "log", "memory", + "mouse", "sync_spin", "waker", "zerocopy", @@ -955,7 +956,6 @@ dependencies = [ "core2", "derive_more", "e1000", - "event_types", "fatfs", "io", "iommu", @@ -1099,15 +1099,6 @@ dependencies = [ "root", ] -[[package]] -name = "event_types" -version = "0.1.0" -dependencies = [ - "geometry", - "keycodes_ascii", - "mouse_data", -] - [[package]] name = "example" version = "0.1.0" @@ -1740,7 +1731,6 @@ name = "keyboard" version = "0.1.0" dependencies = [ "async_channel", - "event_types", "interrupts", "keycodes_ascii", "log", @@ -1815,7 +1805,6 @@ dependencies = [ "dfqueue", "draw", "environment", - "event_types", "font", "geometry", "log", @@ -2223,7 +2212,6 @@ name = "mouse" version = "0.1.0" dependencies = [ "async_channel", - "event_types", "interrupts", "log", "mouse_data", @@ -3334,7 +3322,6 @@ dependencies = [ "core2", "dfqueue", "environment", - "event_types", "fs_node", "keycodes_ascii", "lazy_static", @@ -3975,7 +3962,6 @@ dependencies = [ "bitflags", "core2", "derive_more", - "event_types", "log", "unicode-width", "vte", diff --git a/applications/shell/Cargo.toml b/applications/shell/Cargo.toml index f9e1b4dcec..f9bb7282ea 100644 --- a/applications/shell/Cargo.toml +++ b/applications/shell/Cargo.toml @@ -19,9 +19,6 @@ path = "../../libs/keycodes_ascii" path = "../../libs/dfqueue" version = "0.1.0" -[dependencies.event_types] -path = "../../kernel/event_types" - [dependencies.spawn] path = "../../kernel/spawn" diff --git a/kernel/_doc_root.rs b/kernel/_doc_root.rs index 7da2edb759..a3e8b1b975 100644 --- a/kernel/_doc_root.rs +++ b/kernel/_doc_root.rs @@ -24,7 +24,6 @@ //! * `ata_pio`: Support for ATA hard disks (IDE/PATA) using PIO (not DMA), and not SATA. //! * `captain`: The main driver of Theseus. Controls the loading and initialization of all subsystems and other crates. //! * `compositor`: The trait of a compositor. It composites a list of buffers to a final buffer. -//! * `event_types`: The types used for passing input and output events across the system. //! * `device_manager`: Code for handling the sequence required to initialize each driver. //! * `displayable`: Defines a displayable trait. A displayable can display itself in a framebuffer. //! * `text_display`: A text display is a displayable. It contains a block of text and can display in a framebuffer. diff --git a/kernel/async_channel/src/lib.rs b/kernel/async_channel/src/lib.rs index 30c19012b1..dc48ef2216 100644 --- a/kernel/async_channel/src/lib.rs +++ b/kernel/async_channel/src/lib.rs @@ -11,6 +11,13 @@ use mpmc::Queue; use sync::DeadlockPrevention; use sync_spin::Spin; +/// A bounded, multi-producer, multi-consumer asynchronous channel. +/// +/// The channel can also be used outside of an asynchronous runtime with the +/// [`blocking_send`], and [`blocking_recv`] methods. +/// +/// [`blocking_send`]: Self::blocking_send +/// [`blocking_recv`]: Self::blocking_recv #[derive(Clone)] pub struct Channel where @@ -27,6 +34,28 @@ where T: Send, P: DeadlockPrevention, { + /// Creates a new channel. + /// + /// The provided capacity dictates how many messages can be stored in the + /// queue before the sender blocks. + /// + /// # Examples + /// + /// ``` + /// use async_channel::Channel; + /// + /// let channel = Channel::new(2); + /// + /// assert!(channel.try_send(1).is_ok()); + /// assert!(channel.try_send(2).is_ok()); + /// // The channel is full. + /// assert!(channel.try_send(3).is_err()); + /// + /// assert_eq!(channel.try_recv(), Some(1)); + /// assert_eq!(channel.try_recv(), Some(2)); + /// assert!(channel.try_recv().is_none()); + /// ``` + // TODO: Is a capacity of 0 = rendezvous? pub fn new(capacity: usize) -> Self { Self { inner: Queue::with_capacity(capacity), @@ -35,6 +64,13 @@ where } } + /// Sends `value`. + /// + /// # Cancel safety + /// + /// This method is cancel safe, in that if it is dropped prior to + /// completion, `value` is guaranteed to have not been set. However, in that + /// case `value` will be dropped. pub async fn send(&self, value: T) { let mut temp = Some(value); @@ -52,20 +88,37 @@ where .await } + /// Tries to send `value`. + /// + /// # Errors + /// + /// Returns an error containing `value` if the channel was full. pub fn try_send(&self, value: T) -> Result<(), T> { self.inner.push(value) } + /// Blocks the current thread until `value` is sent. pub fn blocking_send(&self, value: T) { dreadnought::block_on(self.send(value)) } + /// Receives the next value. + /// + /// # Cancel safety + /// + /// This method is cancel safe. pub async fn recv(&self) -> T { let value = self.receivers.wait_until(|| self.inner.pop()).await; self.senders.notify_one(); value } + /// Tries to receive the next value. + pub fn try_recv(&self) -> Option { + self.inner.pop() + } + + /// Blocks the current thread until a value is received. pub fn blocking_recv(&self) -> T { dreadnought::block_on(self.recv()) } @@ -98,6 +151,7 @@ where P: DeadlockPrevention, { fn is_terminated(&self) -> bool { + // NOTE: If we ever implement disconnections, this will need to be modified. false } } diff --git a/kernel/compositor/Cargo.toml b/kernel/compositor/Cargo.toml index 91f41f1bf5..a9b3f9d6a7 100644 --- a/kernel/compositor/Cargo.toml +++ b/kernel/compositor/Cargo.toml @@ -6,12 +6,13 @@ edition = "2021" [dependencies] async_channel = { path = "../async_channel" } dreadnought = { path = "../dreadnought" } -event_types = { path = "../event_types" } futures = { version = "0.3.28", default-features = false, features = ["async-await"] } graphics = { path = "../graphics" } hashbrown = "0.11.2" +keyboard = { path = "../keyboard" } log = "0.4.8" memory = { path = "../memory" } +mouse = { path = "../mouse" } sync_spin = { path = "../../libs/sync_spin" } waker = { path = "../waker" } # TODO: Update to 0.7.8 diff --git a/kernel/compositor/src/lib.rs b/kernel/compositor/src/lib.rs index 011d7e8c69..d2652a5ab9 100644 --- a/kernel/compositor/src/lib.rs +++ b/kernel/compositor/src/lib.rs @@ -12,7 +12,7 @@ mod window; use alloc::sync::Arc; use core::{ ops::Deref, - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; use async_channel::Channel; @@ -20,32 +20,33 @@ use futures::StreamExt; use graphics::GraphicsDriver; pub use graphics::{AlphaPixel, Coordinates, Framebuffer, Pixel, Rectangle}; use hashbrown::HashMap; +use keyboard::KeyEvent; use log::error; -use sync_spin::Mutex; +use mouse::MouseEvent; use window::LockedInner; pub use crate::window::Window; static COMPOSITOR: Option> = None; -static DRIVER: Mutex>> = Mutex::new(None); +static IS_INITIALISED: AtomicBool = AtomicBool::new(false); pub fn init(driver: T) -> Result where T: Into>, { - let mut maybe_driver = DRIVER.lock(); - assert!( - maybe_driver.is_none(), - "initialised compositor multiple times" - ); - *maybe_driver = Some(driver.into()); - - let channels = Channels::new(); - let cloned = channels.clone(); - - dreadnought::task::spawn_async(compositor_loop(cloned))?; - Ok(channels) + // TODO: Orderings?? + if IS_INITIALISED + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .unwrap_or(false) + { + let channels = Channels::new(); + let cloned = channels.clone(); + dreadnought::task::spawn_async(compositor_loop(driver.into(), cloned))?; + Ok(channels) + } else { + panic!("initialised compositor multiple times"); + } } pub fn screen_size() -> (usize, usize) { @@ -69,25 +70,11 @@ enum RequestType { Refresh { dirty: Rectangle }, } -trait Draw { - fn display

( - &mut self, - // coordinate: Coord, - framebuffer: &mut Framebuffer

, - // ) -> Result - ) -> Result<(), &'static str> - where - P: Pixel; - - fn size(&self) -> (usize, usize); - - fn set_size(&mut self, width: usize, height: usize); -} - -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Debug)] pub enum Event { - /// The compositor released the framebuffer. - Release, + Keyboard(KeyEvent), + Mouse(MouseEvent), + Resize(Rectangle), } fn absolute(coordinates: Coordinates, mut rectangle: Rectangle) -> Rectangle { @@ -95,12 +82,10 @@ fn absolute(coordinates: Coordinates, mut rectangle: Rectangle) -> Rectangle { rectangle } -fn redraw(window: T, dirty: Rectangle) +fn refresh(driver: &mut GraphicsDriver, window: T, dirty: Rectangle) where T: Deref, { - let mut locked = DRIVER.lock(); - let driver = locked.as_mut().unwrap(); let framebuffer = driver.back(); // TODO: Take into account windows above. @@ -111,7 +96,8 @@ where framebuffer[start..end].clone_from_slice(row); } - // TODO: Don't swap for every redraw, kinda defeats the purpose. + // TODO: This should be called in an interrupt handler or smthing like that to + // prevent screen tearing. driver.swap(&[absolute(window.coordinates, dirty)]); } @@ -120,9 +106,9 @@ pub struct Channels { // FIXME: Event type window: Channel, // FIXME: Deadlock prevention. - keyboard: Channel, + keyboard: Channel, // FIXME: Deadlock prevention. - mouse: Channel, + mouse: Channel, } impl Channels { @@ -153,13 +139,13 @@ pub fn window(width: usize, height: usize) -> Window { clone } -async fn compositor_loop(mut channels: Channels) { +async fn compositor_loop(mut driver: GraphicsDriver, mut channels: Channels) { loop { // The select macro is not available in no_std. futures::select_biased!( request = channels.window.next() => { let request = request.unwrap(); - handle_window_request(request).await; + handle_window_request(&mut driver, request).await; } request = channels.keyboard.next() => { let request = request.unwrap(); @@ -169,14 +155,12 @@ async fn compositor_loop(mut channels: Channels) { let request = request.unwrap(); handle_mouse_request(request); } - // _ = fut => panic!("compositor loop exited"), - default => panic!("ue"), complete => panic!("compositor loop exited"), ); } } -async fn handle_window_request(request: Request) { +async fn handle_window_request(driver: &mut GraphicsDriver, request: Request) { let id = request.window_id; let windows = WINDOWS.as_ref().unwrap().read(); @@ -187,7 +171,7 @@ async fn handle_window_request(request: Request) { let mut waker = None; if let Some(inner) = inner { - if let Some(mut locked) = inner.locked.try_lock() { + if let Some(mut locked) = inner.locked.try_write() { match request.ty { RequestType::Refresh { dirty } => { // This will be true once we drop the lock. @@ -198,7 +182,7 @@ async fn handle_window_request(request: Request) { None => error!("no registered waker"), } - redraw(locked, dirty); + refresh(driver, locked, dirty); } } } else { @@ -213,10 +197,10 @@ async fn handle_window_request(request: Request) { } } -fn handle_keyboard_request(_request: event_types::Event) { +fn handle_keyboard_request(_request: keyboard::KeyEvent) { todo!(); } -fn handle_mouse_request(_request: event_types::Event) { +fn handle_mouse_request(_request: mouse::MouseEvent) { todo!(); } diff --git a/kernel/compositor/src/window.rs b/kernel/compositor/src/window.rs index 44d060fe25..d52999a40e 100644 --- a/kernel/compositor/src/window.rs +++ b/kernel/compositor/src/window.rs @@ -1,6 +1,5 @@ use alloc::{boxed::Box, sync::Arc}; use core::{ - cell::UnsafeCell, future::Future, ops::{Deref, DerefMut}, pin::Pin, @@ -9,7 +8,7 @@ use core::{ use async_channel::Channel; use graphics::{AlphaPixel, Coordinates, Framebuffer, FramebufferDimensions, Rectangle}; -use sync_spin::{Mutex, MutexGuard}; +use sync_spin::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::{Event, Request, RequestType, COMPOSITOR}; @@ -24,7 +23,8 @@ pub struct Window { } pub(crate) struct Inner { - pub(crate) locked: UnsafeCell, + // TODO: This could be an `UnsafeCell`. + pub(crate) locked: RwLock, // TODO: Not spin. (unbounded?) pub(crate) events: Channel, } @@ -49,7 +49,7 @@ unsafe impl Sync for Inner {} impl Window { pub(crate) fn new(id: usize, width: usize, height: usize) -> (Self, Self) { let inner = Arc::new(Inner { - locked: UnsafeCell::new(LockedInner { + locked: RwLock::new(LockedInner { coordinates: Coordinates::ZERO, framebuffer: Framebuffer::new_software(FramebufferDimensions { width, @@ -70,27 +70,15 @@ impl Window { ) } - pub fn framebuffer(&mut self) -> impl DerefMut> + '_ { - struct Temp<'a> { - inner: MutexGuard<'a, LockedInner>, - } - - impl Deref for Temp<'_> { - type Target = Framebuffer; - - fn deref(&self) -> &Self::Target { - &self.inner.framebuffer - } - } - - impl DerefMut for Temp<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.framebuffer - } + pub fn as_framebuffer(&self) -> impl Deref> + '_ { + FramebufferRef { + inner: self.inner.locked.try_read().unwrap(), } + } - Temp { - inner: self.inner.locked.try_lock().unwrap(), + pub fn as_mut_framebuffer(&mut self) -> impl DerefMut> + '_ { + FramebufferMutRef { + inner: self.inner.locked.try_write().unwrap(), } } @@ -98,23 +86,46 @@ impl Window { todo!(); } - pub fn handle_event(&self) -> Result, &'static str> { - todo!(); + pub async fn recv(&self) -> Event { + self.inner.events.recv().await } - pub fn blocking_refresh(&mut self, dirty: Rectangle) { - let (waker, blocker) = waker::new_waker(); - self.inner.locked.try_lock().unwrap().waker = Some(waker); - COMPOSITOR.as_ref().unwrap().blocking_send(Request { - window_id: self.id, - ty: RequestType::Refresh { dirty }, - }); - blocker.block(); + pub fn try_recv(&self) -> Option { + self.inner.events.try_recv() } + pub fn blocking_recv(&self) -> Event { + self.inner.events.blocking_recv() + } + + // IDEA(tsoutsman): + // + // As it currently stands, the client must wait for the compositor to release + // the buffer before doing anything else. This isn't too bad, as we don't have + // graphics-intesive applications, but it's something that could be improved on. + // + // `refresh` could be done in a two-step process, returning a future that itself + // returns a future. The first future would send the request, and the second + // future would wait for a response. Notably, the second future would still + // be tied to a mutable reference to self. This is both a blessing and a + // curse, because it correctly enforces that the window can't be used until + // both futures are resolved, but I'm pretty sure the borrow checker would + // have a fit if the second future was stored in a variable across a loop + // (i.e. event loop) iteration. However, this could probably be worked around + // with some trickery. + // + // One could imaging a double-buffering client that commits a front buffer (by + // awaiting the first future), and stores the second future. It then starts + // calculating the next frame and drawing it into the back buffer. After it's + // done, it polls the second future (checking if the server has released the + // shared buffer), and if it's ready, switches the back buffer into the shared + // buffer. + // + // A similar thing could be done with `blocking_refresh`. + // Note that Refresh<'_> is tied to a mutable reference, and so no other mutable // accesses of self can occur until the future is consumed. - pub async fn refresh(&mut self, dirty: Rectangle) -> Refresh<'_> { + pub fn refresh(&mut self, dirty: Rectangle) -> Refresh<'_> { Refresh { locked: &self.inner.locked, id: self.id, @@ -123,13 +134,23 @@ impl Window { } } + pub fn blocking_refresh(&mut self, dirty: Rectangle) { + let (waker, blocker) = waker::new_waker(); + self.inner.locked.try_write().unwrap().waker = Some(waker); + COMPOSITOR.as_ref().unwrap().blocking_send(Request { + window_id: self.id, + ty: RequestType::Refresh { dirty }, + }); + blocker.block(); + } + pub async fn event(&self) { todo!(); } } pub struct Refresh<'a> { - locked: &'a Mutex, + locked: &'a RwLock, id: usize, dirty: Rectangle, state: State, @@ -154,7 +175,7 @@ impl<'a> Future for Refresh<'a> { // Manually implementing async functions :) if self.state == State::Init { - let mut locked = self.locked.try_lock().unwrap(); + let mut locked = self.locked.try_write().unwrap(); locked.waker = Some(cx.waker().clone()); locked.is_unlocked = false; drop(locked); @@ -181,7 +202,7 @@ impl<'a> Future for Refresh<'a> { // State::Sent - if let Some(locked) = self.locked.try_lock() { + if let Some(locked) = self.locked.try_read() { if locked.is_unlocked { return Poll::Ready(()); } @@ -193,3 +214,33 @@ impl<'a> Future for Refresh<'a> { Poll::Pending } } + +struct FramebufferRef<'a> { + inner: RwLockReadGuard<'a, LockedInner>, +} + +impl Deref for FramebufferRef<'_> { + type Target = Framebuffer; + + fn deref(&self) -> &Self::Target { + &self.inner.framebuffer + } +} + +struct FramebufferMutRef<'a> { + inner: RwLockWriteGuard<'a, LockedInner>, +} + +impl Deref for FramebufferMutRef<'_> { + type Target = Framebuffer; + + fn deref(&self) -> &Self::Target { + &self.inner.framebuffer + } +} + +impl DerefMut for FramebufferMutRef<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner.framebuffer + } +} diff --git a/kernel/device_manager/Cargo.toml b/kernel/device_manager/Cargo.toml index 2648ebd8cf..6608328524 100644 --- a/kernel/device_manager/Cargo.toml +++ b/kernel/device_manager/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] spin = "0.9.4" core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] } -event_types = { path = "../event_types" } serial_port = { path = "../serial_port" } console = { path = "../console" } logger = { path = "../logger" } diff --git a/kernel/device_manager/src/lib.rs b/kernel/device_manager/src/lib.rs index 25325c8bf6..2c1cc7c302 100644 --- a/kernel/device_manager/src/lib.rs +++ b/kernel/device_manager/src/lib.rs @@ -9,7 +9,6 @@ use log::{info, debug}; use { log::{error, warn}, async_channel::Channel, - event_types::Event, memory::MemoryManagementInfo, alloc::vec::Vec, io::{ByteReaderWriterWrapper, LockableIo, ReaderWriter}, @@ -48,9 +47,9 @@ pub fn early_init( /// * All other devices discovered on the [`pci`] bus. pub fn init( #[cfg(target_arch = "x86_64")] - key_producer: Channel, + key_producer: Channel, #[cfg(target_arch = "x86_64")] - mouse_producer: Channel, + mouse_producer: Channel, ) -> Result<(), &'static str> { let serial_ports = logger::take_early_log_writers(); diff --git a/kernel/draw/src/text.rs b/kernel/draw/src/text.rs index 38b1f59249..0321794fa6 100644 --- a/kernel/draw/src/text.rs +++ b/kernel/draw/src/text.rs @@ -1,7 +1,5 @@ // TODO: Move `font` crate to libs -use core::fmt::Display; - use font::{CHARACTER_HEIGHT, CHARACTER_WIDTH}; use geometry::Coordinates; use graphics::{Framebuffer, Pixel}; diff --git a/kernel/event_types/Cargo.toml b/kernel/event_types/Cargo.toml deleted file mode 100644 index 8512ab963f..0000000000 --- a/kernel/event_types/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -authors = ["Kevin Boos "] -name = "event_types" -description = "Types of input and output events that flow throughout the system" -version = "0.1.0" -edition = "2021" - -[dependencies] -geometry = { path = "../../libs/geometry" } -keycodes_ascii = { path = "../../libs/keycodes_ascii" } -mouse_data = { path = "../../libs/mouse_data" } - -[lib] -crate-type = ["rlib"] diff --git a/kernel/event_types/src/lib.rs b/kernel/event_types/src/lib.rs deleted file mode 100644 index d8e1dc4a94..0000000000 --- a/kernel/event_types/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![no_std] - -extern crate alloc; - -use alloc::string::String; -use keycodes_ascii::KeyEvent; -use mouse_data::MouseEvent; -use geometry::{Coordinates, Rectangle}; - -/// An event describing mouse position rather than movement differential from last event. -/// It contains two position, `coodinate` for the relative position in each window, and `gcoordinate` for global absolute position of the screen. -#[derive(Debug, Clone)] -pub struct MousePositionEvent { - /// the relative position in window - pub coordinate: Coordinates, - /// the global position in window - pub gcoordinate: Coordinates, - /// whether the mouse is scrolling up - pub scrolling_up: bool, - /// whether the mouse is scrolling down - pub scrolling_down: bool, - /// whether the left button holds - pub left_button_hold: bool, - /// whether the right button holds - pub right_button_hold: bool, - /// whether the fourth button holds - pub fourth_button_hold: bool, - /// whether the fifth button holds - pub fifth_button_hold: bool, -} - -impl Default for MousePositionEvent { - fn default() -> Self { - MousePositionEvent { - coordinate: Coordinates::new(0, 0), - gcoordinate: Coordinates::new(0, 0), - scrolling_up: false, - scrolling_down: false, - left_button_hold: false, - right_button_hold: false, - fourth_button_hold: false, - fifth_button_hold: false, - } - } -} - -#[derive(Debug, Clone)] -pub enum Event { - /// An input event from a keyboard - KeyboardEvent(KeyboardInputEvent), - /// An input event from a mouse - MouseMovementEvent(MouseEvent), - /// An event indicating that another entity wants to print the given `String`. - OutputEvent(String), - /// Tells an application that the window manager has resized or moved its window - /// so that it knows to refresh its display and perform any necessary tasks, such as text reflow. - /// - /// The new position and size of the window is given by the `Rectangle` within, - /// and represents the content area within the window that is accessible to the application, - /// which excludes the window title bar, borders, etc. - WindowResizeEvent(Rectangle), - /// The event tells application about mouse's position currently (including relative to a window and relative to a screen) - MousePositionEvent(MousePositionEvent), - ExitEvent, -} - -impl Event { - /// Create a new keyboard event - pub fn new_keyboard_event(kev: KeyEvent) -> Event { - Event::KeyboardEvent(KeyboardInputEvent::new(kev)) - } - - /// Create a new output event - pub fn new_output_event(s: S) -> Event - where - S: Into, - { - Event::OutputEvent(s.into()) - } - - /// Create a new window resize event - pub fn new_window_resize_event(new_position: Rectangle) -> Event { - Event::WindowResizeEvent(new_position) - } -} - -/// A keyboard event, indicating that one or more keys were pressed or released. -#[derive(Debug, Clone)] -pub struct KeyboardInputEvent { - /// The key input event from i/o device - pub key_event: KeyEvent, -} - -impl KeyboardInputEvent { - /// Create a new key board input event. `key` is the key input from the keyboard - pub fn new(key_event: KeyEvent) -> KeyboardInputEvent { - KeyboardInputEvent { key_event } - } -} \ No newline at end of file diff --git a/kernel/keyboard/Cargo.toml b/kernel/keyboard/Cargo.toml index 3bab00dab3..754167a99d 100644 --- a/kernel/keyboard/Cargo.toml +++ b/kernel/keyboard/Cargo.toml @@ -17,9 +17,6 @@ path = "../async_channel" [dependencies.keycodes_ascii] path = "../../libs/keycodes_ascii" -[dependencies.event_types] -path = "../event_types" - [dependencies.ps2] path = "../ps2" diff --git a/kernel/keyboard/src/lib.rs b/kernel/keyboard/src/lib.rs index 1a08eccc10..02dd7b862d 100644 --- a/kernel/keyboard/src/lib.rs +++ b/kernel/keyboard/src/lib.rs @@ -4,12 +4,12 @@ #![feature(abi_x86_interrupt)] use core::sync::atomic::{AtomicBool, Ordering}; -use keycodes_ascii::{Keycode, KeyboardModifiers, KEY_RELEASED_OFFSET, KeyAction, KeyEvent}; +pub use keycodes_ascii::KeyEvent; +use keycodes_ascii::{Keycode, KeyboardModifiers, KEY_RELEASED_OFFSET, KeyAction}; use log::{error, warn, debug}; use once_cell::unsync::Lazy; use spin::Once; use async_channel::Channel; -use event_types::Event; use ps2::{PS2Keyboard, KeyboardType, LEDState, ScancodeSet}; use x86_64::structures::idt::InterruptStackFrame; @@ -24,7 +24,7 @@ static KEYBOARD: Once = Once::new(); struct KeyboardInterruptParams { keyboard: PS2Keyboard<'static>, - queue: Channel, + queue: Channel, } /// Initialize the PS/2 keyboard driver and register its interrupt handler. @@ -33,7 +33,7 @@ struct KeyboardInterruptParams { /// * `keyboard`: a wrapper around keyboard functionality, used by the keyboard interrupt handler. /// * `keyboard_queue_producer`: the queue onto which the keyboard interrupt handler /// will push new keyboard events when a key action occurs. -pub fn init(keyboard: PS2Keyboard<'static>, keyboard_queue_producer: Channel) -> Result<(), &'static str> { +pub fn init(keyboard: PS2Keyboard<'static>, keyboard_queue_producer: Channel) -> Result<(), &'static str> { // Detect which kind of keyboard is connected. // TODO: actually do something with the keyboard type. match keyboard.keyboard_detect() { @@ -114,7 +114,7 @@ extern "x86-interrupt" fn ps2_keyboard_handler(_stack_frame: InterruptStackFrame /// /// Returns Ok(()) if everything was handled properly. /// Otherwise, returns an error string. -fn handle_keyboard_input(keyboard: &PS2Keyboard, queue: &Channel, scan_code: u8, extended: bool) -> Result<(), &'static str> { +fn handle_keyboard_input(keyboard: &PS2Keyboard, queue: &Channel, scan_code: u8, extended: bool) -> Result<(), &'static str> { // SAFE: no real race conditions with keyboard presses let modifiers = unsafe { &mut KBD_MODIFIERS }; // debug!("KBD_MODIFIERS before {}: {:?}", scan_code, modifiers); @@ -194,7 +194,7 @@ fn handle_keyboard_input(keyboard: &PS2Keyboard, queue: &Channel, scan_co }; if let Ok(keycode) = Keycode::try_from(adjusted_scan_code) { - let event = Event::new_keyboard_event(KeyEvent::new(keycode, action, **modifiers)); + let event = KeyEvent::new(keycode, action, **modifiers); queue.try_send(event).map_err(|_| "keyboard input queue is full") } else { error!("handle_keyboard_input(): Unknown scancode: {scan_code:?}, adjusted scancode: {adjusted_scan_code:?}"); diff --git a/kernel/libterm/Cargo.toml b/kernel/libterm/Cargo.toml index 001a6e6512..bee3d15291 100644 --- a/kernel/libterm/Cargo.toml +++ b/kernel/libterm/Cargo.toml @@ -25,9 +25,6 @@ path = "../root" [dependencies.color] path = "../color" -[dependencies.event_types] -path = "../event_types" - [dependencies.time] path = "../time" diff --git a/kernel/libterm/src/lib.rs b/kernel/libterm/src/lib.rs index 03d578fc73..3f5b2d7247 100644 --- a/kernel/libterm/src/lib.rs +++ b/kernel/libterm/src/lib.rs @@ -19,10 +19,9 @@ use alloc::{ }; use color::Color; -use compositor::{Framebuffer, Pixel, Window, AlphaPixel}; +use compositor::{Event, Framebuffer, Pixel, Window}; use draw::{Drawable, Coordinates, Settings, Text, Rectangle}; use geometry::{Horizontal, Vertical}; -use event_types::Event; use font::{CHARACTER_HEIGHT, CHARACTER_WIDTH}; use log::error; use time::Duration; @@ -61,8 +60,8 @@ impl Terminal { pub fn get_text_dimensions(&self) -> (usize, usize) { ( // The type parameter doesn't matter. - Text::<&str>::grid_width(&self.window.framebuffer()), - Text::<&str>::grid_height(&self.window.framebuffer()), + Text::<&str>::grid_width(&self.window.as_framebuffer()), + Text::<&str>::grid_height(&self.window.as_framebuffer()), ) } @@ -370,28 +369,15 @@ impl Terminal { new_end_idx }, }; - let result = self.scrollback_buffer.get(start_idx..=end_idx); // =end_idx includes the end index in the slice + let result = self.scrollback_buffer.get(start_idx..=end_idx); // =end_idx includes the end index in the slice if let Some(slice) = result { - self.display_text(slice)?; + display_text(&mut self.window, slice); } else { return Err("could not get slice of scrollback buffer string"); } Ok(()) } - /// Display the text displayable in the window and render it to the screen - fn display_text(&mut self, text: &str) -> Result<(), &'static str>{ - let settings = Settings { - foreground: FONT_FOREGROUND_COLOR.into(), - background: Some(FONT_BACKGROUND_COLOR.into()), - }; - let area_to_render = Text::new(Coordinates::ZERO, text) - .draw(&mut *self.window.framebuffer(), &settings); - // TODO - self.window.blocking_refresh(Rectangle::MAX); - Ok(()) - } - /// Updates the text display by taking a string index and displaying as much as it can going backwards from the passed string index (i.e. starts from the bottom of the display and goes up) fn update_display_backwards(&mut self, end_idx: usize) -> Result<(), &'static str> { let (start_idx, _cursor_pos) = self.calc_start_idx(end_idx); @@ -400,7 +386,7 @@ impl Terminal { let result = self.scrollback_buffer.get(start_idx..end_idx); if let Some(slice) = result { - self.display_text(slice)?; + display_text(&mut self.window, slice); } else { return Err("could not get slice of scrollback buffer string"); } @@ -573,20 +559,15 @@ impl Terminal { /// Gets an event from the window's event queue. /// /// Returns `None` if no events have been sent to this window. - pub fn get_event(&mut self) -> Option { - match self.window.handle_event() { - Ok(event) => event, - Err(_e) => { - error!("Terminal::get_event(): error in the window's event handler: {:?}.", _e); - Some(Event::ExitEvent) - } - } + // TODO: This function shouldn't exist. It should either block, or return a future. + pub fn get_event(&mut self) -> Event { + self.window.blocking_recv() } /// Display the cursor of the terminal. pub fn display_cursor(&mut self) -> Result<(), &'static str> { // get info about the text displayable - let (next_col, next_row) = Text::<&str>::next_grid_position(todo!(), &*self.window.framebuffer()); + let (next_col, next_row) = Text::<&str>::next_grid_position(todo!(), &*self.window.as_framebuffer()); let (num_col, num_row) = self.get_text_dimensions(); // return if the cursor is not in the screen @@ -601,7 +582,7 @@ impl Terminal { coord, next_col, next_row, - &mut *self.window.framebuffer(), + &mut *self.window.as_mut_framebuffer(), )? }; @@ -630,3 +611,13 @@ impl Terminal { Ok(()) } } + +fn display_text(window: &mut Window, text: &str) { + let settings = Settings { + foreground: FONT_FOREGROUND_COLOR.into(), + background: Some(FONT_BACKGROUND_COLOR.into()), + }; + Text::new(Coordinates::ZERO, text).draw(&mut *window.as_mut_framebuffer(), &settings); + // TODO + window.blocking_refresh(Rectangle::MAX); +} diff --git a/kernel/mouse/Cargo.toml b/kernel/mouse/Cargo.toml index 34cf8416ea..5135e39d19 100644 --- a/kernel/mouse/Cargo.toml +++ b/kernel/mouse/Cargo.toml @@ -22,8 +22,5 @@ path = "../interrupts" [dependencies.ps2] path = "../ps2" -[dependencies.event_types] -path = "../event_types" - [lib] crate-type = ["rlib"] diff --git a/kernel/mouse/src/lib.rs b/kernel/mouse/src/lib.rs index 7d9470bc9a..dc06a0c3dc 100644 --- a/kernel/mouse/src/lib.rs +++ b/kernel/mouse/src/lib.rs @@ -6,9 +6,9 @@ use log::{error, warn}; use spin::Once; use async_channel::Channel; -use event_types::Event; use x86_64::structures::idt::InterruptStackFrame; -use mouse_data::{MouseButtons, MouseEvent, MouseMovementRelative}; +pub use mouse_data::MouseEvent; +use mouse_data::{MouseButtons, MouseMovementRelative}; use ps2::{PS2Mouse, MousePacket}; /// The first PS/2 port for the mouse is connected directly to IRQ 0xC. @@ -19,7 +19,7 @@ static MOUSE: Once = Once::new(); struct MouseInterruptParams { mouse: PS2Mouse<'static>, - queue: Channel, + queue: Channel, } /// Initialize the PS/2 mouse driver and register its interrupt handler. @@ -28,7 +28,7 @@ struct MouseInterruptParams { /// * `mouse`: a wrapper around mouse functionality and id, used by the mouse interrupt handler. /// * `mouse_queue_producer`: the queue onto which the mouse interrupt handler /// will push new mouse events when a mouse action occurs. -pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Channel) -> Result<(), &'static str> { +pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Channel) -> Result<(), &'static str> { // Set MouseId to the highest possible one if let Err(e) = mouse.set_mouse_id() { error!("Failed to set the mouse id: {e}"); @@ -79,13 +79,11 @@ extern "x86-interrupt" fn ps2_mouse_handler(_stack_frame: InterruptStackFrame) { /// enqueue a Mouse Event according to the data -fn handle_mouse_input(mouse_packet: MousePacket, queue: &Channel) -> Result<(), &'static str> { +fn handle_mouse_input(mouse_packet: MousePacket, queue: &Channel) -> Result<(), &'static str> { let buttons = Buttons::from(&mouse_packet).0; let movement = Movement::from(&mouse_packet).0; - let mouse_event = MouseEvent::new(buttons, movement); - let event = Event::MouseMovementEvent(mouse_event); - + let event = MouseEvent::new(buttons, movement); queue.try_send(event).map_err(|_| "failed to enqueue the mouse event") } diff --git a/kernel/text_terminal/Cargo.toml b/kernel/text_terminal/Cargo.toml index a79b5af16d..475fdf939d 100644 --- a/kernel/text_terminal/Cargo.toml +++ b/kernel/text_terminal/Cargo.toml @@ -14,8 +14,5 @@ derive_more = "0.99.0" [dependencies.log] version = "0.4.8" -[dependencies.event_types] -path = "../event_types" - [lib] crate-type = ["rlib"] diff --git a/kernel/text_terminal/src/lib.rs b/kernel/text_terminal/src/lib.rs index 5a0875353b..2f6eed4e7a 100644 --- a/kernel/text_terminal/src/lib.rs +++ b/kernel/text_terminal/src/lib.rs @@ -32,7 +32,6 @@ #[macro_use] extern crate alloc; #[macro_use] extern crate log; #[macro_use] extern crate bitflags; -extern crate event_types; extern crate unicode_width; extern crate core2; extern crate vte; diff --git a/libs/geometry/src/circle.rs b/libs/geometry/src/circle.rs index c760744d91..bc7301e010 100644 --- a/libs/geometry/src/circle.rs +++ b/libs/geometry/src/circle.rs @@ -11,7 +11,7 @@ impl Circle { where T: Containable, { - for coordinates in containable.coordinates() { + for coordinates in containable.vertices() { let diff = self.center.abs_diff(coordinates); if diff.x.pow(2) + diff.y.pow(2) > self.radius.pow(2) { return false; diff --git a/libs/geometry/src/coordinates.rs b/libs/geometry/src/coordinates.rs index 2576b62bf5..8bdf9fd0ec 100644 --- a/libs/geometry/src/coordinates.rs +++ b/libs/geometry/src/coordinates.rs @@ -1,8 +1,8 @@ use core::ops::{Add, AddAssign, Sub, SubAssign}; -use crate::Containable; +use crate::{Containable, ConvexPolygon}; -#[derive(Clone, Copy, PartialEq, Debug, Hash)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Coordinates { pub x: usize, pub y: usize, @@ -62,10 +62,12 @@ impl SubAssign for Coordinates { } } +unsafe impl ConvexPolygon for Coordinates {} + impl Containable for Coordinates { type I = core::iter::Once; - fn coordinates(&self) -> Self::I { + fn vertices(&self) -> Self::I { core::iter::once(*self) } } diff --git a/libs/geometry/src/lib.rs b/libs/geometry/src/lib.rs index 979de3b5de..bd2446e94a 100644 --- a/libs/geometry/src/lib.rs +++ b/libs/geometry/src/lib.rs @@ -20,8 +20,16 @@ pub enum Horizontal { Right, } -pub trait Containable { +/// A shape that can be contained by another shape. +/// +/// Since the shape must be a convex polygon, it is enough to check that all the +/// vertices of the shape are contained. +pub trait Containable: ConvexPolygon { type I: Iterator; - fn coordinates(&self) -> Self::I; + /// The vertices of the shape. + fn vertices(&self) -> Self::I; } + +/// A convex polygon. +pub unsafe trait ConvexPolygon {} diff --git a/libs/geometry/src/rectangle.rs b/libs/geometry/src/rectangle.rs index 0d1e3be581..83f3a87741 100644 --- a/libs/geometry/src/rectangle.rs +++ b/libs/geometry/src/rectangle.rs @@ -1,6 +1,7 @@ -use crate::{Containable, Coordinates, Horizontal, Vertical}; +use crate::{Containable, ConvexPolygon, Coordinates, Horizontal, Vertical}; -#[derive(Clone, Copy, PartialEq, Debug, Hash)] +/// A rectangle. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Rectangle { /// The top left vertex of the rectangle. pub coordinates: Coordinates, @@ -11,6 +12,11 @@ pub struct Rectangle { impl Rectangle { pub const MAX: Self = Self::new(Coordinates::ZERO, usize::MAX, usize::MAX); + /// Returns a rectangle with the top left vertex at `coordinates`. + /// + /// # Panics + /// + /// Panics if `width` is 0, or `height` is 0. pub const fn new(coordinates: Coordinates, width: usize, height: usize) -> Self { assert!(width > 0, "rectangle must have width greater than 0"); assert!(height > 0, "rectangle must have height greater than 0"); @@ -21,28 +27,33 @@ impl Rectangle { } } + /// Returns the wdith of the rectangle. pub const fn width(&self) -> usize { self.width } + /// Returns the height of the rectangle. pub const fn height(&self) -> usize { self.height } - pub const fn x(&self, horizontal: Horizontal) -> usize { - match horizontal { + /// Returns the furthest `x` coordinate in `direction`. + pub const fn x(&self, direction: Horizontal) -> usize { + match direction { Horizontal::Left => self.coordinates.x, Horizontal::Right => self.coordinates.x + self.width - 1, } } - pub const fn y(&self, vertical: Vertical) -> usize { - match vertical { + /// Returns the furthest `y` coordinate in `direction`. + pub const fn y(&self, direction: Vertical) -> usize { + match direction { Vertical::Top => self.coordinates.y, Vertical::Bottom => self.coordinates.y + self.height - 1, } } + /// Returns the vertex specified by `vertical` and `horizontal`. pub const fn vertex(&self, vertical: Vertical, horizontal: Horizontal) -> Coordinates { Coordinates { x: self.x(horizontal), @@ -50,11 +61,12 @@ impl Rectangle { } } + /// Returns whether the rectangle fully contains `T`. pub fn contains(&self, containable: T) -> bool where T: Containable, { - for coordinates in containable.coordinates() { + for coordinates in containable.vertices() { if coordinates.x < self.x(Horizontal::Left) || coordinates.x > self.x(Horizontal::Right) || coordinates.y < self.y(Vertical::Top) @@ -67,10 +79,12 @@ impl Rectangle { } } +unsafe impl ConvexPolygon for Rectangle {} + impl Containable for Rectangle { type I = core::array::IntoIter; - fn coordinates(&self) -> Self::I { + fn vertices(&self) -> Self::I { [ self.vertex(Vertical::Top, Horizontal::Left), self.vertex(Vertical::Top, Horizontal::Right),