Skip to content

Commit

Permalink
Refactore code so that slint code is checked before the screen is locked
Browse files Browse the repository at this point in the history
  • Loading branch information
FriederHannenheim committed Dec 5, 2023
1 parent 30ff08c commit 2bb063c
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 52 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Cthulock
Cthulock is a screen locker for Wayland focused on customizability. You can style your lock screen using the [Slint](https://slint.dev/docs.html) language. An example config is already provided for you to build upon.

Cthulock is still in development, if you have any suggestions for features please open an issue. Code contributions through pull requests are also always welcome.

## Example Screenshot
![Example Screenshot](./docs/example_config_screenshot.png)
## Installation
Expand Down
7 changes: 7 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ forward-focus: password;
// A clock string will be available using this property.
// TODO: The user should be able to switch between 12 and 24 hour clock. Currently it is always 24 hour
in property<string> clock_text;
```

Testing your configuration in a nested wayland session is always encouraged. Cthulock will test if your slint code compiles before locking the screen but if you don't leave a way to unlock the screen you might get locked out of your PC until a reboot.

Testing is best done using labwc:
```
$ labwc -s cthulock
```
32 changes: 25 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::{sync::mpsc, thread};

use futures::executor::block_on;
use slint_interpreter::{ComponentCompiler, ComponentDefinition};

use crate::{
message::{UiMessage, WindowingMessage},
ui::ui_thread,
Expand All @@ -17,15 +20,18 @@ mod windowing_thread;
fn main() -> Result<()> {
init_logger();

let theme = load_theme()?;
let style = load_style()?;

let (sender_to_render, receiver_from_windowing) = mpsc::channel::<WindowingMessage>();
let (sender_to_windowing, receiver_from_render) = mpsc::channel::<UiMessage>();

thread::spawn(move || {
ui_thread(&theme, sender_to_windowing, receiver_from_windowing).unwrap();
if windowing_thread(sender_to_render.clone(), receiver_from_render).is_err() {
sender_to_render.send(WindowingMessage::Quit).unwrap();
}
});

windowing_thread(sender_to_render, receiver_from_render);
ui_thread(style, sender_to_windowing, receiver_from_windowing)?;

Ok(())
}
Expand All @@ -39,7 +45,7 @@ fn init_logger() {
env_logger::init();
}

fn load_theme() -> Result<String> {
fn load_style() -> Result<ComponentDefinition> {
let xdg_dirs = xdg::BaseDirectories::with_prefix("cthulock").map_err(|_| {
CthulockError::new("Failed to get XDG-Directories. This can only happen on Windows. Cthulock is not a Windows program.")
})?;
Expand All @@ -48,7 +54,19 @@ fn load_theme() -> Result<String> {
CthulockError::new("Could not find style.slint in config paths")
)?;

std::fs::read_to_string(theme_path).map_err(|e| {
let style = std::fs::read_to_string(theme_path).map_err(|e| {
CthulockError::new(&e.to_string())
})
})?;


let mut config_dirs = xdg_dirs.get_config_dirs();
config_dirs.push(xdg_dirs.get_config_home());
let mut compiler = ComponentCompiler::default();
compiler.set_include_paths(config_dirs);

let definition = block_on(compiler.build_from_source(style.into(), Default::default()));
slint_interpreter::print_diagnostics(&compiler.diagnostics());
definition.ok_or(
CthulockError::new("Compiling the Slint code failed")
)
}
1 change: 1 addition & 0 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum WindowingMessage {
},
SlintWindowEvent(WindowEvent),
UnlockFailed,
Quit,
}

#[derive(Debug)]
Expand Down
78 changes: 38 additions & 40 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use chrono::Local;
use std::{
sync::mpsc::{Receiver, Sender},
time::Duration, path::PathBuf,
time::Duration, rc::Rc,
};
use futures::executor::block_on;
use crate::{
message::{UiMessage, WindowingMessage},
ui:: {
Expand All @@ -19,43 +18,17 @@ use slint::{
use slint_interpreter::{
Value,
SharedString,
ComponentCompiler,
ComponentHandle, ComponentInstance
ComponentHandle, ComponentInstance, ComponentDefinition
};

mod egl;
mod platform;
mod window_adapter;

pub fn ui_thread(theme: &str, sender: Sender<UiMessage>, receiver: Receiver<WindowingMessage>) -> Result<()>{
let (display_id, surface_id, size) = match receiver.recv().unwrap() {
WindowingMessage::SurfaceReady {
display_id,
surface_id,
size,
} => (display_id, surface_id, size),
message => panic!(
"First message sent to render thread is not ContextCreated. Is {:?}",
message
),
};

let context = OpenGLContext::new(display_id, surface_id, size);
let renderer = FemtoVGRenderer::new(context).unwrap();
let slint_window = MinimalFemtoVGWindow::new(renderer);
slint_window.set_size(slint::WindowSize::Physical(PhysicalSize::new(
size.0, size.1,
)));

let platform = CthulockSlintPlatform::new(slint_window.clone());
slint::platform::set_platform(Box::new(platform)).unwrap();
pub fn ui_thread(style: ComponentDefinition, sender: Sender<UiMessage>, receiver: Receiver<WindowingMessage>) -> Result<()>{
let slint_window = wait_for_configure_and_set_platform(&receiver)?;

let xdg_dirs = xdg::BaseDirectories::with_prefix("cthulock").unwrap();
let mut config_dirs = xdg_dirs.get_config_dirs();
config_dirs.push(xdg_dirs.get_config_home());

log::debug!("Config directories: {:?}", config_dirs);
let ui = create_ui(sender.clone(), theme, config_dirs)?;
let ui = create_ui(sender.clone(), style)?;
ui.show().unwrap();

let running = true;
Expand Down Expand Up @@ -85,7 +58,11 @@ pub fn ui_thread(theme: &str, sender: Sender<UiMessage>, receiver: Receiver<Wind
ui.set_property("password", SharedString::from("").into()).map_err(|_| {
CthulockError::property_fail("password")
})?;
}
},
WindowingMessage::Quit => {
log::info!("quitting UI thread...");
return Ok(());
},
WindowingMessage::SurfaceReady { .. } => panic!("surface already configured"),
}
}
Expand All @@ -109,13 +86,8 @@ pub fn ui_thread(theme: &str, sender: Sender<UiMessage>, receiver: Receiver<Wind
}


fn create_ui(sender: Sender<UiMessage>, theme: &str, include_paths: Vec<PathBuf>) -> Result<ComponentInstance> {
let mut compiler = ComponentCompiler::default();
compiler.set_include_paths(include_paths);

let definition = block_on(compiler.build_from_source(theme.into(), Default::default()));
slint_interpreter::print_diagnostics(&compiler.diagnostics());
let ui = definition.unwrap().create().unwrap();
fn create_ui(sender: Sender<UiMessage>, style: ComponentDefinition) -> Result<ComponentInstance> {
let ui = style.create().unwrap();

let sender_clone = sender.clone();
let ui_ref = ui.as_weak();
Expand All @@ -139,4 +111,30 @@ fn create_ui(sender: Sender<UiMessage>, theme: &str, include_paths: Vec<PathBuf>
})?;

Ok(ui)
}

fn wait_for_configure_and_set_platform(receiver: &Receiver<WindowingMessage>) -> Result<Rc<MinimalFemtoVGWindow>> {
let (display_id, surface_id, size) = match receiver.recv().unwrap() {
WindowingMessage::SurfaceReady {
display_id,
surface_id,
size,
} => (display_id, surface_id, size),
message => panic!(
"First message sent to render thread is not SurfaceReady. Is {:?}",
message
),
};

let context = OpenGLContext::new(display_id, surface_id, size);
let renderer = FemtoVGRenderer::new(context).unwrap();
let slint_window = MinimalFemtoVGWindow::new(renderer);
slint_window.set_size(slint::WindowSize::Physical(PhysicalSize::new(
size.0, size.1,
)));

let platform = CthulockSlintPlatform::new(slint_window.clone());
slint::platform::set_platform(Box::new(platform)).unwrap();

Ok(slint_window)
}
16 changes: 11 additions & 5 deletions src/windowing_thread.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::message::{UiMessage, WindowingMessage};
use crate::{message::{UiMessage, WindowingMessage}, Result, common::CthulockError};
use pam_client::{conv_mock::Conversation, Context, Flag};
use slint::{
platform::{Key, PointerEventButton, WindowEvent},
Expand Down Expand Up @@ -28,8 +28,10 @@ use wayland_protocols::ext::session_lock::v1::client::{
ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1,
};

pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiMessage>) {
let conn = Connection::connect_to_env().unwrap();
pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiMessage>) -> Result<()> {
let conn = Connection::connect_to_env().map_err(|_| {
CthulockError::new("Failed to connect to wayland.")
})?;

let display = conn.display();

Expand All @@ -41,7 +43,9 @@ pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiM
let compositor: wl_compositor::WlCompositor = globals.bind(&qh, 1..=5, ()).unwrap();
let wl_surface = compositor.create_surface(&qh, ());
let output: wl_output::WlOutput = globals.bind(&qh, 1..=1, ()).unwrap();
let session_lock_manager: ext_session_lock_manager_v1::ExtSessionLockManagerV1 = globals.bind(&qh, 1..=1, ()).expect("Your compositor does not support ext-session-lock-v1. Cthulock does not work without it");
let session_lock_manager: ext_session_lock_manager_v1::ExtSessionLockManagerV1 = globals.bind(&qh, 1..=1, ()).map_err(|_| {
CthulockError::new("Could not bind ext-session-lock-v1. Your compositor probably does not support this.")
})?;
let session_lock = session_lock_manager.lock(&qh, ());
let session_lock_surface = session_lock.get_lock_surface(&wl_surface, &output, &qh, ());

Expand All @@ -52,7 +56,7 @@ pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiM
session_lock,
session_lock_surface,
SeatState::new(&globals, &qh),
sender,
sender
);

while state.running {
Expand Down Expand Up @@ -80,6 +84,7 @@ pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiM
&& context.acct_mgmt(Flag::NONE).is_ok()
{
log::info!("authentication successfull, quitting...");
state.render_thread_sender.send(WindowingMessage::Quit).unwrap();
state.session_lock.unlock_and_destroy();
event_queue.roundtrip(&mut state).unwrap();
state.running = false;
Expand All @@ -93,6 +98,7 @@ pub fn windowing_thread(sender: Sender<WindowingMessage>, receiver: Receiver<UiM
}
}
}
Ok(())
}

// This struct represents the state of our app
Expand Down

0 comments on commit 2bb063c

Please sign in to comment.