diff --git a/Cargo.deps.newer b/Cargo.deps.newer index 914cc6aa..8dfae554 100644 --- a/Cargo.deps.newer +++ b/Cargo.deps.newer @@ -1,5 +1,6 @@ -# Dependencies which change based on build flags +# Dependencies and tools which change based on build flags # For the newer-than-Byzantium config + bitflags = "1.3.*" clap = { version = "3.2.*", features=["std"], default-features = false } zbus = "1.9.*" diff --git a/Cargo.toml.in b/Cargo.toml.in index 65b14f37..f246d1c4 100644 --- a/Cargo.toml.in +++ b/Cargo.toml.in @@ -23,6 +23,7 @@ path = "@path@/examples/find_orphan_layouts.rs" [features] glib_v0_14 = [] +zbus_v1_5 = [] # Dependencies which don't change based on build flags [dependencies] diff --git a/meson.build b/meson.build index a80afa29..a3eeb947 100644 --- a/meson.build +++ b/meson.build @@ -99,7 +99,7 @@ cargo_toml_base = configure_file( cargo_patch = [] if get_option('newer') == true - cargo_build_flags += ['--features', 'glib_v0_14'] + cargo_build_flags += ['--features', 'glib_v0_14,zbus_v1_5'] cargo_deps = files('Cargo.deps.newer') cargo_lock = files('Cargo.lock.newer') else diff --git a/src/debug.rs b/src/actors/external/debug.rs similarity index 94% rename from src/debug.rs rename to src/actors/external/debug.rs index bac4fa31..253d1d0e 100644 --- a/src/debug.rs +++ b/src/actors/external/debug.rs @@ -3,12 +3,13 @@ * * SPDX-License-Identifier: GPL-3.0-or-later */ -use std::thread; -use zbus::{Connection, ObjectServer, dbus_interface, fdo}; - use crate::main; use crate::state; +use std::thread; +use zbus::{Connection, ObjectServer, dbus_interface, fdo}; + +use super::Void; use std::convert::TryInto; @@ -37,7 +38,7 @@ impl Manager { } } -fn start(mgr: Manager) -> Result<(), Box> { +fn start(mgr: Manager) -> Result> { let connection = Connection::new_session()?; fdo::DBusProxy::new(&connection)?.request_name( "sm.puri.SqueekDebug", diff --git a/src/actors/external/mod.rs b/src/actors/external/mod.rs new file mode 100644 index 00000000..d591386a --- /dev/null +++ b/src/actors/external/mod.rs @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2022 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/*! Contains actors with custom event loops, not based off of the event_loop module. */ + +pub mod debug; +#[cfg(feature = "zbus_v1_5")] +pub mod screensaver; + +/// The uninhabited type. Cannot be created or returned; means "will never return" as return type. Useful for infinite loops. +enum Void {} \ No newline at end of file diff --git a/src/actors/external/screensaver.rs b/src/actors/external/screensaver.rs new file mode 100644 index 00000000..7f5afe24 --- /dev/null +++ b/src/actors/external/screensaver.rs @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +use crate::actors::Destination; +use crate::actors::popover; +use crate::logging; +use std::thread; +use zbus::{Connection, dbus_proxy}; + +use super::Void; + + +#[dbus_proxy( + interface = "org.freedesktop.ScreenSaver", + default_service = "org.freedesktop.ScreenSaver", + default_path = "/org/freedesktop/ScreenSaver" +)] +pub trait Manager { + #[dbus_proxy(signal)] + fn active_changed(&self, active: bool) -> fdo::Result<()>; +} + +/// Listens to screensaver (screen lock) changes +pub fn init(destination: popover::Destination) { + thread::spawn(move || { + if let Err(e) = start(destination) { + log_print!( + logging::Level::Surprise, + "Could not track screensaver status, giving up: {:?}", + e, + ); + } + }); +} + +fn start(destination: popover::Destination) -> Result { + let conn = Connection::new_session()?; + let manager = ManagerProxy::new(&conn)?; + + manager.connect_active_changed(move |m| { + destination.send(popover::Event::ScreensaverActive(m)); + Ok(()) + })?; + + loop { + match manager.next_signal() { + Ok(None) => {} + other => log_print!( + logging::Level::Bug, + "Encountered unhandled event: {:?}", + other, + ), + } + } +} \ No newline at end of file diff --git a/src/actors/mod.rs b/src/actors/mod.rs index 378da9eb..8e45d673 100644 --- a/src/actors/mod.rs +++ b/src/actors/mod.rs @@ -20,4 +20,14 @@ and by receiving updates from it. // Panel contains state and logic to protect the main state from getting flooded // with low-level wayland and gtk sizing events. -pub mod popover; \ No newline at end of file +pub mod external; +pub mod popover; + +/// The implementing actor is able to receive and handle messages. +/// Typically, it's the sending end of the channel, +/// whose other end is inside an event loop. +// TODO: implement for remaning actors and make the event loop refer to this. +pub trait Destination { + type Event; + fn send(&self, event: Self::Event); +} diff --git a/src/actors/popover.rs b/src/actors/popover.rs index e3020c2a..c2a3caa2 100644 --- a/src/actors/popover.rs +++ b/src/actors/popover.rs @@ -11,30 +11,66 @@ but it cannot get the user-selected overlay, because it's stored in state. To solve this, overlay will be cached in the popover actor, and updated by main state every time it changes. */ +use crate::logging; +use std::borrow::BorrowMut; +use std::sync::{Arc, Mutex}; pub mod c { use super::*; - use crate::util::c::Wrapped; - /// The mutable instance of state - pub type Actor = Wrapped; + use crate::util::c::ArcWrapped; + /// The mutable instance of state. + /// Thread-safe because this actor does not get its own event loop, + /// and therefore can't have a channel to receive messages, + /// so instead messages will be passed directly to the mutexed actor. + pub type Actor = ArcWrapped; } -#[derive(Clone)] -pub struct State { - pub overlay: Option, +pub type Destination = Arc>; + +#[derive(Debug)] +pub enum Event { + Overlay(Option), + ScreensaverActive(bool), } -impl State { - pub fn new() -> Self { - Self { overlay: None } +impl super::Destination for Destination { + type Event = Event; + fn send(&self, event: Self::Event) { + let actor = self.lock(); + match actor { + Ok(mut actor) => { + let actor = actor.borrow_mut(); + **actor = actor.clone().handle_event(event); + }, + Err(e) => log_print!( + logging::Level::Bug, + "Cannot lock popover state: {:?}", + e, + ), + } } } -pub fn set_overlay( - actor: &c::Actor, - overlay: Option, -) { - let actor = actor.clone_ref(); - let mut actor = actor.borrow_mut(); - actor.overlay = overlay; -} \ No newline at end of file +#[derive(Clone, Debug)] +pub struct State { + pub overlay: Option, + /// Settings button active + pub settings_active: bool, +} + +impl State { + pub fn new(settings_active: bool) -> Self { + Self { + overlay: None, + settings_active, + } + } + + fn handle_event(mut self, event: Event) -> Self { + match event { + Event::Overlay(overlay) => { self.overlay = overlay; }, + Event::ScreensaverActive(lock_active) => { self.settings_active = !lock_active; }, + }; + self + } +} diff --git a/src/lib.rs b/src/lib.rs index f1e70c69..16885743 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,10 +23,9 @@ mod assert_matches; mod logging; mod action; -mod actors; +pub mod actors; mod animation; pub mod data; -mod debug; mod drawing; mod event_loop; pub mod float_ord; diff --git a/src/main.rs b/src/main.rs index 8d56c140..bc051ff7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,8 @@ /*! Glue for the main loop. */ use crate::actors; +use crate::actors::external::debug; use crate::animation; -use crate::debug; use crate::data::loading; use crate::event_loop; use crate::panel; @@ -21,6 +21,8 @@ mod c { use std::rc::Rc; use std::time::Instant; + use crate::actors::Destination; + use crate::actors::popover; use crate::event_loop::driver; use crate::imservice::IMService; use crate::imservice::c::InputMethod; @@ -28,7 +30,7 @@ mod c { use crate::outputs::Outputs; use crate::state; use crate::submission::Submission; - use crate::util::c::Wrapped; + use crate::util::c::{ArcWrapped, Wrapped}; use crate::vkeyboard::c::ZwpVirtualKeyboardV1; /// DbusHandler* @@ -121,12 +123,17 @@ mod c { }; let submission = Submission::new(vk, imservice); + let popover = ArcWrapped::new(actors::popover::State::new(true)); + + #[cfg(feature = "zbus_v1_5")] + crate::actors::external::screensaver::init(popover.clone_ref()); + RsObjects { submission: Wrapped::new(submission), state_manager: Wrapped::new(state_manager), receiver: Wrapped::new(receiver), wayland: Box::into_raw(wayland), - popover: Wrapped::new(actors::popover::State::new()), + popover, } } @@ -152,7 +159,7 @@ mod c { main_loop_handle_message( msg, panel_manager.clone(), - &popover, + &popover.clone_ref(), hint_manager, dbus_handler, ); @@ -170,7 +177,7 @@ mod c { fn main_loop_handle_message( msg: Commands, panel_manager: Wrapped, - popover: &actors::popover::c::Actor, + popover: &actors::popover::Destination, hint_manager: HintManager, dbus_handler: *const DBusHandler, ) { @@ -191,7 +198,7 @@ mod c { overlay_name, purpose, } = description; - actors::popover::set_overlay(popover, overlay_name.clone()); + popover.send(popover::Event::Overlay(overlay_name.clone())); let layout = loading::load_layout(&name, kind, purpose, &overlay_name); let layout = Box::into_raw(Box::new(layout)); // CSS can't express "+" in the class diff --git a/src/popover.rs b/src/popover.rs index 48820a3c..8f4e4a5f 100644 --- a/src/popover.rs +++ b/src/popover.rs @@ -364,6 +364,7 @@ pub fn show( }; let settings_action = gio::SimpleAction::new("settings", None); + settings_action.set_enabled(popover.settings_active); settings_action.connect_activate(move |_, _| { let s = CString::new("region").unwrap(); unsafe { c::popover_open_settings_panel(s.as_ptr()) }; diff --git a/src/state.rs b/src/state.rs index 94069a14..063cf051 100644 --- a/src/state.rs +++ b/src/state.rs @@ -5,8 +5,8 @@ /*! Application-wide state is stored here. * It's driven by the loop defined in the loop module. */ +use crate::actors::external::debug; use crate::animation; -use crate::debug; use crate::event_loop; use crate::event_loop::ActorState; use crate::imservice::{ ContentHint, ContentPurpose }; diff --git a/src/util.rs b/src/util.rs index 8e93f096..06229d51 100644 --- a/src/util.rs +++ b/src/util.rs @@ -15,6 +15,7 @@ pub mod c { use std::os::raw::c_char; use std::rc::Rc; use std::str::Utf8Error; + use std::sync::{Arc, Mutex}; // traits @@ -128,6 +129,53 @@ pub mod c { } impl COpaquePtr for Wrapped {} + + /// Similar to Wrapped, except thread-safe. + #[repr(transparent)] + pub struct ArcWrapped(*const Mutex); + + impl ArcWrapped { + pub fn new(value: T) -> Self { + Self::wrap(Arc::new(Mutex::new(value))) + } + pub fn wrap(state: Arc>) -> Self { + Self(Arc::into_raw(state)) + } + /// Extracts the reference to the data. + /// It may cause problems if attempted in more than one place + pub unsafe fn unwrap(self) -> Arc> { + Arc::from_raw(self.0) + } + + /// Creates a new Rc reference to the same data. + /// Use for accessing the underlying data as a reference. + pub fn clone_ref(&self) -> Arc> { + // A bit dangerous: the Rc may be in use elsewhere + let used_rc = unsafe { Arc::from_raw(self.0) }; + let rc = used_rc.clone(); + Arc::into_raw(used_rc); // prevent dropping the original reference + rc + } + } + + impl Clone for ArcWrapped { + fn clone(&self) -> Self { + Self::wrap(self.clone_ref()) + } + } + + /// ToOwned won't work here + impl CloneOwned for ArcWrapped { + type Owned = T; + + fn clone_owned(&self) -> T { + let rc = self.clone_ref(); + // FIXME: this panic here is inelegant. + // It will only happen in case of crashes elsewhere, but still. + let r = rc.lock().unwrap(); + r.to_owned() + } + } } /// Clones the underlying data structure, like ToOwned.