Make launcher runnable
This commit is contained in:
parent
e2013eb694
commit
2965afacae
15 changed files with 98 additions and 30 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/dev-runtime
|
||||
|
|
|
|||
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -68,7 +68,7 @@ version = "1.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -79,7 +79,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -257,7 +257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -856,7 +856,7 @@ version = "0.50.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1037,7 +1037,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1390,7 +1390,7 @@ dependencies = [
|
|||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ bitflags = "2.4"
|
|||
nix = { version = "0.29", features = ["signal", "process", "user", "socket"] }
|
||||
|
||||
# CLI
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
|
||||
# GTK4 UI
|
||||
gtk4 = "0.9"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ TODO:
|
|||
tl;dr:
|
||||
|
||||
1. any Linux with Wayland (optional: TPM-based FDE plus BIOS password to prevent tampering)
|
||||
2. System dependencies (Ubuntu: `apt install curl sway swayidle pkg-config libcairo2-dev libxkbcommon-dev`)
|
||||
2. System dependencies:
|
||||
- **Ubuntu/Debian**: `apt install build-essential pkg-config libglib2.0-dev libgtk-4-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf-xlib-2.0-dev libwayland-dev libx11-dev libxkbcommon-dev libgirepository1.0-dev libgtk4-layer-shell-dev librust-gtk4-layer-shell-sys-dev sway swayidle`
|
||||
3. Rust (`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`)
|
||||
4. binaries (TODO: deployable package that depends on Sway and installs the config)
|
||||
5. test session on login
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::volume::VolumeStatus;
|
|||
use gtk4::glib;
|
||||
use gtk4::prelude::*;
|
||||
use gtk4_layer_shell::{Edge, Layer, LayerShell};
|
||||
use shepherd_api::commands::Command;
|
||||
use shepherd_api::Command;
|
||||
use shepherd_ipc::IpcClient;
|
||||
use std::cell::RefCell;
|
||||
use std::path::PathBuf;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ use tracing_subscriber::EnvFilter;
|
|||
#[command(name = "shepherd-hud")]
|
||||
#[command(about = "GTK4 layer-shell HUD for shepherdd", long_about = None)]
|
||||
struct Args {
|
||||
/// Socket path for shepherdd connection
|
||||
#[arg(short, long, default_value = "/run/shepherdd/shepherdd.sock")]
|
||||
/// Socket path for shepherdd connection (or set SHEPHERD_SOCKET env var)
|
||||
#[arg(short, long, env = "SHEPHERD_SOCKET", default_value = "/run/shepherdd/shepherdd.sock")]
|
||||
socket: PathBuf,
|
||||
|
||||
/// Log level
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
//! The HUD subscribes to events from shepherdd and tracks session state.
|
||||
|
||||
use chrono::Local;
|
||||
use shepherd_api::events::{Event, EventPayload};
|
||||
use shepherd_api::types::SessionEndReason;
|
||||
use shepherd_api::{Event, EventPayload, SessionEndReason};
|
||||
use shepherd_util::{EntryId, SessionId};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::watch;
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ impl LauncherApp {
|
|||
fn build_ui(app: >k4::Application, socket_path: PathBuf) {
|
||||
// Load CSS
|
||||
let provider = gtk4::CssProvider::new();
|
||||
provider.load_from_string(LAUNCHER_CSS);
|
||||
provider.load_from_data(LAUNCHER_CSS);
|
||||
gtk4::style_context_add_provider_for_display(
|
||||
>k4::gdk::Display::default().expect("Could not get default display"),
|
||||
&provider,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! IPC client wrapper for the launcher UI
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use shepherd_api::{Command, Event, Response, ResponsePayload, ResponseResult};
|
||||
use shepherd_api::{Command, Event, ReasonCode, Response, ResponsePayload, ResponseResult};
|
||||
use shepherd_ipc::IpcClient;
|
||||
use shepherd_util::EntryId;
|
||||
use std::path::Path;
|
||||
|
|
@ -182,7 +182,7 @@ impl DaemonClient {
|
|||
ResponsePayload::LaunchDenied { reasons } => {
|
||||
let message = reasons
|
||||
.iter()
|
||||
.map(|r| r.message.as_deref().unwrap_or("Denied"))
|
||||
.map(|r| reason_to_message(r))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
self.state.set(LauncherState::Error { message });
|
||||
|
|
@ -237,3 +237,15 @@ impl CommandClient {
|
|||
client.send(Command::ListEntries { at_time: None }).await.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a ReasonCode enum variant to a human-readable message
|
||||
fn reason_to_message(reason: &ReasonCode) -> &'static str {
|
||||
match reason {
|
||||
ReasonCode::OutsideTimeWindow { .. } => "Outside allowed time window",
|
||||
ReasonCode::QuotaExhausted { .. } => "Daily quota exhausted",
|
||||
ReasonCode::CooldownActive { .. } => "Cooldown period active",
|
||||
ReasonCode::SessionActive { .. } => "Another session is active",
|
||||
ReasonCode::UnsupportedKind { .. } => "Entry type not supported",
|
||||
ReasonCode::Disabled { .. } => "Entry disabled",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use gtk4::subclass::prelude::*;
|
|||
use shepherd_api::EntryView;
|
||||
use shepherd_util::EntryId;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::tile::LauncherTile;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ mod imp {
|
|||
pub struct LauncherGrid {
|
||||
pub flow_box: gtk4::FlowBox,
|
||||
pub tiles: RefCell<Vec<LauncherTile>>,
|
||||
pub on_launch: RefCell<Option<Box<dyn Fn(EntryId) + 'static>>>,
|
||||
pub on_launch: Rc<RefCell<Option<Box<dyn Fn(EntryId) + 'static>>>>,
|
||||
}
|
||||
|
||||
impl Default for LauncherGrid {
|
||||
|
|
@ -23,7 +24,7 @@ mod imp {
|
|||
Self {
|
||||
flow_box: gtk4::FlowBox::new(),
|
||||
tiles: RefCell::new(Vec::new()),
|
||||
on_launch: RefCell::new(None),
|
||||
on_launch: Rc::new(RefCell::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +121,7 @@ impl LauncherGrid {
|
|||
}
|
||||
});
|
||||
|
||||
imp.flow_box.append(&tile);
|
||||
imp.flow_box.insert(&tile, -1);
|
||||
imp.tiles.borrow_mut().push(tile);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ use tracing_subscriber::EnvFilter;
|
|||
#[command(name = "shepherd-launcher")]
|
||||
#[command(about = "GTK4 launcher UI for shepherdd", long_about = None)]
|
||||
struct Args {
|
||||
/// Socket path for shepherdd connection
|
||||
#[arg(short, long, default_value = "/run/shepherdd/shepherdd.sock")]
|
||||
/// Socket path for shepherdd connection (or set SHEPHERD_SOCKET env var)
|
||||
#[arg(short, long, env = "SHEPHERD_SOCKET", default_value = "/run/shepherdd/shepherdd.sock")]
|
||||
socket: PathBuf,
|
||||
|
||||
/// Log level
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ tracing = { workspace = true }
|
|||
tracing-subscriber = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -38,12 +38,12 @@ struct Args {
|
|||
#[arg(short, long, default_value = "/etc/shepherdd/config.toml")]
|
||||
config: PathBuf,
|
||||
|
||||
/// Socket path override
|
||||
#[arg(short, long)]
|
||||
/// Socket path override (or set SHEPHERD_SOCKET env var)
|
||||
#[arg(short, long, env = "SHEPHERD_SOCKET")]
|
||||
socket: Option<PathBuf>,
|
||||
|
||||
/// Data directory override
|
||||
#[arg(short, long)]
|
||||
/// Data directory override (or set SHEPHERD_DATA_DIR env var)
|
||||
#[arg(short, long, env = "SHEPHERD_DATA_DIR")]
|
||||
data_dir: Option<PathBuf>,
|
||||
|
||||
/// Log level
|
||||
|
|
|
|||
46
run-dev
46
run-dev
|
|
@ -1,3 +1,47 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cargo build && WLR_BACKENDS=wayland WLR_LIBINPUT_NO_DEVICES=1 sway -c ./sway.conf
|
||||
set -e
|
||||
|
||||
# Set up dev runtime directory
|
||||
DEV_RUNTIME="./dev-runtime"
|
||||
DATA_DIR="$DEV_RUNTIME/data"
|
||||
SOCKET_PATH="$DEV_RUNTIME/shepherd.sock"
|
||||
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# Export environment variables for shepherd binaries
|
||||
export SHEPHERD_SOCKET="$SOCKET_PATH"
|
||||
export SHEPHERD_DATA_DIR="$DATA_DIR"
|
||||
|
||||
# Build all binaries
|
||||
echo "Building shepherd binaries..."
|
||||
cargo build
|
||||
|
||||
# Function to cleanup background processes on exit
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
if [ ! -z "$SHEPHERDD_PID" ]; then
|
||||
kill $SHEPHERDD_PID 2>/dev/null || true
|
||||
fi
|
||||
if [ ! -z "$HUD_PID" ]; then
|
||||
kill $HUD_PID 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Start the background daemon
|
||||
echo "Starting shepherdd..."
|
||||
./target/debug/shepherdd -c ./config.example.toml &
|
||||
SHEPHERDD_PID=$!
|
||||
|
||||
# Give the daemon a moment to initialize
|
||||
sleep 1
|
||||
|
||||
# Start the HUD in the background
|
||||
echo "Starting shepherd-hud..."
|
||||
./target/debug/shepherd-hud &
|
||||
HUD_PID=$!
|
||||
|
||||
# Start sway with the launcher
|
||||
echo "Starting sway with shepherd-launcher..."
|
||||
WLR_BACKENDS=wayland WLR_LIBINPUT_NO_DEVICES=1 sway -c ./sway.conf
|
||||
|
|
|
|||
16
sway.conf
16
sway.conf
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
### Variables
|
||||
set $launcher ./target/debug/shepherd-launcher
|
||||
set $hud ./target/debug/shepherd-hud
|
||||
|
||||
### Output configuration
|
||||
# Set up displays (adjust as needed for your hardware)
|
||||
|
|
@ -35,7 +36,13 @@ bindsym Mod4+Shift+Escape exit
|
|||
|
||||
### Window rules for kiosk behavior
|
||||
|
||||
# Make all windows fullscreen by default
|
||||
# Shepherd HUD should always be visible on top (likely uses layer-shell protocol)
|
||||
# Layer shell surfaces are automatically handled by sway and don't need window rules
|
||||
|
||||
# Make launcher windows fullscreen
|
||||
for_window [app_id="shepherd-launcher"] fullscreen enable
|
||||
|
||||
# Make other windows fullscreen by default
|
||||
for_window [class=".*"] fullscreen enable
|
||||
for_window [app_id=".*"] fullscreen enable
|
||||
|
||||
|
|
@ -62,9 +69,12 @@ seat * hide_cursor 5000
|
|||
# Use only one workspace for true kiosk mode
|
||||
workspace 1 output *
|
||||
|
||||
### Application launcher
|
||||
### Application startup
|
||||
|
||||
# Start the shepherd-launcher on startup
|
||||
# Note: shepherdd (daemon) and shepherd-hud are started by run-dev script
|
||||
# before sway launches, so they're already running at this point
|
||||
|
||||
# Start the shepherd-launcher on startup (the main "home" screen)
|
||||
exec_always $launcher
|
||||
|
||||
### Disable workspace switching
|
||||
|
|
|
|||
Loading…
Reference in a new issue