/* * This file is part of espanso. * * Copyright (C) 2019-2021 Federico Terzi * * espanso is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * espanso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with espanso. If not, see . */ use std::{path::{PathBuf}, sync::{Arc, Mutex, mpsc::{Sender, channel}}}; use anyhow::{Result, anyhow, bail}; use lazy_static::lazy_static; use log::{error, warn}; use std::process::Command; use winrt_notification::{IconCrop, Toast}; use std::os::windows::process::CommandExt; const ESPANSO_APP_USER_MODEL_ID: &str = "{5E3B6C0F-1A4D-45C4-8872-D8174702101A}"; lazy_static! { static ref SEND_CHANNEL: Arc>>> = Arc::new(Mutex::new(None)); } pub fn initialize_notification_thread(notification_icon_path: PathBuf) -> Result<()> { let (sender, receiver) = channel::(); { let mut lock = SEND_CHANNEL.lock().map_err(|e| anyhow!("failed to define shared notification sender: {}", e))?; *lock = Some(sender); } std::thread::Builder::new().name("notification-thread".to_string()).spawn(move || { // First determine which AppUserModelID we can use lazy_static! { static ref APP_USER_MODEL_ID: &'static str = if is_espanso_app_user_model_id_set() { ESPANSO_APP_USER_MODEL_ID } else { warn!("unable to find espanso AppUserModelID in the list of registered ones, falling back to Powershell"); Toast::POWERSHELL_APP_ID }; } while let Ok(message) = receiver.recv() { if let Err(err) = Toast::new(&APP_USER_MODEL_ID) .icon(¬ification_icon_path, IconCrop::Square, "Espanso") .title("Espanso") .text1(&message) .sound(None) .show() .map_err(|e| anyhow!("failed to show notification: {}", e)) { error!("unable to show notification: {}", err); } } })?; Ok(()) } pub fn show_notification(msg: &str) -> Result<()> { let mut lock = SEND_CHANNEL.lock().map_err(|e| anyhow!("unable to acquire notification send channel: {}", e))?; match &mut *lock { Some(sender) => { sender.send(msg.to_string())?; Ok(()) }, None => bail!("notification sender not available"), } } fn is_espanso_app_user_model_id_set() -> bool { match Command::new("powershell") .args(&["-c", "get-startapps"]) .creation_flags(0x08000000) .output() { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); // Check if espanso is present output_str .lines() .any(|line| line.contains(ESPANSO_APP_USER_MODEL_ID)) } Err(err) => { error!( "unable to determine if AppUserModelID was registered: {}", err ); false } } }