keep working on cli shortcuts, unify visibility management
This commit is contained in:
parent
47a3e1cfef
commit
4b06dce7f4
@ -19,6 +19,7 @@ use crate::{
|
|||||||
ipc,
|
ipc,
|
||||||
server::Server,
|
server::Server,
|
||||||
errors::*,
|
errors::*,
|
||||||
|
shortcuts,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
tray,
|
tray,
|
||||||
};
|
};
|
||||||
@ -99,7 +100,7 @@ async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
|
|||||||
if let Err(_e) = config::set_auto_launch(conf.start_on_login) {
|
if let Err(_e) = config::set_auto_launch(conf.start_on_login) {
|
||||||
setup_errors.push("Error: Failed to manage autolaunch.".into());
|
setup_errors.push("Error: Failed to manage autolaunch.".into());
|
||||||
}
|
}
|
||||||
if let Err(e) = config::register_hotkeys(&conf.hotkeys) {
|
if let Err(e) = shortcuts::register_hotkeys(&conf.hotkeys) {
|
||||||
setup_errors.push(format!("{e}"));
|
setup_errors.push(format!("{e}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ fn main() {
|
|||||||
None | Some(("run", _)) => launch_gui(),
|
None | Some(("run", _)) => launch_gui(),
|
||||||
Some(("get", m)) => cli::get(m),
|
Some(("get", m)) => cli::get(m),
|
||||||
Some(("exec", m)) => cli::exec(m),
|
Some(("exec", m)) => cli::exec(m),
|
||||||
_ => unreachable!(),
|
Some(("shortcut", m)) => cli::invoke_shortcut(m),
|
||||||
|
_ => unreachable!("Unknown subcommand"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
@ -4,15 +4,17 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use clap::{
|
use clap::{
|
||||||
Command,
|
Command,
|
||||||
Arg,
|
Arg,
|
||||||
ArgMatches,
|
ArgMatches,
|
||||||
ArgAction
|
ArgAction,
|
||||||
|
builder::PossibleValuesParser,
|
||||||
};
|
};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
use crate::credentials::Credentials;
|
use crate::credentials::Credentials;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::server::{Request, Response};
|
use crate::server::{Request, Response};
|
||||||
|
use crate::shortcuts::ShortcutAction;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use {
|
use {
|
||||||
@ -63,6 +65,16 @@ pub fn parser() -> Command<'static> {
|
|||||||
.multiple_values(true)
|
.multiple_values(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("shortcut")
|
||||||
|
.about("Invoke an action normally trigged by hotkey (e.g. launch terminal)")
|
||||||
|
.arg(
|
||||||
|
Arg::new("action")
|
||||||
|
.value_parser(
|
||||||
|
PossibleValuesParser::new(["show_window", "launch_terminal"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -129,10 +141,35 @@ pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
|
||||||
async fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
|
let action = match args.get_one::<String>("action").map(|s| s.as_str()) {
|
||||||
|
Some("show_window") => ShortcutAction::ShowWindow,
|
||||||
|
Some("launch_terminal") => ShortcutAction::LaunchTerminal,
|
||||||
|
Some(&_) | None => unreachable!("Unknown shortcut action"), // guaranteed by clap
|
||||||
|
};
|
||||||
|
|
||||||
|
let req = Request::InvokeShortcut(action);
|
||||||
|
match make_request(&req) {
|
||||||
|
Ok(Response::Empty) => Ok(()),
|
||||||
|
Ok(r) => Err(RequestError::Unexpected(r).into()),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
|
||||||
let req = Request::GetAwsCredentials { base };
|
let req = Request::GetAwsCredentials { base };
|
||||||
let mut data = serde_json::to_string(&req).unwrap();
|
match make_request(&req) {
|
||||||
|
Ok(Response::Aws(creds)) => Ok(creds),
|
||||||
|
Ok(r) => Err(RequestError::Unexpected(r)),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn make_request(req: &Request) -> Result<Response, RequestError> {
|
||||||
|
let mut data = serde_json::to_string(req).unwrap();
|
||||||
// server expects newline marking end of request
|
// server expects newline marking end of request
|
||||||
data.push('\n');
|
data.push('\n');
|
||||||
|
|
||||||
@ -142,12 +179,7 @@ async fn get_credentials(base: bool) -> Result<Credentials, RequestError> {
|
|||||||
let mut buf = Vec::with_capacity(1024);
|
let mut buf = Vec::with_capacity(1024);
|
||||||
stream.read_to_end(&mut buf).await?;
|
stream.read_to_end(&mut buf).await?;
|
||||||
let res: Result<Response, ServerError> = serde_json::from_slice(&buf)?;
|
let res: Result<Response, ServerError> = serde_json::from_slice(&buf)?;
|
||||||
match res {
|
Ok(res?)
|
||||||
Ok(Response::Aws(creds)) => Ok(creds),
|
|
||||||
// Eventually we will want this
|
|
||||||
// Ok(r) => Err(RequestError::Unexpected(r)),
|
|
||||||
Err(e) => Err(RequestError::Server(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,12 +26,14 @@ use serde::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
pub trait ErrorPopup {
|
pub trait ShowError {
|
||||||
fn error_popup(self, title: &str);
|
fn error_popup(self, title: &str);
|
||||||
fn error_popup_nowait(self, title: &str);
|
fn error_popup_nowait(self, title: &str);
|
||||||
|
fn error_print(self);
|
||||||
|
fn error_print_prefix(self, prefix: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: std::fmt::Display> ErrorPopup for Result<(), E> {
|
impl<E: std::fmt::Display> ShowError for Result<(), E> {
|
||||||
fn error_popup(self, title: &str) {
|
fn error_popup(self, title: &str) {
|
||||||
if let Err(e) = self {
|
if let Err(e) = self {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
@ -50,6 +52,18 @@ impl<E: std::fmt::Display> ErrorPopup for Result<(), E> {
|
|||||||
.show(|_| {})
|
.show(|_| {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn error_print(self) {
|
||||||
|
if let Err(e) = self {
|
||||||
|
eprintln!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_print_prefix(self, prefix: &str) {
|
||||||
|
if let Err(e) = self {
|
||||||
|
eprintln!("{prefix}: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -164,6 +178,15 @@ pub enum HandlerError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
|
pub enum WindowError {
|
||||||
|
#[error("Failed to find main application window")]
|
||||||
|
NoMainWindow,
|
||||||
|
#[error(transparent)]
|
||||||
|
ManageFailure(#[from] tauri::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, ThisError, AsRefStr)]
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
pub enum GetCredentialsError {
|
pub enum GetCredentialsError {
|
||||||
#[error("Credentials are currently locked")]
|
#[error("Credentials are currently locked")]
|
||||||
@ -324,6 +347,7 @@ impl Serialize for SerializeWrapper<&GetSessionTokenError> {
|
|||||||
impl_serialize_basic!(SetupError);
|
impl_serialize_basic!(SetupError);
|
||||||
impl_serialize_basic!(GetCredentialsError);
|
impl_serialize_basic!(GetCredentialsError);
|
||||||
impl_serialize_basic!(ClientInfoError);
|
impl_serialize_basic!(ClientInfoError);
|
||||||
|
impl_serialize_basic!(WindowError);
|
||||||
|
|
||||||
|
|
||||||
impl Serialize for HandlerError {
|
impl Serialize for HandlerError {
|
||||||
|
@ -7,5 +7,6 @@ mod clientinfo;
|
|||||||
mod ipc;
|
mod ipc;
|
||||||
mod state;
|
mod state;
|
||||||
mod server;
|
mod server;
|
||||||
|
mod shortcuts;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod tray;
|
mod tray;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
use creddy::{
|
use creddy::{
|
||||||
app,
|
app,
|
||||||
cli,
|
cli,
|
||||||
errors::ErrorPopup,
|
errors::ShowError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use tokio::net::windows::named_pipe::{
|
use tokio::net::windows::named_pipe::{
|
||||||
NamedPipeServer,
|
NamedPipeServer,
|
||||||
@ -21,6 +19,7 @@ use crate::clientinfo::{self, Client};
|
|||||||
use crate::credentials::Credentials;
|
use crate::credentials::Credentials;
|
||||||
use crate::ipc::{Approval, AwsRequestNotification};
|
use crate::ipc::{Approval, AwsRequestNotification};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
use crate::shortcuts::{self, ShortcutAction};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -28,12 +27,14 @@ pub enum Request {
|
|||||||
GetAwsCredentials{
|
GetAwsCredentials{
|
||||||
base: bool,
|
base: bool,
|
||||||
},
|
},
|
||||||
|
InvokeShortcut(ShortcutAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum Response {
|
pub enum Response {
|
||||||
Aws(Credentials)
|
Aws(Credentials),
|
||||||
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -102,17 +103,25 @@ async fn handle(stream: &mut NamedPipeServer, app_handle: AppHandle) -> Result<R
|
|||||||
let req: Request = serde_json::from_slice(&buf)?;
|
let req: Request = serde_json::from_slice(&buf)?;
|
||||||
match req {
|
match req {
|
||||||
Request::GetAwsCredentials{ base } => get_aws_credentials(base, client, app_handle).await,
|
Request::GetAwsCredentials{ base } => get_aws_credentials(base, client, app_handle).await,
|
||||||
// etc
|
Request::InvokeShortcut(action) => invoke_shortcut(action).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn invoke_shortcut(action: ShortcutAction) -> Result<Response, HandlerError> {
|
||||||
|
shortcuts::exec_shortcut(action);
|
||||||
|
Ok(Response::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle) -> Result<Response, HandlerError> {
|
async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle) -> Result<Response, HandlerError> {
|
||||||
let state = app_handle.state::<AppState>();
|
let state = app_handle.state::<AppState>();
|
||||||
|
let rehide_ms = {
|
||||||
let main_window = app_handle.get_window("main").ok_or(HandlerError::NoMainWindow)?;
|
let config = state.config.read().await;
|
||||||
let is_currently_visible = main_window.is_visible()?;
|
config.rehide_ms
|
||||||
let rehide_after = state.get_or_set_rehide(!is_currently_visible).await;
|
};
|
||||||
|
let lease = state.acquire_visibility_lease(rehide_ms).await
|
||||||
|
.map_err(|_e| HandlerError::NoMainWindow)?; // automate this conversion eventually?
|
||||||
|
|
||||||
let (chan_send, chan_recv) = oneshot::channel();
|
let (chan_send, chan_recv) = oneshot::channel();
|
||||||
let request_id = state.register_request(chan_send).await;
|
let request_id = state.register_request(chan_send).await;
|
||||||
@ -124,12 +133,6 @@ async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle)
|
|||||||
let notification = AwsRequestNotification {id: request_id, client, base};
|
let notification = AwsRequestNotification {id: request_id, client, base};
|
||||||
app_handle.emit_all("credentials-request", ¬ification)?;
|
app_handle.emit_all("credentials-request", ¬ification)?;
|
||||||
|
|
||||||
if !main_window.is_visible()? {
|
|
||||||
main_window.unminimize()?;
|
|
||||||
main_window.show()?;
|
|
||||||
}
|
|
||||||
main_window.set_focus()?;
|
|
||||||
|
|
||||||
match chan_recv.await {
|
match chan_recv.await {
|
||||||
Ok(Approval::Approved) => {
|
Ok(Approval::Approved) => {
|
||||||
if base {
|
if base {
|
||||||
@ -154,31 +157,6 @@ async fn get_aws_credentials(base: bool, client: Client, app_handle: AppHandle)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
rt::spawn(
|
lease.release();
|
||||||
handle_rehide(rehide_after, app_handle.app_handle())
|
|
||||||
);
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn handle_rehide(rehide_after: bool, app_handle: AppHandle) {
|
|
||||||
let state = app_handle.state::<AppState>();
|
|
||||||
let delay = {
|
|
||||||
let config = state.config.read().await;
|
|
||||||
Duration::from_millis(config.rehide_ms)
|
|
||||||
};
|
|
||||||
tokio::time::sleep(delay).await;
|
|
||||||
|
|
||||||
// if there are no other pending requests, set rehide status back to None
|
|
||||||
if state.req_count().await == 0 {
|
|
||||||
state.clear_rehide().await;
|
|
||||||
// and hide the window if necessary
|
|
||||||
if rehide_after {
|
|
||||||
app_handle.get_window("main").map(|w| {
|
|
||||||
if let Err(e) = w.hide() {
|
|
||||||
eprintln!("{e}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle,
|
GlobalShortcutManager,
|
||||||
Manager,
|
Manager,
|
||||||
|
async_runtime as rt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::APP;
|
use crate::app::APP;
|
||||||
use crate::config::HotkeysConfig;
|
use crate::config::HotkeysConfig;
|
||||||
|
use crate::errors::*;
|
||||||
use crate::terminal;
|
use crate::terminal;
|
||||||
|
|
||||||
|
|
||||||
@ -19,11 +21,18 @@ pub enum ShortcutAction {
|
|||||||
|
|
||||||
pub fn exec_shortcut(action: ShortcutAction) {
|
pub fn exec_shortcut(action: ShortcutAction) {
|
||||||
match action {
|
match action {
|
||||||
ShowWindow => {
|
ShortcutAction::ShowWindow => {
|
||||||
let app = APP.get().unwrap();
|
let app = APP.get().unwrap();
|
||||||
app.get_window("main").map(|w| w.show());
|
app.get_window("main")
|
||||||
|
.ok_or("Couldn't find application main window")
|
||||||
|
.map(|w| w.show().error_popup("Failed to show window"))
|
||||||
|
.error_popup("Failed to show window");
|
||||||
|
},
|
||||||
|
ShortcutAction::LaunchTerminal => {
|
||||||
|
rt::spawn(async {
|
||||||
|
terminal::launch(false).await.error_popup("Failed to launch terminal");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
LaunchTerminal => terminal::launch(false),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +44,7 @@ pub fn register_hotkeys(hotkeys: &HotkeysConfig) -> tauri::Result<()> {
|
|||||||
|
|
||||||
if hotkeys.show_window.enabled {
|
if hotkeys.show_window.enabled {
|
||||||
manager.register(
|
manager.register(
|
||||||
hotkeys.show_window.keys,
|
&hotkeys.show_window.keys,
|
||||||
|| exec_shortcut(ShortcutAction::ShowWindow)
|
|| exec_shortcut(ShortcutAction::ShowWindow)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
sync::oneshot::Sender,
|
sync::oneshot::{self, Sender},
|
||||||
};
|
};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
use tauri::{
|
||||||
|
Manager,
|
||||||
|
async_runtime as rt,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::credentials::{
|
use crate::credentials::{
|
||||||
Session,
|
Session,
|
||||||
@ -14,6 +19,73 @@ use crate::credentials::{
|
|||||||
use crate::{config, config::AppConfig};
|
use crate::{config, config::AppConfig};
|
||||||
use crate::ipc::{self, Approval};
|
use crate::ipc::{self, Approval};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
|
use crate::shortcuts;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Visibility {
|
||||||
|
leases: usize,
|
||||||
|
original: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visibility {
|
||||||
|
fn new() -> Self {
|
||||||
|
Visibility { leases: 0, original: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acquire(&mut self, delay_ms: u64) -> Result<VisibilityLease, WindowError> {
|
||||||
|
let app = crate::app::APP.get().unwrap();
|
||||||
|
let window = app.get_window("main")
|
||||||
|
.ok_or(WindowError::NoMainWindow)?;
|
||||||
|
|
||||||
|
self.leases += 1;
|
||||||
|
if self.original.is_none() {
|
||||||
|
let is_visible = window.is_visible()?;
|
||||||
|
self.original = Some(is_visible);
|
||||||
|
if !is_visible {
|
||||||
|
window.show()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.set_focus()?;
|
||||||
|
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
let lease = VisibilityLease { notify: tx };
|
||||||
|
|
||||||
|
let delay = Duration::from_millis(delay_ms);
|
||||||
|
let handle = app.app_handle();
|
||||||
|
rt::spawn(async move {
|
||||||
|
// We don't care if it's an error; lease being dropped should be handled identically
|
||||||
|
let _ = rx.await;
|
||||||
|
tokio::time::sleep(delay).await;
|
||||||
|
// we can't use `self` here because we would have to move it into the async block
|
||||||
|
let state = handle.state::<AppState>();
|
||||||
|
let mut visibility = state.visibility.write().await;
|
||||||
|
visibility.leases -= 1;
|
||||||
|
if visibility.leases == 0 {
|
||||||
|
if let Some(false) = visibility.original {
|
||||||
|
window.hide().error_print();
|
||||||
|
}
|
||||||
|
visibility.original = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(lease)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VisibilityLease {
|
||||||
|
notify: Sender<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisibilityLease {
|
||||||
|
pub fn release(self) {
|
||||||
|
rt::spawn(async move {
|
||||||
|
if let Err(_) = self.notify.send(()) {
|
||||||
|
eprintln!("Error releasing visibility lease")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -22,11 +94,11 @@ pub struct AppState {
|
|||||||
pub session: RwLock<Session>,
|
pub session: RwLock<Session>,
|
||||||
pub request_count: RwLock<u64>,
|
pub request_count: RwLock<u64>,
|
||||||
pub waiting_requests: RwLock<HashMap<u64, Sender<Approval>>>,
|
pub waiting_requests: RwLock<HashMap<u64, Sender<Approval>>>,
|
||||||
pub current_rehide_status: RwLock<Option<bool>>,
|
|
||||||
pub pending_terminal_request: RwLock<bool>,
|
pub pending_terminal_request: RwLock<bool>,
|
||||||
// setup_errors is never modified and so doesn't need to be wrapped in RwLock
|
// setup_errors is never modified and so doesn't need to be wrapped in RwLock
|
||||||
pub setup_errors: Vec<String>,
|
pub setup_errors: Vec<String>,
|
||||||
pool: sqlx::SqlitePool,
|
pool: sqlx::SqlitePool,
|
||||||
|
visibility: RwLock<Visibility>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@ -41,10 +113,10 @@ impl AppState {
|
|||||||
session: RwLock::new(session),
|
session: RwLock::new(session),
|
||||||
request_count: RwLock::new(0),
|
request_count: RwLock::new(0),
|
||||||
waiting_requests: RwLock::new(HashMap::new()),
|
waiting_requests: RwLock::new(HashMap::new()),
|
||||||
current_rehide_status: RwLock::new(None),
|
|
||||||
pending_terminal_request: RwLock::new(false),
|
pending_terminal_request: RwLock::new(false),
|
||||||
setup_errors,
|
setup_errors,
|
||||||
pool,
|
pool,
|
||||||
|
visibility: RwLock::new(Visibility::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +141,7 @@ impl AppState {
|
|||||||
if new_config.hotkeys.show_window != live_config.hotkeys.show_window
|
if new_config.hotkeys.show_window != live_config.hotkeys.show_window
|
||||||
|| new_config.hotkeys.launch_terminal != live_config.hotkeys.launch_terminal
|
|| new_config.hotkeys.launch_terminal != live_config.hotkeys.launch_terminal
|
||||||
{
|
{
|
||||||
config::register_hotkeys(&new_config.hotkeys)?;
|
shortcuts::register_hotkeys(&new_config.hotkeys)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_config.save(&self.pool).await?;
|
new_config.save(&self.pool).await?;
|
||||||
@ -94,25 +166,9 @@ impl AppState {
|
|||||||
waiting_requests.remove(&id);
|
waiting_requests.remove(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn req_count(&self) -> usize {
|
pub async fn acquire_visibility_lease(&self, delay: u64) -> Result<VisibilityLease, WindowError> {
|
||||||
let waiting_requests = self.waiting_requests.read().await;
|
let mut visibility = self.visibility.write().await;
|
||||||
waiting_requests.len()
|
visibility.acquire(delay)
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_or_set_rehide(&self, new_value: bool) -> bool {
|
|
||||||
let mut rehide = self.current_rehide_status.write().await;
|
|
||||||
match *rehide {
|
|
||||||
Some(original) => original,
|
|
||||||
None => {
|
|
||||||
*rehide = Some(new_value);
|
|
||||||
new_value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn clear_rehide(&self) {
|
|
||||||
let mut rehide = self.current_rehide_status.write().await;
|
|
||||||
*rehide = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_response(&self, response: ipc::RequestResponse) -> Result<(), SendResponseError> {
|
pub async fn send_response(&self, response: ipc::RequestResponse) -> Result<(), SendResponseError> {
|
||||||
|
@ -26,13 +26,8 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
|
|||||||
// if session is unlocked or empty, wait for credentials from frontend
|
// if session is unlocked or empty, wait for credentials from frontend
|
||||||
if !state.is_unlocked().await {
|
if !state.is_unlocked().await {
|
||||||
app.emit_all("launch-terminal-request", ())?;
|
app.emit_all("launch-terminal-request", ())?;
|
||||||
let window = app.get_window("main")
|
let lease = state.acquire_visibility_lease(0).await
|
||||||
.ok_or(LaunchTerminalError::NoMainWindow)?;
|
.map_err(|_e| LaunchTerminalError::NoMainWindow)?; // automate conversion eventually?
|
||||||
if !window.is_visible()? {
|
|
||||||
window.unminimize()?;
|
|
||||||
window.show()?;
|
|
||||||
}
|
|
||||||
window.set_focus()?;
|
|
||||||
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
app.once_global("credentials-event", move |e| {
|
app.once_global("credentials-event", move |e| {
|
||||||
@ -47,6 +42,7 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
|
|||||||
state.unregister_terminal_request().await;
|
state.unregister_terminal_request().await;
|
||||||
return Ok(()); // request was canceled by user
|
return Ok(()); // request was canceled by user
|
||||||
}
|
}
|
||||||
|
lease.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// more lock-management
|
// more lock-management
|
||||||
|
Loading…
x
Reference in New Issue
Block a user