Compare commits

...

2 Commits

24 changed files with 950 additions and 789 deletions

1421
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,3 +10,7 @@ members = [
"systemd",
"xcb-xembed",
]
resolver = "2"
[patch.crates-io]
xkb = { git = "https://github.com/kelnos/rust-xkb", branch = "remove-missing-keysyms" }

View File

@ -63,7 +63,7 @@ install: all
$(INSTALL) -m 0644 $(RELEASE_MANPAGES) $(DESTDIR)$(MANDIR)/man1
$(DEV_TARGETS): $(SOURCES)
HELPER_DIR=target/debug cargo build
HELPER_DIR=$(shell pwd)/target/debug cargo build
$(RELEASE_TARGETS): $(SOURCES)
HELPER_DIR=$(HELPER_DIR) cargo build --release

View File

@ -4,9 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
async-io = "1.6"
async-std = { version = "1.11", features = ["attributes"] }
async-io = "2.3"
async-std = { version = "1.12", features = ["attributes"] }
futures = "0.3"
futures-lite = "1.12"
nix = "0.23"
futures-lite = "2.3"
nix = { version = "0.29", features = ["fs"] }
xcb = "1"

View File

@ -1,12 +1,24 @@
use async_io::{Async, ReadableOwned};
use futures::prelude::*;
use futures_lite::ready;
use nix::{fcntl::{fcntl, F_GETFL, F_SETFL, OFlag}, unistd::read};
use std::{io, os::unix::io::AsRawFd, pin::Pin, sync::Arc, task::{Context, Poll}};
use nix::{fcntl::{fcntl, OFlag, F_GETFL, F_SETFL}, unistd::read};
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 {
conn: Arc<Async<xcb::Connection>>,
readable: Option<ReadableOwned<xcb::Connection>>,
conn: Arc<Async<XcbAsFdWrapper>>,
readable: Option<ReadableOwned<XcbAsFdWrapper>>,
}
impl AsyncConnection {
@ -14,7 +26,7 @@ impl AsyncConnection {
let flags = fcntl(conn.as_raw_fd(), F_GETFL)?;
fcntl(conn.as_raw_fd(), F_SETFL(OFlag::from_bits_truncate(flags) | OFlag::O_NONBLOCK))?;
Ok(Self {
conn: Arc::new(Async::new(conn)?),
conn: Arc::new(Async::new(XcbAsFdWrapper(conn))?),
readable: None,
})
}
@ -23,7 +35,7 @@ impl AsyncConnection {
impl AsyncRead for AsyncConnection {
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
loop {
match read(self.conn.as_raw_fd(), buf) {
match read(self.conn.as_fd().as_raw_fd(), buf) {
Err(nix::Error::EAGAIN) => (),
Err(err) => {
self.readable = None;

View File

@ -5,6 +5,6 @@ edition = "2021"
[dependencies]
bscreensaver-util = { path = "../util" }
clap = "3"
nix = "0.23"
clap = { version = "4", features = ["derive"] }
nix = { version = "0.29", features = ["poll"] }
xcb = "1"

View File

@ -1,8 +1,8 @@
use nix::poll::{poll, PollFd, PollFlags};
use std::{cmp, error::Error as StdError, fmt, time::{Duration, Instant}, os::unix::prelude::AsRawFd};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use std::{error::Error as StdError, fmt, os::unix::prelude::AsRawFd, time::{Duration, Instant}};
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_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 since_start = start.elapsed();
if since_start > to {
0i32
Err(Error::Timeout)
} else {
cmp::max(i32::MAX as u128, (to - since_start).as_millis()) as i32
}
}).unwrap_or(-1);
if poll_timeout == 0 {
break 'outer1 Err(Error::Timeout);
Ok(u16::try_from((to - since_start).as_millis()).unwrap_or(u16::MAX).into())
}
}).unwrap_or(Ok(PollTimeout::NONE))?;
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)?;
}
}

View File

@ -1,58 +1,48 @@
use clap::{Arg, Command};
use std::{env, io, process::exit, time::Duration};
use clap::{CommandFactory, Parser};
use std::{io, process::exit, time::Duration};
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<()> {
let mut command = Command::new("bscreensaver-command")
.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 args = Args::parse();
let command =
if args.is_present("blank") {
if args.blank {
BCommand::Blank
} else if args.is_present("lock") {
} else if args.lock {
BCommand::Lock
} else if args.is_present("deactivate") {
} else if args.deactivate {
BCommand::Deactivate
} else if args.is_present("restart") {
} else if args.restart {
BCommand::Restart
} else if args.is_present("exit") {
} else if args.exit {
BCommand::Exit
} else {
command.print_help()?;
Args::command().print_help()?;
exit(1);
};

View File

@ -12,4 +12,4 @@ bscreensaver-util = { path = "../util" }
futures = "0.3"
log = "0.4"
xcb = { version = "1", features = ["dpms"] }
zbus = "3"
zbus = "4"

View File

@ -3,7 +3,7 @@ use bscreensaver_util::init_logging;
use futures::{future::FutureExt, pin_mut, select};
use log::{debug, error, info, trace, warn};
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_util::opt_contains;
@ -25,7 +25,7 @@ struct ScreenSaver {
state: Arc<Mutex<State>>,
}
#[dbus_interface(name = "org.freedesktop.ScreenSaver")]
#[interface(name = "org.freedesktop.ScreenSaver")]
impl ScreenSaver {
async fn inhibit(
&mut self,
@ -47,7 +47,7 @@ impl ScreenSaver {
return Ok(0);
}
let peer = hdr.sender()?;
let peer = hdr.sender();
let cookie = rand_u32().await
.map_err(|err| fdo::Error::IOError(err.to_string()))?;
self.state.lock().await.inhibitors.push(Inhibitor {

View File

@ -7,12 +7,18 @@ edition = "2021"
anyhow = "1"
bscreensaver-util = { path = "../util" }
chrono = "0.4"
gethostname = "0.2"
glib = { version = "0.15", features = ["log"] }
gtk = "0.15"
gtk-sys = "0.15"
gdk-sys = "0.15"
gdkx11 = "0.15"
gethostname = "0.4"
glib = { version = "0.18", features = ["log"] }
gtk = "0.18"
gtk-sys = "0.18"
gdk-sys = "0.18"
gdkx11 = "0.18"
log = "0.4"
pam-client = "0.5"
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"] }

View File

@ -1,7 +1,7 @@
use chrono::prelude::*;
use gdkx11::X11Window;
use gethostname::gethostname;
use glib::{GString, clone};
use glib::{ControlFlow, GString, clone};
use gtk::{prelude::*, Button, Entry, Label, Plug, Window};
use log::{debug, error, warn};
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);
glib::timeout_add_seconds_local(1, move || {
set_time_label(&label);
glib::Continue(true)
ControlFlow::Continue
});
let sep = gtk::Separator::builder()
@ -275,19 +275,19 @@ fn main() -> anyhow::Result<()> {
.build();
top_vbox.pack_end(&timer, false, false, 0);
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;
if new_fraction <= 0.0 {
DialogExitStatus::AuthTimedOut.exit();
}
timer.set_fraction(new_fraction);
Continue(true)
ControlFlow::Continue
}));
let (tx, rx) = glib::MainContext::channel(glib::Priority::default());
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() {
auth_vbox.remove(&child);
}
@ -366,7 +366,7 @@ fn main() -> anyhow::Result<()> {
if ev.keyval().name() == Some(GString::from("Escape")) {
DialogExitStatus::AuthCanceled.exit();
}
gtk::Inhibit(false)
glib::Propagation::Stop
}));
input_box.grab_focus();
@ -388,7 +388,7 @@ fn main() -> anyhow::Result<()> {
};
dialog.show_all();
glib::Continue(true)
ControlFlow::Continue
}));
thread::spawn(move || {

View File

@ -42,13 +42,13 @@ assets = [
[dependencies]
anyhow = "1"
clap = "3"
clap = { version = "4", features = ["derive"] }
bscreensaver-command = { path = "../command" }
bscreensaver-util = { path = "../util" }
libc = "0.2"
log = "0.4"
nix = "0.23"
nix = { version = "0.29", features = ["process", "signal"] }
xcb = { version = "1", features = ["randr", "xkb", "xfixes", "xinput"] }
xcb-xembed = { path = "../xcb-xembed" }
xkb = { version = "0.3", features = ["x11"] }
xkbcommon-sys = { version = "1", feature = "x11" }
xkbcommon-sys = { version = "=1.4.1", features = ["x11"] }

View File

@ -1,23 +1,13 @@
use clap::{Arg, Command as ClapCommand};
use clap::Parser;
use log::{debug, error, info, trace, warn};
use nix::{
poll::{poll, PollFd, PollFlags},
unistd::{execv, fork, setsid, ForkResult},
sys::{
poll::{poll, PollFd, PollFlags, PollTimeout}, sys::{
signal::{sigprocmask, SigSet, SigmaskHow, Signal},
signalfd::{SignalFd, SfdFlags},
},
signalfd::{SfdFlags, SignalFd},
}, unistd::{execv, fork, setsid, ForkResult}
};
use std::{
env,
ffi::CString,
fs::read_link,
io,
os::unix::io::AsRawFd,
rc::Rc,
process::exit,
sync::Mutex,
time::{Duration, Instant}, path::PathBuf,
env, ffi::CString, fs::read_link, io, os::{fd::AsFd, unix::io::AsRawFd}, path::PathBuf, process::exit, rc::Rc, sync::Mutex, time::{Duration, Instant}
};
use xcb::{randr, x, xfixes, xinput};
@ -30,33 +20,35 @@ const LOCKED_ARG: &str = "locked";
macro_rules! maybe_add_fd {
($pfds:expr, $fd:expr) => {
if let Some(fd) = $fd {
$pfds.push(PollFd::new(fd, PollFlags::POLLIN));
if fd >= 0 {
$pfds.push(PollFd::new(borrow_raw_fd(fd), PollFlags::POLLIN));
Some(fd)
} else {
None
}
} else {
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<()> {
init_logging("BSCREENSAVER_LOG");
let config = Configuration::load()?;
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 args = Args::parse();
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)?;
if args.is_present(LOCKED_ARG) {
if args.locked {
match screensaver.lock_screen() {
Err(err) => error!("POSSIBLY FAILED TO LOCK SCREEN ON STARTUP: {}", err),
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() {
Err(err) => warn!("Possibly failed to blank screen on startup: {}", err),
Ok(_) => debug!("Got --{} arg; screen locked on startup", BLANKED_ARG),
@ -120,9 +112,17 @@ fn main() -> anyhow::Result<()> {
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();
pfds.push(PollFd::new(signal_fd.as_raw_fd(), PollFlags::POLLIN));
pfds.push(PollFd::new(conn.as_raw_fd(), PollFlags::POLLIN));
pfds.push(PollFd::new(borrow_raw_fd(signal_fd.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 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()));
@ -135,21 +135,19 @@ fn main() -> anyhow::Result<()> {
BlankerState::Blanked => Some(config.lock_timeout - since_last_activity),
BlankerState::Locked => None,
};
let poll_timeout = poll_timeout.map(|pt| if pt.as_millis() > i32::MAX as u128 {
i32::MAX
} else {
pt.as_millis() as i32
}).unwrap_or(-1);
let poll_timeout = poll_timeout
.map(|pt| PollTimeout::try_from(pt).unwrap_or(PollTimeout::MAX))
.unwrap_or(PollTimeout::NONE);
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
trace!("polled; {} FD ready", nready);
if nready > 0 {
for pfd in pfds {
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 == 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(&systemd_service_fd, &fd) => subservices.lock().unwrap().handle_quit(),
fd if opt_contains(&dialog_fd, &fd) => screensaver.handle_unlock_dialog_quit(),

View File

@ -547,7 +547,7 @@ fn find_backlight_control(conn: &xcb::Connection, output: randr::Output) -> anyh
if let Ok(reply) = conn.wait_for_reply(cookie) {
let values = reply.valid_values();
if reply.range() && values.len() == 2 {
let min_level = values[0];
let min_level = values[0] + 1;
let max_level = values[1];
let range = max_level - min_level;
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);
return Ok(Some(BacklightControl {
location: BacklightLocation::Sysfs(cur_path),
min_level: 0,
min_level: 1,
max_level,
step: calc_step(0, max_level),
}));

View File

@ -12,7 +12,7 @@ fn pidfd_open(pid: RawFd) -> nix::Result<PidFd> {
} else {
// SAFETY: pointer is checked for null; libc must be sane
let errno = unsafe { *errno_location };
Err(nix::errno::Errno::from_i32(errno))
Err(nix::errno::Errno::from_raw(errno))
}
}
}

View File

@ -6,6 +6,6 @@ edition = "2021"
[dependencies]
anyhow = "1"
bscreensaver-util = { path = "../util" }
glib = "0.15"
gtk = { version = "0.15", features = ["v3_20"] }
glib = "0.18"
gtk = "0.18"
log = "0.4"

View File

@ -1,5 +1,5 @@
use gtk::{glib, prelude::*};
use glib::clone;
use glib::{clone, Propagation::{Proceed, Stop}};
use log::warn;
use std::{env, process::exit, time::Duration, ffi::CString};
@ -31,7 +31,7 @@ fn main() -> anyhow::Result<()> {
.build();
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: &gtk::Application, config: &Configuration) {
@ -226,7 +226,11 @@ fn show_ui(app: &gtk::Application, config: &Configuration) {
handle_brightness_keys_checkbox: handle_brightness_keys_checkbox.clone(),
};
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 |_| {
run_file_chooser(&mainwin, &widgets);

View File

@ -5,13 +5,13 @@ edition = "2021"
[dependencies]
anyhow = "1"
async-std = { version = "1.11", features = ["attributes"] }
async-std = { version = "1.12", features = ["attributes"] }
async-xcb = { path = "../async-xcb" }
bscreensaver-command = { path = "../command" }
bscreensaver-util = { path = "../util" }
futures = "0.3"
log = "0.4"
nix = "0.23"
nix = "0.29"
xcb = "1"
zbus = "3"
logind-zbus = "3"
zbus = "4"
logind-zbus = "^4.0.1"

View File

@ -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,
// which means the generated proxy uses the wrong dbus type signature. So instead, use a raw
// 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?)
}

View File

@ -7,13 +7,13 @@ edition = "2021"
[dependencies]
anyhow = "1"
clap = "3"
env_logger = "0.9"
clap = "4"
env_logger = "0.11"
humantime = "2"
lazy_static = "1"
libc = "0.2"
shell-words = "1"
toml = "0.5"
toml = "0.8"
xcb = "1"
xdg = "2"
zbus = "3"
zbus = "4"

View File

@ -1,4 +1,4 @@
use std::{ffi::CStr, io};
use std::{ffi::CStr, io, os::fd::{BorrowedFd, RawFd}};
use xcb::x;
pub mod desktop;
@ -77,3 +77,10 @@ pub fn get_username() -> io::Result<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) }
}

View File

@ -13,6 +13,6 @@ keywords = ["gui", "x11", "xcb", "xembed"]
categories = ["gui"]
[dependencies]
bitflags = "1"
bitflags = "2"
log = "0.4"
xcb = { version = "1", features = ["xfixes"] }

View File

@ -130,7 +130,7 @@ impl<'a> Embedder<'a> {
match event {
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)?;
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) {
debug!("Mapping client window");
self.conn.send_and_check_request(&x::MapWindow {