Add transparency and lock

This commit is contained in:
Astatin3
2024-10-31 10:42:53 -06:00
parent 0597b0db04
commit 2e62807074
6 changed files with 375 additions and 21 deletions
+7 -2
View File
@@ -4,5 +4,10 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
eframe = "0.29.1" eframe = "0.28"
egui = "0.29.1" egui = "0.28"
portable-pty = "0.8.1"
termwiz = "0.22.0"
vte = "0.13.0"
egui-terminal = { git = "https://github.com/Quinntyx/egui-terminal" }
ecolor = "0.29.1"
+4 -2
View File
@@ -5,6 +5,8 @@ Unfortunatly this is not the most secure desktop locker, as it involves using sw
``` ```
# Add this to your sway config: # Add this to your sway config:
mode "lock" { } for_window [title="^raylock$"] sticky enable, floating enable, resize set 1920 px 1080 px, title_format "", border pixel none
for_window [title="^raylock$"] sticky enable, fullscreen mode "lock" {
bindsym $mod+Shift+q exec true
}
``` ```
+26
View File
@@ -1,19 +1,45 @@
use egui::Key; use egui::Key;
use std::fs::{exists, File};
use std::path::Path;
use std::process::Command; use std::process::Command;
use std::thread; use std::thread;
const LOCK_FILEPATH: &str = "/tmp/.raylock.lock";
pub fn sway_lock_input() { pub fn sway_lock_input() {
thread::spawn(move || { thread::spawn(move || {
let _ = Command::new("swaymsg").args(["mode", "lock"]).spawn(); let _ = Command::new("swaymsg").args(["mode", "lock"]).spawn();
let _ = Command::new("swaymsg")
.args(["input", "type:touchpad", "events", "disabled"])
.spawn();
}); });
} }
pub fn sway_unlock_input() { pub fn sway_unlock_input() {
thread::spawn(move || { thread::spawn(move || {
let _ = Command::new("swaymsg").args(["mode", "default"]).spawn(); let _ = Command::new("swaymsg").args(["mode", "default"]).spawn();
let _ = Command::new("swaymsg")
.args(["input", "type:touchpad", "events", "enabled"])
.spawn();
}); });
} }
pub fn create_lock() {
let _ = File::create(LOCK_FILEPATH).unwrap();
}
pub fn is_locked() -> bool {
Path::exists(Path::new(LOCK_FILEPATH))
}
pub fn remove_lock() {
if !is_locked() {
return;
}
std::fs::remove_file(Path::new(LOCK_FILEPATH));
}
pub fn format_key(key: Key, shift_pressed: bool) -> String { pub fn format_key(key: Key, shift_pressed: bool) -> String {
match key { match key {
// Letters // Letters
+143 -12
View File
@@ -1,17 +1,69 @@
use eframe::egui; use eframe::{egui, App};
use egui::FontFamily;
use egui::Key; use egui::Key;
use egui_terminal;
use input::is_locked;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
// use egui::epaint::text::{FontInsert, InsertFontFamily};
use eframe::CreationContext;
use egui::ecolor::Color32;
use egui::FontId;
use egui::Stroke;
use egui_terminal::prelude::*;
use egui_terminal::render::CursorType;
use egui::ecolor::HexColor;
mod input; mod input;
mod splitter;
mod structs; mod structs;
mod ui; mod ui;
// Demonstrates how to add a font to the existing ones
fn add_font(ctx: &egui::Context) {
// Start with the default fonts (we will be adding to them rather than replacing them).
let mut fonts = egui::FontDefinitions::default();
// Install my own font (maybe supporting non-latin characters).
// .ttf and .otf files supported.
fonts.font_data.insert(
"my_font".to_owned(),
egui::FontData::from_static(include_bytes!(
"/home/astatin3/.fonts/UbuntuMono/UbuntuMonoNerdFontMono-Regular.ttf"
)),
);
// Put my font first (highest priority) for proportional text:
// fonts
// .families
// .entry(egui::FontFamily::Proportional)
// .or_default()
// .insert(0, "my_font".to_owned());
// Put my font as last fallback for monospace:
fonts
.families
.entry(egui::FontFamily::Monospace)
.or_default()
.push("my_font".to_owned());
// Tell egui to use these fonts:
ctx.set_fonts(fonts);
}
use splitter::Splitter;
#[derive(Default)] #[derive(Default)]
struct ExampleApp { struct ExampleApp {
auth_state: Arc<Mutex<structs::AuthState>>, auth_state: Arc<Mutex<structs::AuthState>>,
terminals: HashMap<String, TermHandler>,
} }
impl ExampleApp { impl ExampleApp {
@@ -21,9 +73,34 @@ impl ExampleApp {
} }
impl eframe::App for ExampleApp { impl eframe::App for ExampleApp {
fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
egui::Rgba::from_rgba_unmultiplied(0.1, 0.1, 0.1, 0.9).to_array()
}
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// let s1 = self.terminals.get_mut("0").unwrap();
// s1.style()
// s1.cursor_trail = false;
// s1.cursor_trail_color = Some(HexColor::Hex8(Color32::LIGHT_BLUE.gamma_multiply(0.5)));
// s1.default_focus_cursor = CursorType::OpenBlock(HexColor::Hex8(Color32::RED));
// s1.default_unfocus_cursor = CursorType::None;
// for str in egui::FontDefinitions::builtin_font_names() {
// println!("{}", str);
// }
// s1.cursor_stroke = Stroke::new(1., Color32::WHITE);
// add_font(ctx);
// let font = FontId {
// size: 9.,
// family: FontFamily::Name("my_font".into()),
// };
// s1.font = font;
let mut state = self.auth_state.lock().unwrap(); let mut state = self.auth_state.lock().unwrap();
ctx.set_pixels_per_point(1.5); //ctx.set_pixels_per_point(1.5);
if ctx.input(|i| i.events.len() > 0) { if ctx.input(|i| i.events.len() > 0) {
ctx.input(|i| { ctx.input(|i| {
@@ -62,13 +139,46 @@ impl eframe::App for ExampleApp {
}); });
} }
egui::CentralPanel::default().show(ctx, |ui| { // let sine_points = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000);
// ui.add_space(200.0); // let plot_a_lines = [Line::new(sine_points).name("Sine")];
// ui.heading("*".repeat(state.password.clone().len()));
// ui.add_space(20.0); // let sine_points2 = PlotPoints::from_explicit_callback(|x| x.sin(), .., 5000);
// // let plot_b_lines = [Line::new(sine_points2).name("Sine")];
ui::update(state, ctx, _frame, ui);
}); egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(ctx, |ui| {
// let ht = ui.available_height();
// for (_idx, (_id, term)) in self.terminals.iter_mut().enumerate() {
// ui.terminal_sized(term, egui::vec2(ui.available_width(), ht));
// }
// ui.add_space(200.0);
// ui.heading("*".repeat(state.password.clone().len()));
// ui.add_space(20.0);
//
// ui.terminal_sized(
// self.terminals.get_mut("0").unwrap(),
// egui::vec2(ui.available_width(), ui.available_height()),
// );
// Splitter::new("some_unique_id", splitter::SplitterAxis::Horizontal).show(
// ui,
// |ui_a, ui_b| {
// ui_a.terminal_sized(
// self.terminals.get_mut("0").unwrap(),
// egui::vec2(ui_a.available_width(), ui_a.available_height()),
// );
// ui_b.terminal_sized(
// self.terminals.get_mut("1h-1").unwrap(),
// egui::vec2(ui_a.available_width(), ui_a.available_height()),
// );
// },
// );
ui::update_password_viewer(state, ctx, _frame, ui);
});
} }
} }
@@ -78,6 +188,10 @@ fn main() -> eframe::Result<()> {
..eframe::NativeOptions::default() ..eframe::NativeOptions::default()
}; };
// let system_shell = std::env::var("SHELL")
// .expect("SHELL variable is not defined")
// .to_string();
let state = Arc::new(Mutex::new(structs::AuthState { let state = Arc::new(Mutex::new(structs::AuthState {
password: String::new(), password: String::new(),
to_be_submitted: false, to_be_submitted: false,
@@ -94,12 +208,13 @@ fn main() -> eframe::Result<()> {
let result = try_sudo(&state.password); let result = try_sudo(&state.password);
match result { match result {
Ok(true) => { Ok(true) => {
println!("True"); // println!("True");
input::sway_unlock_input(); input::sway_unlock_input();
input::remove_lock();
std::process::exit(0); std::process::exit(0);
} }
Ok(false) => { Ok(false) => {
println!("False"); // println!("False");
state.failed_attempts += 1; state.failed_attempts += 1;
state.password.clear(); state.password.clear();
} }
@@ -113,12 +228,28 @@ fn main() -> eframe::Result<()> {
thread::sleep(Duration::from_millis(100)); thread::sleep(Duration::from_millis(100));
}); });
let mut map = HashMap::new();
// map.insert(String::from("0"), TermHandler::new_from_str("btop"));
// map.insert(String::from("1h-0"), TermHandler::new_from_str("nvitop"));
// map.insert(String::from("1h-1"), TermHandler::new_from_str("neofetch"));
input::sway_lock_input(); input::sway_lock_input();
if input::is_locked() {
println!("Raylock is already running!");
std::process::exit(1);
}
input::create_lock();
eframe::run_native( eframe::run_native(
ExampleApp::name(), ExampleApp::name(),
native_options, native_options,
Box::new(|_| Ok(Box::<ExampleApp>::new(ExampleApp { auth_state: state }))), Box::new(|_| {
Ok(Box::<ExampleApp>::new(ExampleApp {
auth_state: state,
terminals: map,
}))
}),
) )
} }
+190
View File
@@ -0,0 +1,190 @@
/// https://gist.github.com/mkalte666/f9a982be0ac0276080d3434ab9ea4655
use std::hash::Hash;
use egui::{CursorIcon, Id, Layout, Pos2, Rect, Rounding, Sense, Ui, Vec2};
/// An axis that a Splitter can use
#[derive(Copy, Clone, Debug)]
pub enum SplitterAxis {
Horizontal,
Vertical,
}
/// The internal data used by a splitter. Stored into memory
#[derive(Debug, Clone)]
struct SplitterData {
axis: SplitterAxis,
pos: f32,
min_size: f32,
}
/// Splits a ui in half, using a draggable separator in the middle.
///
pub struct Splitter {
id: Id,
data: SplitterData,
}
impl Splitter {
/// Create a new Splitter
pub fn new(id_source: impl Hash, axis: SplitterAxis) -> Self {
Self {
id: Id::new(id_source),
data: SplitterData {
axis,
pos: 0.5,
min_size: 0.0,
},
}
}
/// Sets the minimum allowed size for the area
pub fn min_size(mut self, points: f32) -> Self {
self.data.min_size = points;
self
}
/// Thes the default position of the splitter separator. Usually it sits in the center, this moves it around.
pub fn default_pos(mut self, pos: f32) -> Self {
self.data.pos = pos;
self
}
/// Show the splitter and fill it with content.
///
/// ```
/// Splitter::new("some_plot_split", SplitterAxis::Vertical)
/// .min_size(250.0)
/// .default_pos(2.0 / 3.0)
/// .show(ui, |ui_a, ui_b| {
/// Plot::new("plot_a")
/// .legend(Legend::default())
/// .x_axis_formatter(log_formatter)
/// .y_axis_formatter(log_formatter)
/// .x_axis_label("X Axis")
/// .y_axis_label("A Axis")
/// .link_axis("axis_link", true, false)
/// .link_cursor("cursor_link", true, false)
/// .show(ui_a, |plot_ui| {
/// for line in plot_a_lines {
/// plot_ui.line(line);
/// }
/// });
///
/// Plot::new("plot_b")
/// .legend(Legend::default())
/// .x_axis_formatter(log_formatter)
/// .x_axis_label("X Axis")
/// .y_axis_label("Y Axis")
/// .link_axis("axis_link", true, false)
/// .link_cursor("cursor_link", true, false)
/// .show(ui_b, |plot_ui| {
/// for line in plot_b_lines {
/// plot_ui.line(line);
/// }
/// });
/// });
/// ```
pub fn show(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui, &mut Ui)) {
let mut data = if let Some(d) = ui.memory(|mem| mem.data.get_temp(self.id)) {
d
} else {
self.data.clone()
};
let sep_size = 10.0;
let sep_stroke = 2.0;
let whole_area = ui.available_size();
let split_axis_size = match data.axis {
SplitterAxis::Horizontal => whole_area.x,
SplitterAxis::Vertical => whole_area.y,
};
let split_a_size = ((split_axis_size - sep_size) * data.pos);
let split_b_size = split_axis_size - sep_size - split_a_size;
let child_size_a = match data.axis {
SplitterAxis::Horizontal => Vec2::new(split_a_size, whole_area.y),
SplitterAxis::Vertical => Vec2::new(whole_area.x, split_a_size),
};
let child_size_b = match data.axis {
SplitterAxis::Horizontal => Vec2::new(split_b_size, whole_area.y),
SplitterAxis::Vertical => Vec2::new(whole_area.x, split_b_size),
};
let child_rect_a = Rect::from_min_size(ui.next_widget_position(), child_size_a);
let mut ui_a = ui.child_ui(child_rect_a, Layout::default(), None);
let sep_rect = match data.axis {
SplitterAxis::Horizontal => Rect::from_min_size(
Pos2::new(child_rect_a.max.x, child_rect_a.min.y),
Vec2::new(sep_size, whole_area.y),
),
SplitterAxis::Vertical => Rect::from_min_size(
Pos2::new(child_rect_a.min.x, child_rect_a.max.y),
Vec2::new(whole_area.x, sep_size),
),
};
let resp = ui.allocate_rect(sep_rect, Sense::hover().union(Sense::click_and_drag()));
let sep_draw_rect = match data.axis {
SplitterAxis::Horizontal => Rect::from_min_size(
Pos2::new(
sep_rect.min.x + sep_size / 2.0 - sep_stroke / 2.0,
sep_rect.min.y,
),
Vec2::new(sep_stroke, sep_rect.height()),
),
SplitterAxis::Vertical => Rect::from_min_size(
Pos2::new(
sep_rect.min.x,
sep_rect.min.y + sep_size / 2.0 - sep_stroke / 2.0,
),
Vec2::new(sep_rect.width(), sep_stroke),
),
};
ui.painter().rect_filled(
sep_draw_rect,
Rounding::ZERO,
ui.style().visuals.noninteractive().bg_stroke.color,
);
let child_rect_b = match data.axis {
SplitterAxis::Horizontal => {
Rect::from_min_size(Pos2::new(sep_rect.max.x, sep_rect.min.y), child_size_b)
}
SplitterAxis::Vertical => {
Rect::from_min_size(Pos2::new(sep_rect.min.x, sep_rect.max.y), child_size_b)
}
};
let mut ui_b = ui.child_ui(child_rect_b, Layout::default(), None);
add_contents(&mut ui_a, &mut ui_b);
if resp.hovered() {
match data.axis {
SplitterAxis::Horizontal => ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical),
SplitterAxis::Vertical => ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal),
}
}
if resp.dragged() {
let delta_pos = match data.axis {
SplitterAxis::Horizontal => resp.drag_delta().x / whole_area.x,
SplitterAxis::Vertical => resp.drag_delta().y / whole_area.y,
};
data.pos += delta_pos;
}
// clip pos
let min_pos = (data.min_size / split_axis_size).min(1.0);
let max_pos = (1.0 - min_pos).max(0.0);
data.pos = data.pos.max(min_pos).min(max_pos);
ui.memory_mut(|mem| {
mem.data.insert_temp(self.id, data);
})
}
}
+5 -5
View File
@@ -33,7 +33,7 @@ const LOGIN_FAIL_COUNT_CIRCLE_STROKE: Stroke = Stroke {
color: LOGIN_FAIL_COLOR, color: LOGIN_FAIL_COLOR,
}; };
fn rotCircle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32) -> Pos2 { fn rot_circle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32) -> Pos2 {
center center
+ (egui::Vec2 { + (egui::Vec2 {
x: rad * f32::cos(i as f32 * ang_per_num + offset_ang), x: rad * f32::cos(i as f32 * ang_per_num + offset_ang),
@@ -41,7 +41,7 @@ fn rotCircle(i: i16, center: Pos2, rad: f32, offset_ang: f32, ang_per_num: f32)
}) })
} }
pub fn update( pub fn update_password_viewer(
wstate: MutexGuard<'_, structs::AuthState>, wstate: MutexGuard<'_, structs::AuthState>,
ctx: &egui::Context, ctx: &egui::Context,
frame: &mut eframe::Frame, frame: &mut eframe::Frame,
@@ -71,7 +71,7 @@ pub fn update(
let ang_per_fail = 2. * PI / state.failed_attempts as f32; let ang_per_fail = 2. * PI / state.failed_attempts as f32;
let len: i16 = state.password.len() as i16; let len: i16 = state.password.len() as i16;
let mut last_pos = rotCircle( let mut last_pos = rot_circle(
len - 1, len - 1,
center, center,
LOGIN_CIRCLE_RADIUS, LOGIN_CIRCLE_RADIUS,
@@ -84,7 +84,7 @@ pub fn update(
if state.failed_attempts <= 1 { if state.failed_attempts <= 1 {
center center
} else { } else {
rotCircle( rot_circle(
i as i16, i as i16,
center, center,
LOGIN_FAIL_COUNT_CIRCLE_RADIUS, LOGIN_FAIL_COUNT_CIRCLE_RADIUS,
@@ -107,7 +107,7 @@ pub fn update(
if len <= 1 { if len <= 1 {
center center
} else { } else {
rotCircle( rot_circle(
i, i,
center, center,
LOGIN_CIRCLE_RADIUS, LOGIN_CIRCLE_RADIUS,