pop-launcher/plugins/src/terminal/mod.rs

129 lines
3.3 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
// Copyright © 2021 System76
use futures::prelude::*;
use pop_launcher::*;
use smol::Unblock;
use std::{io, path::PathBuf};
pub struct App {
last_query: Option<String>,
out: Unblock<io::Stdout>,
shell_only: bool,
}
impl Default for App {
fn default() -> Self {
Self {
last_query: None,
out: async_stdout(),
shell_only: false,
}
}
}
pub async fn main() {
let mut requests = json_input_stream(async_stdin());
let mut app = App::default();
while let Some(result) = requests.next().await {
match result {
Ok(request) => match request {
Request::Activate(id) => app.activate(id).await,
Request::Search(query) => app.search(query).await,
Request::Exit => break,
_ => (),
},
Err(why) => {
tracing::error!("malformed JSON input: {}", why);
}
}
}
}
impl App {
async fn activate(&mut self, _id: u32) {
let exe = match self.last_query.take() {
Some(cmd) => cmd,
None => return,
};
use fork::{daemon, Fork};
crate::send(&mut self.out, PluginResponse::Close).await;
if let Ok(Fork::Child) = daemon(true, false) {
use std::os::unix::process::CommandExt;
use std::process::Command;
let mut cmd;
if self.shell_only {
cmd = Command::new("sh");
cmd.args(&["-c", &exe]);
} else {
let (terminal, arg) = detect_terminal();
cmd = Command::new(terminal);
cmd.args(&[
arg,
"sh",
"-c",
&format!("{}; echo \"Press Enter to exit\"; read t", exe),
]);
}
let _ = cmd.exec();
}
std::process::exit(0);
}
async fn search(&mut self, query: String) {
self.splice_input(&query).await;
crate::send(&mut self.out, PluginResponse::Finished).await;
}
async fn splice_input(&mut self, mut query: &str) {
if let Some(q) = query.strip_prefix(':') {
self.shell_only = true;
query = q.trim();
self.last_query = Some(query.to_owned());
} else {
self.shell_only = false;
let query = if let Some(query) = query.strip_prefix("t:") {
query.trim()
} else if let Some(pos) = query.find(' ') {
query[pos + 1..].trim()
} else {
return;
};
self.last_query = Some(query.to_owned());
}
crate::send(
&mut self.out,
PluginResponse::Append(PluginSearchResult {
id: 0,
name: query.to_owned(),
description: String::from("run command in terminal"),
..Default::default()
}),
)
.await;
}
}
fn detect_terminal() -> (PathBuf, &'static str) {
use std::fs::read_link;
const SYMLINK: &str = "/usr/bin/x-terminal-emulator";
if let Ok(found) = read_link(SYMLINK) {
return (read_link(&found).unwrap_or(found), "-e");
}
(PathBuf::from("/usr/bin/gnome-terminal"), "--")
}