134 lines
4.8 KiB
Rust
134 lines
4.8 KiB
Rust
#![no_std]
|
|
#![forbid(unsafe_code)]
|
|
#![no_main]
|
|
|
|
extern crate libkos;
|
|
|
|
use types::bounded::CapSlot;
|
|
|
|
/// Where init put them (see init's manifest: own endpoint, then `uses`).
|
|
const EP: CapSlot = CapSlot::const_new(0);
|
|
const INPUT: CapSlot = CapSlot::const_new(1);
|
|
/// `themes/`, read-only.
|
|
const THEMES: CapSlot = CapSlot::const_new(2);
|
|
/// Where storage's buffer is mapped while the themes are read.
|
|
const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x1800_0000);
|
|
/// The longest theme file read.
|
|
const THEME_BYTES: usize = 4096;
|
|
|
|
libkos::entry!(main);
|
|
|
|
fn main() {
|
|
// Init hands over the GPU, or nothing if the machine has none.
|
|
let dev = libkos::device::receive_device(EP);
|
|
let input = subscribe_to_input();
|
|
if let Some(fb) = dev.mmio.and_then(driver_framebuffer_virtio::VirtioFramebuffer::try_new) {
|
|
run_virtio(fb, input);
|
|
}
|
|
libkos::println!("display: no GPU, running headless");
|
|
run_headless(input);
|
|
}
|
|
|
|
/// Has the input server signal this thread whenever events arrive; the
|
|
/// server then passes them on to the app in focus.
|
|
fn subscribe_to_input() -> Option<CapSlot> {
|
|
use types::badge::Badge;
|
|
use types::permission::{Permission, PermissionSet};
|
|
let notify = libkos::syscall::create_notification().ok()?;
|
|
libkos::syscall::bind_notification(notify).ok()?;
|
|
let signal = PermissionSet::empty().with(Permission::Write).with(Permission::Delegate);
|
|
let copy = libkos::syscall::mint(notify, Badge::NONE, signal).ok()?;
|
|
let sent = libkos::syscall::call(INPUT, [types::input::SUBSCRIBE, 0, 0, 0], Some(copy.into()));
|
|
libkos::syscall::drop_cap(copy);
|
|
sent.ok().map(|_| INPUT)
|
|
}
|
|
|
|
fn server<F: port_framebuffer::Framebuffer>(fb: F, input: Option<CapSlot>) -> display_server::server::DisplayServer<F, display_server::host::KernelHost> {
|
|
display_server::server::DisplayServer::new(fb, display_server::host::KernelHost::new(EP, input), themes())
|
|
}
|
|
|
|
/// Every theme in `themes/` that reads and passes contrast; the built-in
|
|
/// one if none does.
|
|
fn themes() -> display_server::themes::Themes {
|
|
let mut themes = display_server::themes::Themes::new();
|
|
let Ok(mut fs) = libkos::fs::Storage::attach(THEMES, STORAGE_BUF_VA) else {
|
|
libkos::println!("display: no themes folder; the built-in theme only");
|
|
return themes;
|
|
};
|
|
let mut names = [([0u8; types::storage::DIR_NAME_SIZE], 0usize); display_server::themes::MAX_THEMES];
|
|
let mut found = 0;
|
|
let _ = fs.list_dir(b"", |e| {
|
|
if !e.is_dir && e.name().ends_with(b".toml") && found < names.len() {
|
|
names[found] = (e.name, e.name_len);
|
|
found += 1;
|
|
}
|
|
});
|
|
let mut text = [0u8; THEME_BYTES];
|
|
for (name, len) in &names[..found] {
|
|
let name = &name[..*len];
|
|
let read = fs.open(name).and_then(|h| {
|
|
let n = fs.read(h, &mut text);
|
|
let _ = fs.close(h);
|
|
n
|
|
});
|
|
let shown = core::str::from_utf8(name).unwrap_or("?");
|
|
match read.ok().and_then(|n| core::str::from_utf8(&text[..n]).ok()) {
|
|
Some(t) => {
|
|
if let Err(e) = themes.add(t) {
|
|
libkos::println!("display: theme {} refused: {:?}", shown, e);
|
|
}
|
|
}
|
|
None => libkos::println!("display: cannot read theme {}", shown),
|
|
}
|
|
}
|
|
fs.detach();
|
|
// Storage has few connections to give; this one is done with.
|
|
let _ = libkos::fs::release(THEMES);
|
|
themes
|
|
}
|
|
|
|
#[inline(never)]
|
|
fn run_virtio(fb: driver_framebuffer_virtio::VirtioFramebuffer, input: Option<CapSlot>) -> ! {
|
|
let mut srv = server(fb, input);
|
|
server_framework::serve(&mut srv, EP)
|
|
}
|
|
|
|
/// No GPU: clients still get answers (and the dimensions they draw for);
|
|
/// pixels go nowhere.
|
|
struct Headless;
|
|
|
|
impl port_framebuffer::Framebuffer for Headless {
|
|
fn width(&self) -> types::display::Dimension {
|
|
types::display::Dimension::new(640)
|
|
}
|
|
fn height(&self) -> types::display::Dimension {
|
|
types::display::Dimension::new(480)
|
|
}
|
|
fn write_pixel(&mut self, _: types::display::ScreenCoord, _: types::display::ScreenCoord, _: types::display::Color) {}
|
|
fn fill_rect(
|
|
&mut self,
|
|
_: types::display::ScreenCoord,
|
|
_: types::display::ScreenCoord,
|
|
_: types::display::Dimension,
|
|
_: types::display::Dimension,
|
|
_: types::display::Color,
|
|
) {
|
|
}
|
|
fn clear(&mut self, _: types::display::Color) {}
|
|
fn flush(&mut self) {}
|
|
fn scroll_up(
|
|
&mut self,
|
|
_: types::display::ScreenCoord,
|
|
_: types::display::Dimension,
|
|
_: types::display::Dimension,
|
|
_: types::display::Color,
|
|
) {
|
|
}
|
|
}
|
|
|
|
#[inline(never)]
|
|
fn run_headless(input: Option<CapSlot>) -> ! {
|
|
let mut srv = server(Headless, input);
|
|
server_framework::serve(&mut srv, EP)
|
|
}
|