mirror of
https://github.com/apognu/tuigreet.git
synced 2024-11-26 07:28:57 +03:00
Added a submenu to list system-declared sessions (#1).
This commit is contained in:
parent
f2ec800eed
commit
8322386f50
110
Cargo.lock
generated
110
Cargo.lock
generated
@ -1,5 +1,11 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
@ -41,6 +47,15 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b391911b9a786312a10cb9d2b3d0735adfd5a8113eb3648de26a75e91b0826c"
|
||||
dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
@ -56,6 +71,17 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greetd_ipc"
|
||||
version = "0.6.0"
|
||||
@ -67,6 +93,16 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
@ -126,6 +162,22 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88f947c6799d5eff50e6cf8a2365c17ac4aa8f8f43aceeedc29b616d872a358"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
@ -144,6 +196,47 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
@ -159,6 +252,16 @@ dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a3679dd538c876a7b606f3bb951c8a20fc281a0ff7795f59f7cb490e3f979e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
@ -281,6 +384,7 @@ dependencies = [
|
||||
"getopts",
|
||||
"greetd_ipc",
|
||||
"nix",
|
||||
"rust-ini",
|
||||
"termion",
|
||||
"textwrap",
|
||||
"tui",
|
||||
@ -311,6 +415,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -14,6 +14,7 @@ nix = "0.17.0"
|
||||
textwrap = "0.12.0"
|
||||
chrono = "0.4.11"
|
||||
zeroize = "1.1.0"
|
||||
rust-ini = "0.15.3"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
@ -28,7 +28,7 @@ The default configuration tends to be as minimal as possible, visually speaking,
|
||||
|
||||
The initial prompt container will be 80 column wide. You may change this with `--width` in case you need more space (for example, to account for large PAM challenge messages). Please refer to usage information (`--help`) for more customizaton options.
|
||||
|
||||
You may change the command that will be executed after opening a session by huttint `F2` and amending the command.
|
||||
You may change the command that will be executed after opening a session by hittint `F2` and amending the command. Alternatively, you can list the system-declared sessions by hitting `F3`.
|
||||
|
||||
## Install
|
||||
|
||||
|
@ -32,6 +32,7 @@ pub enum Mode {
|
||||
Username,
|
||||
Password,
|
||||
Command,
|
||||
Sessions,
|
||||
}
|
||||
|
||||
impl Default for Mode {
|
||||
@ -44,18 +45,25 @@ impl Default for Mode {
|
||||
pub struct Greeter {
|
||||
pub config: Option<Matches>,
|
||||
pub stream: Option<UnixStream>,
|
||||
pub command: Option<String>,
|
||||
pub previous_mode: Mode,
|
||||
pub mode: Mode,
|
||||
pub request: Option<Request>,
|
||||
|
||||
pub mode: Mode,
|
||||
pub previous_mode: Mode,
|
||||
pub cursor_offset: i16,
|
||||
pub username: String,
|
||||
pub answer: String,
|
||||
|
||||
pub command: Option<String>,
|
||||
pub new_command: String,
|
||||
pub secret: bool,
|
||||
pub sessions: Vec<(String, String)>,
|
||||
pub selected_session: usize,
|
||||
|
||||
pub username: String,
|
||||
pub prompt: String,
|
||||
pub answer: String,
|
||||
pub secret: bool,
|
||||
|
||||
pub greeting: Option<String>,
|
||||
pub message: Option<String>,
|
||||
|
||||
pub working: bool,
|
||||
pub done: bool,
|
||||
}
|
||||
@ -72,9 +80,13 @@ impl Drop for Greeter {
|
||||
impl Greeter {
|
||||
pub fn new() -> Self {
|
||||
let mut greeter = Self::default();
|
||||
|
||||
greeter.parse_options();
|
||||
greeter.sessions = crate::info::get_sessions().unwrap_or_default();
|
||||
greeter.selected_session = greeter.sessions.iter().position(|(_, command)| Some(command) == greeter.command.as_ref()).unwrap_or(0);
|
||||
greeter
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &Matches {
|
||||
self.config.as_ref().unwrap()
|
||||
}
|
||||
|
31
src/info.rs
31
src/info.rs
@ -1,7 +1,11 @@
|
||||
use std::{env, fs};
|
||||
use std::{env, error::Error, fs, path::Path};
|
||||
|
||||
use ini::Ini;
|
||||
use nix::sys::utsname;
|
||||
|
||||
const X_SESSIONS: &str = "/usr/share/xsessions";
|
||||
const WAYLAND_SESSIONS: &str = "/usr/share/wayland-sessions";
|
||||
|
||||
pub fn get_hostname() -> String {
|
||||
utsname::uname().nodename().to_string()
|
||||
}
|
||||
@ -26,3 +30,28 @@ pub fn get_issue() -> Option<String> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_sessions() -> Result<Vec<(String, String)>, Box<dyn Error>> {
|
||||
let directories = vec![X_SESSIONS, WAYLAND_SESSIONS];
|
||||
|
||||
let files = directories
|
||||
.iter()
|
||||
.flat_map(|directory| fs::read_dir(&directory))
|
||||
.flat_map(|directory| directory.flat_map(|entry| entry.map(|entry| load_desktop_file(entry.path()))).flatten())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
fn load_desktop_file<P>(path: P) -> Result<(String, String), Box<dyn Error>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let desktop = Ini::load_from_file(path)?;
|
||||
let section = desktop.section(Some("Desktop Entry")).ok_or("no Desktop Entry section in desktop file")?;
|
||||
|
||||
let name = section.get("Name").ok_or("no Name property in desktop file")?;
|
||||
let exec = section.get("Exec").ok_or("no Exec property in desktop file")?;
|
||||
|
||||
Ok((name.to_string(), exec.to_string()))
|
||||
}
|
||||
|
@ -11,13 +11,10 @@ use crate::{
|
||||
pub fn handle(greeter: &mut Greeter, events: &Events) -> Result<(), Box<dyn Error>> {
|
||||
if let Event::Input(input) = events.next()? {
|
||||
match input {
|
||||
Key::Esc => {
|
||||
if let Mode::Command = greeter.mode {
|
||||
greeter.mode = greeter.previous_mode;
|
||||
} else {
|
||||
crate::exit(greeter, AuthStatus::Cancel)?;
|
||||
}
|
||||
}
|
||||
Key::Esc => match greeter.mode {
|
||||
Mode::Command | Mode::Sessions => greeter.mode = greeter.previous_mode,
|
||||
_ => crate::exit(greeter, AuthStatus::Cancel)?,
|
||||
},
|
||||
|
||||
Key::Left => greeter.cursor_offset -= 1,
|
||||
Key::Right => greeter.cursor_offset += 1,
|
||||
@ -28,6 +25,27 @@ pub fn handle(greeter: &mut Greeter, events: &Events) -> Result<(), Box<dyn Erro
|
||||
greeter.mode = Mode::Command;
|
||||
}
|
||||
|
||||
Key::F(3) => {
|
||||
greeter.previous_mode = greeter.mode;
|
||||
greeter.mode = Mode::Sessions;
|
||||
}
|
||||
|
||||
Key::Up => {
|
||||
if let Mode::Sessions = greeter.mode {
|
||||
if greeter.selected_session > 0 {
|
||||
greeter.selected_session -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Key::Down => {
|
||||
if let Mode::Sessions = greeter.mode {
|
||||
if greeter.selected_session < greeter.sessions.len() - 1 {
|
||||
greeter.selected_session += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Key::Ctrl('a') => greeter.cursor_offset = -(greeter.username.len() as i16),
|
||||
Key::Ctrl('e') => greeter.cursor_offset = 0,
|
||||
|
||||
@ -52,6 +70,15 @@ pub fn handle(greeter: &mut Greeter, events: &Events) -> Result<(), Box<dyn Erro
|
||||
|
||||
Mode::Command => {
|
||||
greeter.command = Some(greeter.new_command.clone());
|
||||
greeter.selected_session = greeter.sessions.iter().position(|(_, command)| Some(command) == greeter.command.as_ref()).unwrap_or(0);
|
||||
greeter.mode = greeter.previous_mode;
|
||||
}
|
||||
|
||||
Mode::Sessions => {
|
||||
if let Some((_, command)) = greeter.sessions.get(greeter.selected_session) {
|
||||
greeter.command = Some(command.clone());
|
||||
}
|
||||
|
||||
greeter.mode = greeter.previous_mode;
|
||||
}
|
||||
},
|
||||
@ -72,6 +99,7 @@ fn insert_key(greeter: &mut Greeter, c: char) {
|
||||
Mode::Username => &mut greeter.username,
|
||||
Mode::Password => &mut greeter.answer,
|
||||
Mode::Command => &mut greeter.new_command,
|
||||
Mode::Sessions => return,
|
||||
};
|
||||
|
||||
let index = value.len() as i16 + greeter.cursor_offset;
|
||||
@ -84,6 +112,7 @@ fn delete_key(greeter: &mut Greeter, key: Key) {
|
||||
Mode::Username => &mut greeter.username,
|
||||
Mode::Password => &mut greeter.answer,
|
||||
Mode::Command => &mut greeter.new_command,
|
||||
Mode::Sessions => return,
|
||||
};
|
||||
|
||||
let index = match key {
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod prompt;
|
||||
mod sessions;
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
@ -15,9 +16,10 @@ use tui::{
|
||||
Terminal,
|
||||
};
|
||||
|
||||
use crate::Greeter;
|
||||
use crate::{Greeter, Mode};
|
||||
|
||||
const EXIT: &str = "Exit";
|
||||
const SESSIONS: &str = "Choose session";
|
||||
const CHANGE_COMMAND: &str = "Change command";
|
||||
const COMMAND: &str = "COMMAND";
|
||||
|
||||
@ -56,6 +58,8 @@ pub fn draw(terminal: &mut Terminal<TermionBackend<RawTerminal<io::Stdout>>>, gr
|
||||
status_value(EXIT),
|
||||
status_label("F2"),
|
||||
status_value(CHANGE_COMMAND),
|
||||
status_label("F3"),
|
||||
status_value(SESSIONS),
|
||||
status_label(COMMAND),
|
||||
status_value(command),
|
||||
];
|
||||
@ -63,7 +67,10 @@ pub fn draw(terminal: &mut Terminal<TermionBackend<RawTerminal<io::Stdout>>>, gr
|
||||
|
||||
f.render_widget(status, chunks[2]);
|
||||
|
||||
cursor = self::prompt::draw(greeter, &mut f).ok();
|
||||
cursor = match greeter.mode {
|
||||
Mode::Sessions => self::sessions::draw(greeter, &mut f).ok(),
|
||||
_ => self::prompt::draw(greeter, &mut f).ok(),
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Some(cursor) = cursor {
|
||||
|
@ -115,10 +115,12 @@ pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::
|
||||
match greeter.mode {
|
||||
Mode::Username => f.render_widget(message, chunks[ANSWER_INDEX]),
|
||||
Mode::Password => f.render_widget(message, chunks[MESSAGE_INDEX]),
|
||||
Mode::Command => {}
|
||||
Mode::Command | Mode::Sessions => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mode::Sessions => {}
|
||||
}
|
||||
|
||||
match greeter.mode {
|
||||
@ -143,6 +145,8 @@ pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::
|
||||
|
||||
Ok((2 + cursor.x + COMMAND.len() as u16 + offset as u16, USERNAME_INDEX as u16 + cursor.y))
|
||||
}
|
||||
|
||||
Mode::Sessions => Ok((1, 1)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +158,8 @@ fn get_height(greeter: &Greeter) -> u16 {
|
||||
|
||||
let initial = match greeter.mode {
|
||||
Mode::Username | Mode::Command => (2 * container_padding) + 1,
|
||||
Mode::Password => (2 * container_padding) + 2 + (2 * prompt_padding),
|
||||
Mode::Password => (2 * container_padding) + prompt_padding + 2,
|
||||
Mode::Sessions => 0,
|
||||
};
|
||||
|
||||
match greeter.mode {
|
||||
|
51
src/ui/sessions.rs
Normal file
51
src/ui/sessions.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use std::{error::Error, io};
|
||||
|
||||
use termion::raw::RawTerminal;
|
||||
use tui::{
|
||||
backend::TermionBackend,
|
||||
layout::Rect,
|
||||
style::{Modifier, Style},
|
||||
widgets::{Block, BorderType, Borders, Paragraph, Text},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::Greeter;
|
||||
|
||||
const CHANGE_SESSION: &str = "Change session";
|
||||
|
||||
pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
|
||||
let size = f.size();
|
||||
|
||||
let width = greeter.width();
|
||||
let height: u16 = greeter.sessions.len() as u16 + 4;
|
||||
let x = (size.width - width) / 2;
|
||||
let y = (size.height - height) / 2;
|
||||
|
||||
let container = Rect::new(x, y, width, height);
|
||||
|
||||
let title = format!(" {} ", CHANGE_SESSION);
|
||||
let block = Block::default().title(&title).borders(Borders::ALL).border_type(BorderType::Plain);
|
||||
|
||||
for (index, (name, _)) in greeter.sessions.iter().enumerate() {
|
||||
let frame = Rect::new(x + 2, y + 2 + index as u16, width, 1);
|
||||
let option_text = [get_option(&greeter, name, index)];
|
||||
let option = Paragraph::new(option_text.iter());
|
||||
|
||||
f.render_widget(option, frame);
|
||||
}
|
||||
|
||||
f.render_widget(block, container);
|
||||
|
||||
Ok((1, 1))
|
||||
}
|
||||
|
||||
fn get_option<'g, S>(greeter: &Greeter, name: S, index: usize) -> Text<'g>
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
if greeter.selected_session == index {
|
||||
Text::styled(name.into(), Style::default().modifier(Modifier::REVERSED))
|
||||
} else {
|
||||
Text::raw(name.into())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user