app: better panic handling, macos single app instance
This commit is contained in:
parent
d371d4368b
commit
846e30cb38
4 changed files with 63 additions and 58 deletions
|
@ -138,7 +138,7 @@ impl Content {
|
|||
|
||||
if self.first_draw {
|
||||
// Show crash report if needed.
|
||||
if AppConfig::show_crash() {
|
||||
if Settings::crash_report_path().exists() {
|
||||
Modal::new(Self::CRASH_REPORT_MODAL)
|
||||
.closeable(false)
|
||||
.position(ModalPosition::Center)
|
||||
|
@ -415,10 +415,10 @@ impl Content {
|
|||
let text = format!("{} {}", FILE_X, t!("share"));
|
||||
View::colored_text_button(ui, text, Colors::blue(), Colors::white_or_black(false), || {
|
||||
if let Ok(data) = fs::read_to_string(Settings::crash_report_path()) {
|
||||
cb.share_data(Settings::CRASH_REPORT_FILE_NAME.to_string(),
|
||||
data.as_bytes().to_vec()).unwrap_or_default()
|
||||
let name = Settings::CRASH_REPORT_FILE_NAME.to_string();
|
||||
let _ = cb.share_data(name, data.as_bytes().to_vec());
|
||||
}
|
||||
AppConfig::set_show_crash(false);
|
||||
Settings::delete_crash_report();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
|
@ -427,7 +427,7 @@ impl Content {
|
|||
ui.add_space(8.0);
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
AppConfig::set_show_crash(false);
|
||||
Settings::delete_crash_report();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
|
|
62
src/main.rs
62
src/main.rs
|
@ -43,25 +43,39 @@ fn real_main() {
|
|||
|
||||
// Setup callback on panic crash.
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
let backtrace = backtrace::Backtrace::new();
|
||||
// Format error.
|
||||
let backtrace = backtrace::Backtrace::new();
|
||||
let time = grim::gui::views::View::format_time(chrono::Utc::now().timestamp());
|
||||
let target = egui::os::OperatingSystem::from_target_os();
|
||||
let os = egui::os::OperatingSystem::from_target_os();
|
||||
let ver = grim::VERSION;
|
||||
let msg = panic_info_message(info);
|
||||
let err = format!("{} - {:?} - v{}\n\n{}\n\n{:?}", time, target, ver, msg, backtrace);
|
||||
let loc = if let Some(location) = info.location() {
|
||||
format!("{}:{}:{}", location.file(), location.line(), location.column())
|
||||
} else {
|
||||
"no location found.".parse().unwrap()
|
||||
};
|
||||
let err = format!("{} - {:?} - v{}\n{}\n{}\n\n{:?}", time, os, ver, msg, loc, backtrace);
|
||||
// Save backtrace to file.
|
||||
let log = grim::Settings::crash_report_path();
|
||||
if log.exists() {
|
||||
let _ = std::fs::remove_file(log.clone());
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(log)
|
||||
.unwrap();
|
||||
if file.seek(SeekFrom::End(0)).is_ok() {
|
||||
file.write(err.as_bytes()).unwrap_or_default();
|
||||
}
|
||||
} else {
|
||||
std::fs::write(log, err.as_bytes()).unwrap_or_default();
|
||||
}
|
||||
std::fs::write(log, err.as_bytes()).unwrap();
|
||||
// Setup flag to show crash after app restart.
|
||||
grim::AppConfig::set_show_crash(true);
|
||||
// Print message error.
|
||||
println!("{}\n{}", msg, loc);
|
||||
}));
|
||||
|
||||
// Start GUI.
|
||||
match std::panic::catch_unwind(|| {
|
||||
let _ = std::panic::catch_unwind(|| {
|
||||
if is_app_running(&data) {
|
||||
return;
|
||||
} else if let Some(data) = data {
|
||||
|
@ -70,16 +84,13 @@ fn real_main() {
|
|||
let platform = grim::gui::platform::Desktop::new();
|
||||
start_app_socket(platform.clone());
|
||||
start_desktop_gui(platform);
|
||||
}) {
|
||||
Ok(_) => {}
|
||||
Err(e) => println!("{:?}", e)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Get panic message from crash payload.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn panic_info_message<'pi>(panic_info: &'pi std::panic::PanicInfo<'_>) -> &'pi str {
|
||||
fn panic_info_message<'pi>(panic_info: &'pi std::panic::PanicHookInfo<'_>) -> &'pi str {
|
||||
let payload = panic_info.payload();
|
||||
// taken from: https://github.com/rust-lang/rust/blob/4b9f4b221b92193c7e95b1beb502c6eb32c3b613/library/std/src/panicking.rs#L194-L200
|
||||
match payload.downcast_ref::<&'static str>() {
|
||||
|
@ -114,7 +125,7 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
|
|||
.with_min_inner_size([AppConfig::MIN_WIDTH, AppConfig::MIN_HEIGHT])
|
||||
.with_inner_size([width, height]);
|
||||
|
||||
// Setup an icon.
|
||||
// Setup icon.
|
||||
if let Ok(icon) = eframe::icon_data::from_png_bytes(include_bytes!("../img/icon.png")) {
|
||||
viewport = viewport.with_icon(std::sync::Arc::new(icon));
|
||||
}
|
||||
|
@ -176,19 +187,15 @@ fn is_app_running(data: &Option<String>) -> bool {
|
|||
let res: Result<(), Box<dyn std::error::Error>> = runtime
|
||||
.block_on(async {
|
||||
use interprocess::local_socket::{
|
||||
tokio::{prelude::*, Stream},
|
||||
GenericFilePath, GenericNamespaced
|
||||
tokio::{prelude::*, Stream}
|
||||
};
|
||||
use tokio::{
|
||||
io::AsyncWriteExt,
|
||||
};
|
||||
|
||||
let socket_path = grim::Settings::socket_path();
|
||||
let name = if GenericNamespaced::is_supported() {
|
||||
grim::Settings::SOCKET_NAME.to_ns_name::<GenericNamespaced>()?
|
||||
} else {
|
||||
socket_path.clone().to_fs_name::<GenericFilePath>()?
|
||||
};
|
||||
let name = grim::Settings::socket_name(&socket_path)?;
|
||||
|
||||
// Connect to running application socket.
|
||||
let conn = Stream::connect(name).await?;
|
||||
let data = data.clone().unwrap_or("".to_string());
|
||||
|
@ -203,7 +210,7 @@ fn is_app_running(data: &Option<String>) -> bool {
|
|||
drop((rec, sen));
|
||||
Ok(())
|
||||
});
|
||||
return match res {
|
||||
match res {
|
||||
Ok(_) => true,
|
||||
Err(_) => false
|
||||
}
|
||||
|
@ -220,7 +227,7 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
|
|||
.block_on(async {
|
||||
use interprocess::local_socket::{
|
||||
tokio::{prelude::*, Stream},
|
||||
GenericFilePath, GenericNamespaced, Listener, ListenerOptions,
|
||||
Listener, ListenerOptions,
|
||||
};
|
||||
use std::io;
|
||||
use tokio::{
|
||||
|
@ -238,15 +245,12 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
|
|||
Ok(buffer)
|
||||
}
|
||||
|
||||
// Setup socket name.
|
||||
let socket_path = grim::Settings::socket_path();
|
||||
let name = if GenericNamespaced::is_supported() {
|
||||
grim::Settings::SOCKET_NAME.to_ns_name::<GenericNamespaced>()?
|
||||
} else {
|
||||
socket_path.clone().to_fs_name::<GenericFilePath>()?
|
||||
};
|
||||
if socket_path.exists() {
|
||||
let _ = std::fs::remove_file(socket_path);
|
||||
let _ = std::fs::remove_file(&socket_path);
|
||||
}
|
||||
let name = grim::Settings::socket_name(&socket_path)?;
|
||||
|
||||
// Create listener.
|
||||
let opts = ListenerOptions::new().name(name);
|
||||
|
|
|
@ -49,9 +49,6 @@ pub struct AppConfig {
|
|||
|
||||
/// Flag to check if dark theme should be used, use system settings if not set.
|
||||
use_dark_theme: Option<bool>,
|
||||
|
||||
/// Flag to show crash report when happened.
|
||||
show_crash: Option<bool>
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
|
@ -68,7 +65,6 @@ impl Default for AppConfig {
|
|||
y: None,
|
||||
lang: None,
|
||||
use_dark_theme: None,
|
||||
show_crash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,17 +237,4 @@ impl AppConfig {
|
|||
w_config.use_dark_theme = Some(use_dark);
|
||||
w_config.save();
|
||||
}
|
||||
|
||||
/// Check if crash report should be shown on application start.
|
||||
pub fn show_crash() -> bool {
|
||||
let r_config = Settings::app_config_to_read();
|
||||
r_config.show_crash.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Setup flag to show crash report on application start.
|
||||
pub fn set_show_crash(show: bool) {
|
||||
let mut w_config = Settings::app_config_to_update();
|
||||
w_config.show_crash = Some(show);
|
||||
w_config.save();
|
||||
}
|
||||
}
|
|
@ -13,15 +13,17 @@
|
|||
// limitations under the License.
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use egui::os::OperatingSystem;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use grin_config::ConfigError;
|
||||
use interprocess::local_socket::{GenericFilePath, GenericNamespaced, Name, NameType, ToFsName, ToNsName};
|
||||
|
||||
use crate::node::NodeConfig;
|
||||
use crate::settings::AppConfig;
|
||||
|
@ -127,10 +129,7 @@ impl Settings {
|
|||
/// Get base directory path for configuration.
|
||||
pub fn base_path(sub_dir: Option<String>) -> PathBuf {
|
||||
// Check if dir exists.
|
||||
let mut path = match dirs::home_dir() {
|
||||
Some(p) => p,
|
||||
None => PathBuf::new(),
|
||||
};
|
||||
let mut path = dirs::home_dir().unwrap_or_else(|| PathBuf::new());
|
||||
path.push(Self::MAIN_DIR_NAME);
|
||||
if sub_dir.is_some() {
|
||||
path.push(sub_dir.unwrap());
|
||||
|
@ -149,20 +148,39 @@ impl Settings {
|
|||
socket_path
|
||||
}
|
||||
|
||||
/// Get configuration file path from provided name and sub-directory if needed.
|
||||
/// Get desktop application socket name from provided path.
|
||||
pub fn socket_name(path: &PathBuf) -> io::Result<Name> {
|
||||
let name = if OperatingSystem::Mac != OperatingSystem::from_target_os() &&
|
||||
GenericNamespaced::is_supported() {
|
||||
Self::SOCKET_NAME.to_ns_name::<GenericNamespaced>()?
|
||||
} else {
|
||||
path.clone().to_fs_name::<GenericFilePath>()?
|
||||
};
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// Get configuration file path from provided name and subdirectory if needed.
|
||||
pub fn config_path(config_name: &str, sub_dir: Option<String>) -> PathBuf {
|
||||
let mut path = Self::base_path(sub_dir);
|
||||
path.push(config_name);
|
||||
path
|
||||
}
|
||||
|
||||
/// Get configuration file path from provided name and sub-directory if needed.
|
||||
/// Get configuration file path from provided name and subdirectory if needed.
|
||||
pub fn crash_report_path() -> PathBuf {
|
||||
let mut path = Self::base_path(None);
|
||||
path.push(Self::CRASH_REPORT_FILE_NAME);
|
||||
path
|
||||
}
|
||||
|
||||
/// Delete crash report file.
|
||||
pub fn delete_crash_report() {
|
||||
let log = Self::crash_report_path();
|
||||
if log.exists() {
|
||||
let _ = fs::remove_file(log.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Read configuration from the file.
|
||||
pub fn read_from_file<T: DeserializeOwned>(config_path: PathBuf) -> Result<T, ConfigError> {
|
||||
let file_content = fs::read_to_string(config_path.clone())?;
|
||||
|
|
Loading…
Reference in a new issue