feat: implement OS bridge and enhance app launcher functionality

This commit is contained in:
2026-03-15 17:45:24 +01:00
parent 93736ae19d
commit 1a2de21bf6
18 changed files with 363 additions and 294 deletions

View File

@@ -4,3 +4,5 @@ version = "0.1.0"
edition = "2024"
[dependencies]
k-launcher-kernel = { path = "../k-launcher-kernel" }
libc = "0.2"

View File

@@ -1,4 +1,7 @@
/// Configuration for the launcher window.
mod unix_launcher;
pub use unix_launcher::UnixAppLauncher;
pub struct WindowConfig {
pub width: f32,
pub height: f32,

View File

@@ -0,0 +1,118 @@
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use k_launcher_kernel::{AppLauncher, LaunchAction};
fn parse_term_cmd(s: &str) -> (String, Vec<String>) {
let mut parts = s.split_whitespace();
let bin = parts.next().unwrap_or("").to_string();
let args = parts.map(str::to_string).collect();
(bin, args)
}
fn which(bin: &str) -> bool {
Command::new("which")
.arg(bin)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
fn resolve_terminal() -> Option<(String, Vec<String>)> {
if let Ok(val) = std::env::var("TERM_CMD") {
let val = val.trim().to_string();
if !val.is_empty() {
let (bin, args) = parse_term_cmd(&val);
if !bin.is_empty() {
return Some((bin, args));
}
}
}
if let Ok(val) = std::env::var("TERMINAL") {
let bin = val.trim().to_string();
if !bin.is_empty() {
return Some((bin, vec!["-e".to_string()]));
}
}
for (bin, flag) in &[
("foot", "-e"),
("kitty", "-e"),
("alacritty", "-e"),
("wezterm", "start"),
("konsole", "-e"),
("xterm", "-e"),
] {
if which(bin) {
return Some((bin.to_string(), vec![flag.to_string()]));
}
}
None
}
pub struct UnixAppLauncher;
impl UnixAppLauncher {
pub fn new() -> Self {
Self
}
}
impl Default for UnixAppLauncher {
fn default() -> Self {
Self::new()
}
}
impl AppLauncher for UnixAppLauncher {
fn execute(&self, action: &LaunchAction) {
match action {
LaunchAction::SpawnProcess(cmd) => {
let parts: Vec<&str> = cmd.split_whitespace().collect();
if let Some((bin, args)) = parts.split_first() {
let _ = unsafe {
Command::new(bin)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.pre_exec(|| { libc::setsid(); Ok(()) })
.spawn()
};
}
}
LaunchAction::SpawnInTerminal(cmd) => {
let Some((term_bin, term_args)) = resolve_terminal() else { return };
let _ = unsafe {
Command::new(&term_bin)
.args(&term_args)
.arg("sh").arg("-c").arg(cmd)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.pre_exec(|| { libc::setsid(); Ok(()) })
.spawn()
};
}
LaunchAction::OpenPath(path) => {
let _ = Command::new("xdg-open").arg(path).spawn();
}
LaunchAction::CopyToClipboard(val) => {
if Command::new("wl-copy").arg(val).spawn().is_err() {
use std::io::Write;
if let Ok(mut child) = Command::new("xclip")
.args(["-selection", "clipboard"])
.stdin(Stdio::piped())
.spawn()
{
if let Some(stdin) = child.stdin.as_mut() {
let _ = stdin.write_all(val.as_bytes());
}
}
}
}
LaunchAction::Custom(f) => f(),
}
}
}