Compare commits

...

4 Commits

88 changed files with 4466 additions and 1134 deletions

View File

@ -180,9 +180,10 @@ fn make_sysfs_sink_object(index: usize) -> Arc<KObject<usize>> {
}
/// Print a trace message coming from a process
pub fn program_trace(process: &Process, _thread: &Thread, message: &str) {
log::debug!(
pub fn program_trace(process: &Process, _thread: &Thread, level: log::Level, message: &str) {
log::log!(
target: ":program",
level,
"{} ({}) {message}\n",
process.name,
process.id,

View File

@ -1,6 +1,7 @@
use core::mem::MaybeUninit;
pub(crate) use abi::{
debug::TraceLevel,
error::Error,
io::{
AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, MountOptions, OpenOptions,

View File

@ -1,14 +1,24 @@
use abi::{debug::DebugOperation, error::Error, process::ProcessId};
use abi::{
debug::{DebugOperation, TraceLevel},
error::Error,
process::ProcessId,
};
use libk::{
debug,
task::{process::Process, thread::Thread},
};
pub(crate) fn debug_trace(message: &str) {
pub(crate) fn debug_trace(level: TraceLevel, message: &str) {
let thread = Thread::current();
let process = thread.process();
let level = match level {
TraceLevel::Debug => log::Level::Debug,
TraceLevel::Info => log::Level::Info,
TraceLevel::Warn => log::Level::Warn,
TraceLevel::Error => log::Level::Error,
};
debug::program_trace(&process, &thread, message);
debug::program_trace(&process, &thread, level, message);
}
pub(crate) fn debug_control(pid: ProcessId, op: &mut DebugOperation) -> Result<(), Error> {

View File

@ -61,6 +61,13 @@ bitfield MappingFlags(u32) {
EXEC: 1,
}
enum TraceLevel(u32) {
Debug = 0,
Info = 1,
Warn = 2,
Error = 3,
}
// TODO allow splitting types into separate modules
syscall get_random(buffer: &mut [u8]);
@ -174,5 +181,5 @@ syscall fork() -> Result<ProcessId>;
syscall execve(options: &ExecveOptions<'_>) -> Result<()>;
// Debugging
syscall debug_trace(message: &str);
syscall debug_trace(level: TraceLevel, message: &str);
syscall debug_control(pid: ProcessId, op: &mut DebugOperation<'_>) -> Result<()>;

View File

@ -1,6 +1,8 @@
//! Debugging functionality
use crate::{arch::SavedFrame, io::RawFd};
pub use crate::generated::TraceLevel;
/// Describes a debugger operation
#[derive(Debug)]
pub enum DebugOperation<'a> {

View File

@ -2,13 +2,13 @@
use core::fmt;
pub use abi::debug::{DebugFrame, DebugOperation};
pub use abi::debug::{DebugFrame, DebugOperation, TraceLevel};
/// Prints a debug message using DebugTrace syscall
#[macro_export]
macro_rules! debug_trace {
($($args:tt)+) => {
$crate::debug::_debug_trace(format_args!($($args)+))
($level:ident, $($args:tt)+) => {
$crate::debug::_debug_trace($crate::debug::TraceLevel::$level, format_args!($($args)+))
};
}
@ -29,16 +29,16 @@ impl fmt::Write for TracePrinter {
/// Trace raw message into the kernel's log. If the message is not a valid unicode string, it will
/// get discarded.
pub fn trace_raw(data: &[u8]) {
pub fn trace_raw(level: TraceLevel, data: &[u8]) {
if let Ok(str) = core::str::from_utf8(data) {
unsafe {
crate::sys::debug_trace(str);
crate::sys::debug_trace(level, str);
}
}
}
#[doc(hidden)]
pub fn _debug_trace(a: core::fmt::Arguments<'_>) {
pub fn _debug_trace(level: TraceLevel, a: core::fmt::Arguments<'_>) {
use fmt::Write;
let mut printer = TracePrinter {
@ -50,7 +50,7 @@ pub fn _debug_trace(a: core::fmt::Arguments<'_>) {
if printer.len != 0 {
unsafe {
let s = core::str::from_utf8_unchecked(&printer.buf[..printer.len]);
crate::sys::debug_trace(s);
crate::sys::debug_trace(level, s);
}
}
}

View File

@ -55,7 +55,7 @@ unsafe extern "C" fn common_signal_entry(data: &SignalEntryData) -> ! {
// TODO add signal print hook function to dump signal info here
fn terminate_by_signal(signal: Signal) -> ! {
crate::debug_trace!("runtime: terminated by signal: {signal:?}");
crate::debug_trace!(Warn, "runtime: terminated by signal: {signal:?}");
unsafe { crate::sys::exit_process(ExitCode::BySignal(Ok(signal))) };
}

View File

@ -165,7 +165,7 @@ impl<R> Thread<R> {
// Setup TLS as soon as possible
if let Err(err) = thread_local::init_tls(argument.tls_image, true) {
crate::debug_trace!("thread_entry failed: TLS init error: {err:?}");
crate::debug_trace!(Warn, "thread_entry failed: TLS init error: {err:?}");
// TODO result is uninit
unsafe { crate::sys::exit_thread() };
}
@ -261,7 +261,11 @@ impl OwnedStack {
impl Drop for OwnedStack {
fn drop(&mut self) {
crate::debug_trace!("Drop OwnedStack {:#x?}", self.base..self.base + self.size);
crate::debug_trace!(
Debug,
"Drop OwnedStack {:#x?}",
self.base..self.base + self.size
);
unsafe { crate::sys::unmap_memory(self.base, self.size) }.ok();
}
}

View File

@ -178,7 +178,7 @@ impl Dtv {
/// Will panic if key is longer than the DTV itself.
pub fn set_specific(&mut self, key: usize, value: *mut c_void, grow: bool) {
if self.try_set_specific(key, value, grow).is_err() {
crate::debug_trace!("Dtv::set_specific(): invalid key {key}, grow={grow}");
crate::debug_trace!(Debug, "Dtv::set_specific(): invalid key {key}, grow={grow}");
panic!("Dtv::set_specific(): invalid key {key})")
}
}
@ -215,7 +215,7 @@ impl Dtv {
match Self::get_key(&self.entries, key) {
Some(value) => value,
None => {
crate::debug_trace!("Dtv::get(): out-of-bounds DTV key: {key}");
crate::debug_trace!(Debug, "Dtv::get(): out-of-bounds DTV key: {key}");
panic!("Dtv::get(): out-of-bounds DTV key: {key}");
}
}
@ -227,11 +227,12 @@ impl Dtv {
///
/// Will panic if key == 0.
pub fn set(&mut self, key: usize, value: *mut c_void) {
crate::debug_trace!(Debug, "Dtv::set({key}, {value:p})");
if key > self.entries.len() {
self.entries.resize(key, null_mut());
}
if !Self::set_key(&mut self.entries, key, value) {
crate::debug_trace!("Dtv::set(): invalid key {key}");
crate::debug_trace!(Debug, "Dtv::set(): invalid key {key}");
panic!("Dtv::set(): invalid key {key}");
}
}
@ -259,7 +260,7 @@ pub fn init_tls_from_auxv<'a, I: Iterator<Item = &'a AuxValue>>(
/// a dummy (platform-specific) TLS copy will be created.
pub fn init_tls(image: Option<&TlsImage>, force: bool) -> Result<(), Error> {
let Some(image) = image else {
crate::debug_trace!("TODO: handle executables with no TLS");
crate::debug_trace!(Debug, "TODO: handle executables with no TLS");
return Err(Error::NotImplemented);
};
@ -292,6 +293,11 @@ fn setup_dtv(image: &TlsImage, tls_info: &TlsInfo) -> Result<(), Error> {
// NOTE if module 1 is specified again by the dynamic loader, it will be overriden with
// what dynamic loader says
if let Some(module0_offset) = tls_info.module0_offset {
crate::debug_trace!(
Info,
"DTV[1] = {:#x}",
tls_info.base + module0_offset + DTV_OFFSET
);
dtv.set(
1,
core::ptr::without_provenance_mut(tls_info.base + module0_offset + DTV_OFFSET),
@ -303,6 +309,11 @@ fn setup_dtv(image: &TlsImage, tls_info: &TlsInfo) -> Result<(), Error> {
}
for &(module_id, module_offset) in image.module_offsets.iter() {
assert!(module_offset < image.full_size);
crate::debug_trace!(
Info,
"DTV[{module_id}] = {:#x}",
tls_info.base + module_offset + DTV_OFFSET
);
dtv.set(
module_id,
core::ptr::with_exposed_provenance_mut(tls_info.base + module_offset + DTV_OFFSET),

View File

@ -21,11 +21,18 @@ pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
const TCB_SIZE: usize = size_of::<usize>() * 2;
if !image.align.is_power_of_two() {
crate::debug_trace!("TLS layout not aligned to a power of two: {}", image.align);
crate::debug_trace!(
Debug,
"TLS layout not aligned to a power of two: {}",
image.align
);
unsafe { crate::sys::exit_process(ExitCode::Exited(1)) };
}
if image.align > 0x1000 {
crate::debug_trace!("TODO: TLS alignment larger than a page size is not supported");
crate::debug_trace!(
Warn,
"TODO: TLS alignment larger than a page size is not supported"
);
unsafe { crate::sys::exit_process(ExitCode::Exited(1)) };
}
@ -73,7 +80,7 @@ pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
// Zero the TCB after the self-pointer
tcb[size_of::<usize>()..].fill(0);
crate::debug_trace!("TLS: base={:#x}, tp={:#x}", base, tp);
crate::debug_trace!(Debug, "TLS: base={:#x}, tp={:#x}", base, tp);
Ok(TlsInfo {
base,

View File

@ -71,7 +71,7 @@ pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
// Zero the TCB after the self-pointer
tcb[size_of::<usize>()..].fill(0);
crate::debug_trace!("TLS: base={:#x}, tp={:#x}", base, tp);
crate::debug_trace!(Debug, "TLS: base={:#x}, tp={:#x}", base, tp);
Ok(TlsInfo {
base,

View File

@ -17,6 +17,7 @@ mod riscv64;
mod generated {
// Import all the necessary types from generated ABI
use abi::{
debug::TraceLevel,
error::Error,
io::{
AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, MountOptions, OpenOptions,

1961
userspace/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,8 +17,10 @@ members = [
"rsh",
"lib/cross",
"crypt",
"lib/runtime"
, "lib/uipc"]
"lib/runtime",
"lib/uipc",
"lib/logsink"
, "lib/libpsf"]
exclude = ["dynload-program", "test-kernel-module", "lib/ygglibc"]
[workspace.dependencies]
@ -29,10 +31,13 @@ serde_json = "1.0.132"
serde = { version = "1.0.214", features = ["derive"] }
bytemuck = "1.19.0"
thiserror = "1.0.64"
env_logger = "0.11.5"
sha2 = { version = "0.10.8" }
chrono = { version = "0.4.31", default-features = false }
postcard = { version = "1.1.1", features = ["alloc"] }
raqote = { version = "0.8.3", default-features = false }
# Vendored/patched dependencies
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" }
ring = { git = "https://git.alnyan.me/yggdrasil/ring.git", branch = "alnyan/yggdrasil" }
@ -45,11 +50,13 @@ ed25519-dalek = { git = "https://git.alnyan.me/yggdrasil/curve25519-dalek.git",
libterm.path = "lib/libterm"
runtime.path = "lib/runtime"
libcolors = { path = "lib/libcolors", default-features = false }
libpsf.path = "lib/libpsf"
cross.path = "lib/cross"
uipc.path = "lib/uipc"
yggdrasil-rt.path = "../lib/runtime"
yggdrasil-abi = { path = "../lib/abi", features = ["serde", "alloc", "bytemuck"] }
abi-serde = { path = "../lib/abi-serde" }
logsink.path = "lib/logsink"
[workspace.lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }

View File

@ -4,16 +4,33 @@ version = "0.1.0"
edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[[bin]]
name = "colors-bar"
[dependencies]
uipc.workspace = true
cross.workspace = true
logsink.workspace = true
libcolors.workspace = true
libpsf.workspace = true
serde.workspace = true
thiserror.workspace = true
libcolors = { workspace = true, default-features = false }
log.workspace = true
clap.workspace = true
chrono.workspace = true
[target.'cfg(target_os = "yggdrasil")'.dependencies]
yggdrasil-abi.workspace = true
runtime.workspace = true
[target.'cfg(unix)'.dependencies]
winit = "0.30.9"
softbuffer = "0.4.6"
[dev-dependencies]
winit = "0.30.9"
softbuffer = "0.4.6"
[lints]
workspace = true

View File

@ -0,0 +1,95 @@
#![feature(yggdrasil_os, rustc_private)]
use std::{os::fd::AsRawFd, process::ExitCode, sync::Arc, time::Duration};
use chrono::{DateTime, Timelike};
use cross::io::{Poll, TimerFd};
use libcolors::{
application::{window::Window, Application},
error::Error,
message::{CreateWindowInfo, WindowType},
};
use libpsf::PcScreenFont;
use runtime::rt::time::get_real_time;
fn draw_string(dt: &mut [u32], font: &PcScreenFont, x: u32, y: u32, text: &str, stride: usize) {
let y = y as usize;
for (i, ch) in text.chars().enumerate() {
let x = x as usize + i * font.width() as usize;
font.map_glyph_pixels(ch as u32, |px, py, set| {
let color = if set { 0xFFFFFFFF } else { 0xFF000000 };
dt[(y + py) * stride + x + px] = color;
});
}
}
fn string_width(font: &PcScreenFont, text: &str) -> u32 {
(font.width() as usize * text.len()) as u32
}
fn redraw(dt: &mut [u32], font: &PcScreenFont, width: u32, _height: u32) {
dt.fill(0);
let now = get_real_time().unwrap();
if let Some(now) = DateTime::from_timestamp(now.seconds() as _, now.subsec_nanos() as _) {
let (pm, hour) = now.hour12();
let ampm = if pm { "PM" } else { "AM" };
let text = format!("{}:{:02} {ampm}", hour, now.minute());
let tw = string_width(font, &text);
draw_string(dt, font, width - tw - 4, 2, &text, width as usize);
}
}
fn run() -> Result<ExitCode, Error> {
let font = Arc::new(PcScreenFont::default());
let mut poll = Poll::new()?;
let mut timer = TimerFd::new()?;
let mut app = Application::new()?;
let mut window = Window::new_with_info(
&app,
CreateWindowInfo {
ty: WindowType::Reservation(24),
},
)?;
window.set_on_redraw_requested(move |dt, width, height| {
redraw(dt, &font, width, height);
});
app.add_window(window);
// TODO signals, events in Application?
let app_fd = app.connection().lock().unwrap().as_raw_fd();
let timer_fd = timer.as_raw_fd();
poll.add(&app_fd)?;
poll.add(&timer_fd)?;
timer.start(Duration::from_secs(1))?;
while app.is_running() {
let fd = poll.wait(None)?.unwrap();
if fd == app_fd {
app.poll_events()?;
} else if fd == timer_fd {
app.redraw()?;
timer.start(Duration::from_secs(1))?;
} else {
log::warn!("Unexpected poll event: {fd:?}");
}
}
Ok(ExitCode::SUCCESS)
}
fn main() -> ExitCode {
logsink::setup_logging(true);
log::info!("colors-bar starting");
match run() {
Ok(code) => code,
Err(error) => {
log::error!("colors-bar: {error}");
ExitCode::FAILURE
}
}
}

View File

@ -10,65 +10,11 @@ use std::{
use crate::error::Error;
pub struct Display<'a> {
#[allow(unused)]
mapping: FileMapping<'a>,
data: &'a mut [u32],
width: usize,
height: usize,
// TODO use those
_stride: usize,
_size: usize,
}
pub struct Point<T>(pub T, pub T);
impl Display<'_> {
pub fn open(framebuffer: impl AsRef<Path>) -> Result<Self, Error> {
let file = OpenOptions::new().open(framebuffer)?;
let mut buffer = [0; 128];
device::device_request::<device::AcquireDevice>(file.as_raw_fd(), &mut buffer, &())
.map_err(std::io::Error::from)?;
let framebuffer = device::device_request::<device::GetActiveFramebuffer>(
file.as_raw_fd(),
&mut buffer,
&(),
)
.map_err(std::io::Error::from)?;
let width = framebuffer.width as usize;
let height = framebuffer.height as usize;
let mut mapping = FileMapping::new(file, 0, framebuffer.size)?;
let data = unsafe {
std::slice::from_raw_parts_mut(mapping.as_mut_ptr() as *mut u32, width * height)
};
Ok(Self {
mapping,
data,
width,
height,
_stride: framebuffer.stride / size_of::<u32>(),
_size: framebuffer.size / size_of::<u32>(),
})
}
pub fn flush(&mut self) {
let mut buffer = [0; 0];
device::device_request::<device::FlushDisplay>(self.mapping.as_raw_fd(), &mut buffer, &()).ok();
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn fill(&mut self, color: u32) {
self.data.fill(color);
}

View File

@ -1,15 +1,9 @@
use std::{
fs::File,
io::Read,
os::fd::{AsRawFd, RawFd},
use libcolors::{
event::{KeyInput, KeyModifiers},
input::Key,
};
// use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use libcolors::event::{KeyInput, KeyModifiers};
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use crate::error::Error;
pub struct KeyboardInput(File);
#[derive(Default)]
pub struct InputState {
@ -21,37 +15,15 @@ pub struct InputState {
ralt: bool,
}
impl KeyboardInput {
pub fn open() -> Result<Self, Error> {
let file = File::open("/dev/kbd")?;
Ok(Self(file))
}
pub fn as_poll_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
pub fn read_event(&mut self) -> Result<KeyboardKeyEvent, Error> {
let mut buf = [0; 4];
let len = self.0.read(&mut buf)?;
if len == 4 {
Ok(KeyboardKeyEvent::from_bytes(buf))
} else {
todo!()
}
}
}
impl InputState {
pub fn update(&mut self, key: KeyboardKey, state: bool) {
pub fn update(&mut self, key: Key, state: bool) {
match key {
KeyboardKey::LAlt => self.lalt = state,
KeyboardKey::RAlt => self.ralt = state,
KeyboardKey::LShift => self.lshift = state,
KeyboardKey::RShift => self.rshift = state,
KeyboardKey::LControl => self.lctrl = state,
KeyboardKey::RControl => self.rctrl = state,
Key::LAlt => self.lalt = state,
Key::RAlt => self.ralt = state,
Key::LShift => self.lshift = state,
Key::RShift => self.rshift = state,
Key::LControl => self.lctrl = state,
Key::RControl => self.rctrl = state,
_ => (),
}
}
@ -75,15 +47,15 @@ impl InputState {
}
}
pub fn make_input(&self, key: KeyboardKey) -> KeyInput {
pub fn make_input(&self, key: Key) -> KeyInput {
let modifiers = self.modifiers();
let input = match (key, modifiers) {
(KeyboardKey::Char(ch), KeyModifiers::NONE) => Some(ch as _),
(Key::Char(ch), KeyModifiers::NONE) => Some(ch as _),
// TODO proper shift key translation
(KeyboardKey::Char(ch), KeyModifiers::SHIFT) => Some(Self::translate_shift(ch) as _),
(KeyboardKey::Tab, KeyModifiers::NONE) => Some('\t'),
(KeyboardKey::Enter, KeyModifiers::NONE) => Some('\n'),
(Key::Char(ch), KeyModifiers::SHIFT) => Some(Self::translate_shift(ch) as _),
// (KeyboardKey::Tab, KeyModifiers::NONE) => Some('\t'),
(Key::Enter, KeyModifiers::NONE) => Some('\n'),
(_, _) => None,
};

View File

@ -1,205 +1,91 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os, rustc_private))]
#![feature(map_many_mut, iter_chain)]
use std::{
collections::{BTreeMap, HashMap},
env,
os::{
fd::{AsRawFd, RawFd},
yggdrasil::io::poll::PollChannel,
},
path::Path,
marker::PhantomData,
os::fd::{AsRawFd, RawFd},
process::{Command, ExitCode},
};
use cross::mem::{FileMapping, SharedMemory};
use display::Display;
use error::Error;
use input::{InputState, KeyboardInput};
use cross::mem::SharedMemory;
use input::InputState;
use libcolors::{
event::{EventData, KeyModifiers, KeyboardKey, KeyboardKeyEvent, WindowEvent, WindowInfo},
message::{ClientMessage, ServerMessage},
event::{EventData, KeyModifiers, KeyboardKeyEvent, WindowEvent, WindowInfo},
input::Key,
message::{ClientMessage, CreateWindowInfo, WindowType},
};
use uipc::{Channel, PeerAddress, Receiver, Sender};
use sys::{Backend, DisplaySurface, FromClient, Point, ServerSender, WindowServer};
use uipc::PeerAddress;
use window::Window;
use wm::{Direction, Workspace};
// TODO rewrite and split this into meaningful components
pub mod display;
pub mod error;
pub mod input;
pub mod sys;
pub mod window;
pub mod wm;
pub struct Window<'a> {
window_id: u32,
client_id: PeerAddress,
surface_mapping: FileMapping,
surface_data: &'a [u32],
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Backend(#[from] sys::Error),
}
pub struct Frame {
x: u32,
y: u32,
w: u32,
h: u32,
dirty: bool,
window: Option<u32>,
}
pub struct Row {
frames: Vec<Frame>,
x: u32,
y: u32,
width: u32,
height: u32,
}
pub struct ServerSender(Sender<ServerMessage>);
pub struct Server<'a, 'd> {
display: Display<'d>,
pub struct Server<'a> {
input_state: InputState,
last_client_id: u32,
client_map: HashMap<u32, PeerAddress>,
// Window management
workspace: Workspace<u32>,
windows: BTreeMap<u32, Window<'a>>,
rows: Vec<Row>,
last_window_id: u32,
focused_frame: Option<(usize, usize)>,
// Outer frame
padding: usize,
background: u32,
// Event generators
poll: PollChannel,
receiver: Receiver<ClientMessage>,
input: KeyboardInput,
// Comms
sender: ServerSender,
_pd: PhantomData<&'a ()>,
}
impl Row {
pub fn new(x: u32, y: u32, w: u32, h: u32) -> Self {
Self {
frames: vec![],
x,
y,
width: w,
height: h,
}
}
pub fn balance_frames(&mut self) {
if self.frames.is_empty() {
return;
}
let spacing = 4;
let wc = self.frames.len() as u32;
let w = (self.width - spacing * (wc - 1)) / wc;
let h = self.height;
let mut x = self.x;
let y = self.y;
for frame in self.frames.iter_mut() {
frame.dirty = true;
frame.x = x;
frame.y = y;
frame.w = w;
frame.h = h;
x += w + spacing;
}
}
pub fn place_frame(&mut self) -> &mut Frame {
self.frames.push(Frame {
x: 0,
y: 0,
w: 0,
h: 0,
dirty: true,
window: None,
});
self.balance_frames();
self.frames.last_mut().unwrap()
}
pub fn remove_frame(&mut self, col: usize) {
self.frames.remove(col);
self.balance_frames();
}
}
impl<'a> Server<'a, '_> {
pub fn new(framebuffer: &str) -> Result<Self, Error> {
let mut poll = PollChannel::new()?;
let mut display = Display::open(framebuffer)?;
let input = KeyboardInput::open()?;
let channel = Channel::bind(libcolors::CHANNEL_NAME)?;
let (sender, receiver) = channel.split();
let sender = ServerSender(sender);
poll.add(input.as_poll_fd())?;
poll.add(receiver.as_raw_fd())?;
let background = 0xFFCCCCCC;
display.fill(background);
display.flush();
impl<'a> Server<'a> {
fn new() -> Result<Self, Error> {
Ok(Self {
display,
input_state: InputState::default(),
poll,
receiver,
input,
sender,
padding: 4,
background,
background: 0xCCCCCC,
last_client_id: 0,
client_map: HashMap::new(),
windows: BTreeMap::new(),
rows: vec![],
workspace: Workspace::new(800, 600),
last_window_id: 1,
focused_frame: None,
_pd: PhantomData,
})
}
fn create_window(&mut self, client_id: &PeerAddress) -> Result<(WindowInfo, RawFd), Error> {
if self.rows.is_empty() {
self.rows.push(Row::new(
self.padding as _,
self.padding as _,
(self.display.width() - self.padding * 2) as _,
(self.display.height() - self.padding * 2) as _,
));
}
// Create a frame
let row = self.rows.last_mut().unwrap();
let frame = row.place_frame();
// Create the actual window
let window_id = self.last_window_id;
fn create_window(
&mut self,
surface: &mut DisplaySurface,
tx: &mut ServerSender,
peer: &PeerAddress,
info: CreateWindowInfo
) -> Result<(WindowInfo, RawFd), Error> {
let wid = self.last_window_id;
self.last_window_id += 1;
let mapping_size = self.display.width() * self.display.height() * 4;
let need_focus = match info.ty {
WindowType::Default => {
if !self.workspace.create_window(wid) {
todo!()
}
true
}
WindowType::Reservation(height) => {
self.workspace.add_reservation(wid, height);
false
}
};
let frame = self.workspace.window_frame(wid).unwrap();
let mapping_size = surface.width() * surface.height() * 4;
// let mapping_size = self.display.width() * self.display.height() * 4;
let surface_shm = SharedMemory::new(mapping_size).unwrap();
let fd = surface_shm.as_raw_fd();
let mut surface_mapping = surface_shm.map().unwrap();
@ -211,298 +97,135 @@ impl<'a> Server<'a, '_> {
)
};
frame.window = Some(window_id);
let window = Window {
window_id,
client_id: client_id.clone(),
wid,
peer: peer.clone(),
surface_mapping,
surface_data,
};
self.windows.insert(window_id, window);
self.windows.insert(wid, window);
let info = WindowInfo {
window_id,
surface_stride: self.display.width() * 4,
window_id: wid,
surface_stride: surface.width() * 4, // self.display.width() * 4,
surface_mapping_size: mapping_size,
width: frame.w,
height: frame.h,
};
self.display.fill(self.background);
self.set_focused_window(window_id)?;
self.flush_dirty_frames();
surface.fill(self.background);
if need_focus {
self.focus_window(tx, wid);
}
Ok((info, fd))
}
fn remove_window(&mut self, window_id: u32) {
// Find the window
if !self.windows.contains_key(&window_id) {
return;
fn focus_window(&mut self, tx: &mut ServerSender, wid: u32) {
let (old_wid, new) = self.workspace.focus_window(wid);
let old = old_wid.and_then(|wid| {
let window = self.windows.get(&wid)?;
Some((wid, window))
});
let new = new.and_then(|_| self.windows.get(&wid));
if let Some((wid, window)) = old {
log::info!("wid #{wid} focus=false");
tx.send_event(
EventData::WindowEvent(wid, WindowEvent::FocusChanged(false)),
&window.peer,
);
}
// TODO this is ugly
let mut res = None;
for (i, row) in self.rows.iter().enumerate() {
let j = row
.frames
.iter()
.position(|f| f.window.map(|w| w == window_id).unwrap_or(false));
if let Some(j) = j {
res = Some((i, j));
}
if let Some(window) = new {
log::info!("wid #{wid} focus=true");
tx.send_event(
EventData::WindowEvent(wid, WindowEvent::FocusChanged(true)),
&window.peer,
);
}
}
// Remove the frame
if let Some((row, col)) = res {
self.rows[row].remove_frame(col);
self.display.fill(self.background);
self.flush_dirty_frames();
fn move_focus(&mut self, tx: &mut ServerSender, direction: Direction) {
if let Some(wid) = self.workspace.window_towards_wrap(direction) {
self.focus_window(tx, wid);
}
}
self.windows.remove(&window_id);
fn move_window(&mut self, surface: &mut DisplaySurface, tx: &mut ServerSender, direction: Direction) {
if self.workspace.move_window(direction) {
surface.fill(self.background);
self.flush_dirty_frames(tx);
self.workspace.all_windows().for_each(|(wid, _)| {
if let Some(window) = self.windows.get(&wid) {
tx.send_event(
EventData::WindowEvent(wid, WindowEvent::RedrawRequested),
&window.peer,
);
}
});
}
}
if self.focused_frame == res {
self.focused_frame = None;
let new_focus = if let Some((row, col)) = res {
// Focus some other frame in the same row
if let Some(f_row) = self.rows.get(row) {
let row_len = f_row.frames.len();
if col == 0 && row_len != 0 {
Some((row, 1))
} else if col > 0 {
Some((row, col - 1))
} else {
// Empty row
None
}
} else {
// No row exists
None
}
} else {
// No frames?
None
fn flush_dirty_frames(&mut self, tx: &mut ServerSender) {
for (wid, rect) in self.workspace.dirty_windows() {
let Some(window) = self.windows.get_mut(&wid) else {
continue;
};
log::info!("Resize #{}: {}x{}", wid, rect.w, rect.h);
self.set_focused_frame(new_focus);
window.resize(rect.w, rect.h);
tx.send_event(
EventData::WindowEvent(
wid,
WindowEvent::Resized {
width: rect.w,
height: rect.h,
},
),
&window.peer,
);
}
}
}
fn handle_keyboard_event(&mut self, event: KeyboardKeyEvent) -> Result<(), Error> {
let (key, state) = event.split();
impl WindowServer for Server<'_> {
fn handle_initial(&mut self, mut surface: DisplaySurface) {
self.workspace.resize(surface.width() as u32, surface.height() as u32);
surface.fill(self.background);
surface.present();
self.input_state.update(key, state);
if state {
let input = self.input_state.make_input(key);
// Non-window keys
#[allow(clippy::single_match)]
match (input.modifiers, input.key) {
(KeyModifiers::ALT, KeyboardKey::Enter) => {
// TODO do something with spawned child
Command::new("/bin/term").spawn().ok();
return Ok(());
}
_ => (),
}
// Window keys
if let Some((row, col)) = self.focused_frame {
let row_len = self.rows[row].frames.len();
match (input.modifiers, input.key) {
(KeyModifiers::ALT, KeyboardKey::Char(b'l')) => {
if col + 1 < row_len {
self.set_focused_frame(Some((row, col + 1)));
} else {
self.set_focused_frame(Some((row, 0)));
}
return Ok(());
}
(KeyModifiers::ALT, KeyboardKey::Char(b'h')) => {
if col > 0 {
self.set_focused_frame(Some((row, col - 1)));
} else if row_len != 0 {
self.set_focused_frame(Some((row, row_len - 1)));
}
return Ok(());
}
_ => (),
}
}
if let Some((_, window)) = self.get_focused_window() {
// Deliver event to the window
self.sender
.send_event(
EventData::WindowEvent(window.window_id, WindowEvent::KeyInput(input)),
&window.client_id,
)
.ok();
} else {
self.focused_frame = None;
}
}
Ok(())
}
fn get_window(&self, window_id: u32) -> Option<(&Frame, &Window<'a>)> {
let window = self.windows.get(&window_id)?;
for row in self.rows.iter() {
if let Some(f) = row
.frames
.iter()
.find(|f| f.window.map(|w| w == window_id).unwrap_or(false))
{
return Some((f, window));
}
}
// TODO Orphaned frame/window?
None
}
fn get_focused_window(&self) -> Option<(&Frame, &Window<'a>)> {
let (row, col) = self.focused_frame?;
let frame = &self.rows[row].frames[col];
let window = frame.window.and_then(|w| self.windows.get(&w))?;
Some((frame, window))
}
fn set_focused_frame(&mut self, focus: Option<(usize, usize)>) {
if self.focused_frame == focus {
return;
}
if let Some((_, old_window)) = self.get_focused_window() {
self.sender
.send_event(
EventData::WindowEvent(old_window.window_id, WindowEvent::FocusChanged(false)),
&old_window.client_id,
)
.ok();
}
self.focused_frame = focus;
if let Some((row, col)) = focus {
let Some(f_row) = self.rows.get(row) else {
return;
};
let Some(frame) = f_row.frames.get(col) else {
return;
};
let Some(window) = frame.window.and_then(|w| self.windows.get(&w)) else {
return;
};
self.sender
.send_event(
EventData::WindowEvent(window.window_id, WindowEvent::FocusChanged(true)),
&window.client_id,
)
.ok();
}
}
fn set_focused_window(&mut self, window_id: u32) -> Result<(), Error> {
// TODO this is ugly
let mut res = None;
for (i, row) in self.rows.iter().enumerate() {
let j = row
.frames
.iter()
.position(|f| f.window.map(|w| w == window_id).unwrap_or(false));
if let Some(j) = j {
res = Some((i, j));
}
}
self.set_focused_frame(res);
Ok(())
}
fn flush_dirty_frames(&mut self) {
for row in self.rows.iter() {
for frame in row.frames.iter() {
if !frame.dirty {
continue;
}
let Some(window) = frame.window.and_then(|w| self.windows.get_mut(&w)) else {
// TODO handle orphaned frame
continue;
};
let new_surface_data = unsafe {
std::slice::from_raw_parts_mut(
window.surface_mapping.as_mut_ptr() as *mut u32,
(frame.w * frame.h) as usize,
)
};
window.surface_data = new_surface_data;
self.sender
.send_event(
EventData::WindowEvent(
window.window_id,
WindowEvent::Resized {
width: frame.w,
height: frame.h,
},
),
&window.client_id,
)
.ok();
}
}
Command::new("/bin/colors-bar").spawn().ok();
}
fn handle_client_message(
&mut self,
client_id: PeerAddress,
message: ClientMessage,
) -> Result<(), Error> {
match message {
mut surface: DisplaySurface,
tx: &mut ServerSender,
message: FromClient,
) {
let peer = message.peer;
match message.message {
ClientMessage::ClientHello => {
debug_trace!("{:?}: ClientHello", client_id);
log::info!("{:?}: ClientHello", peer);
// Echo the ID back
self.last_client_id += 1;
let id = self.last_client_id;
self.client_map.insert(id, client_id.clone());
self.sender.send_event(EventData::ServerHello(id), &client_id)
self.client_map.insert(id, peer.clone());
tx.send_event(EventData::ServerHello(id), &peer);
}
ClientMessage::CreateWindow => {
debug_trace!("{:?}: CreateWindow", client_id);
let (info, shm_fd) = self.create_window(&client_id)?;
ClientMessage::CreateWindow(info) => {
log::info!("{:?}: CreateWindow", peer);
let (info, shm_fd) = self.create_window(&mut surface, tx, &peer, info).unwrap();
let window_id = info.window_id;
self.sender.send_event_with_file(
EventData::NewWindowInfo(info),
&shm_fd,
&client_id,
)?;
self.sender.send_event(
tx.send_event_with_file(EventData::NewWindowInfo(info), &shm_fd, &peer);
self.flush_dirty_frames(tx);
tx.send_event(
EventData::WindowEvent(window_id, WindowEvent::RedrawRequested),
&client_id,
)?;
Ok(())
&peer,
);
surface.present();
}
ClientMessage::BlitWindow {
window_id,
@ -511,98 +234,148 @@ impl<'a> Server<'a, '_> {
w,
h,
} => {
if let Some((frame, window)) = self.get_window(window_id) {
log::trace!("{:?}: BlitWindow", peer);
if let Some(window) = self.windows.get(&window_id) {
let Some(frame) = self.workspace.window_frame(window_id) else {
return;
};
let x = x.min(frame.w);
let y = y.min(frame.h);
let w = w.min(frame.w - x);
let h = h.min(frame.h - y);
let w = w.min(frame.w.saturating_sub(x));
let h = h.min(frame.h.saturating_sub(y));
if w == 0 || h == 0 {
// Invalid rectangle, skip it
return Ok(());
return;
}
self.display.blit_buffer(
let dst = Point(frame.x as _, frame.y as _);
let src = Point(x as _, y as _);
log::trace!("Blit {src:?} {w}x{h} -> {dst:?}");
surface.blit_buffer(
window.surface_data,
display::Point(frame.x as _, frame.y as _),
display::Point(x as _, y as _),
dst,
src,
w as _,
h as _,
frame.w as usize,
);
}
Ok(())
}
ClientMessage::DestroyWindow(window_id) => {
debug_trace!("{:?}: DestroyWindow {}", client_id, window_id);
self.remove_window(window_id);
Ok(())
ClientMessage::DestroyWindow(wid) => {
log::info!("{:?}: DestroyWindow", peer);
let window = self.windows.remove(&wid);
if window.is_some() {
self.workspace.remove_window(wid);
self.flush_dirty_frames(tx);
}
surface.present();
}
}
}
fn run_inner(mut self) -> Result<(), Error> {
loop {
match self.poll.wait(None, true)? {
Some((fd, Ok(_))) if fd == self.input.as_poll_fd() => {
let event = self.input.read_event()?;
self.handle_keyboard_event(event)?;
fn handle_keyboard_event(
&mut self,
mut surface: DisplaySurface,
tx: &mut ServerSender,
event: KeyboardKeyEvent,
) {
self.input_state.update(event.key, event.state);
if event.state {
let input = self.input_state.make_input(event.key);
// Non-window keys
#[allow(clippy::single_match)]
match (input.modifiers, input.key) {
(KeyModifiers::ALT, Key::Enter) => {
// TODO do something with spawned child
Command::new("/bin/term").spawn().ok();
return;
}
Some((fd, Ok(_))) if fd == self.receiver.as_raw_fd() => {
let (data, client_id) = self.receiver.receive_from()?;
self.handle_client_message(client_id, data)?;
}
Some((_, Ok(_))) => {
todo!()
}
Some((_, Err(error))) => {
return Err(Error::from(error));
}
None => (),
_ => (),
}
}
}
pub fn run(self) -> ExitCode {
match self.run_inner() {
Ok(_) => ExitCode::SUCCESS,
Err(error) => {
debug_trace!("colors server finished with an error: {}", error);
ExitCode::FAILURE
let focus = self.workspace.focused_window().and_then(|wid| {
let window = self.windows.get(&wid)?;
Some((wid, window))
});
match (input.modifiers, input.key) {
(KeyModifiers::ALT, Key::Char(b'l')) => {
self.move_focus(tx, Direction::Right);
return;
}
(KeyModifiers::ALT, Key::Char(b'h')) => {
self.move_focus(tx, Direction::Left);
return;
}
(KeyModifiers::ALT, Key::Char(b'j')) => {
self.move_focus(tx, Direction::Down);
return;
}
(KeyModifiers::ALT, Key::Char(b'k')) => {
self.move_focus(tx, Direction::Up);
return;
}
(KeyModifiers::ALT_SHIFT, Key::Char(b'l')) => {
self.move_window(&mut surface, tx, Direction::Right);
surface.present();
return;
}
(KeyModifiers::ALT_SHIFT, Key::Char(b'h')) => {
self.move_window(&mut surface, tx, Direction::Left);
surface.present();
return;
}
(KeyModifiers::ALT_SHIFT, Key::Char(b'j')) => {
self.move_window(&mut surface, tx, Direction::Down);
surface.present();
return;
}
(KeyModifiers::ALT_SHIFT, Key::Char(b'k')) => {
self.move_window(&mut surface, tx, Direction::Up);
surface.present();
return;
}
_ => (),
}
if let Some((wid, window)) = focus {
// Deliver event to the window
tx.send_event(
EventData::WindowEvent(wid, WindowEvent::KeyInput(input)),
&window.peer,
);
}
// // Window keys
// if let Some((row, col)) = self.focused_frame {
// let row_len = self.rows[row].frames.len();
// if let Some((_, window)) = self.get_focused_window() {
// } else {
// self.focused_frame = None;
// }
}
}
}
impl ServerSender {
pub fn send_event(&self, event: EventData, client_id: &PeerAddress) -> Result<(), Error> {
self.0.send_to(&ServerMessage::Event(event), client_id)?;
Ok(())
}
pub fn send_event_with_file<F: AsRawFd>(
&self,
event: EventData,
file: &F,
client_id: &PeerAddress,
) -> Result<(), Error> {
self.0
.send_with_file_to(&ServerMessage::Event(event), file, client_id)?;
Ok(())
}
}
fn main() -> ExitCode {
let args = env::args().skip(1).collect::<Vec<_>>();
let framebuffer = args.first().map_or("/dev/fb0", |s| s.as_str());
let path: &Path = framebuffer.as_ref();
logsink::setup_logging(false);
log::info!("Colors starting");
if !path.exists() {
debug_trace!("{framebuffer} does not exist, colors won't start");
return ExitCode::FAILURE;
let server = Server::new().unwrap();
let backend = Backend::new(server).unwrap();
log::info!("Run backend");
match backend.run() {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
let server = Server::new(framebuffer).unwrap();
server.run()
}

View File

@ -0,0 +1,66 @@
use std::os::fd::{AsRawFd, OwnedFd};
use libcolors::{
event::{EventData, KeyboardKeyEvent},
message::{ClientMessage, ServerMessage},
};
use uipc::{PeerAddress, Sender};
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
pub mod yggdrasil;
#[cfg(any(rust_analyzer, unix))]
pub mod unix;
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
pub use yggdrasil::{Backend, DisplaySurface, Error};
#[cfg(any(rust_analyzer, unix))]
pub use unix::{Backend, DisplaySurface, Error};
#[derive(Debug)]
pub struct FromClient {
pub peer: PeerAddress,
pub message: ClientMessage,
pub file: Option<OwnedFd>,
}
pub struct ServerSender {
sender: Sender<ServerMessage>,
}
#[derive(Debug)]
pub struct Point<T>(pub T, pub T);
pub trait WindowServer {
fn handle_initial(&mut self, surface: DisplaySurface);
fn handle_client_message(
&mut self,
surface: DisplaySurface,
tx: &mut ServerSender,
message: FromClient,
);
fn handle_keyboard_event(
&mut self,
surface: DisplaySurface,
tx: &mut ServerSender,
event: KeyboardKeyEvent,
);
}
impl ServerSender {
pub fn send_event(&self, event: EventData, client_id: &PeerAddress) {
self.sender
.send_to(&ServerMessage::Event(event), client_id)
.ok();
}
pub fn send_event_with_file<F: AsRawFd>(
&self,
event: EventData,
file: &F,
client_id: &PeerAddress,
) {
self.sender
.send_with_file_to(&ServerMessage::Event(event), file, client_id)
.ok();
}
}

View File

@ -0,0 +1,262 @@
use std::{
cmp, fs, io,
num::NonZero,
ops::{Deref, DerefMut},
rc::Rc,
};
use clap::Parser;
use libcolors::{
event::KeyboardKeyEvent,
input::Key,
message::{ClientMessage, ServerMessage},
};
use softbuffer::{Buffer, Rect, SoftBufferError};
use uipc::{Receiver, Sender};
use winit::{
dpi::LogicalSize,
error::{EventLoopError, OsError},
event::{ElementState, Event, WindowEvent},
event_loop::{EventLoop, EventLoopProxy},
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowAttributes},
};
use super::{FromClient, Point, ServerSender, WindowServer};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Event loop error: {0}")]
EventLoop(#[from] EventLoopError),
#[error("OS error: {0}")]
Os(#[from] OsError),
#[error("Display error: {0}")]
Softbuffer(#[from] SoftBufferError),
}
pub struct Backend<S: WindowServer> {
event_loop: EventLoop<FromClient>,
window: Rc<Window>,
tx: Sender<ServerMessage>,
server: S,
}
pub struct DisplaySurface<'a> {
inner: Buffer<'a, Rc<Window>, Rc<Window>>,
width: usize,
height: usize,
}
#[derive(Debug, Parser)]
struct Args {
#[clap(short, help = "Framebuffer width", default_value_t = 1024)]
width: u32,
#[clap(short, help = "Framebuffer height", default_value_t = 768)]
height: u32,
}
impl DisplaySurface<'_> {
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn blit_buffer(
mut self,
source: &[u32],
dst: Point<usize>,
src: Point<usize>,
w: usize,
h: usize,
src_stride: usize,
) {
let src_w = (self.width - src.0).min(w);
let dst_w = (self.width - dst.0).min(w);
let src_h = (self.height - src.1).min(h);
let dst_h = (self.height - dst.1).min(h);
let w = cmp::min(src_w, dst_w);
let h = cmp::min(src_h, dst_h);
for y in 0..h {
let dst_offset = (y + src.1 + dst.1) * self.width + dst.0 + src.0;
let src_offset = (y + src.1) * src_stride + src.0;
let src_chunk = &source[src_offset..src_offset + w];
let dst_chunk = &mut self[dst_offset..dst_offset + w];
dst_chunk.copy_from_slice(src_chunk);
}
self.inner
.present_with_damage(&[Rect {
x: dst.0 as _,
y: dst.1 as _,
width: NonZero::new(w as _).unwrap(),
height: NonZero::new(h as _).unwrap(),
}])
.unwrap();
}
pub fn present(self) {
self.inner.present().unwrap();
}
}
impl Deref for DisplaySurface<'_> {
type Target = [u32];
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl DerefMut for DisplaySurface<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
impl<S: WindowServer> Backend<S> {
pub fn new(server: S) -> Result<Self, Error> {
let args = Args::parse();
let event_loop = EventLoop::with_user_event().build()?;
let window = event_loop.create_window(
WindowAttributes::new()
.with_title("colors")
.with_resizable(false)
.with_inner_size(LogicalSize::new(args.width, args.height)),
)?;
fs::remove_file(libcolors::CHANNEL_NAME).ok();
let event_proxy = event_loop.create_proxy();
let listener = uipc::Channel::bind(libcolors::CHANNEL_NAME).unwrap();
let (tx, rx) = listener.split();
std::thread::spawn(move || {
Self::io_worker(rx, event_proxy);
});
Ok(Self {
event_loop,
window: Rc::new(window),
tx,
server,
})
}
fn io_worker(mut rx: Receiver<ClientMessage>, event_proxy: EventLoopProxy<FromClient>) {
loop {
let (message, file, peer) = rx.receive_with_file_from().unwrap();
let message = FromClient {
message,
file,
peer,
};
event_proxy.send_event(message).unwrap();
}
}
pub fn run(mut self) -> Result<(), Error> {
let mut tx = ServerSender { sender: self.tx };
let context = softbuffer::Context::new(self.window.clone())?;
let mut surface = softbuffer::Surface::new(&context, self.window.clone())?;
let size = self.window.inner_size();
surface.resize(NonZero::new(size.width).unwrap(), NonZero::new(size.height).unwrap()).unwrap();
self.server.handle_initial(DisplaySurface {
inner: surface.buffer_mut().unwrap(),
width: size.width as _,
height: size.height as _,
});
self.event_loop.run(|event, el| match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => {
el.exit();
}
WindowEvent::Resized(size) => {
let width = NonZero::new(size.width).unwrap();
let height = NonZero::new(size.height).unwrap();
surface.resize(width, height).unwrap();
surface.buffer_mut().unwrap().present().unwrap();
}
WindowEvent::KeyboardInput { event, .. } => {
if let Some(event) = convert_key_event(event) {
let size = self.window.inner_size();
let display_surface = DisplaySurface {
inner: surface.buffer_mut().unwrap(),
width: size.width as usize,
height: size.height as usize,
};
self.server
.handle_keyboard_event(display_surface, &mut tx, event);
}
}
_ => (),
},
Event::UserEvent(event) => {
let size = self.window.inner_size();
let display_surface = DisplaySurface {
inner: surface.buffer_mut().unwrap(),
width: size.width as usize,
height: size.height as usize,
};
self.server
.handle_client_message(display_surface, &mut tx, event);
}
_ => (),
})?;
Ok(())
}
}
fn convert_key_event(raw: winit::event::KeyEvent) -> Option<KeyboardKeyEvent> {
let PhysicalKey::Code(code) = raw.physical_key else {
return None;
};
let key = match code {
KeyCode::ShiftLeft => Key::LShift,
KeyCode::ShiftRight => Key::RShift,
KeyCode::ControlLeft => Key::LControl,
KeyCode::ControlRight => Key::RControl,
KeyCode::AltLeft => Key::LAlt,
KeyCode::AltRight => Key::RAlt,
KeyCode::KeyQ => Key::Char(b'q'),
KeyCode::KeyW => Key::Char(b'w'),
KeyCode::KeyE => Key::Char(b'e'),
KeyCode::KeyR => Key::Char(b'r'),
KeyCode::KeyT => Key::Char(b't'),
KeyCode::KeyY => Key::Char(b'y'),
KeyCode::KeyU => Key::Char(b'u'),
KeyCode::KeyI => Key::Char(b'i'),
KeyCode::KeyO => Key::Char(b'o'),
KeyCode::KeyP => Key::Char(b'p'),
KeyCode::KeyA => Key::Char(b'a'),
KeyCode::KeyS => Key::Char(b's'),
KeyCode::KeyD => Key::Char(b'd'),
KeyCode::KeyF => Key::Char(b'f'),
KeyCode::KeyG => Key::Char(b'g'),
KeyCode::KeyH => Key::Char(b'h'),
KeyCode::KeyJ => Key::Char(b'j'),
KeyCode::KeyK => Key::Char(b'k'),
KeyCode::KeyL => Key::Char(b'l'),
KeyCode::KeyZ => Key::Char(b'z'),
KeyCode::KeyX => Key::Char(b'x'),
KeyCode::KeyC => Key::Char(b'c'),
KeyCode::KeyV => Key::Char(b'v'),
KeyCode::KeyB => Key::Char(b'b'),
KeyCode::KeyN => Key::Char(b'n'),
KeyCode::KeyM => Key::Char(b'm'),
_ => return None,
};
let state = match raw.state {
ElementState::Pressed => true,
ElementState::Released => false,
};
Some(KeyboardKeyEvent { key, state })
}

View File

@ -0,0 +1,278 @@
use std::{
cmp,
fs::{File, OpenOptions},
io::{self, Read},
ops::{Deref, DerefMut},
os::fd::{AsRawFd, RawFd},
path::{Path, PathBuf},
};
use clap::Parser;
use cross::{io::Poll, mem::FileMapping};
use libcolors::{event::KeyboardKeyEvent, input::Key, message::ClientMessage};
use runtime::rt::io::device;
use uipc::{Channel, Receiver};
use yggdrasil_abi::io::KeyboardKey;
use crate::sys::FromClient;
use super::{Point, ServerSender, WindowServer};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("IPC error: {0}")]
Ipc(#[from] uipc::Error),
}
pub struct Backend<'a, S: WindowServer> {
poll: Poll,
rx: Receiver<ClientMessage>,
tx: ServerSender,
display: Display<'a>,
input: KeyboardInput,
server: S,
}
struct Display<'a> {
#[allow(unused)]
mapping: FileMapping,
data: &'a mut [u32],
width: usize,
height: usize,
// TODO use those
_stride: usize,
_size: usize,
}
struct KeyboardInput(File);
pub struct DisplaySurface<'a, 'd> {
display: &'a mut Display<'d>,
}
impl DisplaySurface<'_, '_> {
pub fn width(&self) -> usize {
self.display.width
}
pub fn height(&self) -> usize {
self.display.height
}
pub fn blit_buffer(
mut self,
source: &[u32],
dst: Point<usize>,
src: Point<usize>,
w: usize,
h: usize,
src_stride: usize,
) {
let src_w = (self.display.width - src.0).min(w);
let dst_w = (self.display.width - dst.0).min(w);
let src_h = (self.display.height - src.1).min(h);
let dst_h = (self.display.height - dst.1).min(h);
let w = cmp::min(src_w, dst_w);
let h = cmp::min(src_h, dst_h);
for y in 0..h {
let dst_offset = (y + src.1 + dst.1) * self.display.width + dst.0 + src.0;
let src_offset = (y + src.1) * src_stride + src.0;
let src_chunk = &source[src_offset..src_offset + w];
let dst_chunk = &mut self[dst_offset..dst_offset + w];
dst_chunk.copy_from_slice(src_chunk);
}
self.present();
}
pub fn present(self) {
self.display.flush();
}
}
impl Deref for DisplaySurface<'_, '_> {
type Target = [u32];
fn deref(&self) -> &Self::Target {
self.display.data
}
}
impl DerefMut for DisplaySurface<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.display.data
}
}
#[derive(Debug, Parser)]
struct Args {
framebuffer: PathBuf,
}
impl<S: WindowServer> Backend<'_, S> {
pub fn new(server: S) -> Result<Self, Error> {
let args = Args::parse();
let channel = Channel::bind(libcolors::CHANNEL_NAME)?;
let (tx, rx) = channel.split();
let tx = ServerSender { sender: tx };
let input = KeyboardInput::open()?;
let mut poll = Poll::new()?;
poll.add(&rx)?;
poll.add(&input)?;
let display = Display::open(args.framebuffer)?;
Ok(Self {
poll,
tx,
rx,
display,
input,
server,
})
}
pub fn run(mut self) -> Result<(), Error> {
self.server.handle_initial(DisplaySurface {
display: &mut self.display
});
loop {
let fd = self.poll.wait(None)?.unwrap();
if fd == self.rx.as_raw_fd() {
let (message, file, peer) = self.rx.receive_with_file_from()?;
let event = FromClient {
message,
file,
peer,
};
let surface = DisplaySurface {
display: &mut self.display,
};
self.server
.handle_client_message(surface, &mut self.tx, event);
} else if fd == self.input.as_raw_fd() {
let event = self.input.read_event()?;
if let Some(event) = convert_key_event(event) {
let surface = DisplaySurface {
display: &mut self.display,
};
self.server.handle_keyboard_event(surface, &mut self.tx, event);
}
}
}
}
}
impl Display<'_> {
pub fn open(framebuffer: impl AsRef<Path>) -> Result<Self, Error> {
let framebuffer = framebuffer.as_ref();
let file = OpenOptions::new().open(framebuffer)?;
let mut buffer = [0; 128];
device::device_request::<device::AcquireDevice>(file.as_raw_fd(), &mut buffer, &())
.map_err(std::io::Error::from)?;
let framebuffer = device::device_request::<device::GetActiveFramebuffer>(
file.as_raw_fd(),
&mut buffer,
&(),
)
.map_err(std::io::Error::from)?;
let width = framebuffer.width as usize;
let height = framebuffer.height as usize;
let mut mapping = FileMapping::map(file, framebuffer.size)?;
let data = unsafe {
std::slice::from_raw_parts_mut(mapping.as_mut_ptr() as *mut u32, width * height)
};
Ok(Self {
mapping,
data,
width,
height,
_stride: framebuffer.stride / size_of::<u32>(),
_size: framebuffer.size / size_of::<u32>(),
})
}
pub fn flush(&mut self) {
let mut buffer = [0; 0];
device::device_request::<device::FlushDisplay>(self.mapping.as_raw_fd(), &mut buffer, &())
.ok();
}
}
impl KeyboardInput {
pub fn open() -> Result<Self, Error> {
let file = File::open("/dev/kbd")?;
Ok(Self(file))
}
pub fn read_event(&mut self) -> Result<yggdrasil_abi::io::KeyboardKeyEvent, Error> {
let mut buf = [0; 4];
let len = self.0.read(&mut buf)?;
if len == 4 {
Ok(yggdrasil_abi::io::KeyboardKeyEvent::from_bytes(buf))
} else {
todo!()
}
}
}
impl AsRawFd for KeyboardInput {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
fn convert_key_event(raw: yggdrasil_abi::io::KeyboardKeyEvent) -> Option<KeyboardKeyEvent> {
let (key, state) = raw.split();
let key = match key {
KeyboardKey::Char(ch) => Key::Char(ch),
KeyboardKey::Backspace => Key::Backspace,
KeyboardKey::Enter => Key::Enter,
KeyboardKey::Home => Key::Home,
KeyboardKey::End => Key::End,
KeyboardKey::PageUp => Key::PageUp,
KeyboardKey::PageDown => Key::PageDown,
KeyboardKey::Escape => Key::Escape,
KeyboardKey::Up => Key::Up,
KeyboardKey::Down => Key::Down,
KeyboardKey::Left => Key::Left,
KeyboardKey::Right => Key::Right,
KeyboardKey::LAlt => Key::LAlt,
KeyboardKey::RAlt => Key::RAlt,
KeyboardKey::LShift => Key::LShift,
KeyboardKey::RShift => Key::RShift,
KeyboardKey::LControl => Key::LControl,
KeyboardKey::RControl => Key::RControl,
KeyboardKey::Insert => return None,
KeyboardKey::Delete => return None,
KeyboardKey::Unknown => return None,
KeyboardKey::CapsLock => return None,
KeyboardKey::Tab => return None,
KeyboardKey::F(_) => return None,
};
Some(KeyboardKeyEvent {
key,
state
})
}

View File

@ -0,0 +1,23 @@
use cross::mem::FileMapping;
use uipc::PeerAddress;
pub struct Window<'d> {
pub wid: u32,
pub peer: PeerAddress,
pub surface_mapping: FileMapping,
pub surface_data: &'d [u32],
}
impl Window<'_> {
pub fn resize(&mut self, w: u32, h: u32) {
let new_surface_data = unsafe {
std::slice::from_raw_parts_mut(
self.surface_mapping.as_mut_ptr() as *mut u32,
(w * h) as usize,
)
};
self.surface_data = new_surface_data;
}
}

View File

@ -0,0 +1,703 @@
use std::{cell::Cell, collections::HashMap, hash::Hash, iter};
pub type NodeId = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rect {
pub x: u32,
pub y: u32,
pub w: u32,
pub h: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Orientation {
Horizontal,
Vertical,
}
pub enum NodeContent<T> {
Container(ContainerNode),
Window(WindowNode<T>),
}
pub struct NodeLayout {
rect: Cell<(Option<Rect>, bool)>,
}
pub struct Node<T> {
parent: Option<NodeId>,
layout: NodeLayout,
content: NodeContent<T>,
}
pub struct ContainerNode {
children: Vec<NodeId>,
orientation: Orientation,
}
pub struct WindowNode<T> {
pub wid: T,
}
pub struct Reservation<T> {
layout: NodeLayout,
size: u32,
wid: T,
}
pub struct Workspace<T> {
nodes: HashMap<NodeId, Node<T>>,
wid_to_nid: HashMap<T, NodeId>,
last_node_id: NodeId,
root: NodeId,
focus: Option<NodeId>,
margin: u32,
spacing: u32,
reservations_top: Vec<Reservation<T>>,
reservation_top: u32,
width: u32,
height: u32,
}
impl<T> Node<T> {
pub fn as_window(&self) -> Option<&WindowNode<T>> {
match &self.content {
NodeContent::Window(window) => Some(window),
_ => None,
}
}
pub fn as_container(&self) -> Option<&ContainerNode> {
match &self.content {
NodeContent::Container(container) => Some(container),
_ => None,
}
}
pub fn as_container_mut(&mut self) -> Option<&mut ContainerNode> {
match &mut self.content {
NodeContent::Container(container) => Some(container),
_ => None,
}
}
}
impl NodeLayout {
pub fn set(&self, new: Rect) {
let (old, _) = self.rect.get();
let dirty = old.map_or(true, |old| old != new);
self.rect.set((Some(new), dirty));
}
pub fn get(&self) -> Option<Rect> {
let (value, _) = self.rect.get();
value
}
pub fn clear(&self) {
self.rect.set((None, false));
}
pub fn clear_dirty(&self) -> bool {
let (value, dirty) = self.rect.get();
self.rect.set((value, false));
dirty && value.is_some()
}
}
impl<T: Eq + Hash + Copy> Workspace<T> {
pub fn new(width: u32, height: u32) -> Self {
let root = Node {
parent: None,
layout: NodeLayout {
rect: Cell::new((None, false)),
},
content: NodeContent::Container(ContainerNode {
children: vec![],
orientation: Orientation::Horizontal,
}),
};
let nodes = HashMap::from_iter([(0, root)]);
let mut this = Self {
nodes,
wid_to_nid: HashMap::new(),
last_node_id: 0,
margin: 4,
spacing: 4,
root: 0,
focus: None,
reservation_top: 0,
reservations_top: Vec::new(),
width,
height,
};
this.update_layout();
this
}
pub fn add_reservation(&mut self, wid: T, height: u32) {
self.reservations_top.push(Reservation {
layout: NodeLayout {
rect: Cell::new((None, false)),
},
size: height,
wid,
});
self.update_layout();
}
pub fn all_windows(&self) -> impl Iterator<Item = (T, &NodeLayout)> + '_ {
let nodes_windows = self.nodes.iter().filter_map(|(_, node)| {
let window = node.as_window()?;
Some((window.wid, &node.layout))
// Some(node.as_window()?.wid)
});
let reservation_windows = self
.reservations_top
.iter()
.map(|res| (res.wid, &res.layout));
iter::chain(reservation_windows, nodes_windows)
}
pub fn dirty_windows(&self) -> impl Iterator<Item = (T, Rect)> + '_ {
self.all_windows().filter_map(|(wid, layout)| {
if !layout.clear_dirty() {
return None;
}
let rect = layout.get()?;
Some((wid, rect))
})
}
pub fn window_towards_wrap(&self, direction: Direction) -> Option<T> {
let current_nid = self.focus?;
let current_node = self.nodes.get(&current_nid)?;
let parent_nid = current_node.parent?;
let parent_node = self.nodes.get(&parent_nid)?;
let parents_parent_nid = parent_node.parent;
let parent_container = parent_node.as_container()?;
let (orientation, delta) = direction.split();
let nid = if orientation == parent_container.orientation {
log::info!("Within parent {delta}, {:?}", parent_container.orientation);
let position_in_parent = parent_container
.children
.iter()
.position(|&n| n == current_nid)?;
if delta > 0 {
if position_in_parent < parent_container.children.len() - 1 {
parent_container.children[position_in_parent + 1]
} else {
parent_container.children[0]
}
} else {
if position_in_parent > 0 {
parent_container.children[position_in_parent - 1]
} else {
parent_container.children[parent_container.children.len() - 1]
}
}
} else if let Some(parents_parent_nid) = parents_parent_nid {
let parents_parent_node = self.nodes.get(&parents_parent_nid)?;
let parents_parent_container = parents_parent_node.as_container()?;
assert_eq!(parents_parent_container.orientation, orientation);
let position_in_parent = parents_parent_container
.children
.iter()
.position(|&n| n == parent_nid)?;
log::info!(
"Within parent's parent {delta}, {:?}",
parents_parent_container.orientation
);
log::info!("position_in_parent = {position_in_parent}");
if delta > 0 {
if position_in_parent < parent_container.children.len() - 1 {
parents_parent_container.children[position_in_parent + 1]
} else {
parents_parent_container.children[0]
}
} else {
if position_in_parent > 0 {
parents_parent_container.children[position_in_parent - 1]
} else {
parents_parent_container.children[parent_container.children.len() - 1]
}
}
} else {
return None;
};
self.first_window_in(nid)
}
pub fn first_window_in(&self, nid: NodeId) -> Option<T> {
let node = self.nodes.get(&nid)?;
match &node.content {
NodeContent::Window(window) => Some(window.wid),
NodeContent::Container(container) => {
let first = *container.children.first()?;
self.first_window_in(first)
}
}
}
pub fn move_window(&mut self, direction: Direction) -> bool
where
T: core::fmt::Debug,
{
if let Some(container_nid) = self.move_window_inner(direction) {
self.invalidate_layout(container_nid);
self.fixup_containers(self.root);
self.dump(self.root, 0);
true
} else {
false
}
}
fn dump(&self, nid: NodeId, depth: usize)
where
T: core::fmt::Debug,
{
let Some(node) = self.nodes.get(&nid) else {
return;
};
let rect = node.layout.get();
match &node.content {
NodeContent::Window(window) => {
log::info!(
"{:depth$}Window #{:?} {rect:?}",
"",
window.wid,
depth = depth * 2
);
}
NodeContent::Container(container) => {
log::info!("{:depth$}Container {rect:?} {{", "", depth = depth * 2);
for &child_nid in container.children.iter() {
self.dump(child_nid, depth + 1);
}
log::info!("{:depth$}}}", "", depth = depth * 2);
}
}
}
// pub fn walk_windows<F: FnMut(T)>(&self, mut mapper: F) {
// self.walk_windows_inner(&mut mapper, self.root)
// }
// fn walk_windows_inner<F: FnMut(T)>(&self, mapper: &mut F, nid: NodeId) {
// // let Some(node) = self.nodes.get(&nid) else {
// // return;
// // };
// // match &node.content {
// // NodeContent::Container(container) => {
// // for &child_nid in container.children.iter() {
// // self.walk_windows_inner(mapper, child_nid);
// // }
// // }
// // NodeContent::Window(window) => {
// // mapper(window.wid);
// // }
// // }
// }
fn invalidate_layout(&mut self, nid: NodeId) {
for reservation in self.reservations_top.iter() {
reservation.layout.clear();
}
self.invalidate_layout_inner(nid);
self.update_layout();
}
fn invalidate_layout_inner(&self, nid: NodeId) {
let Some(node) = self.nodes.get(&nid) else {
return;
};
node.layout.clear();
match &node.content {
NodeContent::Container(container) => {
for &child_nid in container.children.iter() {
self.invalidate_layout_inner(child_nid);
}
}
_ => (),
}
}
fn fixup_containers(&mut self, nid: NodeId) {
let Some(node) = self.nodes.get_mut(&nid) else {
return;
};
if let NodeContent::Container(container) = &mut node.content {
if container.children.is_empty() {
let Some(parent_nid) = node.parent else {
return;
};
// Remove the empty container
if let Some(parent_container) = self
.nodes
.get_mut(&parent_nid)
.and_then(Node::as_container_mut)
{
parent_container.children.retain(|&n| n != nid);
self.nodes.remove(&nid);
self.invalidate_layout(parent_nid);
}
} else if container.children.len() == 1 {
// Remove the empty container and reparent its only child
if let Some(parent_nid) = node.parent {
let child_nid = container.children.remove(0);
let [Some(child), Some(parent)] =
self.nodes.get_many_mut([&child_nid, &parent_nid])
else {
return;
};
child.parent = Some(parent_nid);
if let Some(parent_container) = parent.as_container_mut() {
parent_container.children.retain(|&n| n != nid);
parent_container.children.push(child_nid);
}
self.invalidate_layout(parent_nid);
}
} else {
// TODO borrowing stuff
let children = container.children.clone();
for child_nid in children {
self.fixup_containers(child_nid);
}
}
}
}
fn move_window_inner(&mut self, direction: Direction) -> Option<NodeId> {
let current_nid = self.focus?;
let current_node = self.nodes.get(&current_nid)?;
let parent_nid = current_node.parent?;
let parent_node = self.nodes.get_mut(&parent_nid)?;
let parents_parent_nid = parent_node.parent;
let parent_container = parent_node.as_container_mut()?;
let (orientation, delta) = direction.split();
if parent_container.orientation == orientation {
let position_in_parent = parent_container
.children
.iter()
.position(|&n| n == current_nid)?;
// TODO check if this item is also a container
if delta < 0 && position_in_parent > 0 {
parent_container
.children
.swap(position_in_parent - 1, position_in_parent);
Some(parent_nid)
} else if delta > 0 && position_in_parent < parent_container.children.len() - 1 {
parent_container
.children
.swap(position_in_parent, position_in_parent + 1);
Some(parent_nid)
} else {
None
}
} else if let Some(parents_parent_nid) = parents_parent_nid {
// Go to parent's parent
let parents_parent_node = self.nodes.get_mut(&parents_parent_nid)?;
let parents_parent_container = parents_parent_node.as_container_mut()?;
assert_eq!(parents_parent_container.orientation, orientation);
todo!()
} else {
self.last_node_id += 1;
let new_root_nid = self.last_node_id;
// Remove child from parent
parent_container.children.retain(|&n| n != current_nid);
self.nodes.get_mut(&current_nid)?.parent = Some(new_root_nid);
self.nodes.get_mut(&self.root)?.parent = Some(new_root_nid);
let children = if delta > 0 {
vec![self.root, current_nid]
} else {
vec![current_nid, self.root]
};
// Create a new root
let new_root = Node {
parent: None,
layout: NodeLayout {
rect: Cell::new((None, false)),
},
content: NodeContent::Container(ContainerNode {
children,
orientation,
}),
};
self.root = new_root_nid;
self.nodes.insert(new_root_nid, new_root);
Some(self.root)
}
}
pub fn focus_window(&mut self, wid: T) -> (Option<T>, Option<NodeId>) {
let old = self.focus.take().and_then(|nid| self.wid(nid));
let new = self.wid_to_nid.get(&wid).copied();
self.focus = new;
(old, new)
}
pub fn focused_window(&self) -> Option<T> {
let nid = self.focus?;
Some(self.nodes.get(&nid)?.as_window()?.wid)
}
pub fn window(&self, wid: T) -> Option<&Node<T>> {
let nid = *self.wid_to_nid.get(&wid)?;
self.nodes.get(&nid)
}
pub fn reservation(&self, wid: T) -> Option<&Reservation<T>> {
self.reservations_top.iter().find(|r| r.wid == wid)
}
pub fn window_frame(&self, wid: T) -> Option<Rect> {
if let Some(reservation) = self.reservation(wid) {
reservation.layout.get()
} else {
let node = self.window(wid)?;
node.layout.get()
}
}
pub fn resize(&mut self, w: u32, h: u32) {
self.width = w;
self.height = h;
self.update_layout();
}
fn update_reservation_layout(&mut self) -> u32 {
let mut res_y = 0;
for reservation in self.reservations_top.iter() {
reservation.layout.set(Rect {
x: 0,
y: res_y,
w: self.width,
h: reservation.size,
});
res_y += reservation.size;
}
self.reservation_top = res_y;
res_y
}
pub fn update_layout(&mut self) {
let res_y = self.update_reservation_layout();
self.update_layout_for(
self.root,
Rect {
x: self.margin,
y: self.margin + res_y,
w: self.width - self.margin * 2,
h: self.height - self.margin * 2 - res_y,
},
);
}
pub fn create_window(&mut self, wid: T) -> bool {
if self.create_window_in(self.root, wid) {
for res in self.reservations_top.iter() {
res.layout.clear();
}
self.update_reservation_layout();
true
} else {
false
}
}
pub fn remove_window(&mut self, wid: T) -> bool {
let Some(nid) = self.wid_to_nid.remove(&wid) else {
return false;
};
if self.remove_inner(nid) {
self.fixup_containers(self.root);
self.update_layout();
true
} else {
false
}
}
fn create_window_in(&mut self, container_nid: NodeId, wid: T) -> bool {
self.last_node_id += 1;
let nid = self.last_node_id;
self.nodes.insert(
nid,
Node {
parent: Some(container_nid),
layout: NodeLayout {
rect: Cell::new((None, false)),
},
content: NodeContent::Window(WindowNode { wid }),
},
);
self.wid_to_nid.insert(wid, nid);
if !self.add_child_to(container_nid, nid) {
self.wid_to_nid.remove(&wid);
self.nodes.remove(&nid);
return false;
}
self.update_layout();
true
}
fn wid(&self, nid: NodeId) -> Option<T> {
self.nodes.get(&nid).and_then(|node| match &node.content {
NodeContent::Window(window) => Some(window.wid),
_ => None,
})
}
fn update_layout_for(&self, node_id: NodeId, rect: Rect) {
let Some(node) = self.nodes.get(&node_id) else {
log::warn!("update_layout_for: no node {node_id}");
return;
};
node.layout.set(rect);
match &node.content {
NodeContent::Container(container) => {
if container.children.is_empty() {
return;
}
match container.orientation {
Orientation::Vertical => {
let hstep = (rect.h - self.spacing * container.children.len() as u32)
/ container.children.len() as u32;
let ystep = hstep + self.spacing;
for (i, &child_nid) in container.children.iter().enumerate() {
let i = i as u32;
let child_rect = Rect {
x: rect.x,
y: rect.y + i * ystep,
w: rect.w,
h: hstep,
};
log::info!("Frame #{child_nid} size: {child_rect:?}");
self.update_layout_for(child_nid, child_rect);
}
}
Orientation::Horizontal => {
let wstep = (rect.w - self.spacing * container.children.len() as u32)
/ container.children.len() as u32;
let xstep = wstep + self.spacing;
for (i, &child_nid) in container.children.iter().enumerate() {
let i = i as u32;
let child_rect = Rect {
x: rect.x + i * xstep,
y: rect.y,
w: wstep,
h: rect.h,
};
log::info!("Frame #{child_nid} size: {child_rect:?}");
self.update_layout_for(child_nid, child_rect);
}
}
}
}
_ => (),
}
}
fn remove_inner(&mut self, node_id: NodeId) -> bool {
if node_id == self.root {
return false;
}
let Some(node) = self.nodes.remove(&node_id) else {
return false;
};
let container_nid = unsafe { node.parent.unwrap_unchecked() };
let Some(container_node) = self.nodes.get_mut(&container_nid) else {
return false;
};
let NodeContent::Container(container) = &mut container_node.content else {
return false;
};
container_node.layout.clear();
container.children.retain(|&c| c != node_id);
// Only root container can be empty
debug_assert!(!(container_node.parent.is_some() && container.children.is_empty()));
if container.children.len() == 1 && container_node.parent.is_some() {
// Lift the remaining node to the parent container
todo!()
}
true
}
fn add_child_to(&mut self, container_nid: NodeId, child_nid: NodeId) -> bool {
let [Some(container_node), Some(child_node)] =
self.nodes.get_many_mut([&container_nid, &child_nid])
else {
return false;
};
let NodeContent::Container(container) = &mut container_node.content else {
return false;
};
container_node.layout.clear();
child_node.layout.clear();
child_node.parent = Some(container_nid);
container.children.push(child_nid);
true
}
}
impl Direction {
pub fn split(&self) -> (Orientation, isize) {
match self {
Self::Left => (Orientation::Horizontal, -1),
Self::Right => (Orientation::Horizontal, 1),
Self::Up => (Orientation::Vertical, -1),
Self::Down => (Orientation::Vertical, 1),
}
}
}

View File

@ -5,10 +5,13 @@ edition = "2021"
[dependencies]
yggdrasil-rt.workspace = true
logsink.workspace = true
thiserror.workspace = true
bytemuck.workspace = true
log.workspace = true
elf = "0.7.4"
bytemuck = "1.19.0"
cfg-if = "1.0.0"
rustc-demangle = { version = "0.1.24" }

View File

@ -2,7 +2,7 @@ use std::io;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error")]
#[error("{0}")]
Io(#[from] io::Error),
#[error("ELF parse error")]
ElfParse(#[from] elf::ParseError),

View File

@ -73,7 +73,7 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
for module in layout.segments.iter() {
auxv.push(AuxValue {
tag: auxv::TLS_MODULE_ID,
val: module.object_id as u64 + 1,
val: module.module_id as u64,
});
auxv.push(AuxValue {
tag: auxv::TLS_MODULE_OFFSET,
@ -98,7 +98,7 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
// Setup arguments and enter the main object
let entry = objects.root.entry().ok_or(Error::NoEntrypoint)?;
debug_trace!("entry = {:p}", entry);
log::info!("entry = {:p}", entry);
let argument = env::build_argument(args, &auxv)?;
unsafe { enter(entry, argument) };
@ -134,6 +134,7 @@ unsafe fn enter(entry: extern "C" fn(usize), argument: usize) -> ! {
}
fn main() -> ExitCode {
logsink::setup_logging(false);
let args: Vec<String> = std::env::args().skip(1).collect();
if args.is_empty() {

View File

@ -187,8 +187,8 @@ impl Object {
return Ok(());
}
debug_trace!("Load {:?}", self.path);
debug_trace!("Image range: {:#x?}", self.vma_start..self.vma_end);
log::info!("Load {:?}", self.path);
log::info!("Image range: {:#x?}", self.vma_start..self.vma_end);
// TODO segment granularity mappings
let mapping_size = self.vma_end - self.vma_start;
@ -200,7 +200,7 @@ impl Object {
ElfType::Static => todo!(),
};
debug_trace!(
log::info!(
"Actual range: {:#x?} ({:+#x})",
mapping.range(),
mapping.base() as i64 - self.vma_start as i64
@ -228,14 +228,14 @@ impl Object {
}
if let (Some(ptr), Some(size)) = (dt_preinit_array, dt_preinit_array_sz) {
debug_trace!("{:?}: DT_PREINIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
log::debug!("{:?}: DT_PREINIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
let ptr = ptr as i64 + mapping.base() as i64 - self.vma_start as i64;
let ptr = ptr as usize;
self.pre_init_array = Some(ptr..ptr + size as usize);
}
if let (Some(ptr), Some(size)) = (dt_init_array, dt_init_array_sz) {
// This address is subject to relocation
debug_trace!("{:?}: DT_INIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
log::debug!("{:?}: DT_INIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
let ptr = ptr as i64 + mapping.base() as i64 - self.vma_start as i64;
let ptr = ptr as usize;
self.init_array = Some(ptr..ptr + size as usize);
@ -324,7 +324,7 @@ impl Object {
}
let func = mem::transmute::<_, Constructor>(func);
debug_trace!("ld: call constructor {func:p}");
log::debug!("ld: call constructor {func:p}");
func();
}

View File

@ -72,14 +72,14 @@ impl Relocation for Rela {
R_AARCH64_TLS_TPREL => {
let layout = state.tls_layout.as_ref().unwrap();
let offset = layout.offset(tls.module_id, tls.offset).unwrap();
debug_trace!("{}@tprel -> {}", name, offset);
log::debug!("{}@tprel -> {}", name, offset);
Ok(Some(RelaValue::QWord(offset as _)))
}
_ => todo!("Unsupported relocation against TLS symbol: {}", self.r_type),
},
ResolvedSymbol::Null(object_id) => match self.r_type {
// See make_tlsdesc_relocation()
R_AARCH64_TLSDESC => Ok(Some(make_tlsdesc_relocation(false, state, object_id, 0))),
R_AARCH64_TLSDESC => Ok(Some(make_tlsdesc_relocation(false, state, object_id + 1, 0))),
// B + A
R_AARCH64_RELATIVE => Ok(Some(RelaValue::QWord(load_base as i64 + self.r_addend))),
_ => todo!(

View File

@ -41,7 +41,7 @@ impl Relocation for Rel {
// Offset from TLS start
let offset = tls_layout.offset(tls.module_id, tls.offset).unwrap();
let offset_from_tp = -((tls_layout.tp_offset - offset) as i32);
debug_trace!("{}@tpoff -> {}", name, offset_from_tp);
log::debug!("{}@tpoff -> {}", name, offset_from_tp);
Ok(Some(RelValue::DWordNoAddend(offset_from_tp)))
}

View File

@ -42,10 +42,11 @@ impl Relocation for Rela {
R_X86_64_TPOFF64 => {
// Need to extract fixed global offset
let tls_layout = state.tls_layout.as_ref().unwrap();
log::info!("TPOFF64: module_id={}", tls.module_id);
// Offset from TLS start
let offset = tls_layout.offset(tls.module_id, tls.offset).unwrap();
let offset_from_tp = -((tls_layout.tp_offset - offset) as i64);
debug_trace!("{}@tpoff -> {}", name, offset_from_tp);
log::debug!("{}@tpoff -> {}", name, offset_from_tp);
Ok(Some(RelaValue::QWord(offset_from_tp)))
}
@ -54,7 +55,7 @@ impl Relocation for Rela {
},
&ResolvedSymbol::Null(object_id) => match self.r_type {
// TLS module ID
R_X86_64_DTPMOD64 => Ok(Some(RelaValue::QWord(object_id as _))),
R_X86_64_DTPMOD64 => Ok(Some(RelaValue::QWord((object_id + 1) as _))),
// B + A
R_X86_64_RELATIVE => Ok(Some(RelaValue::QWord(load_base as i64 + self.r_addend))),

View File

@ -107,19 +107,19 @@ impl State {
*export = ExportedTlsSymbol {
source,
offset,
module_id: object_id,
module_id: object_id + 1,
weak,
};
}
Some(_) => (),
None => {
debug_trace!("{:?}: TLS {:?} -> {}:{:#x}", source, sym.name, object_id, offset);
log::debug!("{:?}: TLS {:?} -> {}:{:#x}", source, sym.name, object_id + 1, offset);
self.tls_symbol_table.insert(
sym.name.clone(),
ExportedTlsSymbol {
source,
offset,
module_id: object_id,
module_id: object_id + 1,
weak,
},
);

View File

@ -35,7 +35,7 @@ pub struct TlsLayout {
pub struct TlsSegment {
pub offset: usize,
pub size: usize,
pub object_id: u32,
pub module_id: u32,
}
pub struct TlsImage {
@ -47,8 +47,8 @@ pub struct TlsImage {
}
impl TlsLayout {
pub fn offset(&self, object_id: u32, offset: usize) -> Option<usize> {
let segment = self.segments.iter().find(|seg| seg.object_id == object_id)?;
pub fn offset(&self, module_id: u32, offset: usize) -> Option<usize> {
let segment = self.segments.iter().find(|seg| seg.module_id == module_id)?;
Some(segment.offset + offset)
}
}
@ -67,16 +67,17 @@ pub fn build_tls_image(
let tp_offset = layout.tp_offset;
for segment in layout.segments.iter() {
debug_trace!(
log::info!(
"Load TLS segment: tlsoffset_i={:#x?} (-{:#x}), module_id={}",
segment.offset..segment.offset + segment.size,
layout.prefix_len,
segment.object_id
segment.module_id
);
let object = match segment.object_id {
0 => &mut *root,
_ => libraries.get_mut(&segment.object_id).unwrap(),
let object = match segment.module_id {
0 => unreachable!(),
1 => &mut *root,
_ => libraries.get_mut(&(segment.module_id - 1)).unwrap(),
};
object.tls_offset = Some(segment.offset);
let load_offset = segment.offset - layout.prefix_len;

View File

@ -30,7 +30,7 @@ impl TlsLayoutBuilder for TlsLayoutImpl {
segments.push(TlsSegment {
offset: total_size,
size: tls.p_memsz as _,
object_id: 0,
module_id: 1,
});
total_size += tls.p_memsz as usize;
@ -52,7 +52,7 @@ impl TlsLayoutBuilder for TlsLayoutImpl {
segments.push(TlsSegment {
offset: total_size,
size: tls.p_memsz as _,
object_id,
module_id: object_id + 1,
});
total_size += tls.p_memsz as usize;

View File

@ -65,7 +65,7 @@ impl TlsLayoutBuilder for TlsLayoutImpl {
.map(|(object_id, offset, size)| TlsSegment {
offset,
size,
object_id,
module_id: object_id + 1,
})
.collect();

View File

@ -16,6 +16,9 @@ path = "src/rc.rs"
[dependencies]
cross.workspace = true
yggdrasil-rt.workspace = true
logsink.workspace = true
log.workspace = true
serde.workspace = true
serde_json.workspace = true
yggdrasil-rt.workspace = true

View File

@ -11,7 +11,6 @@ use std::{
use cross::net::LocalPacketSocket;
use init::InitMsg;
use yggdrasil_rt::debug_trace;
const INITTAB_PATH: &str = "/etc/inittab";
@ -162,19 +161,20 @@ fn main_loop(socket: LocalPacketSocket) -> io::Result<!> {
let len = socket.receive(&mut buf)?;
if let Ok(msg) = serde_json::from_slice::<InitMsg>(&buf[..len]) {
if let Err(err) = handle_message(msg) {
debug_trace!("init::handle_message: {err}");
log::warn!("init::handle_message: {err}");
}
}
}
}
fn main() -> ExitCode {
debug_trace!("Userspace init starting");
logsink::setup_logging(false);
log::info!("Userspace init starting");
let rules = match load_rules(INITTAB_PATH) {
Ok(s) => s,
Err(e) => {
debug_trace!("init: failed to load rules: {:?}", e);
log::error!("init: failed to load rules: {:?}", e);
return ExitCode::FAILURE;
}
};
@ -182,16 +182,16 @@ fn main() -> ExitCode {
//let channel = MessageChannel::open("service-control", true).unwrap();
let control_socket = LocalPacketSocket::bind("/init.sock").unwrap();
debug_trace!("Rules loaded");
log::info!("Rules loaded");
for rule in rules {
debug_trace!("rc: {:?}", rule);
log::info!("rc: {:?}", rule);
if let Err(err) = rule.run() {
debug_trace!("rc: failed to execute rule {:?}: {:?}", rule, err);
log::error!("rc: failed to execute rule {:?}: {:?}", rule, err);
}
}
let Err(error) = main_loop(control_socket);
debug_trace!("init: main_loop returned {error}");
log::error!("init: main_loop returned {error}");
ExitCode::FAILURE
}

View File

@ -39,7 +39,7 @@ impl FromStr for Mode {
fn exec_script<P: AsRef<Path>>(path: P, arg: &str) -> Result<(), Error> {
let path = path.as_ref();
yggdrasil_rt::debug_trace!("rc: {:?} {}", path, arg);
log::info!("rc: {:?} {}", path, arg);
// TODO run those in parallel, if allowed
let mut process = Command::new(path).arg(arg).spawn()?;

View File

@ -6,3 +6,4 @@ pub(crate) mod sys;
pub mod io;
pub mod net;
pub mod mem;
pub mod signal;

View File

@ -0,0 +1,6 @@
use crate::sys;
pub fn set_sigint_handler(handler: fn()) {
sys::set_sigint_handler(handler);
}

View File

@ -1,39 +1,75 @@
use std::{
ffi::c_void,
io,
ops::{Deref, DerefMut},
os::fd::{AsRawFd, OwnedFd, RawFd},
os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
ptr::null_mut,
};
use crate::sys;
pub struct SharedMemoryImpl {}
pub struct SharedMemoryImpl {
fd: OwnedFd,
size: usize,
}
pub struct FileMappingImpl {}
pub struct FileMappingImpl {
fd: OwnedFd,
pointer: *mut c_void,
size: usize,
}
impl sys::SharedMemory for SharedMemoryImpl {
type Mapping = FileMappingImpl;
fn map(self) -> io::Result<Self::Mapping> {
todo!()
<FileMappingImpl as sys::FileMapping>::map(self.fd, self.size)
}
fn new(size: usize) -> io::Result<Self> {
let _ = size;
todo!()
let fd = unsafe { libc::memfd_create(c"cross-shm".as_ptr(), libc::MFD_CLOEXEC) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
if unsafe { libc::ftruncate(fd.as_raw_fd(), size as i64) } != 0 {
return Err(io::Error::last_os_error());
}
Ok(Self { fd, size })
}
}
impl AsRawFd for SharedMemoryImpl {
fn as_raw_fd(&self) -> RawFd {
todo!()
self.fd.as_raw_fd()
}
}
impl sys::FileMapping for FileMappingImpl {
fn map<F: Into<OwnedFd>>(file: F, size: usize) -> io::Result<Self> {
let _ = file;
let _ = size;
todo!()
let fd: OwnedFd = file.into();
let pointer = unsafe {
libc::mmap(
null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd.as_raw_fd(),
0,
)
};
if pointer == libc::MAP_FAILED {
return Err(io::Error::last_os_error());
}
Ok(Self { fd, pointer, size })
}
}
impl Drop for FileMappingImpl {
fn drop(&mut self) {
unsafe {
libc::munmap(self.pointer, self.size);
}
}
}
@ -41,18 +77,18 @@ impl Deref for FileMappingImpl {
type Target = [u8];
fn deref(&self) -> &Self::Target {
todo!()
unsafe { core::slice::from_raw_parts(self.pointer.cast(), self.size) }
}
}
impl DerefMut for FileMappingImpl {
fn deref_mut(&mut self) -> &mut Self::Target {
todo!()
unsafe { core::slice::from_raw_parts_mut(self.pointer.cast(), self.size) }
}
}
impl AsRawFd for FileMappingImpl {
fn as_raw_fd(&self) -> RawFd {
todo!()
self.fd.as_raw_fd()
}
}

View File

@ -6,6 +6,8 @@ pub mod socket;
pub mod term;
pub mod timer;
use std::{ffi::c_int, sync::Mutex};
pub use mem::{FileMappingImpl, SharedMemoryImpl};
pub use pid::PidFdImpl;
pub use pipe::PipeImpl;
@ -13,3 +15,17 @@ pub use poll::PollImpl;
pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
pub use term::RawStdinImpl;
pub use timer::TimerFdImpl;
fn dummy_sigint() {}
static SIGINT_HANDLER: Mutex<fn()> = Mutex::new(dummy_sigint);
extern "C" fn sigint_proxy(_: c_int) {
let handler = *SIGINT_HANDLER.lock().unwrap();
handler();
}
pub fn set_sigint_handler(handler: fn()) {
*SIGINT_HANDLER.lock().unwrap() = handler;
unsafe { libc::signal(libc::SIGINT, sigint_proxy as usize) };
}

View File

@ -13,3 +13,6 @@ pub use pipe::PipeImpl;
pub use term::RawStdinImpl;
pub use socket::{LocalPacketSocketImpl, OwnedAddressImpl, BorrowedAddressImpl};
pub use mem::{SharedMemoryImpl, FileMappingImpl};
pub fn set_sigint_handler(_handler: fn()) {
}

View File

@ -4,6 +4,14 @@ version = "0.1.0"
edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[[example]]
name = "bar"
required-features = ["client"]
[[example]]
name = "window"
required-features = ["client"]
[dependencies]
cross.workspace = true
uipc.workspace = true
@ -11,13 +19,13 @@ yggdrasil-abi.workspace = true
serde.workspace = true
thiserror.workspace = true
log.workspace = true
# client_raqote
raqote = { version = "0.8.3", default-features = false, optional = true }
[dev-dependencies]
raqote.workspace = true
[features]
default = []
client_raqote = ["client", "raqote"]
default = ["client"]
client = []
[lints]

View File

@ -0,0 +1,66 @@
use std::{
env,
process::ExitCode,
sync::atomic::{AtomicBool, Ordering},
};
use libcolors::{
application::{
window::{EventOutcome, Window},
Application,
},
error::Error,
message::{CreateWindowInfo, WindowType},
};
use raqote::{Color, DrawTarget, Gradient, GradientStop, Point, SolidSource, Source, Spread};
fn run_bar() -> Result<ExitCode, Error> {
let mut app = Application::new()?;
let mut window = Window::new_with_info(
&app,
CreateWindowInfo {
ty: WindowType::Reservation(32),
},
)?;
window.set_on_redraw_requested(|surface: &mut DrawTarget<&mut [u32]>| {
let source = Source::new_linear_gradient(
Gradient {
stops: vec![
GradientStop {
position: 0.0,
color: Color::new(255, 255, 0, 0),
},
GradientStop {
position: 1.0,
color: Color::new(255, 255, 255, 0),
},
],
},
Point::new(0.0, 0.0),
Point::new(0.0, surface.height() as _),
Spread::Pad,
);
surface.fill_rect(
0.0,
0.0,
surface.width() as _,
surface.height() as _,
&source,
&Default::default(),
);
});
app.add_window(window);
Ok(app.run())
}
fn main() -> ExitCode {
match run_bar() {
Ok(code) => code,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}

View File

@ -0,0 +1,61 @@
use std::{
env,
process::ExitCode,
sync::atomic::{AtomicBool, Ordering},
};
use libcolors::{
application::{
window::{EventOutcome, Window},
Application,
},
error::Error, message::{CreateWindowInfo, WindowType},
};
use raqote::{DrawTarget, SolidSource};
fn run_window() -> Result<ExitCode, Error> {
static FOCUSED: AtomicBool = AtomicBool::new(false);
println!("libcolors test application");
let mut app = Application::new()?;
let mut window = Window::new(&app)?;
window.set_on_redraw_requested(|surface: &mut DrawTarget<&mut [u32]>| {
let color = if FOCUSED.load(Ordering::Acquire) {
SolidSource::from_unpremultiplied_argb(255, 255, 0, 0)
} else {
SolidSource::from_unpremultiplied_argb(255, 0, 0, 255)
};
let border = 8;
let border_color = SolidSource::from_unpremultiplied_argb(255, 0, 255, 0);
let w = surface.width();
let h = surface.height();
surface.clear(border_color);
surface.fill_rect(
border as _,
border as _,
(w - border * 2) as _,
(h - border * 2) as _,
&color.into(),
&Default::default(),
);
});
window.set_on_focus_changed(|focused| {
FOCUSED.store(focused, Ordering::Release);
EventOutcome::Redraw
});
app.add_window(window);
Ok(app.run())
}
fn main() -> ExitCode {
match run_window() {
Ok(code) => code,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}

View File

@ -1,12 +1,10 @@
use std::{
collections::VecDeque,
os::{
fd::{AsRawFd, RawFd},
yggdrasil::io::poll::PollChannel,
},
os::fd::{AsRawFd, RawFd},
time::Duration,
};
use cross::io::Poll;
use uipc::{Channel, Receiver, Sender};
use crate::{
@ -19,7 +17,8 @@ pub struct Connection {
sender: Sender<ClientMessage>,
receiver: Receiver<ServerMessage>,
event_queue: VecDeque<Event>,
poll: PollChannel,
poll: Poll,
// poll: PollChannel,
timeout: Duration,
}
@ -28,10 +27,10 @@ impl Connection {
let channel = Channel::connect(crate::CHANNEL_NAME)?;
let (sender, receiver) = channel.split();
let timeout = Duration::from_secs(1);
let mut poll = PollChannel::new()?;
let mut poll = Poll::new()?;
let event_queue = VecDeque::new();
poll.add(receiver.as_raw_fd())?;
poll.add(&receiver)?;
Ok(Self {
sender,
@ -42,16 +41,12 @@ impl Connection {
})
}
pub fn as_poll_fd(&self) -> RawFd {
self.poll.as_raw_fd()
}
pub fn filter_events<T, F: Fn(Event) -> Result<T, Event>>(
&mut self,
predicate: F,
) -> Result<T, Error> {
loop {
let Some((_, Ok(_))) = self.poll.wait(Some(self.timeout), true)? else {
let Some(_) = self.poll.wait(Some(self.timeout))? else {
return Err(Error::CommunicationTimeout);
};
@ -74,11 +69,8 @@ impl Connection {
}
loop {
match self.poll.wait(Some(self.timeout), true)? {
Some((_, Ok(_))) => (),
Some((_, Err(e))) => {
todo!("Poll error: {e:?}")
}
match self.poll.wait(Some(self.timeout))? {
Some(_) => (),
None => continue,
}
@ -111,3 +103,9 @@ impl Connection {
})
}
}
impl AsRawFd for Connection {
fn as_raw_fd(&self) -> RawFd {
self.poll.as_raw_fd()
}
}

View File

@ -1,9 +1,11 @@
use std::{
collections::BTreeMap,
process::ExitCode,
sync::{Arc, Mutex},
sync::{atomic::{AtomicBool, Ordering}, Arc, Mutex},
};
use cross::signal::set_sigint_handler;
use crate::{
error::Error,
event::{Event, EventData, WindowEvent},
@ -20,8 +22,16 @@ pub struct Application<'a> {
windows: BTreeMap<u32, Window<'a>>,
}
static EXIT_SIGNAL: AtomicBool = AtomicBool::new(false);
fn sigint_handler() {
EXIT_SIGNAL.store(true, Ordering::Release);
}
impl<'a> Application<'a> {
pub fn new() -> Result<Self, Error> {
set_sigint_handler(sigint_handler);
let mut connection = Connection::new()?;
connection.connect()?;
@ -36,10 +46,15 @@ impl<'a> Application<'a> {
self.windows.insert(window.id(), window);
}
pub fn is_running(&self) -> bool {
!EXIT_SIGNAL.load(Ordering::Acquire)
}
fn run_inner(mut self) -> Result<ExitCode, Error> {
loop {
while self.is_running() {
self.poll_events()?;
}
Ok(ExitCode::SUCCESS)
}
pub fn handle_event(&mut self, event: Event) -> Result<(), Error> {
@ -47,7 +62,7 @@ impl<'a> Application<'a> {
if let Some(window) = self.windows.get_mut(&window_id) {
window.handle_event(ev)?;
} else {
debug_trace!("Unexpected window_id received: {:?}", window_id);
log::warn!("Unknown window ID received: {window_id}");
}
}
@ -74,8 +89,8 @@ impl<'a> Application<'a> {
pub fn run(self) -> ExitCode {
match self.run_inner() {
Ok(exit) => exit,
Err(e) => {
debug_trace!("Application finished with error {:?}", e);
Err(error) => {
log::error!("Application finished with error: {error}");
ExitCode::FAILURE
}
}

View File

@ -5,7 +5,7 @@ use cross::mem::FileMapping;
use crate::{
error::Error,
event::{EventData, KeyInput, WindowEvent},
message::ClientMessage,
message::{ClientMessage, CreateWindowInfo},
};
use super::{connection::Connection, Application};
@ -15,11 +15,7 @@ pub trait OnKeyInput = Fn(KeyInput) -> EventOutcome;
pub trait OnResized = Fn(u32, u32) -> EventOutcome;
pub trait OnFocusChanged = Fn(bool) -> EventOutcome;
#[cfg(feature = "client_raqote")]
pub trait OnRedrawRequested = Fn(&mut raqote::DrawTarget<&mut [u32]>);
#[cfg(not(feature = "client_raqote"))]
pub trait OnRedrawRequested = Fn(&mut [u32]);
pub trait OnRedrawRequested = Fn(&mut [u32], u32, u32);
#[derive(Debug, PartialEq, Eq)]
pub enum EventOutcome {
@ -32,9 +28,6 @@ pub struct Window<'a> {
connection: Arc<Mutex<Connection>>,
window_id: u32,
surface_mapping: FileMapping,
#[cfg(feature = "client_raqote")]
surface_draw_target: raqote::DrawTarget<&'a mut [u32]>,
#[cfg(not(feature = "client_raqote"))]
surface_data: &'a mut [u32],
width: u32,
height: u32,
@ -47,11 +40,11 @@ pub struct Window<'a> {
on_focus_changed: Box<dyn OnFocusChanged>,
}
impl Window<'_> {
pub fn new(application: &Application) -> Result<Self, Error> {
impl<'a> Window<'a> {
pub fn new_with_info(application: &Application, info: CreateWindowInfo) -> Result<Self, Error> {
let mut connection = application.connection.lock().unwrap();
connection.send(&ClientMessage::CreateWindow)?;
connection.send(&ClientMessage::CreateWindow(info))?;
let (create_info, surface_shm_fd) = connection.filter_events(|r| match r.data {
EventData::NewWindowInfo(info) => {
@ -71,22 +64,12 @@ impl Window<'_> {
)
};
#[cfg(feature = "client_raqote")]
let surface_draw_target = raqote::DrawTarget::from_backing(
create_info.width as _,
create_info.height as _,
surface_data,
);
Ok(Self {
connection: application.connection.clone(),
window_id: create_info.window_id,
width: create_info.width,
height: create_info.height,
surface_mapping,
#[cfg(feature = "client_raqote")]
surface_draw_target,
#[cfg(not(feature = "client_raqote"))]
surface_data,
focused: false,
@ -95,20 +78,19 @@ impl Window<'_> {
// Do nothing
EventOutcome::Destroy
}),
#[cfg(feature = "client_raqote")]
on_redraw_requested: Box::new(|dt| {
dt.clear(SolidSource::from_unpremultiplied_argb(255, 127, 127, 127));
}),
#[cfg(not(feature = "client_raqote"))]
on_redraw_requested: Box::new(|dt| {
on_redraw_requested: Box::new(|dt, _, _| {
dt.fill(0xFF888888);
}),
on_key_input: Box::new(|_ev| EventOutcome::None),
on_resized: Box::new(|_w, _h| EventOutcome::None),
on_resized: Box::new(|_w, _h| EventOutcome::Redraw),
on_focus_changed: Box::new(|_| EventOutcome::None),
})
}
pub fn new(application: &Application) -> Result<Self, Error> {
Self::new_with_info(application, Default::default())
}
pub fn id(&self) -> u32 {
self.window_id
}
@ -138,15 +120,7 @@ impl Window<'_> {
}
pub fn redraw(&mut self) -> Result<(), Error> {
#[cfg(feature = "client_raqote")]
{
let dt = &mut self.surface_draw_target;
(self.on_redraw_requested)(dt);
}
#[cfg(not(feature = "client_raqote"))]
{
(self.on_redraw_requested)(self.surface_data);
}
(self.on_redraw_requested)(self.surface_data, self.width, self.height);
// Blit
self.blit_rect(0, 0, self.width, self.height)
@ -159,6 +133,9 @@ impl Window<'_> {
EventOutcome::None
}
WindowEvent::Resized { width, height } => {
self.width = width;
self.height = height;
let new_surface_data = unsafe {
std::slice::from_raw_parts_mut(
self.surface_mapping.as_mut_ptr() as *mut u32,
@ -166,18 +143,7 @@ impl Window<'_> {
)
};
#[cfg(feature = "client_raqote")]
{
let new_draw_target =
raqote::DrawTarget::from_backing(width as _, height as _, new_surface_data);
self.surface_draw_target = new_draw_target;
}
#[cfg(not(feature = "client_raqote"))]
{
self.surface_data = new_surface_data;
}
self.surface_data = new_surface_data;
(self.on_resized)(width, height)
}
@ -197,11 +163,6 @@ impl Window<'_> {
Ok(outcome)
}
#[cfg(feature = "client_raqote")]
pub fn as_draw_target(&mut self) -> &mut raqote::DrawTarget<&'a mut [u32]> {
&mut self.surface_draw_target
}
fn blit_rect(&mut self, x: u32, y: u32, w: u32, h: u32) -> Result<(), Error> {
// Clip to self bounds
let x = x.min(self.width);

View File

@ -1,9 +1,11 @@
use std::os::fd::OwnedFd;
pub use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
// pub use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use serde::{Deserialize, Serialize};
use crate::input::Key;
#[derive(Debug, Serialize, Deserialize)]
pub struct WindowInfo {
pub window_id: u32,
@ -23,16 +25,22 @@ pub struct KeyModifiers {
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyEvent {
pub modifiers: KeyModifiers,
pub key: KeyboardKey,
pub key: Key
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyInput {
pub modifiers: KeyModifiers,
pub key: KeyboardKey,
pub key: Key,
pub input: Option<char>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyboardKeyEvent {
pub key: Key,
pub state: bool
}
#[derive(Debug, Serialize, Deserialize)]
pub enum WindowEvent {
KeyPressed(KeyEvent),
@ -90,6 +98,12 @@ impl KeyModifiers {
alt: true,
};
pub const ALT_SHIFT: Self = Self {
shift: true,
ctrl: false,
alt: true,
};
pub const NONE: Self = Self {
shift: false,
ctrl: false,

View File

@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Key {
Char(u8),
LControl,
RControl,
LShift,
RShift,
LAlt,
RAlt,
PageUp,
PageDown,
Left,
Right,
Up,
Down,
Escape,
Enter,
Home,
End,
Backspace,
}

View File

@ -13,3 +13,4 @@ pub mod error;
pub mod event;
pub mod message;
pub mod input;

View File

@ -7,10 +7,22 @@ pub enum ServerMessage {
Event(EventData),
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub enum WindowType {
#[default]
Default,
Reservation(u32),
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct CreateWindowInfo {
pub ty: WindowType,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum ClientMessage {
ClientHello,
CreateWindow,
CreateWindow(CreateWindowInfo),
BlitWindow {
window_id: u32,
x: u32,

View File

@ -0,0 +1,10 @@
[package]
name = "libpsf"
version = "0.1.0"
edition = "2021"
[dependencies]
bytemuck.workspace = true
[lints]
workspace = true

View File

@ -9,7 +9,7 @@ struct Align<T, D: ?Sized> {
static FONT_DATA: &Align<u32, [u8]> = &Align {
_align: 0,
data: *include_bytes!("../../etc/fonts/regular.psfu"),
data: *include_bytes!("../../../etc/fonts/regular.psfu"),
};
#[repr(C)]
@ -71,4 +71,28 @@ impl<'a> PcScreenFont<'a> {
pub fn raw_glyph_data(&self, index: u32) -> &[u8] {
&self.glyph_data[(index * self.header.bytes_per_glyph) as usize..]
}
pub fn map_glyph_pixels<F: FnMut(usize, usize, bool)>(&self, mut c: u32, mut put_pixel: F) {
let bytes_per_line = (self.width() as usize + 7) / 8;
// Draw character
if c >= self.len() {
c = b'?' as u32;
}
let mut glyph = self.raw_glyph_data(c);
let mut y = 0;
while y < self.height() as usize {
let mut mask = 1 << (self.width() - 1);
let mut x = 0;
// let row_off = (cy + y) * self.width;
while x < self.width() as usize {
put_pixel(x, y, glyph[0] & mask != 0);
mask >>= 1;
x += 1;
}
glyph = &glyph[bytes_per_line..];
y += 1;
}
}
}

View File

@ -0,0 +1,20 @@
[package]
name = "logsink"
version = "0.1.0"
edition = "2021"
[dependencies]
log.workspace = true
[target.'cfg(target_os = "yggdrasil")'.dependencies]
yggdrasil-rt.workspace = true
[target.'cfg(unix)'.dependencies]
env_logger.workspace = true
[dev-dependencies]
yggdrasil-rt.workspace = true
env_logger.workspace = true
[lints]
workspace = true

View File

@ -0,0 +1,11 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os))]
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
pub mod yggdrasil;
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
pub use yggdrasil::*;
#[cfg(any(rust_analyzer, unix))]
pub fn setup_logging(_stdout: bool) {
env_logger::init();
}

View File

@ -0,0 +1,43 @@
use std::io::stdout;
use log::LevelFilter;
use yggdrasil_rt::debug::TraceLevel;
struct LogSink;
impl log::Log for LogSink {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
// debug_trace!("[{}] {}", record.level(), record.args());
let trace_level = match record.level() {
log::Level::Trace | log::Level::Debug => TraceLevel::Debug,
log::Level::Info => TraceLevel::Info,
log::Level::Warn => TraceLevel::Warn,
log::Level::Error => TraceLevel::Error,
};
let message = format!("{}", record.args());
unsafe { yggdrasil_rt::sys::debug_trace(trace_level, &message) };
if unsafe { TO_STDOUT } {
use std::io::Write;
let mut stdout = stdout();
writeln!(stdout, "[{}] {}", record.level(), record.args()).ok();
}
}
fn flush(&self) {
}
}
static SINK: LogSink = LogSink;
static mut TO_STDOUT: bool = true;
pub fn setup_logging(stdout: bool) {
unsafe { TO_STDOUT = stdout };
log::set_max_level(LevelFilter::Debug);
log::set_logger(&SINK).unwrap();
}

View File

@ -591,6 +591,7 @@ dependencies = [
"cbindgen",
"chrono",
"libyalloc",
"log",
"yggdrasil-abi",
"yggdrasil-rt",
]

View File

@ -11,6 +11,7 @@ yggdrasil-rt = { path = "../../../lib/runtime", features = ["__tls_get_addr"] }
yggdrasil-abi = { path = "../../../lib/abi", features = ["alloc", "bytemuck"] }
libyalloc = { path = "../../../lib/libyalloc" }
log = "0.4.22"
bitflags = "2.6.0"
chrono = { version = "0.4.31", default-features = false }

View File

@ -0,0 +1,66 @@
use core::fmt;
use yggdrasil_rt::debug::TraceLevel;
struct LogSink;
struct MessageBuffer {
buffer: [u8; 256],
len: usize,
}
impl MessageBuffer {
pub const fn new() -> Self {
Self {
buffer: [0; 256],
len: 0,
}
}
}
impl fmt::Write for MessageBuffer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let len = (self.buffer.len() - self.len).min(s.len());
self.buffer[self.len..self.len + len].copy_from_slice(&s.as_bytes()[..len]);
self.len += len;
Ok(())
}
}
impl log::Log for LogSink {
fn flush(&self) {}
fn log(&self, record: &log::Record) {
use fmt::Write;
let level = match record.level() {
log::Level::Trace | log::Level::Debug => TraceLevel::Debug,
log::Level::Info => TraceLevel::Info,
log::Level::Warn => TraceLevel::Warn,
log::Level::Error => TraceLevel::Error,
};
let mut buffer = MessageBuffer::new();
write!(buffer, "{}", record.args()).ok();
let text = match core::str::from_utf8(&buffer.buffer[..buffer.len]) {
Ok(text) => text,
Err(error) => {
let valid = unsafe {
core::str::from_utf8_unchecked(&buffer.buffer[..error.valid_up_to()])
};
valid
}
};
unsafe {
yggdrasil_rt::sys::debug_trace(level, text);
}
}
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
}
static LOG: LogSink = LogSink;
pub(crate) fn init_log_sink() {
log::set_max_level(log::LevelFilter::Debug);
log::set_logger(&LOG).ok();
}

View File

@ -144,7 +144,7 @@ unsafe extern "C" fn pthread_attr_setdetachstate(
#[no_mangle]
unsafe extern "C" fn pthread_attr_setguardsize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_attr_setguardsize()");
log::error!("TODO: pthread_attr_setguardsize()");
0
}
@ -153,7 +153,7 @@ unsafe extern "C" fn pthread_attr_setinheritsched(
_attr: *mut pthread_attr_t,
_inherit: c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_attr_setinheritsched()");
log::error!("TODO: pthread_attr_setinheritsched()");
0
}
@ -162,7 +162,7 @@ unsafe extern "C" fn pthread_attr_setschedparam(
_attr: *mut pthread_attr_t,
_param: *const __ygg_sched_param_t,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_attr_setschedparam()");
log::error!("TODO: pthread_attr_setschedparam()");
0
}
@ -171,13 +171,13 @@ unsafe extern "C" fn pthread_attr_setschedpolicy(
_attr: *mut pthread_attr_t,
_policy: c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_attr_setschedpolicy()");
log::error!("TODO: pthread_attr_setschedpolicy()");
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setscope(_attr: *mut pthread_attr_t, _scope: c_int) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_attr_setscope()");
log::error!("TODO: pthread_attr_setscope()");
0
}

View File

@ -57,7 +57,7 @@ impl pthread_barrier_t {
#[no_mangle]
unsafe extern "C" fn pthread_barrier_destroy(_barrier: *mut pthread_barrier_t) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_barrier_destroy()");
log::error!("TODO: pthread_barrier_destroy()");
0
}
@ -83,7 +83,7 @@ unsafe extern "C" fn pthread_barrier_wait(barrier: *mut pthread_barrier_t) -> c_
#[no_mangle]
unsafe extern "C" fn pthread_barrierattr_destroy(_attr: *mut pthread_barrierattr_t) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_barrierattr_destroy()");
log::error!("TODO: pthread_barrierattr_destroy()");
0
}
@ -100,7 +100,7 @@ unsafe extern "C" fn pthread_barrierattr_getpshared(
#[no_mangle]
unsafe extern "C" fn pthread_barrierattr_init(_attr: *mut pthread_barrierattr_t) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_barrierattr_init()");
log::error!("TODO: pthread_barrierattr_init()");
0
}
@ -112,7 +112,7 @@ unsafe extern "C" fn pthread_barrierattr_setpshared(
if shared == PTHREAD_PROCESS_PRIVATE {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_barrierattr_setpshared()");
log::error!("TODO: pthread_barrierattr_setpshared()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}

View File

@ -150,7 +150,7 @@ unsafe extern "C" fn pthread_condattr_setclock(
if clockid == CLOCK_MONOTONIC {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_condattr_setclock()");
log::error!("TODO: pthread_condattr_setclock()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}
@ -164,7 +164,7 @@ unsafe extern "C" fn pthread_condattr_setpshared(
if shared == PTHREAD_PROCESS_PRIVATE {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_condattr_setpshared()");
log::error!("TODO: pthread_condattr_setpshared()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}

View File

@ -113,13 +113,13 @@ impl Default for pthread_mutexattr_t {
#[no_mangle]
unsafe extern "C" fn pthread_mutex_consistent(_mutex: *mut pthread_mutex_t) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutex_consistent()");
log::error!("TODO: pthread_mutex_consistent()");
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_destroy(_mutex: *mut pthread_mutex_t) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutex_destroy()");
log::error!("TODO: pthread_mutex_destroy()");
0
}
@ -128,7 +128,7 @@ unsafe extern "C" fn pthread_mutex_getprioceiling(
_mutex: *const pthread_mutex_t,
maxprio: *mut c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutex_getprioceiling()");
log::error!("TODO: pthread_mutex_getprioceiling()");
if let Some(maxprio) = NonNull::new(maxprio) {
maxprio.write(10);
}
@ -168,7 +168,7 @@ unsafe extern "C" fn pthread_mutex_setprioceiling(
new: c_int,
old: *mut c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutex_setprioceiling()");
log::error!("TODO: pthread_mutex_setprioceiling()");
if let Some(old) = NonNull::new(old) {
old.write(new);
}
@ -207,7 +207,7 @@ unsafe extern "C" fn pthread_mutexattr_getprioceiling(
_attr: *const pthread_mutexattr_t,
maxprio: *mut c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_getprioceiling()");
log::error!("TODO: pthread_mutexattr_getprioceiling()");
if let Some(maxprio) = NonNull::new(maxprio) {
maxprio.write(10);
}
@ -219,7 +219,7 @@ unsafe extern "C" fn pthread_mutexattr_getprotocol(
_attr: *const pthread_mutexattr_t,
protocol: *mut c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_getprotocol()");
log::error!("TODO: pthread_mutexattr_getprotocol()");
if let Some(protocol) = NonNull::new(protocol) {
protocol.write(PTHREAD_PRIO_NONE);
}
@ -242,7 +242,7 @@ unsafe extern "C" fn pthread_mutexattr_getrobust(
_attr: *const pthread_mutexattr_t,
robust: *mut c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_getrobust()");
log::error!("TODO: pthread_mutexattr_getrobust()");
if let Some(robust) = NonNull::new(robust) {
robust.write(PTHREAD_MUTEX_STALLED);
}
@ -273,7 +273,7 @@ unsafe extern "C" fn pthread_mutexattr_setprioceiling(
_attr: *mut pthread_mutexattr_t,
_maxprio: c_int,
) -> c_int {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_setprioceiling()");
log::error!("TODO: pthread_mutexattr_setprioceiling()");
0
}
@ -285,7 +285,7 @@ unsafe extern "C" fn pthread_mutexattr_setprotocol(
if protocol == PTHREAD_PRIO_NONE {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_setprotocol()");
log::error!("TODO: pthread_mutexattr_setprotocol()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}
@ -299,7 +299,7 @@ unsafe extern "C" fn pthread_mutexattr_setpshared(
if shared == PTHREAD_PROCESS_PRIVATE {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_setpshared()");
log::error!("TODO: pthread_mutexattr_setpshared()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}
@ -313,7 +313,7 @@ unsafe extern "C" fn pthread_mutexattr_setrobust(
if robust == PTHREAD_MUTEX_STALLED {
CIntZeroResult::SUCCESS
} else {
yggdrasil_rt::debug_trace!("TODO: pthread_mutexattr_setrobust()");
log::error!("TODO: pthread_mutexattr_setrobust()");
error::errno = Errno::EINVAL;
CIntZeroResult::ERROR
}

View File

@ -21,7 +21,7 @@ unsafe extern "C" fn pthread_key_create(
#[no_mangle]
unsafe extern "C" fn pthread_key_delete(_key: pthread_key_t) -> CIntZeroResult {
yggdrasil_rt::debug_trace!("TODO: pthread_key_delete()");
log::error!("TODO: pthread_key_delete()");
CIntZeroResult::SUCCESS
}

View File

@ -209,7 +209,7 @@ unsafe extern "C" fn sigaction(
_new: *const sigaction,
_old: *mut sigaction,
) -> CIntZeroResult {
yggdrasil_rt::debug_trace!("TODO: sigaction({:?})", signum);
log::error!("TODO: sigaction({:?})", signum);
CIntZeroResult::SUCCESS
}
@ -282,7 +282,7 @@ unsafe extern "C" fn signal(handler: sig_handler_t, signum: SigNumber) -> sig_ha
let address = handler as usize;
// NULL or SIG_ERR
if address == 0 || address == 1 {
yggdrasil_rt::debug_trace!("libc: signal() was passed an invalid handler");
log::error!("libc: signal() was passed an invalid handler");
error::errno = Errno::EINVAL;
// SIG_ERR
return core::mem::transmute(1usize);

View File

@ -5,7 +5,6 @@ use core::{
use alloc::boxed::Box;
use yggdrasil_rt::{
debug_trace,
io::{OpenOptions, RawFd, SeekFrom},
path::Path,
};
@ -61,7 +60,7 @@ unsafe extern "C" fn fclose(fp: *mut FILE) -> CEofResult {
let fp = NonNull::new(fp).expect("fclose(): fp == NULL");
if !io::managed::deregister_file(fp) {
debug_trace!("fclose() on non-registered file: {fp:p}");
log::error!("fclose() on non-registered file: {fp:p}");
}
FILE::close(fp)?;

View File

@ -1,13 +1,30 @@
use core::{ffi::{c_int, c_void}, mem::MaybeUninit, ptr::{null, null_mut}};
use core::{
ffi::{c_int, c_void},
net::SocketAddr,
ptr::{null, null_mut},
};
use yggdrasil_abi::{
abi_serde::wire,
net::{MessageHeader, MessageHeaderMut},
};
use yggdrasil_rt::{io::RawFd, sys as syscall};
use crate::{error::{CIsizeResult, ResultExt, TryFromExt}, headers::{errno::Errno, sys_socket::SocketAddrExt}, util::PointerExt};
use crate::{
error::{CIsizeResult, ResultExt, TryFromExt},
headers::errno::Errno,
util::PointerExt,
};
use super::{msghdr, sockaddr, socklen_t};
use super::{msghdr, sockaddr, socklen_t, SocketAddrExt};
#[no_mangle]
unsafe extern "C" fn recv(fd: c_int, buffer: *mut c_void, len: usize, flags: c_int) -> CIsizeResult {
unsafe extern "C" fn recv(
fd: c_int,
buffer: *mut c_void,
len: usize,
flags: c_int,
) -> CIsizeResult {
recvfrom(fd, buffer, len, flags, null_mut(), null_mut())
}
@ -21,12 +38,24 @@ unsafe extern "C" fn recvfrom(
remote_len: *mut socklen_t,
) -> CIsizeResult {
let _ = flags;
let buffer = buffer.cast::<u8>().ensure_slice_mut(len);
let fd = RawFd::e_try_from(fd)?;
let mut peer = MaybeUninit::uninit();
let len = syscall::receive_from(fd, buffer, &mut peer).e_map_err(Errno::from)?;
if !remote.is_null() && !remote_len.is_null() {
let peer = peer.assume_init();
let payload = buffer.cast::<u8>().ensure_slice_mut(len);
let mut source = if remote.is_null() || remote_len.is_null() {
None
} else {
Some([0u8; 32])
};
let mut message = MessageHeaderMut {
payload,
source: source.as_mut().map(|x| &mut x[..]),
ancillary: None,
ancillary_len: 0,
};
let len = syscall::receive_message(fd, &mut message).e_map_err(Errno::from)?;
if let Some(source) = source.as_ref() {
let peer: SocketAddr = wire::from_slice(source)
.map_err(yggdrasil_rt::Error::from)
.e_map_err(Errno::from)?;
*remote_len = peer.to_c(remote, *remote_len)?;
}
CIsizeResult::success(len)
@ -41,7 +70,12 @@ unsafe extern "C" fn recvmsg(fd: c_int, message: *mut msghdr, flags: c_int) -> C
}
#[no_mangle]
unsafe extern "C" fn send(fd: c_int, data: *const c_void, len: usize, flags: c_int) -> CIsizeResult {
unsafe extern "C" fn send(
fd: c_int,
data: *const c_void,
len: usize,
flags: c_int,
) -> CIsizeResult {
sendto(fd, data, len, flags, null(), 0)
}
@ -64,12 +98,22 @@ unsafe extern "C" fn sendto(
) -> CIsizeResult {
let _ = flags;
let fd = RawFd::e_try_from(fd)?;
let data = data.cast::<u8>().ensure_slice(len);
let peer = if !remote.is_null() && remote_len != 0 {
Some(remote.to_rust(remote_len)?)
let payload = data.cast::<u8>().ensure_slice(len);
let destination = if remote.is_null() || remote_len == 0 {
let mut sockaddr_buf = [0; 32];
let sockaddr = remote.to_rust(len)?;
wire::to_slice(&sockaddr, &mut sockaddr_buf)
.map_err(yggdrasil_rt::Error::from)
.e_map_err(Errno::from)?;
Some(sockaddr_buf)
} else {
None
};
let len = syscall::send_to(fd, data, &peer).e_map_err(Errno::from)?;
let message = MessageHeader {
destination: destination.as_ref().map(|x| &x[..]),
payload,
ancillary: &[],
};
let len = syscall::send_message(fd, &message).e_map_err(Errno::from)?;
CIsizeResult::success(len)
}

View File

@ -115,7 +115,7 @@ impl SocketAddrExt<sockaddr> for SocketAddr {
match self {
Self::V4(v4) if len >= size_of::<sockaddr_in>() => v4.to_c(storage.cast(), len),
Self::V6(v6) if len >= size_of::<sockaddr_in6>() => v6.to_c(storage.cast(), len),
_ => EResult::Err(Errno::EINVAL)
_ => EResult::Err(Errno::EINVAL),
}
}
}

View File

@ -1,160 +1,151 @@
use core::ffi::{c_int, c_void};
use yggdrasil_rt::{
io::RawFd,
net::{self as rt, options},
};
use crate::{
error::{self, CIntZeroResult, CResult, OptionExt, ResultExt, TryFromExt},
headers::{
errno::Errno,
netinet_in::{IPPROTO_TCP, TCP_NODELAY},
sys_socket::{SocketAddrExt, SOL_SOCKET, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO},
sys_time::timeval,
},
};
use crate::error::CIntZeroResult;
use super::{sockaddr, socklen_t};
#[no_mangle]
unsafe extern "C" fn getsockopt(
fd: c_int,
level: c_int,
name: c_int,
value: *mut c_void,
size: *mut socklen_t,
_fd: c_int,
_level: c_int,
_name: c_int,
_value: *mut c_void,
_size: *mut socklen_t,
) -> CIntZeroResult {
if value.is_null() || size.is_null() {
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
todo!()
// if value.is_null() || size.is_null() {
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
let space = *size;
let fd = RawFd::e_try_from(fd)?;
// let space = *size;
// let fd = RawFd::e_try_from(fd)?;
*size = match (level, name) {
(SOL_SOCKET, SO_RCVTIMEO) if space >= size_of::<timeval>() => {
let timeout =
rt::get_socket_option!(fd, options::RecvTimeout).e_map_err(Errno::from)?;
let value = value.cast::<timeval>();
match timeout {
Some(timeout) => value.write(timeval::from(timeout)),
None => value.write(timeval::zero()),
}
size_of::<timeval>()
}
(SOL_SOCKET, SO_SNDTIMEO) => {
let timeout =
rt::get_socket_option!(fd, options::SendTimeout).e_map_err(Errno::from)?;
let value = value.cast::<timeval>();
match timeout {
Some(timeout) => value.write(timeval::from(timeout)),
None => value.write(timeval::zero()),
}
size_of::<timeval>()
}
(SOL_SOCKET, SO_BROADCAST) => {
let broadcast =
rt::get_socket_option!(fd, options::Broadcast).e_map_err(Errno::from)?;
let value = value.cast::<c_int>();
value.write(broadcast as c_int);
size_of::<c_int>()
}
(IPPROTO_TCP, TCP_NODELAY) => {
let broadcast = rt::get_socket_option!(fd, options::NoDelay).e_map_err(Errno::from)?;
let value = value.cast::<c_int>();
value.write(broadcast as c_int);
size_of::<c_int>()
}
_ => {
yggdrasil_rt::debug_trace!("Unhandled getsockopt({level}, {name}, {space})");
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
};
// *size = match (level, name) {
// (SOL_SOCKET, SO_RCVTIMEO) if space >= size_of::<timeval>() => {
// let timeout =
// rt::get_socket_option!(fd, options::RecvTimeout).e_map_err(Errno::from)?;
// let value = value.cast::<timeval>();
// match timeout {
// Some(timeout) => value.write(timeval::from(timeout)),
// None => value.write(timeval::zero()),
// }
// size_of::<timeval>()
// }
// (SOL_SOCKET, SO_SNDTIMEO) => {
// let timeout =
// rt::get_socket_option!(fd, options::SendTimeout).e_map_err(Errno::from)?;
// let value = value.cast::<timeval>();
// match timeout {
// Some(timeout) => value.write(timeval::from(timeout)),
// None => value.write(timeval::zero()),
// }
// size_of::<timeval>()
// }
// (SOL_SOCKET, SO_BROADCAST) => {
// let broadcast =
// rt::get_socket_option!(fd, options::Broadcast).e_map_err(Errno::from)?;
// let value = value.cast::<c_int>();
// value.write(broadcast as c_int);
// size_of::<c_int>()
// }
// (IPPROTO_TCP, TCP_NODELAY) => {
// let broadcast = rt::get_socket_option!(fd, options::NoDelay).e_map_err(Errno::from)?;
// let value = value.cast::<c_int>();
// value.write(broadcast as c_int);
// size_of::<c_int>()
// }
// _ => {
// yggdrasil_rt::debug_trace!("Unhandled getsockopt({level}, {name}, {space})");
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
// };
CIntZeroResult::SUCCESS
// CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn setsockopt(
fd: c_int,
level: c_int,
name: c_int,
value: *const c_void,
size: socklen_t,
_fd: c_int,
_level: c_int,
_name: c_int,
_value: *const c_void,
_size: socklen_t,
) -> CIntZeroResult {
if value.is_null() {
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
todo!()
// if value.is_null() {
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
let fd = RawFd::e_try_from(fd)?;
// let fd = RawFd::e_try_from(fd)?;
match (level, name) {
(SOL_SOCKET, SO_RCVTIMEO) if size == size_of::<timeval>() => {
let timeval = *value.cast::<timeval>();
let timeout = timeval.to_duration_opt();
rt::set_socket_option::<options::RecvTimeout>(fd, &timeout).e_map_err(Errno::from)?;
}
(SOL_SOCKET, SO_SNDTIMEO) if size == size_of::<timeval>() => {
let timeval = *value.cast::<timeval>();
let timeout = timeval.to_duration_opt();
rt::set_socket_option::<options::SendTimeout>(fd, &timeout).e_map_err(Errno::from)?;
}
(SOL_SOCKET, SO_BROADCAST) if size == size_of::<c_int>() => {
let value = *value.cast::<c_int>() != 0;
rt::set_socket_option::<options::Broadcast>(fd, &value).e_map_err(Errno::from)?;
}
(IPPROTO_TCP, TCP_NODELAY) if size == size_of::<c_int>() => {
let value = *value.cast::<c_int>() != 0;
rt::set_socket_option::<options::NoDelay>(fd, &value).e_map_err(Errno::from)?;
}
_ => {
yggdrasil_rt::debug_trace!("Unhandled setsockopt({level}, {name}, {size})");
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
}
// match (level, name) {
// (SOL_SOCKET, SO_RCVTIMEO) if size == size_of::<timeval>() => {
// let timeval = *value.cast::<timeval>();
// let timeout = timeval.to_duration_opt();
// rt::set_socket_option::<options::RecvTimeout>(fd, &timeout).e_map_err(Errno::from)?;
// }
// (SOL_SOCKET, SO_SNDTIMEO) if size == size_of::<timeval>() => {
// let timeval = *value.cast::<timeval>();
// let timeout = timeval.to_duration_opt();
// rt::set_socket_option::<options::SendTimeout>(fd, &timeout).e_map_err(Errno::from)?;
// }
// (SOL_SOCKET, SO_BROADCAST) if size == size_of::<c_int>() => {
// let value = *value.cast::<c_int>() != 0;
// rt::set_socket_option::<options::Broadcast>(fd, &value).e_map_err(Errno::from)?;
// }
// (IPPROTO_TCP, TCP_NODELAY) if size == size_of::<c_int>() => {
// let value = *value.cast::<c_int>() != 0;
// rt::set_socket_option::<options::NoDelay>(fd, &value).e_map_err(Errno::from)?;
// }
// _ => {
// yggdrasil_rt::debug_trace!("Unhandled setsockopt({level}, {name}, {size})");
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
// }
CIntZeroResult::SUCCESS
// CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn getpeername(
fd: c_int,
remote: *mut sockaddr,
len: *mut socklen_t,
_fd: c_int,
_remote: *mut sockaddr,
_len: *mut socklen_t,
) -> CIntZeroResult {
if remote.is_null() || len.is_null() {
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
todo!()
// if remote.is_null() || len.is_null() {
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
let fd = RawFd::e_try_from(fd)?;
let address = rt::get_socket_option!(fd, options::PeerAddress).e_map_err(Errno::from)?;
let address = address.e_ok_or(Errno::ENOTCONN)?;
// let fd = RawFd::e_try_from(fd)?;
// let address = rt::get_socket_option!(fd, options::PeerAddress).e_map_err(Errno::from)?;
// let address = address.e_ok_or(Errno::ENOTCONN)?;
*len = address.to_c(remote, *len)?;
CIntZeroResult::SUCCESS
// *len = address.to_c(remote, *len)?;
// CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn getsockname(
fd: c_int,
local: *mut sockaddr,
len: *mut socklen_t,
_fd: c_int,
_local: *mut sockaddr,
_len: *mut socklen_t,
) -> CIntZeroResult {
if local.is_null() || len.is_null() {
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
todo!()
// if local.is_null() || len.is_null() {
// error::errno = Errno::EINVAL;
// return CIntZeroResult::ERROR;
// }
let fd = RawFd::e_try_from(fd)?;
let address = rt::get_socket_option!(fd, options::LocalAddress).e_map_err(Errno::from)?;
let address = address.e_ok_or(Errno::ENOTCONN)?;
// let fd = RawFd::e_try_from(fd)?;
// let address = rt::get_socket_option!(fd, options::LocalAddress).e_map_err(Errno::from)?;
// let address = address.e_ok_or(Errno::ENOTCONN)?;
*len = address.to_c(local, *len)?;
CIntZeroResult::SUCCESS
// *len = address.to_c(local, *len)?;
// CIntZeroResult::SUCCESS
}

View File

@ -1,44 +1,59 @@
use core::{ffi::c_int, mem::MaybeUninit};
use core::{ffi::c_int, net::SocketAddr};
use yggdrasil_abi::abi_serde::wire;
use yggdrasil_rt::{io::RawFd, net as rt, sys as syscall};
use crate::{
error::{self, CFdResult, CIntCountResult, CIntZeroResult, CResult, ResultExt, TryFromExt},
headers::{
errno::Errno,
sys_socket::{AF_INET, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_STREAM},
sys_socket::{
SocketAddrExt, AF_INET, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_STREAM,
},
},
};
use super::{sockaddr, socklen_t, SocketAddrExt};
use super::{sockaddr, socklen_t};
#[no_mangle]
unsafe extern "C" fn accept(fd: c_int, remote: *mut sockaddr, len: *mut socklen_t) -> CFdResult {
let listener_fd = RawFd::e_try_from(fd)?;
let mut address = MaybeUninit::uninit();
let stream_fd = syscall::accept(listener_fd, &mut address).e_map_err(Errno::from)?;
if !remote.is_null() && !len.is_null() {
let address = address.assume_init();
*len = address.to_c(remote, *len)?;
}
let stream_fd = if remote.is_null() || len.is_null() {
syscall::accept(listener_fd, None).e_map_err(Errno::from)?
} else {
let mut sockaddr_buf = [0; 32];
let stream_fd =
syscall::accept(listener_fd, Some(&mut sockaddr_buf)).e_map_err(Errno::from)?;
let sockaddr: SocketAddr = wire::from_slice(&sockaddr_buf)
.map_err(yggdrasil_rt::Error::from)
.e_map_err(Errno::from)?;
*len = sockaddr.to_c(remote, *len)?;
stream_fd
};
CFdResult::success(stream_fd)
}
#[no_mangle]
unsafe extern "C" fn bind(fd: c_int, local: *const sockaddr, len: socklen_t) -> CIntZeroResult {
let mut sockaddr_buf = [0; 32];
let fd = RawFd::e_try_from(fd)?;
let local = local.to_rust(len)?;
syscall::bind(fd, &local).e_map_err(Errno::from)?;
let sockaddr_len = wire::to_slice(&local, &mut sockaddr_buf)
.map_err(yggdrasil_rt::Error::from)
.e_map_err(Errno::from)?;
syscall::bind(fd, &sockaddr_buf[..sockaddr_len]).e_map_err(Errno::from)?;
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn connect(fd: c_int, remote: *const sockaddr, len: socklen_t) -> CIntZeroResult {
let fd = RawFd::e_try_from(fd)?;
let mut sockaddr_buf = [0; 256];
let remote = remote.to_rust(len)?;
syscall::connect(fd, &remote).e_map_err(Errno::from)?;
let sockaddr_len = wire::to_slice(&remote, &mut sockaddr_buf)
.map_err(yggdrasil_rt::Error::from)
.e_map_err(Errno::from)?;
syscall::connect(fd, &sockaddr_buf[..sockaddr_len]).e_map_err(Errno::from)?;
CIntZeroResult::SUCCESS
}
@ -81,7 +96,7 @@ unsafe extern "C" fn socket(domain: c_int, ty: c_int, proto: c_int) -> CFdResult
(AF_INET, SOCK_STREAM, 0) => rt::SocketType::TcpStream,
(AF_INET, SOCK_DGRAM, 0) => rt::SocketType::UdpPacket,
(_, _, _) => {
yggdrasil_rt::debug_trace!("Unsupported socket({domain}, {ty}, {proto})");
log::error!("Unsupported socket({domain}, {ty}, {proto})");
error::errno = Errno::ENOTSUPP;
return CFdResult::ERROR;
}

View File

@ -1,6 +1,6 @@
use core::{ffi::{c_char, c_int, c_void}, mem::MaybeUninit};
use yggdrasil_rt::{debug_trace, io::{RawFd, SeekFrom}};
use yggdrasil_rt::{io::{RawFd, SeekFrom}};
use crate::{
error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, ResultExt, TryFromExt},
@ -32,19 +32,19 @@ unsafe extern "C" fn dup2(oldfd: c_int, newfd: c_int) -> CFdResult {
#[no_mangle]
unsafe extern "C" fn fdatasync(fd: c_int) -> CIntZeroResult {
debug_trace!("TODO: fdatasync()");
log::error!("TODO: fdatasync()");
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn fsync(fd: c_int) -> CIntZeroResult {
debug_trace!("TODO: fsync()");
log::error!("TODO: fsync()");
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn isatty(fd: c_int) -> c_int {
debug_trace!("TODO: isatty()");
log::error!("TODO: isatty()");
1
}
@ -85,7 +85,7 @@ unsafe extern "C" fn pwrite(fd: c_int, buf: *const c_void, len: usize, pos: off_
#[no_mangle]
unsafe extern "C" fn sync() {
debug_trace!("TODO: sync()");
log::error!("TODO: sync()");
}
#[no_mangle]

View File

@ -3,8 +3,6 @@ use core::{
time::Duration,
};
use yggdrasil_rt::debug_trace;
use crate::{
error::{self, CIntCountResult, CIntZeroResult, ResultExt},
headers::{
@ -76,7 +74,7 @@ unsafe extern "C" fn getuid() -> uid_t {
#[no_mangle]
unsafe extern "C" fn nice(incr: c_int) -> CIntCountResult {
debug_trace!("TODO: nice()");
log::error!("TODO: nice()");
CIntCountResult::success(0)
}

View File

@ -21,7 +21,7 @@ pub extern "C" fn init() {
}
unsafe fn call_constructor_list(start: usize, end: usize) {
yggdrasil_rt::debug_trace!("call_constructor_list {:#x?}", start..end);
log::debug!("call_constructor_list {:#x?}", start..end);
let base = start as *const usize;
let len = (end - start) / size_of::<usize>();
for i in 0..len {
@ -29,7 +29,7 @@ unsafe fn call_constructor_list(start: usize, end: usize) {
if addr == 0 {
continue;
}
yggdrasil_rt::debug_trace!("call constructor: {:#x}", addr);
log::debug!("call constructor: {:#x}", addr);
let func = core::mem::transmute::<_, extern "C" fn()>(addr);
func();
}

View File

@ -136,7 +136,7 @@ pub fn realpath<P: AsRef<Path>>(path: P) -> EResult<PathBuf> {
let mut path = path.as_ref();
let mut buffer = PathBuf::from("/");
yggdrasil_rt::debug_trace!("realpath({path:?})");
log::debug!("realpath({path:?})");
loop {
if path.is_empty() {
@ -144,7 +144,7 @@ pub fn realpath<P: AsRef<Path>>(path: P) -> EResult<PathBuf> {
}
let (head, tail) = path.split_left();
yggdrasil_rt::debug_trace!("* {head:?}");
log::debug!("* {head:?}");
match head {
".." => {
@ -160,7 +160,7 @@ pub fn realpath<P: AsRef<Path>>(path: P) -> EResult<PathBuf> {
path = tail;
}
yggdrasil_rt::debug_trace!("-> {buffer:?}");
log::debug!("-> {buffer:?}");
EResult::Ok(buffer)
}

View File

@ -42,6 +42,7 @@ extern crate compiler_builtins;
#[cfg(test)]
extern crate std;
mod debug;
mod allocator;
mod cxxabi;
mod env;
@ -64,6 +65,8 @@ unsafe extern "C" fn __ygglibc_entry(
main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int,
arg: usize,
) {
// Setup a logging sink
debug::init_log_sink();
init::call_constructors();
signal::init(true);
init::init();

View File

@ -36,7 +36,9 @@ impl PanicPrinter {
let text = &self.buffer[..self.pos];
if !text.is_empty() {
// Print to debug trace
yggdrasil_rt::debug::trace_raw(text);
if let Ok(text) = core::str::from_utf8(text) {
log::error!("{text}");
}
// Print to stderr
if let Some(print) = self.print.as_mut() {
@ -94,8 +96,8 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
printer.flush();
}
1 => {
yggdrasil_rt::debug_trace!("{pi:?}");
yggdrasil_rt::debug_trace!("!!! ygglibc panicked while panicking !!!");
log::error!("{pi:?}");
log::error!("!!! ygglibc panicked while panicking !!!");
}
_ => {}
}

View File

@ -4,9 +4,11 @@ version = "0.1.0"
edition = "2021"
[dependencies]
log.workspace = true
yggdrasil-abi.workspace = true
runtime.workspace = true
logsink.workspace = true
log.workspace = true
bytemuck.workspace = true
serde_json.workspace = true
serde.workspace = true

View File

@ -63,11 +63,11 @@ fn handle_query(
match (ty, class) {
(DnsType::A, DnsClass::IN) => {
if let Some(ip) = a_cache.get(name) {
debug_trace!("Cache hit: {} -> {}", name, ip);
log::debug!("Cache hit: {} -> {}", name, ip);
return Ok(IpAddr::V4(*ip));
}
debug_trace!("Cache miss: {}", name);
log::debug!("Cache miss: {}", name);
let result = perform_query(nameservers, name, ty)?;
for answer in result.answers {
@ -78,7 +78,7 @@ fn handle_query(
#[allow(clippy::single_match)]
match (answer.ty, answer.class, answer.rdata) {
(DnsType::A, DnsClass::IN, DnsRecordData::A(ip)) => {
debug_trace!("Resolved: {} -> {}", name, ip);
log::debug!("Resolved: {} -> {}", name, ip);
a_cache.insert(name, ip);
}
_ => (),
@ -131,7 +131,7 @@ fn run_server(addr: SocketAddr, nameservers: &[SocketAddr]) -> Result<(), Error>
let reply = DnsMessage::reply(reply_code, message.questions, answers, message.xid);
reply.serialize(&mut packet);
if let Err(error) = socket.send_to(&packet, remote) {
debug_trace!("sendto: {}", error);
log::error!("sendto: {}", error);
}
}
}
@ -192,6 +192,7 @@ fn perform_query(nameservers: &[SocketAddr], name: &str, ty: DnsType) -> Result<
}
fn main() -> ExitCode {
logsink::setup_logging(true);
let mut args: Arguments = Arguments::parse();
if let Some(server) = args.server {

View File

@ -159,10 +159,11 @@ fn run(args: &Args) -> io::Result<()> {
}
fn sigint_handler(_: Signal) {
debug_trace!("SIGINT handler!")
log::debug!("SIGINT handler!")
}
fn main() -> ExitCode {
logsink::setup_logging(true);
let _ = set_signal_handler(Signal::Interrupted, SignalHandler::Function(sigint_handler));
let args = Args::parse();

View File

@ -7,13 +7,16 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand.workspace = true
libterm.workspace = true
yggdrasil-abi.workspace = true
yggdrasil-rt.workspace = true
cross.workspace = true
logsink.workspace = true
log.workspace = true
rand.workspace = true
thiserror.workspace = true
clap.workspace = true
cross.workspace = true
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true

View File

@ -46,6 +46,7 @@ fn login_attempt(erase: bool) -> Result<(), io::Error> {
}
fn main() -> ExitCode {
logsink::setup_logging(false);
let args: Vec<_> = env::args().skip(1).collect();
if args.len() != 1 {
eprintln!("Usage: /sbin/login TTY");
@ -54,16 +55,16 @@ fn main() -> ExitCode {
let terminal = args[0].as_str();
// TODO check that `terminal` is a terminal
debug_trace!("Starting a session at {}", terminal);
log::info!("Starting a session at {}", terminal);
if let Err(err) = unsafe { start_terminal_session(terminal) } {
debug_trace!("Error: {:?}", err);
log::error!("Error: {:?}", err);
eprintln!("Could not setup a session at {}: {:?}", terminal, err);
return ExitCode::FAILURE;
}
let mut attempt_number = 0;
loop {
debug_trace!("Login attempt {}", attempt_number);
log::debug!("Login attempt {}", attempt_number);
// "Attach" the terminal
let group_id = std::os::yggdrasil::process::group_id();

View File

@ -162,13 +162,14 @@ struct Args {
}
fn main() -> ExitCode {
logsink::setup_logging(true);
// TODO check if running in a terminal
let args = Args::parse();
let view = View::open(&args.filename, !args.no_bar).unwrap();
match view.run() {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
yggdrasil_rt::debug_trace!("view: {error}");
log::error!("view: {error}");
ExitCode::FAILURE
}
}

View File

@ -5,7 +5,10 @@ edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
clap = { version = "4.3.19", features = ["std", "derive"], default-features = false }
bytemuck = { workspace = true, features = ["derive"] }
libcolors = { workspace = true, default-features = false, features = ["client"] }
libpsf.workspace = true
libcolors.workspace = true
logsink.workspace = true
log.workspace = true
thiserror.workspace = true
clap.workspace = true

View File

@ -23,19 +23,18 @@ use std::{
};
use error::Error;
use font::PcScreenFont;
use libpsf::PcScreenFont;
use libcolors::{
application::{
window::{EventOutcome, Window},
Application,
},
event::{KeyModifiers, KeyboardKey},
event::KeyModifiers, input::Key,
};
use state::{Cursor, State};
pub mod attr;
pub mod error;
pub mod font;
pub mod state;
struct DrawState {
@ -85,12 +84,10 @@ impl DrawState {
dt.fill(default_bg);
}
if cursor_dirty {
state.buffer.set_row_dirty(self.old_cursor.row);
}
let bytes_per_line = (self.font.width() as usize + 7) / 8;
let scroll = state.adjust_scroll();
let cursor_visible = scroll == 0;
state.buffer.visible_rows_mut(scroll, |i, row| {
@ -112,32 +109,13 @@ impl DrawState {
continue;
}
// Draw character
let mut c = cell.char as u32;
if c >= self.font.len() {
c = b'?' as u32;
}
let mut glyph = self.font.raw_glyph_data(c);
let mut y = 0;
while y < self.font.height() as usize {
let mut mask = 1 << (self.font.width() - 1);
let mut x = 0;
let row_off = (cy + y) * self.width;
while x < self.font.width() as usize {
let v = if glyph[0] & mask != 0 { fg } else { bg };
dt[row_off + cx + x] = v;
mask >>= 1;
x += 1;
}
glyph = &glyph[bytes_per_line..];
y += 1;
}
let c = cell.char as u32;
self.font.map_glyph_pixels(c, |x, y, set| {
let color = if set { fg } else { bg };
dt[(cy + y) * self.width + cx + x] = color;
});
}
});
// for (i, row) in state.buffer.dirty_rows() {
// }
// TODO check if there's a character under cursor
if cursor_visible {
@ -232,32 +210,32 @@ impl Terminal<'_> {
need_redraw = s.scroll_end();
} else {
match (ev.modifiers, ev.key) {
(KeyModifiers::NONE, KeyboardKey::Escape) => {
(KeyModifiers::NONE, Key::Escape) => {
pty_master.write_all(b"\x1B").unwrap();
need_redraw = s.scroll_end();
}
(KeyModifiers::NONE, KeyboardKey::Backspace) => {
(KeyModifiers::NONE, Key::Backspace) => {
pty_master.write_all(&[termios.chars.erase]).unwrap();
need_redraw = s.scroll_end();
}
(KeyModifiers::CTRL, KeyboardKey::Char(b'c')) => {
(KeyModifiers::CTRL, Key::Char(b'c')) => {
pty_master.write_all(&[termios.chars.interrupt]).unwrap();
need_redraw = s.scroll_end();
}
(KeyModifiers::CTRL, KeyboardKey::Char(b'd')) => {
(KeyModifiers::CTRL, Key::Char(b'd')) => {
pty_master.write_all(&[termios.chars.eof]).unwrap();
need_redraw = s.scroll_end();
}
(KeyModifiers::SHIFT, KeyboardKey::PageUp) => {
(KeyModifiers::SHIFT, Key::PageUp) => {
need_redraw = s.scroll_up();
}
(KeyModifiers::SHIFT, KeyboardKey::PageDown) => {
(KeyModifiers::SHIFT, Key::PageDown) => {
need_redraw = s.scroll_down();
}
(KeyModifiers::SHIFT, KeyboardKey::Home) => {
(KeyModifiers::SHIFT, Key::Home) => {
need_redraw = s.scroll_home();
}
(KeyModifiers::SHIFT, KeyboardKey::End) => {
(KeyModifiers::SHIFT, Key::End) => {
need_redraw = s.scroll_end();
}
_ => (),
@ -282,7 +260,7 @@ impl Terminal<'_> {
});
let state_c = state.clone();
window.set_on_redraw_requested(move |dt: &mut [u32]| {
window.set_on_redraw_requested(move |dt: &mut [u32], _, _| {
let mut s = state_c.lock().unwrap();
let mut ds = draw_state.lock().unwrap();
@ -291,7 +269,7 @@ impl Terminal<'_> {
app.add_window(window);
let conn_fd = app.connection().lock().unwrap().as_poll_fd();
let conn_fd = app.connection().lock().unwrap().as_raw_fd();
poll.add(conn_fd)?;
poll.add(pty_master_fd)?;
@ -300,7 +278,7 @@ impl Terminal<'_> {
let pty_slave_stdout = fd::clone_fd(pty_slave_stdin)?;
let pty_slave_stderr = fd::clone_fd(pty_slave_stdin)?;
debug_trace!("stdin = {pty_slave_stdin:?}, stdout = {pty_slave_stdout:?}, stderr = {pty_slave_stderr:?}");
log::debug!("stdin = {pty_slave_stdin:?}, stdout = {pty_slave_stdout:?}, stderr = {pty_slave_stderr:?}");
let group_id = yggdrasil::process::create_process_group();
let shell = unsafe {

View File

@ -181,6 +181,9 @@ impl Buffer {
}
pub fn set_row_dirty(&mut self, row: usize) {
if row >= self.rows.len() {
return;
}
self.rows[row].dirty = true;
}

View File

@ -57,6 +57,7 @@ const PROGRAMS: &[(&str, &str)] = &[
("ncap", "sbin/ncap"),
// colors
("colors", "bin/colors"),
("colors-bar", "bin/colors-bar"),
("term", "bin/term"),
// red
("red", "bin/red"),