#![no_std]
extern crate spin;
#[macro_use] extern crate log;
extern crate alloc;
extern crate mpmc;
extern crate event_types;
extern crate compositor;
extern crate framebuffer;
extern crate framebuffer_compositor;
extern crate framebuffer_drawer;
extern crate keycodes_ascii;
extern crate mod_mgmt;
extern crate mouse_data;
extern crate path;
extern crate scheduler;
extern crate spawn;
extern crate window_inner;
extern crate shapes;
extern crate color;
use alloc::collections::VecDeque;
use alloc::string::ToString;
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use compositor::{Compositor, FramebufferUpdates, CompositableRegion};
use mpmc::Queue;
use event_types::{Event, MousePositionEvent};
use framebuffer::{Framebuffer, AlphaPixel};
use color::Color;
use shapes::{Coord, Rectangle};
use framebuffer_compositor::{FRAME_COMPOSITOR};
use keycodes_ascii::{KeyAction, KeyEvent, Keycode};
use mouse_data::MouseEvent;
use spin::{Mutex, Once};
use window_inner::{WindowInner, WindowMovingStatus};
pub static WINDOW_MANAGER: Once<Mutex<WindowManager>> = Once::new();
const MOUSE_POINTER_SIZE_Y: usize = 18;
const MOUSE_POINTER_SIZE_X: usize = 11;
static MOUSE_POINTER_IMAGE: [[Color; MOUSE_POINTER_SIZE_Y]; MOUSE_POINTER_SIZE_X] = {
const T: Color = color::TRANSPARENT;
const C: Color = color::BLACK; const B: Color = color::WHITE; [
[B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, T, T],
[T, B, C, C, C, C, C, C, C, C, C, C, C, C, B, T, T, T],
[T, T, B, C, C, C, C, C, C, C, C, C, C, B, T, T, T, T],
[T, T, T, B, C, C, C, C, C, C, C, C, B, T, T, T, T, T],
[T, T, T, T, B, C, C, C, C, C, C, C, C, B, B, T, T, T],
[T, T, T, T, T, B, C, C, C, C, C, C, C, C, C, B, B, T],
[T, T, T, T, T, T, B, C, C, C, C, B, B, C, C, C, C, B],
[T, T, T, T, T, T, T, B, C, C, B, T, T, B, B, C, B, T],
[T, T, T, T, T, T, T, T, B, C, B, T, T, T, T, B, B, T],
[T, T, T, T, T, T, T, T, T, B, B, T, T, T, T, T, T, T],
[T, T, T, T, T, T, T, T, T, T, B, T, T, T, T, T, T, T],
]
};
const WINDOW_BORDER_SIZE: usize = 3;
const WINDOW_BORDER_COLOR_INNER: Color = Color::new(0x00CA6F1E);
pub struct WindowManager {
hide_list: VecDeque<Weak<Mutex<WindowInner>>>,
show_list: VecDeque<Weak<Mutex<WindowInner>>>,
active: Weak<Mutex<WindowInner>>, mouse: Coord,
repositioned_border: Option<Rectangle>,
bottom_fb: Framebuffer<AlphaPixel>,
top_fb: Framebuffer<AlphaPixel>,
pub final_fb: Framebuffer<AlphaPixel>,
}
impl WindowManager {
pub fn set_active(
&mut self,
inner_ref: &Arc<Mutex<WindowInner>>,
refresh: bool,
) -> Result<bool, &'static str> {
let first_active = match self.active.upgrade() {
Some(current_active) => {
if Arc::ptr_eq(¤t_active, inner_ref) {
return Ok(true); } else {
self.show_list.push_front(self.active.clone());
}
false
}
None => true,
};
if let Some(i) = self.is_window_in_show_list(inner_ref) {
self.show_list.remove(i);
}
if let Some(i) = self.is_window_in_hide_list(inner_ref) {
self.hide_list.remove(i);
}
self.active = Arc::downgrade(inner_ref);
let area = {
let window = inner_ref.lock();
let top_left = window.get_position();
let (width, height) = window.get_size();
Rectangle {
top_left,
bottom_right: top_left + (width as isize, height as isize)
}
};
if refresh {
self.refresh_bottom_windows(Some(area), true)?;
}
Ok(first_active)
}
fn is_window_in_show_list(&mut self, window: &Arc<Mutex<WindowInner>>) -> Option<usize> {
for (i, item) in self.show_list.iter().enumerate() {
if let Some(item_ptr) = item.upgrade() {
if Arc::ptr_eq(&item_ptr, window) {
return Some(i);
}
}
}
None
}
fn is_window_in_hide_list(&mut self, window: &Arc<Mutex<WindowInner>>) -> Option<usize> {
for (i, item) in self.hide_list.iter().enumerate() {
if let Some(item_ptr) = item.upgrade() {
if Arc::ptr_eq(&item_ptr, window) {
return Some(i);
}
}
}
None
}
pub fn delete_window(&mut self, inner_ref: &Arc<Mutex<WindowInner>>) -> Result<(), &'static str> {
let (top_left, bottom_right) = {
let inner = inner_ref.lock();
let top_left = inner.get_position();
let (width, height) = inner.get_size();
let bottom_right = top_left + (width as isize, height as isize);
(top_left, bottom_right)
};
let area = Some(
Rectangle {
top_left,
bottom_right
}
);
if let Some(current_active) = self.active.upgrade() {
if Arc::ptr_eq(¤t_active, inner_ref) {
self.refresh_bottom_windows(area, false)?;
if let Some(window) = self.show_list.remove(0) {
self.active = window;
} else if let Some(window) = self.hide_list.remove(0) {
self.active = window;
} else {
self.active = Weak::new(); }
return Ok(());
}
}
if let Some(index) = self.is_window_in_show_list(inner_ref) {
self.show_list.remove(index);
self.refresh_windows(area)?;
return Ok(())
}
if let Some(index) = self.is_window_in_hide_list(inner_ref) {
self.hide_list.remove(index);
return Ok(())
}
Err("cannot find this window")
}
pub fn refresh_bottom_windows<B: CompositableRegion + Clone>(
&mut self,
bounding_box: impl IntoIterator<Item = B> + Clone,
active: bool,
) -> Result<(), &'static str> {
let bottom_fb_area = FramebufferUpdates {
src_framebuffer: &self.bottom_fb,
coordinate_in_dest_framebuffer: Coord::new(0, 0),
};
let mut window_ref_list = Vec::new();
for window in &self.hide_list {
if let Some(window_ref) = window.upgrade() {
window_ref_list.push(window_ref);
}
}
for window in &self.show_list {
if let Some(window_ref) = window.upgrade() {
window_ref_list.push(window_ref);
}
}
if active {
if let Some(window_ref) = self.active.upgrade() {
window_ref_list.push(window_ref)
}
}
let locked_window_list = &window_ref_list.iter().map(|x| x.lock()).collect::<Vec<_>>();
let window_bufferlist = locked_window_list.iter().map(|window| {
FramebufferUpdates {
src_framebuffer: window.framebuffer(),
coordinate_in_dest_framebuffer: window.get_position(),
}
});
let buffer_iter = Some(bottom_fb_area).into_iter().chain(window_bufferlist);
FRAME_COMPOSITOR.lock().composite(buffer_iter, &mut self.final_fb, bounding_box)?;
Ok(())
}
pub fn refresh_top<B: CompositableRegion + Clone>(
&mut self,
bounding_box: impl IntoIterator<Item = B> + Clone
) -> Result<(), &'static str> {
let top_buffer = FramebufferUpdates {
src_framebuffer: &self.top_fb,
coordinate_in_dest_framebuffer: Coord::new(0, 0),
};
FRAME_COMPOSITOR.lock().composite(Some(top_buffer), &mut self.final_fb, bounding_box)
}
pub fn refresh_windows<B: CompositableRegion + Clone>(
&mut self,
bounding_box: impl IntoIterator<Item = B> + Clone,
) -> Result<(), &'static str> {
let mut window_ref_list = Vec::new();
for window in &self.hide_list {
if let Some(window_ref) = window.upgrade() {
window_ref_list.push(window_ref);
}
}
for window in &self.show_list {
if let Some(window_ref) = window.upgrade() {
window_ref_list.push(window_ref);
}
}
if let Some(window_ref) = self.active.upgrade() {
window_ref_list.push(window_ref)
}
let locked_window_list = &window_ref_list.iter().map(|x| x.lock()).collect::<Vec<_>>();
let bufferlist = locked_window_list.iter().map(|window| {
FramebufferUpdates {
src_framebuffer: window.framebuffer(),
coordinate_in_dest_framebuffer: window.get_position(),
}
});
FRAME_COMPOSITOR.lock().composite(bufferlist, &mut self.final_fb, bounding_box)
}
pub fn refresh_active_window(&mut self, bounding_box: Option<Rectangle>) -> Result<(), &'static str> {
if let Some(window_ref) = self.active.upgrade() {
let window = window_ref.lock();
let buffer_update = FramebufferUpdates {
src_framebuffer: window.framebuffer(),
coordinate_in_dest_framebuffer: window.get_position(),
};
FRAME_COMPOSITOR.lock().composite(Some(buffer_update), &mut self.final_fb, bounding_box)
} else {
Ok(())
}
}
fn pass_keyboard_event_to_window(&self, key_event: KeyEvent) -> Result<(), &'static str> {
let active_window = self.active.upgrade().ok_or("no window was set as active to receive a keyboard event")?;
active_window.lock().send_event(Event::new_keyboard_event(key_event))
.map_err(|_e| "Failed to enqueue the keyboard event; window event queue was full.")?;
Ok(())
}
fn pass_mouse_event_to_window(&self, mouse_event: MouseEvent) -> Result<(), &'static str> {
let coordinate = { &self.mouse };
let mut event: MousePositionEvent = MousePositionEvent {
coordinate: Coord::new(0, 0),
gcoordinate: *coordinate,
scrolling_up: mouse_event.movement.scroll_movement > 0, scrolling_down: mouse_event.movement.scroll_movement < 0, left_button_hold: mouse_event.buttons.left(),
right_button_hold: mouse_event.buttons.right(),
fourth_button_hold: mouse_event.buttons.fourth(),
fifth_button_hold: mouse_event.buttons.fifth(),
};
if let Some(current_active) = self.active.upgrade() {
let current_active_win = current_active.lock();
let current_coordinate = current_active_win.get_position();
if current_active_win.contains(*coordinate - current_coordinate) || matches!(current_active_win.moving, WindowMovingStatus::Moving(_))
{
event.coordinate = *coordinate - current_coordinate;
current_active_win.send_event(Event::MousePositionEvent(event))
.map_err(|_e| "Failed to enqueue the mouse event; window event queue was full.")?;
return Ok(());
}
}
for i in 0..self.show_list.len() {
if let Some(now_inner_mutex) = self.show_list[i].upgrade() {
let now_inner = now_inner_mutex.lock();
let current_coordinate = now_inner.get_position();
if now_inner.contains(*coordinate - current_coordinate) {
event.coordinate = *coordinate - current_coordinate;
now_inner.send_event(Event::MousePositionEvent(event))
.map_err(|_e| "Failed to enqueue the mouse event; window event queue was full.")?;
return Ok(());
}
}
}
Err("the mouse position does not fall within the bounds of any window")
}
fn refresh_floating_border(
&mut self,
show: bool,
new_border: Rectangle,
) -> Result<(), &'static str> {
if let Some(border) = self.repositioned_border {
let pixels = self.draw_floating_border(&border, color::TRANSPARENT);
self.refresh_bottom_windows(pixels.into_iter(), true)?;
}
if show {
let pixels = self.draw_floating_border(&new_border, WINDOW_BORDER_COLOR_INNER);
self.refresh_top(pixels.into_iter())?;
self.repositioned_border = Some(new_border);
} else {
self.repositioned_border = None;
}
Ok(())
}
fn draw_floating_border(&mut self, border: &Rectangle, color: Color) -> Vec<Coord> {
let mut coordinates = Vec::new();
let pixel = color.into();
for i in 0..(WINDOW_BORDER_SIZE) as isize {
let width = (border.bottom_right.x - border.top_left.x) - 2 * i;
let height = (border.bottom_right.y - border.top_left.y) - 2 * i;
let coordinate = border.top_left + (i, i);
if width <= 0 || height <= 0 {
break;
}
framebuffer_drawer::draw_rectangle(
&mut self.top_fb,
coordinate,
width as usize,
height as usize,
pixel
);
for m in 0..width {
coordinates.push(coordinate + (m, 0));
coordinates.push(coordinate + (m, height));
}
for m in 1..height - 1 {
coordinates.push(coordinate + (0, m));
coordinates.push(coordinate + (width, m));
}
}
coordinates
}
pub fn move_active_window(&mut self) -> Result<(), &'static str> {
if let Some(current_active) = self.active.upgrade() {
let border = Rectangle {
top_left: Coord::new(0, 0),
bottom_right: Coord::new(0, 0)
};
self.refresh_floating_border(false, border)?;
let (old_top_left, old_bottom_right, new_top_left, new_bottom_right) = {
let mut current_active_win = current_active.lock();
let (current_x, current_y) = {
let m = &self.mouse;
(m.x, m.y)
};
match current_active_win.moving {
WindowMovingStatus::Moving(base) => {
let old_top_left = current_active_win.get_position();
let new_top_left = old_top_left + ((current_x - base.x), (current_y - base.y));
let (width, height) = current_active_win.get_size();
let old_bottom_right = old_top_left + (width as isize, height as isize);
let new_bottom_right = new_top_left + (width as isize, height as isize);
current_active_win.set_position(new_top_left);
(old_top_left, old_bottom_right, new_top_left, new_bottom_right)
},
WindowMovingStatus::Stationary => {
return Err("The window is not moving");
}
}
};
self.refresh_bottom_windows(Some(Rectangle{top_left: old_top_left, bottom_right: old_bottom_right}), false)?;
self.refresh_active_window(Some(Rectangle{top_left: new_top_left, bottom_right: new_bottom_right}))?;
self.refresh_mouse()?;
} else {
return Err("cannot find active window to move");
}
Ok(())
}
pub fn refresh_mouse(&mut self) -> Result<(), &'static str> {
let bounding_box = Some(Rectangle {
top_left: self.mouse,
bottom_right: self.mouse + (MOUSE_POINTER_SIZE_X as isize, MOUSE_POINTER_SIZE_Y as isize)
});
self.refresh_top(bounding_box)
}
fn move_mouse(&mut self, relative: Coord) -> Result<(), &'static str> {
let old = self.mouse;
let mut new = old + relative;
let (screen_width, screen_height) = self.get_screen_size();
if new.x < 0 {
new.x = 0;
}
if new.y < 0 {
new.y = 0;
}
const MOUSE_POINTER_BORDER: isize = 3;
new.x = core::cmp::min(new.x, screen_width as isize - MOUSE_POINTER_BORDER);
new.y = core::cmp::min(new.y, screen_height as isize - MOUSE_POINTER_BORDER);
self.move_mouse_to(new)
}
fn move_mouse_to(&mut self, new: Coord) -> Result<(), &'static str> {
for y in self.mouse.y..self.mouse.y + MOUSE_POINTER_SIZE_Y as isize {
for x in
self.mouse.x..self.mouse.x + MOUSE_POINTER_SIZE_X as isize {
let coordinate = Coord::new(x, y);
self.top_fb.overwrite_pixel(coordinate, color::TRANSPARENT.into());
}
}
let bounding_box = Some(Rectangle {
top_left: self.mouse,
bottom_right: self.mouse + (MOUSE_POINTER_SIZE_X as isize, MOUSE_POINTER_SIZE_Y as isize)
});
self.refresh_bottom_windows(bounding_box.into_iter(), true)?;
self.mouse = new;
for y in new.y..new.y + MOUSE_POINTER_SIZE_Y as isize {
for x in new.x..new.x + MOUSE_POINTER_SIZE_X as isize {
let coordinate = Coord::new(x, y);
let pixel = MOUSE_POINTER_IMAGE[(x - new.x) as usize][(y - new.y) as usize].into();
self.top_fb.overwrite_pixel(coordinate, pixel);
}
}
self.refresh_mouse()?;
Ok(())
}
pub fn move_floating_border(&mut self) -> Result<(), &'static str> {
let (new_x, new_y) = {
let m = &self.mouse;
(m.x, m.y)
};
if let Some(current_active) = self.active.upgrade() {
let (is_draw, border_start, border_end) = {
let current_active_win = current_active.lock();
match current_active_win.moving {
WindowMovingStatus::Moving(base) => {
let coordinate = current_active_win.get_position();
let (width, height) = current_active_win.get_size();
let border_start = coordinate + (new_x - base.x, new_y - base.y);
let border_end = border_start + (width as isize, height as isize);
(true, border_start, border_end)
}
WindowMovingStatus::Stationary => (false, Coord::new(0, 0), Coord::new(0, 0)),
}
};
let border = Rectangle {
top_left: border_start,
bottom_right: border_end,
};
self.refresh_floating_border(is_draw, border)?;
} else {
let border = Rectangle {
top_left: Coord::new(0, 0),
bottom_right: Coord::new(0, 0),
};
self.refresh_floating_border(false, border)?;
}
Ok(())
}
pub fn is_active(&self, window: &Arc<Mutex<WindowInner>>) -> bool {
self.active.upgrade()
.map(|active| Arc::ptr_eq(&active, window))
.unwrap_or(false)
}
pub fn get_screen_size(&self) -> (usize, usize) {
self.final_fb.get_size()
}
}
pub fn init() -> Result<(Queue<Event>, Queue<Event>), &'static str> {
let final_fb: Framebuffer<AlphaPixel> = framebuffer::init()?;
let (width, height) = final_fb.get_size();
let mut bottom_fb = Framebuffer::new(width, height, None)?;
let mut top_fb = Framebuffer::new(width, height, None)?;
let (screen_width, screen_height) = bottom_fb.get_size();
bottom_fb.fill(color::LIGHT_GRAY.into());
top_fb.fill(color::TRANSPARENT.into());
let mouse = Coord {
x: screen_width as isize / 2,
y: screen_height as isize / 2,
};
let window_manager = WindowManager {
hide_list: VecDeque::new(),
show_list: VecDeque::new(),
active: Weak::new(),
mouse,
repositioned_border: None,
bottom_fb,
top_fb,
final_fb,
};
WINDOW_MANAGER.call_once(|| Mutex::new(window_manager));
let key_consumer: Queue<Event> = Queue::with_capacity(100);
let key_producer = key_consumer.clone();
let mouse_consumer: Queue<Event> = Queue::with_capacity(100);
let mouse_producer = mouse_consumer.clone();
spawn::new_task_builder(window_manager_loop, (key_consumer, mouse_consumer))
.name("window_manager_loop".to_string())
.spawn()?;
Ok((key_producer, mouse_producer))
}
fn window_manager_loop(
(key_consumer, mouse_consumer): (Queue<Event>, Queue<Event>),
) -> Result<(), &'static str> {
loop {
let event_opt = key_consumer.pop()
.or_else(||mouse_consumer.pop())
.or_else(||{
scheduler::schedule();
None
});
if let Some(event) = event_opt {
match event {
Event::KeyboardEvent(ref input_event) => {
let key_input = input_event.key_event;
keyboard_handle_application(key_input)?;
}
Event::MouseMovementEvent(ref mouse_event) => {
let mut x = mouse_event.movement.x_movement as isize;
let mut y = mouse_event.movement.y_movement as isize;
while let Some(next_event) = mouse_consumer.pop() {
match next_event {
Event::MouseMovementEvent(ref next_mouse_event) => {
if next_mouse_event.movement.scroll_movement
== mouse_event.movement.scroll_movement
&& next_mouse_event.buttons.left()
== mouse_event.buttons.left()
&& next_mouse_event.buttons.right()
== mouse_event.buttons.right()
&& next_mouse_event.buttons.fourth()
== mouse_event.buttons.fourth()
&& next_mouse_event.buttons.fifth()
== mouse_event.buttons.fifth() {
x = x.saturating_add(next_mouse_event.movement.x_movement as isize);
y = y.saturating_add(next_mouse_event.movement.y_movement as isize);
}
}
_ => {
break;
}
}
}
if x != 0 || y != 0 {
let mut wm = WINDOW_MANAGER
.get()
.ok_or("The static window manager was not yet initialized")?
.lock();
wm.move_mouse(
Coord::new(x, -y)
)?;
}
cursor_handle_application(mouse_event.clone())?; }
_other => {
trace!("WINDOW_MANAGER: ignoring unexpected event: {:?}", _other);
}
}
}
}
}
fn keyboard_handle_application(key_input: KeyEvent) -> Result<(), &'static str> {
let win_mgr = WINDOW_MANAGER.get().ok_or("The window manager was not yet initialized")?;
if key_input.modifiers.is_super_key() && key_input.action == KeyAction::Pressed {
let screen_dimensions = win_mgr.lock().get_screen_size();
let (width, height) = (screen_dimensions.0 as isize, screen_dimensions.1 as isize);
let new_position: Option<Rectangle> = match key_input.keycode {
Keycode::Left => Some(Rectangle {
top_left: Coord { x: 0, y: 0 },
bottom_right: Coord { x: width / 2, y: height },
}),
Keycode::Right => Some(Rectangle {
top_left: Coord { x: width / 2, y: 0 },
bottom_right: Coord { x: width, y: height },
}),
Keycode::Up => Some(Rectangle {
top_left: Coord { x: 0, y: 0 },
bottom_right: Coord { x: width, y: height / 2 },
}),
Keycode::Down => Some(Rectangle {
top_left: Coord { x: 0, y: height / 2 },
bottom_right: Coord { x: width, y: height },
}),
_ => None,
};
if let Some(position) = new_position {
let mut wm = win_mgr.lock();
if let Some(active_window) = wm.active.upgrade() {
debug!("window_manager: resizing active window to {:?}", new_position);
active_window.lock().resize(position)?;
wm.refresh_bottom_windows(Option::<Rectangle>::None, true)?;
}
}
return Ok(());
}
if key_input.modifiers.is_control()
&& key_input.modifiers.is_alt()
&& key_input.keycode == Keycode::T
&& key_input.action == KeyAction::Pressed
{
let new_app_namespace = mod_mgmt::create_application_namespace(None)?;
let shell_objfile = new_app_namespace.dir().get_file_starting_with("shell-")
.ok_or("Couldn't find shell application file to run upon Ctrl+Alt+T")?;
let path = shell_objfile.lock().get_absolute_path();
spawn::new_application_task_builder(path.as_ref(), Some(new_app_namespace))?
.name("shell".to_string())
.spawn()?;
debug!("window_manager: spawned new shell app in new app namespace.");
return Ok(());
}
if let Err(_e) = win_mgr.lock().pass_keyboard_event_to_window(key_input) {
warn!("window_manager: failed to pass keyboard event to active window. Error: {:?}", _e);
}
Ok(())
}
fn cursor_handle_application(mouse_event: MouseEvent) -> Result<(), &'static str> {
let wm = WINDOW_MANAGER.get().ok_or("The static window manager was not yet initialized")?.lock();
if wm.pass_mouse_event_to_window(mouse_event).is_err() {
}
Ok(())
}