#![no_std]
extern crate alloc;
use rustix::{
fd::{FromRawFd, OwnedFd},
net::AddressFamily,
};
use ::alloc::{boxed::Box, string::String, vec::Vec};
use types::ObjectId;
mod wayland;
pub use bitflags;
pub use rustix;
pub mod objman;
pub mod shm;
pub mod types;
pub mod wire;
pub use wire::Error;
pub struct Waybackend {
pub wire_msg_builder: wire::MessageBuilder,
pub wayland_fd: OwnedFd,
}
impl Waybackend {
#[inline]
#[must_use]
fn new(wayland_fd: OwnedFd) -> Self {
Self {
wire_msg_builder: wire::MessageBuilder::new(),
wayland_fd,
}
}
#[inline]
pub fn flush(&mut self) -> Result<(), wire::Error> {
self.wire_msg_builder.flush(&self.wayland_fd)
}
}
use core::num::NonZeroU32;
pub const WL_DISPLAY: types::ObjectId = types::ObjectId::new(NonZeroU32::new(1).unwrap());
#[derive(Debug)]
pub enum ConnectionError {
InvalidWaylandSocketEnvVar,
InvalidSocketAddrFamily(rustix::net::AddressFamily),
GetSocketNameFailed(rustix::io::Errno),
SocketCreationFailed(rustix::io::Errno),
ConnectionFailed(rustix::io::Errno),
}
impl core::fmt::Display for ConnectionError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
ConnectionError::InvalidWaylandSocketEnvVar => write!(
f,
"WAYLAND_SOCKET environment variable contains a value we failed to parse"
),
ConnectionError::InvalidSocketAddrFamily(actual) => write!(
f,
"socket address in WAYLAND_SOCKET is not a unix socket. It's actual address family is: {actual:?}"
),
ConnectionError::GetSocketNameFailed(errno) => {
write!(f, "failed to get socket name: {errno}")
}
ConnectionError::SocketCreationFailed(errno) => {
write!(f, "failed to create socket: {errno}")
}
ConnectionError::ConnectionFailed(errno) => {
write!(f, "failed to connect to the unix stream: {errno}")
}
}
}
}
impl core::error::Error for ConnectionError {}
fn get_env(var: &core::ffi::CStr) -> Option<&'static core::ffi::CStr> {
unsafe {
let ptr = libc::getenv(var.as_ptr());
if !ptr.is_null() {
Some(core::ffi::CStr::from_ptr(ptr))
} else {
None
}
}
}
#[inline]
pub fn connect<T: Copy + PartialEq>(
display: T,
) -> Result<(Waybackend, objman::ObjectManager<T>, wire::Receiver), ConnectionError> {
let objman = objman::ObjectManager::new(display);
let receiver = wire::Receiver::new();
if let Some(txt) = get_env(c"WAYLAND_SOCKET") {
let fd = txt
.to_str()
.map(str::parse::<i32>)
.map_err(|_| ConnectionError::InvalidWaylandSocketEnvVar)?
.map_err(|_| ConnectionError::InvalidWaylandSocketEnvVar)?;
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
match rustix::net::getsockname(&fd) {
Ok(socket_addr) => {
if socket_addr.address_family() == AddressFamily::UNIX {
Ok((Waybackend::new(fd), objman, receiver))
} else {
Err(ConnectionError::InvalidSocketAddrFamily(
socket_addr.address_family(),
))
}
}
Err(e) => Err(ConnectionError::GetSocketNameFailed(e)),
}
} else {
let socket_name = get_env(c"WAYLAND_DISPLAY").unwrap_or_else(|| {
log::warn!("WAYLAND_DISPLAY is not set! Defaulting to wayland-0");
c"wayland-0"
});
let unix_addr = if socket_name.to_bytes()[0] == b'/' {
rustix::net::SocketAddrUnix::new(socket_name).unwrap()
} else {
use core::fmt::Write;
let mut socket_fullpath = String::new();
match get_env(c"XDG_RUNTIME_DIR") {
Some(socket_path) => {
core::write!(&mut socket_fullpath, "{}/", socket_path.to_str().unwrap())
.unwrap()
}
None => {
log::warn!("XDG_RUNTIME_DIR is not set! Defaulting to /run/user/UID");
let uid = rustix::process::getuid();
core::write!(&mut socket_fullpath, "/run/user/{}/", uid.as_raw()).unwrap();
}
}
core::write!(&mut socket_fullpath, "{}", socket_name.to_str().unwrap()).unwrap();
rustix::net::SocketAddrUnix::new(socket_fullpath.as_str()).unwrap()
};
let socket = rustix::net::socket_with(
rustix::net::AddressFamily::UNIX,
rustix::net::SocketType::STREAM,
rustix::net::SocketFlags::CLOEXEC,
None,
)
.map_err(ConnectionError::SocketCreationFailed)?;
rustix::net::connect(&socket, &unix_addr).map_err(ConnectionError::ConnectionFailed)?;
Ok((Waybackend::new(socket), objman, receiver))
}
}
#[derive(Debug)]
pub enum RoundtripError {
WireError(wire::Error),
WaylandError((ObjectId, u32, Box<str>)),
MessageFromUnknownObject(ObjectId),
UnexpectedDeleteId(u32),
}
impl core::fmt::Display for RoundtripError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
RoundtripError::WireError(error) => {
write!(f, "roundtrip failed due to wayland wire error: {error}")
}
RoundtripError::MessageFromUnknownObject(id) => {
write!(f, "received message from unknown object of id: {id}")
}
RoundtripError::UnexpectedDeleteId(id) => {
write!(
f,
"Received a delete_id message from the display for id: {id}.\n\
This should never happen during the roundtrip initialization.\n\
This wayland implementation is probably fucked."
)
}
RoundtripError::WaylandError((id, code, msg)) => write!(
f,
"Wayland protocol error. Object: {id}. Code: {code}. Message: {msg}"
),
}
}
}
impl core::error::Error for RoundtripError {}
#[derive(Debug)]
pub struct Global {
name: u32,
interface: Box<str>,
version: u32,
}
impl Global {
#[inline]
pub fn name(&self) -> u32 {
self.name
}
#[inline]
pub fn interface(&self) -> &str {
&self.interface
}
#[inline]
pub fn version(&self) -> u32 {
self.version
}
#[inline]
pub fn bind<T: Copy + PartialEq>(
&self,
backend: &mut crate::Waybackend,
registry: ObjectId,
objman: &mut objman::ObjectManager<T>,
object: T,
) -> Result<(), wire::Error> {
let id = objman.create(object);
wayland::wl_registry::req::bind(
backend,
registry,
self.name,
id,
&self.interface,
self.version,
)
}
}
struct GlobalHandler {
globals: Vec<Global>,
error: Option<RoundtripError>,
done: bool,
delete_callback: bool,
}
impl GlobalHandler {
fn new() -> Self {
Self {
globals: Vec::new(),
error: None,
done: false,
delete_callback: false,
}
}
}
impl wayland::wl_display::EvHandler for GlobalHandler {
fn error(&mut self, _: ObjectId, object_id: ObjectId, code: u32, message: &str) {
self.error = Some(RoundtripError::WaylandError((
object_id,
code,
Box::from(message),
)));
}
fn delete_id(&mut self, _: ObjectId, id: u32) {
if id != 3 {
self.error = Some(RoundtripError::UnexpectedDeleteId(id));
} else {
self.delete_callback = true;
}
}
}
impl wayland::wl_registry::EvHandler for GlobalHandler {
fn global(&mut self, _: ObjectId, name: u32, interface: &str, version: u32) {
self.globals.push(Global {
name,
interface: Box::from(interface),
version,
});
}
fn global_remove(&mut self, _: ObjectId, name: u32) {
self.globals.retain(|g| g.name != name)
}
}
impl wayland::wl_callback::EvHandler for GlobalHandler {
fn done(&mut self, _: ObjectId, _: u32) {
self.done = true;
}
}
#[inline]
pub fn roundtrip(
backend: &mut Waybackend,
receiver: &mut wire::Receiver,
registry_id: ObjectId,
callback_id: ObjectId,
) -> Result<(Vec<Global>, bool), RoundtripError> {
let mut global_handler = GlobalHandler::new();
wayland::wl_display::req::get_registry(backend, WL_DISPLAY, registry_id)
.map_err(RoundtripError::WireError)?;
wayland::wl_display::req::sync(backend, WL_DISPLAY, callback_id)
.map_err(RoundtripError::WireError)?;
backend.flush().map_err(RoundtripError::WireError)?;
while !global_handler.done && global_handler.error.is_none() {
let mut msgs = receiver
.recv(&backend.wayland_fd)
.map_err(RoundtripError::WireError)?;
while let Some(sender_id) = msgs.next() {
match sender_id.map_err(RoundtripError::WireError)? {
WL_DISPLAY => wayland::wl_display::event(&mut global_handler, &mut msgs)
.map_err(RoundtripError::WireError)?,
id if id == registry_id => {
wayland::wl_registry::event(&mut global_handler, &mut msgs)
.map_err(RoundtripError::WireError)?
}
id if id == callback_id => {
wayland::wl_callback::event(&mut global_handler, &mut msgs)
.map_err(RoundtripError::WireError)?
}
otherwise => return Err(RoundtripError::MessageFromUnknownObject(otherwise)),
}
}
}
if let Some(error) = global_handler.error {
return Err(error);
}
Ok((global_handler.globals, global_handler.delete_callback))
}
#[macro_export]
macro_rules! bind_globals {
(
$backend:ident,
$objman:ident,
$registry:ident,
$globals:ident,
$(($interface:ident, $object:path)),*$(,)?
) => {
for global in $globals.iter() {
match global.interface() {
$(
$interface::NAME => {
global.bind(&mut $backend, $registry, &mut $objman, $object)
.expect("failed to bind global");
}
)*
_ => (),
}
}
}
}
#[macro_export]
macro_rules! match_enum_with_interface {
($handler:ident, $object:ident, $msgs:ident, $(($variant:path, $interface:ident)),*$(,)?) => {
match $object {
$(
$variant => {
$interface::event(&mut $handler, &mut $msgs)
.expect("failed to dispatch event handler");
}
)*
}
}
}