Compare commits
4 Commits
82175f342e
...
dcc5d56750
Author | SHA1 | Date | |
---|---|---|---|
dcc5d56750 | |||
0105be8fea | |||
c2cf314dcd | |||
f605b0a80c |
@ -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,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
pub(crate) use abi::{
|
||||
debug::TraceLevel,
|
||||
error::Error,
|
||||
io::{
|
||||
AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, MountOptions, OpenOptions,
|
||||
|
@ -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> {
|
||||
|
@ -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<()>;
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))) };
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
1961
userspace/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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)'] }
|
||||
|
@ -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
|
||||
|
95
userspace/colors/src/bin/colors-bar.rs
Normal file
95
userspace/colors/src/bin/colors-bar.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
66
userspace/colors/src/sys/mod.rs
Normal file
66
userspace/colors/src/sys/mod.rs
Normal 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();
|
||||
}
|
||||
}
|
262
userspace/colors/src/sys/unix.rs
Normal file
262
userspace/colors/src/sys/unix.rs
Normal 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 })
|
||||
}
|
278
userspace/colors/src/sys/yggdrasil.rs
Normal file
278
userspace/colors/src/sys/yggdrasil.rs
Normal 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
|
||||
})
|
||||
}
|
23
userspace/colors/src/window.rs
Normal file
23
userspace/colors/src/window.rs
Normal 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;
|
||||
}
|
||||
}
|
703
userspace/colors/src/wm/mod.rs
Normal file
703
userspace/colors/src/wm/mod.rs
Normal 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(¤t_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(¤t_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(¤t_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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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" }
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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!(
|
||||
|
@ -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)))
|
||||
}
|
||||
|
@ -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))),
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -65,7 +65,7 @@ impl TlsLayoutBuilder for TlsLayoutImpl {
|
||||
.map(|(object_id, offset, size)| TlsSegment {
|
||||
offset,
|
||||
size,
|
||||
object_id,
|
||||
module_id: object_id + 1,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()?;
|
||||
|
@ -6,3 +6,4 @@ pub(crate) mod sys;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod mem;
|
||||
pub mod signal;
|
||||
|
6
userspace/lib/cross/src/signal.rs
Normal file
6
userspace/lib/cross/src/signal.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use crate::sys;
|
||||
|
||||
|
||||
pub fn set_sigint_handler(handler: fn()) {
|
||||
sys::set_sigint_handler(handler);
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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) };
|
||||
}
|
||||
|
@ -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()) {
|
||||
}
|
||||
|
@ -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]
|
||||
|
66
userspace/lib/libcolors/examples/bar.rs
Normal file
66
userspace/lib/libcolors/examples/bar.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
61
userspace/lib/libcolors/examples/window.rs
Normal file
61
userspace/lib/libcolors/examples/window.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
24
userspace/lib/libcolors/src/input.rs
Normal file
24
userspace/lib/libcolors/src/input.rs
Normal 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,
|
||||
}
|
@ -13,3 +13,4 @@ pub mod error;
|
||||
|
||||
pub mod event;
|
||||
pub mod message;
|
||||
pub mod input;
|
||||
|
@ -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,
|
||||
|
10
userspace/lib/libpsf/Cargo.toml
Normal file
10
userspace/lib/libpsf/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "libpsf"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bytemuck.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
20
userspace/lib/logsink/Cargo.toml
Normal file
20
userspace/lib/logsink/Cargo.toml
Normal 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
|
11
userspace/lib/logsink/src/lib.rs
Normal file
11
userspace/lib/logsink/src/lib.rs
Normal 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();
|
||||
}
|
43
userspace/lib/logsink/src/yggdrasil.rs
Normal file
43
userspace/lib/logsink/src/yggdrasil.rs
Normal 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();
|
||||
}
|
1
userspace/lib/ygglibc/Cargo.lock
generated
1
userspace/lib/ygglibc/Cargo.lock
generated
@ -591,6 +591,7 @@ dependencies = [
|
||||
"cbindgen",
|
||||
"chrono",
|
||||
"libyalloc",
|
||||
"log",
|
||||
"yggdrasil-abi",
|
||||
"yggdrasil-rt",
|
||||
]
|
||||
|
@ -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 }
|
||||
|
||||
|
66
userspace/lib/ygglibc/src/debug.rs
Normal file
66
userspace/lib/ygglibc/src/debug.rs
Normal 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();
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)?;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 !!!");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user