Compare commits
2 Commits
8e66dc234e
...
5766c7d627
Author | SHA1 | Date | |
---|---|---|---|
5766c7d627 | |||
3cb1b1793d |
1421
Cargo.lock
generated
1421
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -10,3 +10,7 @@ members = [
|
|||||||
"systemd",
|
"systemd",
|
||||||
"xcb-xembed",
|
"xcb-xembed",
|
||||||
]
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
xkb = { git = "https://github.com/kelnos/rust-xkb", branch = "remove-missing-keysyms" }
|
||||||
|
2
Makefile
2
Makefile
@ -63,7 +63,7 @@ install: all
|
|||||||
$(INSTALL) -m 0644 $(RELEASE_MANPAGES) $(DESTDIR)$(MANDIR)/man1
|
$(INSTALL) -m 0644 $(RELEASE_MANPAGES) $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
$(DEV_TARGETS): $(SOURCES)
|
$(DEV_TARGETS): $(SOURCES)
|
||||||
HELPER_DIR=target/debug cargo build
|
HELPER_DIR=$(shell pwd)/target/debug cargo build
|
||||||
|
|
||||||
$(RELEASE_TARGETS): $(SOURCES)
|
$(RELEASE_TARGETS): $(SOURCES)
|
||||||
HELPER_DIR=$(HELPER_DIR) cargo build --release
|
HELPER_DIR=$(HELPER_DIR) cargo build --release
|
||||||
|
@ -4,9 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-io = "1.6"
|
async-io = "2.3"
|
||||||
async-std = { version = "1.11", features = ["attributes"] }
|
async-std = { version = "1.12", features = ["attributes"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-lite = "1.12"
|
futures-lite = "2.3"
|
||||||
nix = "0.23"
|
nix = { version = "0.29", features = ["fs"] }
|
||||||
xcb = "1"
|
xcb = "1"
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
use async_io::{Async, ReadableOwned};
|
use async_io::{Async, ReadableOwned};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures_lite::ready;
|
use futures_lite::ready;
|
||||||
use nix::{fcntl::{fcntl, F_GETFL, F_SETFL, OFlag}, unistd::read};
|
use nix::{fcntl::{fcntl, OFlag, F_GETFL, F_SETFL}, unistd::read};
|
||||||
use std::{io, os::unix::io::AsRawFd, pin::Pin, sync::Arc, task::{Context, Poll}};
|
use std::{io, os::{fd::{AsFd, BorrowedFd}, unix::io::AsRawFd}, pin::Pin, sync::Arc, task::{Context, Poll}};
|
||||||
|
|
||||||
|
struct XcbAsFdWrapper(xcb::Connection);
|
||||||
|
|
||||||
|
impl AsFd for XcbAsFdWrapper {
|
||||||
|
fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
|
||||||
|
let fd = self.0.as_raw_fd();
|
||||||
|
if fd < 0 {
|
||||||
|
panic!("XCB file descriptor is invalid");
|
||||||
|
}
|
||||||
|
unsafe { BorrowedFd::borrow_raw(fd) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AsyncConnection {
|
pub struct AsyncConnection {
|
||||||
conn: Arc<Async<xcb::Connection>>,
|
conn: Arc<Async<XcbAsFdWrapper>>,
|
||||||
readable: Option<ReadableOwned<xcb::Connection>>,
|
readable: Option<ReadableOwned<XcbAsFdWrapper>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncConnection {
|
impl AsyncConnection {
|
||||||
@ -14,7 +26,7 @@ impl AsyncConnection {
|
|||||||
let flags = fcntl(conn.as_raw_fd(), F_GETFL)?;
|
let flags = fcntl(conn.as_raw_fd(), F_GETFL)?;
|
||||||
fcntl(conn.as_raw_fd(), F_SETFL(OFlag::from_bits_truncate(flags) | OFlag::O_NONBLOCK))?;
|
fcntl(conn.as_raw_fd(), F_SETFL(OFlag::from_bits_truncate(flags) | OFlag::O_NONBLOCK))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn: Arc::new(Async::new(conn)?),
|
conn: Arc::new(Async::new(XcbAsFdWrapper(conn))?),
|
||||||
readable: None,
|
readable: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -23,7 +35,7 @@ impl AsyncConnection {
|
|||||||
impl AsyncRead for AsyncConnection {
|
impl AsyncRead for AsyncConnection {
|
||||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
|
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
|
||||||
loop {
|
loop {
|
||||||
match read(self.conn.as_raw_fd(), buf) {
|
match read(self.conn.as_fd().as_raw_fd(), buf) {
|
||||||
Err(nix::Error::EAGAIN) => (),
|
Err(nix::Error::EAGAIN) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.readable = None;
|
self.readable = None;
|
||||||
|
@ -5,6 +5,6 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bscreensaver-util = { path = "../util" }
|
bscreensaver-util = { path = "../util" }
|
||||||
clap = "3"
|
clap = { version = "4", features = ["derive"] }
|
||||||
nix = "0.23"
|
nix = { version = "0.29", features = ["poll"] }
|
||||||
xcb = "1"
|
xcb = "1"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use nix::poll::{poll, PollFd, PollFlags};
|
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
|
||||||
use std::{cmp, error::Error as StdError, fmt, time::{Duration, Instant}, os::unix::prelude::AsRawFd};
|
use std::{error::Error as StdError, fmt, os::unix::prelude::AsRawFd, time::{Duration, Instant}};
|
||||||
use xcb::{x, Xid};
|
use xcb::{x, Xid};
|
||||||
|
|
||||||
use bscreensaver_util::{create_atom, BSCREENSAVER_WM_CLASS};
|
use bscreensaver_util::{borrow_raw_fd, create_atom, BSCREENSAVER_WM_CLASS};
|
||||||
|
|
||||||
const COMMAND_WINDOW_ID_ATOM_NAME: &[u8] = b"BSCREENSAVER_COMMAND_WINDOW_ID";
|
const COMMAND_WINDOW_ID_ATOM_NAME: &[u8] = b"BSCREENSAVER_COMMAND_WINDOW_ID";
|
||||||
const COMMAND_WINDOW_WM_NAME: &[u8] = b"bscreensaver command window";
|
const COMMAND_WINDOW_WM_NAME: &[u8] = b"bscreensaver command window";
|
||||||
@ -268,16 +268,13 @@ pub fn bscreensaver_command(command: BCommand, timeout: Option<Duration>) -> Res
|
|||||||
let poll_timeout = timeout.map(|to| {
|
let poll_timeout = timeout.map(|to| {
|
||||||
let since_start = start.elapsed();
|
let since_start = start.elapsed();
|
||||||
if since_start > to {
|
if since_start > to {
|
||||||
0i32
|
Err(Error::Timeout)
|
||||||
} else {
|
} else {
|
||||||
cmp::max(i32::MAX as u128, (to - since_start).as_millis()) as i32
|
Ok(u16::try_from((to - since_start).as_millis()).unwrap_or(u16::MAX).into())
|
||||||
}
|
}
|
||||||
}).unwrap_or(-1);
|
}).unwrap_or(Ok(PollTimeout::NONE))?;
|
||||||
if poll_timeout == 0 {
|
|
||||||
break 'outer1 Err(Error::Timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pfds = vec![PollFd::new(conn.as_raw_fd(), PollFlags::POLLIN)];
|
let mut pfds = vec![PollFd::new(borrow_raw_fd(conn.as_raw_fd()), PollFlags::POLLIN)];
|
||||||
poll(pfds.as_mut_slice(), poll_timeout)?;
|
poll(pfds.as_mut_slice(), poll_timeout)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,48 @@
|
|||||||
use clap::{Arg, Command};
|
use clap::{CommandFactory, Parser};
|
||||||
use std::{env, io, process::exit, time::Duration};
|
use std::{io, process::exit, time::Duration};
|
||||||
|
|
||||||
use bscreensaver_command::{BCommand, Error, bscreensaver_command};
|
use bscreensaver_command::{BCommand, Error, bscreensaver_command};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "bscreensaver-command", version, author, about = "Send commands to the running bscreensaver instance", long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Blanks the screen right now
|
||||||
|
#[arg(short = 'b', long = "blank")]
|
||||||
|
blank: bool,
|
||||||
|
|
||||||
|
/// Lock the screen right now
|
||||||
|
#[arg(short = 'l', long = "lock")]
|
||||||
|
lock: bool,
|
||||||
|
|
||||||
|
/// Deactivates the screen lock, presenting the unlock dialog if needed. This can be used to 'reset' things so the screensaver thinks there has been user input
|
||||||
|
#[arg(short = 'd', long = "deactivate")]
|
||||||
|
deactivate: bool,
|
||||||
|
|
||||||
|
/// Restarts the bscreensaver daemon
|
||||||
|
#[arg(short = 'r', long = "restart")]
|
||||||
|
restart: bool,
|
||||||
|
|
||||||
|
/// Causes the bscreensaver daemon to exit now, even if the screen is locked
|
||||||
|
#[arg(short = 'x', long = "exit")]
|
||||||
|
exit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
let mut command = Command::new("bscreensaver-command")
|
let args = Args::parse();
|
||||||
.author(env!("CARGO_PKG_AUTHORS"))
|
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
|
||||||
.about("Send commands to the running bscreensaver instance")
|
|
||||||
.arg(
|
|
||||||
Arg::new("blank")
|
|
||||||
.long("blank")
|
|
||||||
.short('b')
|
|
||||||
.help("Blanks the screen right now")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("lock")
|
|
||||||
.long("lock")
|
|
||||||
.short('l')
|
|
||||||
.help("Lock the screen right now")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("deactivate")
|
|
||||||
.long("deactivate")
|
|
||||||
.short('d')
|
|
||||||
.help("Deactivates the screen lock, presenting the unlock dialog if needed. This can be used to 'reset' things so the screensaver thinks there has been user input")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("restart")
|
|
||||||
.long("restart")
|
|
||||||
.short('r')
|
|
||||||
.help("Restarts the bscreensaver daemon")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("exit")
|
|
||||||
.long("exit")
|
|
||||||
.short('x')
|
|
||||||
.help("Causes the bscreensaver daemon to exit now, even if the screen is locked")
|
|
||||||
);
|
|
||||||
let args = command.get_matches_mut();
|
|
||||||
|
|
||||||
let command =
|
let command =
|
||||||
if args.is_present("blank") {
|
if args.blank {
|
||||||
BCommand::Blank
|
BCommand::Blank
|
||||||
} else if args.is_present("lock") {
|
} else if args.lock {
|
||||||
BCommand::Lock
|
BCommand::Lock
|
||||||
} else if args.is_present("deactivate") {
|
} else if args.deactivate {
|
||||||
BCommand::Deactivate
|
BCommand::Deactivate
|
||||||
} else if args.is_present("restart") {
|
} else if args.restart {
|
||||||
BCommand::Restart
|
BCommand::Restart
|
||||||
} else if args.is_present("exit") {
|
} else if args.exit {
|
||||||
BCommand::Exit
|
BCommand::Exit
|
||||||
} else {
|
} else {
|
||||||
command.print_help()?;
|
Args::command().print_help()?;
|
||||||
exit(1);
|
exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,4 +12,4 @@ bscreensaver-util = { path = "../util" }
|
|||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
xcb = { version = "1", features = ["dpms"] }
|
xcb = { version = "1", features = ["dpms"] }
|
||||||
zbus = "3"
|
zbus = "4"
|
||||||
|
@ -3,7 +3,7 @@ use bscreensaver_util::init_logging;
|
|||||||
use futures::{future::FutureExt, pin_mut, select};
|
use futures::{future::FutureExt, pin_mut, select};
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use std::{io, process::exit, time::{Duration, Instant}};
|
use std::{io, process::exit, time::{Duration, Instant}};
|
||||||
use zbus::{dbus_interface, fdo::{self, DBusProxy, RequestNameFlags}, names::{BusName, UniqueName, WellKnownName}, ConnectionBuilder, MessageHeader};
|
use zbus::{interface, fdo::{self, DBusProxy, RequestNameFlags}, names::{BusName, UniqueName, WellKnownName}, ConnectionBuilder, MessageHeader};
|
||||||
|
|
||||||
use bscreensaver_command::{bscreensaver_command, BCommand};
|
use bscreensaver_command::{bscreensaver_command, BCommand};
|
||||||
use bscreensaver_util::opt_contains;
|
use bscreensaver_util::opt_contains;
|
||||||
@ -25,7 +25,7 @@ struct ScreenSaver {
|
|||||||
state: Arc<Mutex<State>>,
|
state: Arc<Mutex<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(name = "org.freedesktop.ScreenSaver")]
|
#[interface(name = "org.freedesktop.ScreenSaver")]
|
||||||
impl ScreenSaver {
|
impl ScreenSaver {
|
||||||
async fn inhibit(
|
async fn inhibit(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -47,7 +47,7 @@ impl ScreenSaver {
|
|||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let peer = hdr.sender()?;
|
let peer = hdr.sender();
|
||||||
let cookie = rand_u32().await
|
let cookie = rand_u32().await
|
||||||
.map_err(|err| fdo::Error::IOError(err.to_string()))?;
|
.map_err(|err| fdo::Error::IOError(err.to_string()))?;
|
||||||
self.state.lock().await.inhibitors.push(Inhibitor {
|
self.state.lock().await.inhibitors.push(Inhibitor {
|
||||||
|
@ -7,12 +7,18 @@ edition = "2021"
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
bscreensaver-util = { path = "../util" }
|
bscreensaver-util = { path = "../util" }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
gethostname = "0.2"
|
gethostname = "0.4"
|
||||||
glib = { version = "0.15", features = ["log"] }
|
glib = { version = "0.18", features = ["log"] }
|
||||||
gtk = "0.15"
|
gtk = "0.18"
|
||||||
gtk-sys = "0.15"
|
gtk-sys = "0.18"
|
||||||
gdk-sys = "0.15"
|
gdk-sys = "0.18"
|
||||||
gdkx11 = "0.15"
|
gdkx11 = "0.18"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pam-client = "0.5"
|
pam-client = "0.5"
|
||||||
shell-words = "1"
|
shell-words = "1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
# pam-sys specifies default-features = false, which drops the 'runtime'
|
||||||
|
# feature; this ends up conflicting with the xkbcommon-sys build in another
|
||||||
|
# module, which causes pam-sys to fail to build
|
||||||
|
bindgen = { version = "0.69", features = ["runtime"] }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use gdkx11::X11Window;
|
use gdkx11::X11Window;
|
||||||
use gethostname::gethostname;
|
use gethostname::gethostname;
|
||||||
use glib::{GString, clone};
|
use glib::{ControlFlow, GString, clone};
|
||||||
use gtk::{prelude::*, Button, Entry, Label, Plug, Window};
|
use gtk::{prelude::*, Button, Entry, Label, Plug, Window};
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use std::{io::{self, Write}, sync::mpsc, thread, time::Duration, ffi::CString};
|
use std::{io::{self, Write}, sync::mpsc, thread, time::Duration, ffi::CString};
|
||||||
@ -215,7 +215,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
vbox.pack_start(&label, false, false, 0);
|
vbox.pack_start(&label, false, false, 0);
|
||||||
glib::timeout_add_seconds_local(1, move || {
|
glib::timeout_add_seconds_local(1, move || {
|
||||||
set_time_label(&label);
|
set_time_label(&label);
|
||||||
glib::Continue(true)
|
ControlFlow::Continue
|
||||||
});
|
});
|
||||||
|
|
||||||
let sep = gtk::Separator::builder()
|
let sep = gtk::Separator::builder()
|
||||||
@ -275,19 +275,19 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.build();
|
.build();
|
||||||
top_vbox.pack_end(&timer, false, false, 0);
|
top_vbox.pack_end(&timer, false, false, 0);
|
||||||
let delta = (DIALOG_UPDATE_INTERVAL.as_millis() as f64) / (DIALOG_TIMEOUT.as_millis() as f64);
|
let delta = (DIALOG_UPDATE_INTERVAL.as_millis() as f64) / (DIALOG_TIMEOUT.as_millis() as f64);
|
||||||
glib::timeout_add_local(DIALOG_UPDATE_INTERVAL, clone!(@weak timer => @default-return Continue(false), move || {
|
glib::timeout_add_local(DIALOG_UPDATE_INTERVAL, clone!(@weak timer => @default-return ControlFlow::Break, move || {
|
||||||
let new_fraction = timer.fraction() - delta;
|
let new_fraction = timer.fraction() - delta;
|
||||||
if new_fraction <= 0.0 {
|
if new_fraction <= 0.0 {
|
||||||
DialogExitStatus::AuthTimedOut.exit();
|
DialogExitStatus::AuthTimedOut.exit();
|
||||||
}
|
}
|
||||||
timer.set_fraction(new_fraction);
|
timer.set_fraction(new_fraction);
|
||||||
Continue(true)
|
ControlFlow::Continue
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (tx, rx) = glib::MainContext::channel(glib::Priority::default());
|
let (tx, rx) = glib::MainContext::channel(glib::Priority::default());
|
||||||
let username = bscreensaver_util::get_username()?;
|
let username = bscreensaver_util::get_username()?;
|
||||||
|
|
||||||
rx.attach(None, clone!(@weak dialog, @weak auth_vbox, @weak accept_button_box, @weak timer, @strong username, => @default-return glib::Continue(false), move |op| {
|
rx.attach(None, clone!(@weak dialog, @weak auth_vbox, @weak accept_button_box, @weak timer, @strong username, => @default-return ControlFlow::Break, move |op| {
|
||||||
for child in auth_vbox.children() {
|
for child in auth_vbox.children() {
|
||||||
auth_vbox.remove(&child);
|
auth_vbox.remove(&child);
|
||||||
}
|
}
|
||||||
@ -366,7 +366,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
if ev.keyval().name() == Some(GString::from("Escape")) {
|
if ev.keyval().name() == Some(GString::from("Escape")) {
|
||||||
DialogExitStatus::AuthCanceled.exit();
|
DialogExitStatus::AuthCanceled.exit();
|
||||||
}
|
}
|
||||||
gtk::Inhibit(false)
|
glib::Propagation::Stop
|
||||||
}));
|
}));
|
||||||
input_box.grab_focus();
|
input_box.grab_focus();
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dialog.show_all();
|
dialog.show_all();
|
||||||
glib::Continue(true)
|
ControlFlow::Continue
|
||||||
}));
|
}));
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -42,13 +42,13 @@ assets = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = "3"
|
clap = { version = "4", features = ["derive"] }
|
||||||
bscreensaver-command = { path = "../command" }
|
bscreensaver-command = { path = "../command" }
|
||||||
bscreensaver-util = { path = "../util" }
|
bscreensaver-util = { path = "../util" }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nix = "0.23"
|
nix = { version = "0.29", features = ["process", "signal"] }
|
||||||
xcb = { version = "1", features = ["randr", "xkb", "xfixes", "xinput"] }
|
xcb = { version = "1", features = ["randr", "xkb", "xfixes", "xinput"] }
|
||||||
xcb-xembed = { path = "../xcb-xembed" }
|
xcb-xembed = { path = "../xcb-xembed" }
|
||||||
xkb = { version = "0.3", features = ["x11"] }
|
xkb = { version = "0.3", features = ["x11"] }
|
||||||
xkbcommon-sys = { version = "1", feature = "x11" }
|
xkbcommon-sys = { version = "=1.4.1", features = ["x11"] }
|
||||||
|
@ -1,23 +1,13 @@
|
|||||||
use clap::{Arg, Command as ClapCommand};
|
use clap::Parser;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use nix::{
|
use nix::{
|
||||||
poll::{poll, PollFd, PollFlags},
|
poll::{poll, PollFd, PollFlags, PollTimeout}, sys::{
|
||||||
unistd::{execv, fork, setsid, ForkResult},
|
|
||||||
sys::{
|
|
||||||
signal::{sigprocmask, SigSet, SigmaskHow, Signal},
|
signal::{sigprocmask, SigSet, SigmaskHow, Signal},
|
||||||
signalfd::{SignalFd, SfdFlags},
|
signalfd::{SfdFlags, SignalFd},
|
||||||
},
|
}, unistd::{execv, fork, setsid, ForkResult}
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env, ffi::CString, fs::read_link, io, os::{fd::AsFd, unix::io::AsRawFd}, path::PathBuf, process::exit, rc::Rc, sync::Mutex, time::{Duration, Instant}
|
||||||
ffi::CString,
|
|
||||||
fs::read_link,
|
|
||||||
io,
|
|
||||||
os::unix::io::AsRawFd,
|
|
||||||
rc::Rc,
|
|
||||||
process::exit,
|
|
||||||
sync::Mutex,
|
|
||||||
time::{Duration, Instant}, path::PathBuf,
|
|
||||||
};
|
};
|
||||||
use xcb::{randr, x, xfixes, xinput};
|
use xcb::{randr, x, xfixes, xinput};
|
||||||
|
|
||||||
@ -30,33 +20,35 @@ const LOCKED_ARG: &str = "locked";
|
|||||||
macro_rules! maybe_add_fd {
|
macro_rules! maybe_add_fd {
|
||||||
($pfds:expr, $fd:expr) => {
|
($pfds:expr, $fd:expr) => {
|
||||||
if let Some(fd) = $fd {
|
if let Some(fd) = $fd {
|
||||||
$pfds.push(PollFd::new(fd, PollFlags::POLLIN));
|
if fd >= 0 {
|
||||||
Some(fd)
|
$pfds.push(PollFd::new(borrow_raw_fd(fd), PollFlags::POLLIN));
|
||||||
|
Some(fd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "bscreensaver", version, author, about = "Blanks and locks the screen after a period of time", long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Starts up in already blanked
|
||||||
|
#[arg(long = BLANKED_ARG)]
|
||||||
|
blanked: bool,
|
||||||
|
|
||||||
|
/// Stats up already blanked and locked
|
||||||
|
#[arg(long = LOCKED_ARG)]
|
||||||
|
locked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
init_logging("BSCREENSAVER_LOG");
|
init_logging("BSCREENSAVER_LOG");
|
||||||
|
|
||||||
let config = Configuration::load()?;
|
let config = Configuration::load()?;
|
||||||
|
let args = Args::parse();
|
||||||
let args = ClapCommand::new("Blanks and locks the screen after a period of time")
|
|
||||||
.author(env!("CARGO_PKG_AUTHORS"))
|
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
|
||||||
.arg(
|
|
||||||
Arg::new("blanked")
|
|
||||||
.long(BLANKED_ARG)
|
|
||||||
.help("Starts up in the blanked screensaver")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("locked")
|
|
||||||
.long(LOCKED_ARG)
|
|
||||||
.help("Starts up in the blanked and locked screensaver")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let mut signal_fd = init_signals()?;
|
let mut signal_fd = init_signals()?;
|
||||||
|
|
||||||
@ -91,12 +83,12 @@ fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
let mut screensaver = Screensaver::new(&config, &helper_dir, &command_handlers, &conn, screen)?;
|
let mut screensaver = Screensaver::new(&config, &helper_dir, &command_handlers, &conn, screen)?;
|
||||||
|
|
||||||
if args.is_present(LOCKED_ARG) {
|
if args.locked {
|
||||||
match screensaver.lock_screen() {
|
match screensaver.lock_screen() {
|
||||||
Err(err) => error!("POSSIBLY FAILED TO LOCK SCREEN ON STARTUP: {}", err),
|
Err(err) => error!("POSSIBLY FAILED TO LOCK SCREEN ON STARTUP: {}", err),
|
||||||
Ok(_) => debug!("Got --{} arg; screen locked on startup", LOCKED_ARG),
|
Ok(_) => debug!("Got --{} arg; screen locked on startup", LOCKED_ARG),
|
||||||
}
|
}
|
||||||
} else if args.is_present(BLANKED_ARG) {
|
} else if args.blanked {
|
||||||
match screensaver.blank_screen() {
|
match screensaver.blank_screen() {
|
||||||
Err(err) => warn!("Possibly failed to blank screen on startup: {}", err),
|
Err(err) => warn!("Possibly failed to blank screen on startup: {}", err),
|
||||||
Ok(_) => debug!("Got --{} arg; screen locked on startup", BLANKED_ARG),
|
Ok(_) => debug!("Got --{} arg; screen locked on startup", BLANKED_ARG),
|
||||||
@ -120,9 +112,17 @@ fn main() -> anyhow::Result<()> {
|
|||||||
warn!("Error handling event: {}", err);
|
warn!("Error handling event: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let conn_fd = conn.as_raw_fd();
|
||||||
|
if conn_fd < 0 {
|
||||||
|
error!("Lost connection to X server; attempting to restart");
|
||||||
|
(command_handlers.restart_handler)(&mut screensaver, None)?;
|
||||||
|
}
|
||||||
|
let conn_fd = borrow_raw_fd(conn_fd);
|
||||||
|
|
||||||
|
|
||||||
let mut pfds = Vec::new();
|
let mut pfds = Vec::new();
|
||||||
pfds.push(PollFd::new(signal_fd.as_raw_fd(), PollFlags::POLLIN));
|
pfds.push(PollFd::new(borrow_raw_fd(signal_fd.as_raw_fd()), PollFlags::POLLIN));
|
||||||
pfds.push(PollFd::new(conn.as_raw_fd(), PollFlags::POLLIN));
|
pfds.push(PollFd::new(conn_fd.as_fd(), PollFlags::POLLIN));
|
||||||
let dbus_service_fd = maybe_add_fd!(&mut pfds, subservices.lock().unwrap().dbus_service().map(|ds| ds.pidfd().as_raw_fd()));
|
let dbus_service_fd = maybe_add_fd!(&mut pfds, subservices.lock().unwrap().dbus_service().map(|ds| ds.pidfd().as_raw_fd()));
|
||||||
let systemd_service_fd = maybe_add_fd!(&mut pfds, subservices.lock().unwrap().systemd_service().map(|ds| ds.pidfd().as_raw_fd()));
|
let systemd_service_fd = maybe_add_fd!(&mut pfds, subservices.lock().unwrap().systemd_service().map(|ds| ds.pidfd().as_raw_fd()));
|
||||||
let dialog_fd = maybe_add_fd!(&mut pfds, screensaver.unlock_dialog_pidfd().map(|udpfd| udpfd.as_raw_fd()));
|
let dialog_fd = maybe_add_fd!(&mut pfds, screensaver.unlock_dialog_pidfd().map(|udpfd| udpfd.as_raw_fd()));
|
||||||
@ -135,21 +135,19 @@ fn main() -> anyhow::Result<()> {
|
|||||||
BlankerState::Blanked => Some(config.lock_timeout - since_last_activity),
|
BlankerState::Blanked => Some(config.lock_timeout - since_last_activity),
|
||||||
BlankerState::Locked => None,
|
BlankerState::Locked => None,
|
||||||
};
|
};
|
||||||
let poll_timeout = poll_timeout.map(|pt| if pt.as_millis() > i32::MAX as u128 {
|
let poll_timeout = poll_timeout
|
||||||
i32::MAX
|
.map(|pt| PollTimeout::try_from(pt).unwrap_or(PollTimeout::MAX))
|
||||||
} else {
|
.unwrap_or(PollTimeout::NONE);
|
||||||
pt.as_millis() as i32
|
|
||||||
}).unwrap_or(-1);
|
|
||||||
|
|
||||||
trace!("about to poll (timeout={})", poll_timeout);
|
trace!("about to poll (timeout={})", poll_timeout.as_millis().map(|pt| pt.to_string()).unwrap_or("(?)".into()));
|
||||||
let nready = poll(pfds.as_mut_slice(), poll_timeout)?; // FIXME: maybe shouldn't quit here on errors if screen is locked
|
let nready = poll(pfds.as_mut_slice(), poll_timeout)?; // FIXME: maybe shouldn't quit here on errors if screen is locked
|
||||||
trace!("polled; {} FD ready", nready);
|
trace!("polled; {} FD ready", nready);
|
||||||
if nready > 0 {
|
if nready > 0 {
|
||||||
for pfd in pfds {
|
for pfd in pfds {
|
||||||
if pfd.revents().filter(|pf| pf.contains(PollFlags::POLLIN)).is_some() {
|
if pfd.revents().filter(|pf| pf.contains(PollFlags::POLLIN)).is_some() {
|
||||||
let result = match pfd.as_raw_fd() {
|
let result = match pfd.as_fd().as_raw_fd() {
|
||||||
fd if fd == signal_fd.as_raw_fd() => handle_signals(&mut screensaver, &mut subservices.lock().unwrap(), &mut signal_fd),
|
fd if fd == signal_fd.as_raw_fd() => handle_signals(&mut screensaver, &mut subservices.lock().unwrap(), &mut signal_fd),
|
||||||
fd if fd == conn.as_raw_fd() => screensaver.handle_xcb_events(),
|
fd if fd == conn_fd.as_raw_fd() => screensaver.handle_xcb_events(),
|
||||||
fd if opt_contains(&dbus_service_fd, &fd) => subservices.lock().unwrap().handle_quit(),
|
fd if opt_contains(&dbus_service_fd, &fd) => subservices.lock().unwrap().handle_quit(),
|
||||||
fd if opt_contains(&systemd_service_fd, &fd) => subservices.lock().unwrap().handle_quit(),
|
fd if opt_contains(&systemd_service_fd, &fd) => subservices.lock().unwrap().handle_quit(),
|
||||||
fd if opt_contains(&dialog_fd, &fd) => screensaver.handle_unlock_dialog_quit(),
|
fd if opt_contains(&dialog_fd, &fd) => screensaver.handle_unlock_dialog_quit(),
|
||||||
|
@ -547,7 +547,7 @@ fn find_backlight_control(conn: &xcb::Connection, output: randr::Output) -> anyh
|
|||||||
if let Ok(reply) = conn.wait_for_reply(cookie) {
|
if let Ok(reply) = conn.wait_for_reply(cookie) {
|
||||||
let values = reply.valid_values();
|
let values = reply.valid_values();
|
||||||
if reply.range() && values.len() == 2 {
|
if reply.range() && values.len() == 2 {
|
||||||
let min_level = values[0];
|
let min_level = values[0] + 1;
|
||||||
let max_level = values[1];
|
let max_level = values[1];
|
||||||
let range = max_level - min_level;
|
let range = max_level - min_level;
|
||||||
if range > 0 {
|
if range > 0 {
|
||||||
@ -577,7 +577,7 @@ fn find_backlight_control(conn: &xcb::Connection, output: randr::Output) -> anyh
|
|||||||
debug!("Found sysfs backlight control at {:?}", cur_path);
|
debug!("Found sysfs backlight control at {:?}", cur_path);
|
||||||
return Ok(Some(BacklightControl {
|
return Ok(Some(BacklightControl {
|
||||||
location: BacklightLocation::Sysfs(cur_path),
|
location: BacklightLocation::Sysfs(cur_path),
|
||||||
min_level: 0,
|
min_level: 1,
|
||||||
max_level,
|
max_level,
|
||||||
step: calc_step(0, max_level),
|
step: calc_step(0, max_level),
|
||||||
}));
|
}));
|
||||||
|
@ -12,7 +12,7 @@ fn pidfd_open(pid: RawFd) -> nix::Result<PidFd> {
|
|||||||
} else {
|
} else {
|
||||||
// SAFETY: pointer is checked for null; libc must be sane
|
// SAFETY: pointer is checked for null; libc must be sane
|
||||||
let errno = unsafe { *errno_location };
|
let errno = unsafe { *errno_location };
|
||||||
Err(nix::errno::Errno::from_i32(errno))
|
Err(nix::errno::Errno::from_raw(errno))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
bscreensaver-util = { path = "../util" }
|
bscreensaver-util = { path = "../util" }
|
||||||
glib = "0.15"
|
glib = "0.18"
|
||||||
gtk = { version = "0.15", features = ["v3_20"] }
|
gtk = "0.18"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use gtk::{glib, prelude::*};
|
use gtk::{glib, prelude::*};
|
||||||
use glib::clone;
|
use glib::{clone, Propagation::{Proceed, Stop}};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::{env, process::exit, time::Duration, ffi::CString};
|
use std::{env, process::exit, time::Duration, ffi::CString};
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.build();
|
.build();
|
||||||
app.connect_activate(move |app| show_ui(&app, &config));
|
app.connect_activate(move |app| show_ui(&app, &config));
|
||||||
|
|
||||||
exit(app.run_with_args(&env::args().into_iter().collect::<Vec<String>>()));
|
exit(app.run_with_args(&env::args().into_iter().collect::<Vec<String>>()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_ui(app: >k::Application, config: &Configuration) {
|
fn show_ui(app: >k::Application, config: &Configuration) {
|
||||||
@ -226,7 +226,11 @@ fn show_ui(app: >k::Application, config: &Configuration) {
|
|||||||
handle_brightness_keys_checkbox: handle_brightness_keys_checkbox.clone(),
|
handle_brightness_keys_checkbox: handle_brightness_keys_checkbox.clone(),
|
||||||
};
|
};
|
||||||
mainwin.connect_delete_event(clone!(@strong config, @strong widgets, @strong app, @strong mainwin => move |_,_| {
|
mainwin.connect_delete_event(clone!(@strong config, @strong widgets, @strong app, @strong mainwin => move |_,_| {
|
||||||
Inhibit(!confirm_cancel(&config, &widgets, &mainwin))
|
if !confirm_cancel(&config, &widgets, &mainwin) {
|
||||||
|
Stop
|
||||||
|
} else {
|
||||||
|
Proceed
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
custom_new_login_command_button.connect_clicked(clone!(@strong mainwin, @strong widgets => move |_| {
|
custom_new_login_command_button.connect_clicked(clone!(@strong mainwin, @strong widgets => move |_| {
|
||||||
run_file_chooser(&mainwin, &widgets);
|
run_file_chooser(&mainwin, &widgets);
|
||||||
|
@ -5,13 +5,13 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
async-std = { version = "1.11", features = ["attributes"] }
|
async-std = { version = "1.12", features = ["attributes"] }
|
||||||
async-xcb = { path = "../async-xcb" }
|
async-xcb = { path = "../async-xcb" }
|
||||||
bscreensaver-command = { path = "../command" }
|
bscreensaver-command = { path = "../command" }
|
||||||
bscreensaver-util = { path = "../util" }
|
bscreensaver-util = { path = "../util" }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nix = "0.23"
|
nix = "0.29"
|
||||||
xcb = "1"
|
xcb = "1"
|
||||||
zbus = "3"
|
zbus = "4"
|
||||||
logind-zbus = "3"
|
logind-zbus = "^4.0.1"
|
||||||
|
@ -95,5 +95,5 @@ async fn register_sleep_lock<'a>(manager_proxy: &ManagerProxy<'a>) -> anyhow::Re
|
|||||||
// ManagerProxy uses RawFd for the return value, which rust's type system thinks is an i32,
|
// ManagerProxy uses RawFd for the return value, which rust's type system thinks is an i32,
|
||||||
// which means the generated proxy uses the wrong dbus type signature. So instead, use a raw
|
// which means the generated proxy uses the wrong dbus type signature. So instead, use a raw
|
||||||
// Proxy instance and do it all ourselves.
|
// Proxy instance and do it all ourselves.
|
||||||
Ok((*manager_proxy).call("Inhibit", &(InhibitType::Sleep, "bscreensaver", "blank before sleep", "delay")).await?)
|
Ok(manager_proxy.inhibit(InhibitType::Sleep, "bscreensaver", "blank before sleep", "delay").await?)
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = "3"
|
clap = "4"
|
||||||
env_logger = "0.9"
|
env_logger = "0.11"
|
||||||
humantime = "2"
|
humantime = "2"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
shell-words = "1"
|
shell-words = "1"
|
||||||
toml = "0.5"
|
toml = "0.8"
|
||||||
xcb = "1"
|
xcb = "1"
|
||||||
xdg = "2"
|
xdg = "2"
|
||||||
zbus = "3"
|
zbus = "4"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{ffi::CStr, io};
|
use std::{ffi::CStr, io, os::fd::{BorrowedFd, RawFd}};
|
||||||
use xcb::x;
|
use xcb::x;
|
||||||
|
|
||||||
pub mod desktop;
|
pub mod desktop;
|
||||||
@ -77,3 +77,10 @@ pub fn get_username() -> io::Result<String> {
|
|||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow_raw_fd<'a>(fd: RawFd) -> BorrowedFd<'a> {
|
||||||
|
if fd < 0 {
|
||||||
|
panic!("Can't borrow invalid file descriptor");
|
||||||
|
}
|
||||||
|
unsafe { BorrowedFd::borrow_raw(fd) }
|
||||||
|
}
|
||||||
|
@ -13,6 +13,6 @@ keywords = ["gui", "x11", "xcb", "xembed"]
|
|||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1"
|
bitflags = "2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
xcb = { version = "1", features = ["xfixes"] }
|
xcb = { version = "1", features = ["xfixes"] }
|
||||||
|
@ -130,7 +130,7 @@ impl<'a> Embedder<'a> {
|
|||||||
match event {
|
match event {
|
||||||
x::Event::PropertyNotify(ev) if ev.window() == self.client && ev.atom() == intern_atom(self.conn, XEMBED_INFO_ATOM_NAME)? => {
|
x::Event::PropertyNotify(ev) if ev.window() == self.client && ev.atom() == intern_atom(self.conn, XEMBED_INFO_ATOM_NAME)? => {
|
||||||
let info = fetch_xembed_info(self.conn, self.client)?;
|
let info = fetch_xembed_info(self.conn, self.client)?;
|
||||||
if (self.flags & XEmbedFlags::MAPPED) != (info.flags & XEmbedFlags::MAPPED) {
|
if self.flags.contains(XEmbedFlags::MAPPED) != info.flags.contains(XEmbedFlags::MAPPED) {
|
||||||
if info.flags.contains(XEmbedFlags::MAPPED) {
|
if info.flags.contains(XEmbedFlags::MAPPED) {
|
||||||
debug!("Mapping client window");
|
debug!("Mapping client window");
|
||||||
self.conn.send_and_check_request(&x::MapWindow {
|
self.conn.send_and_check_request(&x::MapWindow {
|
||||||
|
Loading…
Reference in New Issue
Block a user