portal: start portal automatically with compositor
This commit is contained in:
parent
39d1e49672
commit
4d6f226254
14 changed files with 331 additions and 125 deletions
3
etc/jay-portals.conf
Normal file
3
etc/jay-portals.conf
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[preferred]
|
||||||
|
default=gtk
|
||||||
|
org.freedesktop.impl.portal.ScreenCast=jay
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
[portal]
|
[portal]
|
||||||
DBusName=org.freedesktop.impl.portal.desktop.jay
|
DBusName=org.freedesktop.impl.portal.desktop.jay
|
||||||
Interfaces=org.freedesktop.impl.portal.ScreenCast;
|
Interfaces=org.freedesktop.impl.portal.ScreenCast;
|
||||||
UseIn=jay
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
[D-BUS Service]
|
|
||||||
Name=org.freedesktop.impl.portal.desktop.jay
|
|
||||||
Exec=/bin/false
|
|
||||||
SystemdService=xdg-desktop-portal-jay.service
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Jay Portal
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=dbus
|
|
||||||
BusName=org.freedesktop.impl.portal.desktop.jay
|
|
||||||
ExecStart=/home/julian/bin/jay portal
|
|
||||||
|
|
@ -226,7 +226,7 @@ pub fn main() {
|
||||||
Cmd::Unlock => unlock::main(cli.global),
|
Cmd::Unlock => unlock::main(cli.global),
|
||||||
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
|
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
|
||||||
Cmd::SeatTest(a) => seat_test::main(cli.global, a),
|
Cmd::SeatTest(a) => seat_test::main(cli.global, a),
|
||||||
Cmd::Portal => portal::run(cli.global),
|
Cmd::Portal => portal::run_freestanding(cli.global),
|
||||||
Cmd::Randr(a) => randr::main(cli.global, a),
|
Cmd::Randr(a) => randr::main(cli.global, a),
|
||||||
Cmd::Input(a) => input::main(cli.global, a),
|
Cmd::Input(a) => input::main(cli.global, a),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
io_uring::{IoUring, IoUringError},
|
io_uring::{IoUring, IoUringError},
|
||||||
leaks,
|
leaks,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
|
portal::{self, PortalStartup},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
sighand::{self, SighandError},
|
sighand::{self, SighandError},
|
||||||
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
|
||||||
|
|
@ -52,8 +53,16 @@ pub const MAX_EXTENTS: i32 = (1 << 22) - 1;
|
||||||
|
|
||||||
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
let forker = create_forker();
|
let forker = create_forker();
|
||||||
|
let portal = portal::run_from_compositor(global.log_level.into());
|
||||||
let logger = Logger::install_compositor(global.log_level.into());
|
let logger = Logger::install_compositor(global.log_level.into());
|
||||||
let res = start_compositor2(Some(forker), Some(logger.clone()), args, None);
|
let portal = match portal {
|
||||||
|
Ok(p) => Some(p),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not spawn portal: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res = start_compositor2(Some(forker), portal, Some(logger.clone()), args, None);
|
||||||
leaks::log_leaked();
|
leaks::log_leaked();
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
let e = ErrorFmt(e);
|
let e = ErrorFmt(e);
|
||||||
|
|
@ -67,7 +76,7 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
|
||||||
|
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
|
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
|
||||||
let res = start_compositor2(None, None, RunArgs::default(), Some(future));
|
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future));
|
||||||
leaks::log_leaked();
|
leaks::log_leaked();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +115,7 @@ pub type TestFuture = Box<dyn Fn(&Rc<State>) -> Box<dyn Future<Output = ()>>>;
|
||||||
|
|
||||||
fn start_compositor2(
|
fn start_compositor2(
|
||||||
forker: Option<Rc<ForkerProxy>>,
|
forker: Option<Rc<ForkerProxy>>,
|
||||||
|
portal: Option<PortalStartup>,
|
||||||
logger: Option<Arc<Logger>>,
|
logger: Option<Arc<Logger>>,
|
||||||
run_args: RunArgs,
|
run_args: RunArgs,
|
||||||
test_future: Option<TestFuture>,
|
test_future: Option<TestFuture>,
|
||||||
|
|
@ -161,7 +171,7 @@ fn start_compositor2(
|
||||||
pending_float_titles: Default::default(),
|
pending_float_titles: Default::default(),
|
||||||
dbus: Dbus::new(&engine, &ring, &run_toplevel),
|
dbus: Dbus::new(&engine, &ring, &run_toplevel),
|
||||||
fdcloser: FdCloser::new(),
|
fdcloser: FdCloser::new(),
|
||||||
logger,
|
logger: logger.clone(),
|
||||||
connectors: Default::default(),
|
connectors: Default::default(),
|
||||||
outputs: Default::default(),
|
outputs: Default::default(),
|
||||||
drm_devs: Default::default(),
|
drm_devs: Default::default(),
|
||||||
|
|
@ -225,6 +235,10 @@ fn start_compositor2(
|
||||||
forker.setenv(key.as_bytes(), val.as_bytes());
|
forker.setenv(key.as_bytes(), val.as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut _portal = None;
|
||||||
|
if let (Some(portal), Some(logger)) = (portal, &logger) {
|
||||||
|
_portal = Some(engine.spawn(portal.spawn(engine.clone(), ring.clone(), logger.clone())));
|
||||||
|
}
|
||||||
let _compositor = engine.spawn(start_compositor3(state.clone(), test_future));
|
let _compositor = engine.spawn(start_compositor3(state.clone(), test_future));
|
||||||
ring.run()?;
|
ring.run()?;
|
||||||
state.clear();
|
state.clear();
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
mod clone3;
|
|
||||||
mod io;
|
mod io;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
compositor::{DISPLAY, WAYLAND_DISPLAY},
|
compositor::{DISPLAY, WAYLAND_DISPLAY},
|
||||||
forker::{
|
forker::io::{IoIn, IoOut},
|
||||||
clone3::{fork_with_pidfd, Forked},
|
|
||||||
io::{IoIn, IoOut},
|
|
||||||
},
|
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
buffd::BufFdError, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
buffd::BufFdError,
|
||||||
|
clone3::{fork_with_pidfd, Forked},
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
numcell::NumCell,
|
||||||
|
process_name::set_process_name,
|
||||||
queue::AsyncQueue,
|
queue::AsyncQueue,
|
||||||
},
|
},
|
||||||
xwayland,
|
xwayland,
|
||||||
|
|
@ -323,7 +324,7 @@ impl Forker {
|
||||||
env::set_var("XDG_SESSION_TYPE", "wayland");
|
env::set_var("XDG_SESSION_TYPE", "wayland");
|
||||||
env::remove_var(DISPLAY);
|
env::remove_var(DISPLAY);
|
||||||
env::remove_var(WAYLAND_DISPLAY);
|
env::remove_var(WAYLAND_DISPLAY);
|
||||||
setup_name("the ol' forker");
|
set_process_name("the ol' forker");
|
||||||
setup_deathsig(ppid);
|
setup_deathsig(ppid);
|
||||||
reset_signals();
|
reset_signals();
|
||||||
let socket = Rc::new(setup_fds(socket));
|
let socket = Rc::new(setup_fds(socket));
|
||||||
|
|
@ -568,13 +569,6 @@ fn setup_deathsig(ppid: c::pid_t) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_name(name: &str) {
|
|
||||||
unsafe {
|
|
||||||
let name = name.into_ustr();
|
|
||||||
c::prctl(c::PR_SET_NAME, name.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_fds(fds: Vec<(i32, OwnedFd)>) -> Result<Vec<OwnedFd>, SpawnError> {
|
fn map_fds(fds: Vec<(i32, OwnedFd)>) -> Result<Vec<OwnedFd>, SpawnError> {
|
||||||
let mut desired: Vec<_> = fds.iter().map(|v| v.0).collect();
|
let mut desired: Vec<_> = fds.iter().map(|v| v.0).collect();
|
||||||
desired.sort_by(|a, b| b.cmp(a));
|
desired.sort_by(|a, b| b.cmp(a));
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,10 @@ impl JayCompositor {
|
||||||
let log_file = Rc::new(JayLogFile::new(req.id, &self.client));
|
let log_file = Rc::new(JayLogFile::new(req.id, &self.client));
|
||||||
track!(self.client, log_file);
|
track!(self.client, log_file);
|
||||||
self.client.add_client_obj(&log_file)?;
|
self.client.add_client_obj(&log_file)?;
|
||||||
let path = match &self.client.state.logger {
|
match &self.client.state.logger {
|
||||||
Some(logger) => logger.path(),
|
Some(logger) => log_file.send_path(logger.path().as_bstr()),
|
||||||
_ => "".as_bytes().as_bstr(),
|
_ => log_file.send_path(b"".as_bstr()),
|
||||||
};
|
};
|
||||||
log_file.send_path(path);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
138
src/logger.rs
138
src/logger.rs
|
|
@ -1,8 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::utils::{errorfmt::ErrorFmt, oserror::OsError},
|
crate::utils::{errorfmt::ErrorFmt, oserror::OsError},
|
||||||
backtrace::Backtrace,
|
backtrace::Backtrace,
|
||||||
bstr::{BStr, BString, ByteSlice},
|
bstr::BString,
|
||||||
log::{Level, Log, Metadata, Record},
|
log::{Level, Log, Metadata, Record},
|
||||||
|
parking_lot::Mutex,
|
||||||
std::{
|
std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
fs::DirBuilder,
|
fs::DirBuilder,
|
||||||
|
|
@ -10,12 +11,12 @@ use {
|
||||||
os::unix::{ffi::OsStringExt, fs::DirBuilderExt},
|
os::unix::{ffi::OsStringExt, fs::DirBuilderExt},
|
||||||
ptr,
|
ptr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU32, Ordering::Relaxed},
|
atomic::{AtomicI32, AtomicU32, Ordering::Relaxed},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
},
|
},
|
||||||
uapi::{c, format_ustr, Errno, Fd, OwnedFd},
|
uapi::{c, format_ustr, Errno, Fd, OwnedFd, Ustring},
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
|
@ -24,8 +25,9 @@ thread_local! {
|
||||||
|
|
||||||
pub struct Logger {
|
pub struct Logger {
|
||||||
level: AtomicU32,
|
level: AtomicU32,
|
||||||
path: BString,
|
path: Mutex<Arc<BString>>,
|
||||||
file: OwnedFd,
|
_file: Mutex<OwnedFd>,
|
||||||
|
file_fd: AtomicI32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
|
|
@ -37,67 +39,31 @@ impl Logger {
|
||||||
fatal!("Error: Could not dup stderr: {}", ErrorFmt(e));
|
fatal!("Error: Could not dup stderr: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Self::install(level, b"", file)
|
Self::install(level, b"STDERR", file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_compositor(level: Level) -> Arc<Self> {
|
pub fn install_compositor(level: Level) -> Arc<Self> {
|
||||||
let log_dir = create_log_dir();
|
let (path, file) = open_log_file("jay");
|
||||||
let (path, file) = 'file: {
|
|
||||||
for i in 0.. {
|
|
||||||
let file_name = format_ustr!(
|
|
||||||
"{}/jay-{}-{}.txt",
|
|
||||||
log_dir,
|
|
||||||
humantime::format_rfc3339_millis(SystemTime::now()),
|
|
||||||
i,
|
|
||||||
);
|
|
||||||
match uapi::open(
|
|
||||||
&file_name,
|
|
||||||
c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY,
|
|
||||||
0o644,
|
|
||||||
) {
|
|
||||||
Ok(f) => break 'file (file_name, f),
|
|
||||||
Err(Errno(c::EEXIST)) => {}
|
|
||||||
Err(e) => {
|
|
||||||
let e: OsError = e.into();
|
|
||||||
fatal!("Error: Could not create log file: {}", ErrorFmt(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
std::panic::set_hook(Box::new(|p| {
|
|
||||||
if let Some(loc) = p.location() {
|
|
||||||
log::error!(
|
|
||||||
"Panic at {} line {} column {}",
|
|
||||||
loc.file(),
|
|
||||||
loc.line(),
|
|
||||||
loc.column()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log::error!("Panic at unknown location");
|
|
||||||
}
|
|
||||||
if let Some(msg) = p.payload().downcast_ref::<&str>() {
|
|
||||||
log::error!("Message: {}", msg);
|
|
||||||
}
|
|
||||||
if let Some(msg) = p.payload().downcast_ref::<String>() {
|
|
||||||
log::error!("Message: {}", msg);
|
|
||||||
}
|
|
||||||
log::error!("Backtrace:\n{:?}", Backtrace::new());
|
|
||||||
}));
|
|
||||||
Self::install(level, path.as_bytes(), file)
|
Self::install(level, path.as_bytes(), file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn install_pipe(file: OwnedFd, level: Level) -> Arc<Self> {
|
||||||
|
Self::install(level, b"PIPE", file)
|
||||||
|
}
|
||||||
|
|
||||||
fn install(level: Level, path: &[u8], file: OwnedFd) -> Arc<Self> {
|
fn install(level: Level, path: &[u8], file: OwnedFd) -> Arc<Self> {
|
||||||
let slf = Arc::new(Self {
|
let slf = Arc::new(Self {
|
||||||
level: AtomicU32::new(level as _),
|
level: AtomicU32::new(level as _),
|
||||||
path: path.to_vec().into(),
|
path: Mutex::new(Arc::new(path.to_vec().into())),
|
||||||
file,
|
file_fd: AtomicI32::new(file.raw()),
|
||||||
|
_file: Mutex::new(file),
|
||||||
});
|
});
|
||||||
log::set_boxed_logger(Box::new(LogWrapper {
|
log::set_boxed_logger(Box::new(LogWrapper {
|
||||||
logger: slf.clone(),
|
logger: slf.clone(),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
log::set_max_level(level.to_level_filter());
|
log::set_max_level(level.to_level_filter());
|
||||||
|
set_panic_hook();
|
||||||
slf
|
slf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,17 +72,57 @@ impl Logger {
|
||||||
log::set_max_level(level.to_level_filter());
|
log::set_max_level(level.to_level_filter());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> &BStr {
|
pub fn path(&self) -> Arc<BString> {
|
||||||
self.path.as_bstr()
|
self.path.lock().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redirect(&self, ty: &str) {
|
||||||
|
let (file, fd) = open_log_file(ty);
|
||||||
|
log::info!("Redirecting logs to {}", file.display());
|
||||||
|
*self.path.lock() = Arc::new(file.as_bytes().into());
|
||||||
|
self.file_fd.store(fd.raw(), Relaxed);
|
||||||
|
*self._file.lock() = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_raw(&self, buf: &[u8]) {
|
||||||
|
let mut fd = Fd::new(self.file_fd.load(Relaxed));
|
||||||
|
let _ = fd.write_all(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_log_dir() -> BString {
|
pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) {
|
||||||
|
let log_dir = create_log_dir(ty);
|
||||||
|
for i in 0.. {
|
||||||
|
let file_name = format_ustr!(
|
||||||
|
"{}/{ty}-{}-{}.txt",
|
||||||
|
log_dir,
|
||||||
|
humantime::format_rfc3339_millis(SystemTime::now()),
|
||||||
|
i,
|
||||||
|
);
|
||||||
|
match uapi::open(
|
||||||
|
&file_name,
|
||||||
|
c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY,
|
||||||
|
0o644,
|
||||||
|
) {
|
||||||
|
Ok(f) => return (file_name, f),
|
||||||
|
Err(Errno(c::EEXIST)) => {}
|
||||||
|
Err(e) => {
|
||||||
|
let e: OsError = e.into();
|
||||||
|
fatal!("Error: Could not create log file: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_log_dir(ty: &str) -> BString {
|
||||||
let mut log_dir = match dirs::data_local_dir() {
|
let mut log_dir = match dirs::data_local_dir() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => fatal!("Error: $HOME is not set"),
|
None => fatal!("Error: $HOME is not set"),
|
||||||
};
|
};
|
||||||
log_dir.push("jay");
|
log_dir.push("jay");
|
||||||
|
log_dir.push("logs");
|
||||||
|
log_dir.push(ty);
|
||||||
let res = DirBuilder::new()
|
let res = DirBuilder::new()
|
||||||
.recursive(true)
|
.recursive(true)
|
||||||
.mode(0o755)
|
.mode(0o755)
|
||||||
|
|
@ -131,6 +137,28 @@ fn create_log_dir() -> BString {
|
||||||
log_dir.into_os_string().into_vec().into()
|
log_dir.into_os_string().into_vec().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_panic_hook() {
|
||||||
|
std::panic::set_hook(Box::new(|p| {
|
||||||
|
if let Some(loc) = p.location() {
|
||||||
|
log::error!(
|
||||||
|
"Panic at {} line {} column {}",
|
||||||
|
loc.file(),
|
||||||
|
loc.line(),
|
||||||
|
loc.column()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::error!("Panic at unknown location");
|
||||||
|
}
|
||||||
|
if let Some(msg) = p.payload().downcast_ref::<&str>() {
|
||||||
|
log::error!("Message: {}", msg);
|
||||||
|
}
|
||||||
|
if let Some(msg) = p.payload().downcast_ref::<String>() {
|
||||||
|
log::error!("Message: {}", msg);
|
||||||
|
}
|
||||||
|
log::error!("Backtrace:\n{:?}", Backtrace::new());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
struct LogWrapper {
|
struct LogWrapper {
|
||||||
logger: Arc<Logger>,
|
logger: Arc<Logger>,
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +198,7 @@ impl Log for LogWrapper {
|
||||||
record.args(),
|
record.args(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut fd = Fd::new(self.logger.file.raw());
|
let mut fd = Fd::new(self.logger.file_fd.load(Relaxed));
|
||||||
let _ = fd.write_all(buffer);
|
let _ = fd.write_all(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
205
src/portal.rs
205
src/portal.rs
|
|
@ -11,8 +11,9 @@ use {
|
||||||
Dbus, DbusSocket, BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
Dbus, DbusSocket, BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
||||||
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
|
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
|
||||||
},
|
},
|
||||||
|
forker::ForkerError,
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
logger,
|
logger::Logger,
|
||||||
pipewire::pw_con::{PwCon, PwConHolder, PwConOwner},
|
pipewire::pw_con::{PwCon, PwConHolder, PwConOwner},
|
||||||
portal::{
|
portal::{
|
||||||
ptl_display::{watch_displays, PortalDisplay, PortalDisplayId},
|
ptl_display::{watch_displays, PortalDisplay, PortalDisplayId},
|
||||||
|
|
@ -20,15 +21,29 @@ use {
|
||||||
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
buf::Buf,
|
||||||
run_toplevel::RunToplevel, xrd::xrd,
|
clone3::{fork_with_pidfd, Forked},
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::OsError,
|
||||||
|
process_name::set_process_name,
|
||||||
|
run_toplevel::RunToplevel,
|
||||||
|
vecdeque_ext::VecDequeExt,
|
||||||
|
xrd::xrd,
|
||||||
},
|
},
|
||||||
video::dmabuf::DmaBufIds,
|
video::dmabuf::DmaBufIds,
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire_dbus::org,
|
wire_dbus::org,
|
||||||
},
|
},
|
||||||
std::rc::{Rc, Weak},
|
log::Level,
|
||||||
uapi::c,
|
std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
sync::Arc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{c, OwnedFd},
|
||||||
};
|
};
|
||||||
|
|
||||||
const PORTAL_SUCCESS: u32 = 0;
|
const PORTAL_SUCCESS: u32 = 0;
|
||||||
|
|
@ -37,8 +52,110 @@ const PORTAL_CANCELLED: u32 = 1;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const PORTAL_ENDED: u32 = 2;
|
const PORTAL_ENDED: u32 = 2;
|
||||||
|
|
||||||
pub fn run(global: GlobalArgs) {
|
pub fn run_freestanding(global: GlobalArgs) {
|
||||||
logger::Logger::install_stderr(global.log_level.into());
|
let logger = Logger::install_stderr(global.log_level.into());
|
||||||
|
run(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum PortalError {
|
||||||
|
#[error("Could not create pipe")]
|
||||||
|
CreatePipe(#[source] OsError),
|
||||||
|
#[error("Could not fork")]
|
||||||
|
Fork(#[source] ForkerError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PortalStartup {
|
||||||
|
logs: Rc<OwnedFd>,
|
||||||
|
pid: c::pid_t,
|
||||||
|
pidfd: Rc<OwnedFd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PortalStartup {
|
||||||
|
pub async fn spawn(self, eng: Rc<AsyncEngine>, ring: Rc<IoUring>, logger: Arc<Logger>) {
|
||||||
|
let f1 = eng.spawn({
|
||||||
|
let ring = ring.clone();
|
||||||
|
async move {
|
||||||
|
if let Err(e) = ring.readable(&self.pidfd).await {
|
||||||
|
log::error!(
|
||||||
|
"Could not wait for portal pidfd to become readable: {}",
|
||||||
|
ErrorFmt(e)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (_, status) = match uapi::waitpid(self.pid, 0) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Could not retrieve exit status of portal ({}): {}",
|
||||||
|
self.pid,
|
||||||
|
ErrorFmt(OsError::from(e))
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let status = uapi::WEXITSTATUS(status);
|
||||||
|
if status != 0 {
|
||||||
|
log::error!("Portal exited with non-0 exit code: {status}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let f2 = eng.spawn({
|
||||||
|
let ring = ring.clone();
|
||||||
|
let logger = logger.clone();
|
||||||
|
async move {
|
||||||
|
let mut buf = VecDeque::<u8>::new();
|
||||||
|
let mut buf2 = Buf::new(1024);
|
||||||
|
let mut done = false;
|
||||||
|
while !done {
|
||||||
|
match ring.read(&self.logs, buf2.clone()).await {
|
||||||
|
Ok(n) if n > 0 => buf.extend(&buf2[..n]),
|
||||||
|
Ok(_) => done = true,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not read portal logs: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
while let Some(pos) = buf.iter().position(|b| b == &b'\n') {
|
||||||
|
let (left, right) = buf.get_slices(..pos);
|
||||||
|
logger.write_raw(left);
|
||||||
|
logger.write_raw(right);
|
||||||
|
logger.write_raw(b" (portal)\n");
|
||||||
|
buf.drain(..=pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
f1.await;
|
||||||
|
f2.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_from_compositor(level: Level) -> Result<PortalStartup, PortalError> {
|
||||||
|
let (read, write) = match uapi::pipe2(c::O_CLOEXEC) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(PortalError::CreatePipe(e.into())),
|
||||||
|
};
|
||||||
|
let fork = match fork_with_pidfd(false) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => return Err(PortalError::Fork(e)),
|
||||||
|
};
|
||||||
|
match fork {
|
||||||
|
Forked::Parent { pidfd, pid } => Ok(PortalStartup {
|
||||||
|
logs: Rc::new(read),
|
||||||
|
pid,
|
||||||
|
pidfd: Rc::new(pidfd),
|
||||||
|
}),
|
||||||
|
Forked::Child { .. } => {
|
||||||
|
drop(read);
|
||||||
|
let logger = Logger::install_pipe(write, level);
|
||||||
|
run(logger);
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(logger: Arc<Logger>) {
|
||||||
let eng = AsyncEngine::new();
|
let eng = AsyncEngine::new();
|
||||||
let ring = match IoUring::new(&eng, 32) {
|
let ring = match IoUring::new(&eng, 32) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
|
@ -46,13 +163,16 @@ pub fn run(global: GlobalArgs) {
|
||||||
fatal!("Could not create an IO-uring: {}", ErrorFmt(e));
|
fatal!("Could not create an IO-uring: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _f = eng.spawn(run_async(eng.clone(), ring.clone()));
|
let _f = eng.spawn(run_async(eng.clone(), ring.clone(), logger));
|
||||||
if let Err(e) = ring.run() {
|
if let Err(e) = ring.run() {
|
||||||
fatal!("The IO-uring returned an error: {}", ErrorFmt(e));
|
fatal!("The IO-uring returned an error: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_async(eng: Rc<AsyncEngine>, ring: Rc<IoUring>) {
|
async fn run_async(eng: Rc<AsyncEngine>, ring: Rc<IoUring>, logger: Arc<Logger>) {
|
||||||
|
let (_rtl_future, rtl) = RunToplevel::install(&eng);
|
||||||
|
let dbus = Dbus::new(&eng, &ring, &rtl);
|
||||||
|
let dbus = init_dbus_session(&dbus, logger).await;
|
||||||
let xrd = match xrd() {
|
let xrd = match xrd() {
|
||||||
Some(xrd) => xrd,
|
Some(xrd) => xrd,
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -71,9 +191,6 @@ async fn run_async(eng: Rc<AsyncEngine>, ring: Rc<IoUring>) {
|
||||||
fatal!("Could not connect to pipewire: {}", ErrorFmt(e));
|
fatal!("Could not connect to pipewire: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (_rtl_future, rtl) = RunToplevel::install(&eng);
|
|
||||||
let dbus = Dbus::new(&eng, &ring, &rtl);
|
|
||||||
let dbus = init_dbus_session(&dbus).await;
|
|
||||||
let state = Rc::new(PortalState {
|
let state = Rc::new(PortalState {
|
||||||
xrd,
|
xrd,
|
||||||
ring,
|
ring,
|
||||||
|
|
@ -101,37 +218,53 @@ async fn run_async(eng: Rc<AsyncEngine>, ring: Rc<IoUring>) {
|
||||||
|
|
||||||
const UNIQUE_NAME: &str = "org.freedesktop.impl.portal.desktop.jay";
|
const UNIQUE_NAME: &str = "org.freedesktop.impl.portal.desktop.jay";
|
||||||
|
|
||||||
async fn init_dbus_session(dbus: &Dbus) -> Rc<DbusSocket> {
|
async fn init_dbus_session(dbus: &Dbus, logger: Arc<Logger>) -> Rc<DbusSocket> {
|
||||||
let session = match dbus.session().await {
|
let session = match dbus.session().await {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
fatal!("Could not connect to dbus session daemon: {}", ErrorFmt(e));
|
fatal!("Could not connect to dbus session daemon: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session.call(
|
let rv = session
|
||||||
BUS_DEST,
|
.call_async(
|
||||||
BUS_PATH,
|
BUS_DEST,
|
||||||
org::freedesktop::dbus::RequestName {
|
BUS_PATH,
|
||||||
name: UNIQUE_NAME.into(),
|
org::freedesktop::dbus::RequestName {
|
||||||
flags: DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
name: UNIQUE_NAME.into(),
|
||||||
},
|
flags: DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
||||||
|rv| match rv {
|
},
|
||||||
Ok(r) if r.rv == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER => {
|
)
|
||||||
log::info!("Acquired unique name {}", UNIQUE_NAME);
|
.await;
|
||||||
return;
|
match rv {
|
||||||
|
Ok(r) if r.get().rv == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER => {
|
||||||
|
log::info!("Acquired unique name {}", UNIQUE_NAME);
|
||||||
|
logger.redirect("portal");
|
||||||
|
let fork = match fork_with_pidfd(false) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => fatal!("Could not fork: {}", ErrorFmt(e)),
|
||||||
|
};
|
||||||
|
match fork {
|
||||||
|
Forked::Parent { .. } => std::process::exit(0),
|
||||||
|
Forked::Child { .. } => {
|
||||||
|
if let Err(e) = uapi::setsid() {
|
||||||
|
log::error!("setsid failed: {}", ErrorFmt(OsError::from(e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(r) => {
|
set_process_name("jay portal");
|
||||||
fatal!("Could not acquire unique name {}: {}", UNIQUE_NAME, r.rv);
|
session
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Ok(_) => {
|
||||||
fatal!(
|
log::info!("Portal is already running");
|
||||||
"Could not communicate with the session bus: {}",
|
std::process::exit(0);
|
||||||
ErrorFmt(e)
|
}
|
||||||
);
|
Err(e) => {
|
||||||
}
|
fatal!(
|
||||||
},
|
"Could not communicate with the session bus: {}",
|
||||||
);
|
ErrorFmt(e)
|
||||||
session
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PortalState {
|
struct PortalState {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub mod buf;
|
||||||
pub mod buffd;
|
pub mod buffd;
|
||||||
pub mod bufio;
|
pub mod bufio;
|
||||||
pub mod cell_ext;
|
pub mod cell_ext;
|
||||||
|
pub mod clone3;
|
||||||
pub mod clonecell;
|
pub mod clonecell;
|
||||||
pub mod copyhashmap;
|
pub mod copyhashmap;
|
||||||
pub mod debug_fn;
|
pub mod debug_fn;
|
||||||
|
|
@ -27,6 +28,7 @@ pub mod opaque_cell;
|
||||||
pub mod option_ext;
|
pub mod option_ext;
|
||||||
pub mod oserror;
|
pub mod oserror;
|
||||||
pub mod page_size;
|
pub mod page_size;
|
||||||
|
pub mod process_name;
|
||||||
pub mod ptr_ext;
|
pub mod ptr_ext;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
pub mod rc_eq;
|
pub mod rc_eq;
|
||||||
|
|
@ -45,6 +47,7 @@ pub mod tri;
|
||||||
pub mod trim;
|
pub mod trim;
|
||||||
pub mod unlink_on_drop;
|
pub mod unlink_on_drop;
|
||||||
pub mod vec_ext;
|
pub mod vec_ext;
|
||||||
|
pub mod vecdeque_ext;
|
||||||
pub mod vecstorage;
|
pub mod vecstorage;
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
pub mod xrd;
|
pub mod xrd;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub fn fork_with_pidfd(pidfd_for_child: bool) -> Result<Forked, ForkerError> {
|
||||||
let mut args = clone_args {
|
let mut args = clone_args {
|
||||||
flags: c::CLONE_PIDFD as u64,
|
flags: c::CLONE_PIDFD as u64,
|
||||||
pidfd: (&mut pidfd as *mut c::c_int) as _,
|
pidfd: (&mut pidfd as *mut c::c_int) as _,
|
||||||
|
exit_signal: c::SIGCHLD as _,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut child_pidfd = None;
|
let mut child_pidfd = None;
|
||||||
8
src/utils/process_name.rs
Normal file
8
src/utils/process_name.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
use uapi::{c, IntoUstr};
|
||||||
|
|
||||||
|
pub fn set_process_name(name: &str) {
|
||||||
|
unsafe {
|
||||||
|
let name = name.into_ustr();
|
||||||
|
c::prctl(c::PR_SET_NAME, name.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/utils/vecdeque_ext.rs
Normal file
35
src/utils/vecdeque_ext.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use std::{
|
||||||
|
collections::{Bound, VecDeque},
|
||||||
|
ops::RangeBounds,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait VecDequeExt<T> {
|
||||||
|
fn get_slices(&self, range: impl RangeBounds<usize>) -> (&[T], &[T]);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VecDequeExt<T> for VecDeque<T> {
|
||||||
|
fn get_slices(&self, range: impl RangeBounds<usize>) -> (&[T], &[T]) {
|
||||||
|
let (l, r) = self.as_slices();
|
||||||
|
let start = match range.start_bound().cloned() {
|
||||||
|
Bound::Included(n) => n,
|
||||||
|
Bound::Excluded(n) => n + 1,
|
||||||
|
Bound::Unbounded => 0,
|
||||||
|
};
|
||||||
|
let end = match range.end_bound().cloned() {
|
||||||
|
Bound::Included(n) => n + 1,
|
||||||
|
Bound::Excluded(n) => n,
|
||||||
|
Bound::Unbounded => self.len(),
|
||||||
|
};
|
||||||
|
let left = {
|
||||||
|
let lo = start.min(l.len());
|
||||||
|
let hi = end.min(l.len());
|
||||||
|
&l[lo..hi]
|
||||||
|
};
|
||||||
|
let right = {
|
||||||
|
let lo = start.saturating_sub(l.len());
|
||||||
|
let hi = end.saturating_sub(l.len());
|
||||||
|
&r[lo..hi]
|
||||||
|
};
|
||||||
|
(left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue