Add session list
This commit is contained in:
parent
4dac2f7ea9
commit
f374bd1cc5
3 changed files with 206 additions and 56 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
|
@ -760,10 +760,12 @@ name = "cosmic-greeter"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"freedesktop_entry_parser",
|
||||
"greetd_ipc",
|
||||
"libcosmic",
|
||||
"log",
|
||||
"pwd",
|
||||
"shlex",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
|
@ -1432,6 +1434,16 @@ dependencies = [
|
|||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "freedesktop_entry_parser"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "freetype-rs"
|
||||
version = "0.26.0"
|
||||
|
|
@ -3488,6 +3500,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
env_logger = "0.10"
|
||||
freedesktop_entry_parser = "1"
|
||||
log = "0.4"
|
||||
pwd = "1"
|
||||
shlex = "1"
|
||||
|
||||
[dependencies.greetd_ipc]
|
||||
version = "0.9"
|
||||
|
|
|
|||
242
src/main.rs
242
src/main.rs
|
|
@ -6,7 +6,7 @@
|
|||
use cosmic::app::{message, Command, Core, Settings};
|
||||
use cosmic::{executor, iced, widget, Element};
|
||||
use greetd_ipc::{codec::SyncCodec, AuthMessageType, Request, Response};
|
||||
use std::{env, fs, io, path::Path, sync::Arc};
|
||||
use std::{collections::HashMap, env, fs, io, path::Path, sync::Arc};
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
@ -47,6 +47,101 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.collect()
|
||||
};
|
||||
|
||||
//TODO: allow custom directories?
|
||||
let session_dirs = &[
|
||||
Path::new("/usr/share/wayland-sessions"),
|
||||
Path::new("/usr/share/xsessions"),
|
||||
];
|
||||
|
||||
let sessions = {
|
||||
let mut sessions = HashMap::new();
|
||||
for session_dir in session_dirs {
|
||||
let read_dir = match fs::read_dir(&session_dir) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"failed to read session directory {:?}: {:?}",
|
||||
session_dir,
|
||||
err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for dir_entry_res in read_dir {
|
||||
let dir_entry = match dir_entry_res {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"failed to read session directory {:?} entry: {:?}",
|
||||
session_dir,
|
||||
err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let entry = match freedesktop_entry_parser::parse_entry(dir_entry.path()) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"failed to read session file {:?}: {:?}",
|
||||
dir_entry.path(),
|
||||
err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let name = match entry.section("Desktop Entry").attr("Name") {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
log::warn!(
|
||||
"failed to read session file {:?}: no Desktop Entry/Name attribute",
|
||||
dir_entry.path()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let exec = match entry.section("Desktop Entry").attr("Exec") {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
log::warn!(
|
||||
"failed to read session file {:?}: no Desktop Entry/Exec attribute",
|
||||
dir_entry.path()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let split = match shlex::split(exec) {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
log::warn!(
|
||||
"failed to parse session file {:?} Exec field {:?}",
|
||||
dir_entry.path(),
|
||||
exec
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
match sessions.insert(name.to_string(), split) {
|
||||
Some(some) => {
|
||||
log::warn!("session overwritten with command {:?}", some);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
sessions
|
||||
};
|
||||
|
||||
println!("{:?}", sessions);
|
||||
|
||||
let flags = Flags { users, sessions };
|
||||
|
||||
let settings = Settings::default()
|
||||
.antialiasing(true)
|
||||
.client_decorations(true)
|
||||
|
|
@ -56,41 +151,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.scale_factor(1.0)
|
||||
.theme(cosmic::Theme::dark());
|
||||
|
||||
cosmic::app::run::<App>(settings, users)?;
|
||||
cosmic::app::run::<App>(settings, flags)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SocketState {
|
||||
/// Opening GREETD_SOCK
|
||||
Pending,
|
||||
/// GREETD_SOCK is open
|
||||
Open(Arc<UnixStream>),
|
||||
/// No GREETD_SOCK variable set
|
||||
NotSet,
|
||||
/// Failed to open GREETD_SOCK
|
||||
Error(Arc<io::Error>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InputState {
|
||||
None,
|
||||
Username,
|
||||
Auth(bool, String, String),
|
||||
}
|
||||
|
||||
/// Messages that are used specifically by our [`App`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
None,
|
||||
Socket(SocketState),
|
||||
Input(InputState),
|
||||
Username(String),
|
||||
Auth(String),
|
||||
Login,
|
||||
}
|
||||
|
||||
async fn request(socket: Arc<UnixStream>, request: Request) -> Message {
|
||||
//TODO: handle errors
|
||||
socket.writable().await.unwrap();
|
||||
|
|
@ -156,10 +221,49 @@ async fn request(socket: Arc<UnixStream>, request: Request) -> Message {
|
|||
Message::None
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Flags {
|
||||
users: Vec<(pwd::Passwd, Option<widget::image::Handle>)>,
|
||||
sessions: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SocketState {
|
||||
/// Opening GREETD_SOCK
|
||||
Pending,
|
||||
/// GREETD_SOCK is open
|
||||
Open(Arc<UnixStream>),
|
||||
/// No GREETD_SOCK variable set
|
||||
NotSet,
|
||||
/// Failed to open GREETD_SOCK
|
||||
Error(Arc<io::Error>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InputState {
|
||||
None,
|
||||
Username,
|
||||
Auth(bool, String, String),
|
||||
}
|
||||
|
||||
/// Messages that are used specifically by our [`App`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
None,
|
||||
Socket(SocketState),
|
||||
Input(InputState),
|
||||
Session(String),
|
||||
Username(String),
|
||||
Auth(String),
|
||||
Login,
|
||||
}
|
||||
|
||||
/// The [`App`] stores application-specific state.
|
||||
pub struct App {
|
||||
core: Core,
|
||||
users: Vec<(pwd::Passwd, Option<widget::image::Handle>)>,
|
||||
flags: Flags,
|
||||
session_names: Vec<String>,
|
||||
selected_session: String,
|
||||
socket_state: SocketState,
|
||||
input_state: InputState,
|
||||
}
|
||||
|
|
@ -170,7 +274,7 @@ impl cosmic::Application for App {
|
|||
type Executor = executor::Default;
|
||||
|
||||
/// Argument received [`cosmic::Application::new`].
|
||||
type Flags = Vec<(pwd::Passwd, Option<widget::image::Handle>)>;
|
||||
type Flags = Flags;
|
||||
|
||||
/// Message type specific to our [`App`].
|
||||
type Message = Message;
|
||||
|
|
@ -187,7 +291,7 @@ impl cosmic::Application for App {
|
|||
}
|
||||
|
||||
/// Creates the application, and optionally emits command on initialize.
|
||||
fn init(mut core: Core, users: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
core.window.show_window_menu = false;
|
||||
core.window.show_headerbar = false;
|
||||
core.window.sharp_corners = true;
|
||||
|
|
@ -195,10 +299,18 @@ impl cosmic::Application for App {
|
|||
core.window.show_minimize = false;
|
||||
core.window.use_template = false;
|
||||
|
||||
let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect();
|
||||
session_names.sort();
|
||||
|
||||
//TODO: determine default session?
|
||||
let selected_session = session_names.first().cloned().unwrap_or(String::new());
|
||||
|
||||
(
|
||||
App {
|
||||
core,
|
||||
users,
|
||||
flags,
|
||||
session_names,
|
||||
selected_session,
|
||||
socket_state: SocketState::Pending,
|
||||
//TODO: set to pending until socket is open?
|
||||
input_state: InputState::Username,
|
||||
|
|
@ -228,6 +340,9 @@ impl cosmic::Application for App {
|
|||
Message::Input(input_state) => {
|
||||
self.input_state = input_state;
|
||||
}
|
||||
Message::Session(selected_session) => {
|
||||
self.selected_session = selected_session;
|
||||
}
|
||||
Message::Username(username) => match &self.socket_state {
|
||||
SocketState::Open(socket) => {
|
||||
let socket = socket.clone();
|
||||
|
|
@ -264,23 +379,27 @@ impl cosmic::Application for App {
|
|||
},
|
||||
Message::Login => match &self.socket_state {
|
||||
SocketState::Open(socket) => {
|
||||
let socket = socket.clone();
|
||||
return Command::perform(
|
||||
async move {
|
||||
message::app(
|
||||
request(
|
||||
socket,
|
||||
//TODO: get session information from /usr/share/wayland-sessions
|
||||
Request::StartSession {
|
||||
cmd: vec!["start-cosmic".to_string()],
|
||||
env: vec![],
|
||||
},
|
||||
)
|
||||
.await,
|
||||
)
|
||||
},
|
||||
|x| x,
|
||||
);
|
||||
match self.flags.sessions.get(&self.selected_session).cloned() {
|
||||
Some(cmd) => {
|
||||
let socket = socket.clone();
|
||||
return Command::perform(
|
||||
async move {
|
||||
message::app(
|
||||
request(
|
||||
socket,
|
||||
Request::StartSession {
|
||||
cmd,
|
||||
env: Vec::new(),
|
||||
},
|
||||
)
|
||||
.await,
|
||||
)
|
||||
},
|
||||
|x| x,
|
||||
);
|
||||
}
|
||||
None => todo!("session {:?} not found", self.selected_session),
|
||||
}
|
||||
}
|
||||
_ => todo!("socket not open but attempting to log in"),
|
||||
},
|
||||
|
|
@ -298,8 +417,8 @@ impl cosmic::Application for App {
|
|||
widget::text("").into()
|
||||
}
|
||||
InputState::Username => {
|
||||
let mut row = widget::row::with_capacity(self.users.len()).spacing(12.0);
|
||||
for (user, icon_opt) in &self.users {
|
||||
let mut row = widget::row::with_capacity(self.flags.users.len()).spacing(12.0);
|
||||
for (user, icon_opt) in &self.flags.users {
|
||||
let mut column = widget::column::with_capacity(2).spacing(12.0);
|
||||
match icon_opt {
|
||||
Some(icon) => {
|
||||
|
|
@ -353,7 +472,18 @@ impl cosmic::Application for App {
|
|||
}
|
||||
};
|
||||
|
||||
let centered = widget::container(content)
|
||||
let session_picker = widget::pick_list(
|
||||
&self.session_names,
|
||||
Some(self.selected_session.clone()),
|
||||
Message::Session,
|
||||
);
|
||||
|
||||
let column = widget::column::with_capacity(2)
|
||||
.push(content)
|
||||
.push(session_picker)
|
||||
.spacing(12.0);
|
||||
|
||||
let centered = widget::container(column)
|
||||
.width(iced::Length::Fill)
|
||||
.height(iced::Length::Fill)
|
||||
.align_x(iced::alignment::Horizontal::Center)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue