Skip to content

Commit 39b5229

Browse files
Desktop: Limit application to a single instance (#3441)
* only allow single instance * more reliable CEF cache cleanup * some cleanup * fix lock file location * add simple signal handling * fix skew handles on desktop * mac remove unused helpers
1 parent 600fb5c commit 39b5229

File tree

15 files changed

+206
-34
lines changed

15 files changed

+206
-34
lines changed

Cargo.lock

Lines changed: 117 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

desktop/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ open = { workspace = true }
4343
rand = { workspace = true, features = ["thread_rng"] }
4444
serde = { workspace = true }
4545
clap = { workspace = true, features = ["derive"] }
46+
pidlock = "0.2.2"
47+
ctrlc = "3.5.1"
4648

4749
# Windows-specific dependencies
4850
[target.'cfg(target_os = "windows")'.dependencies]

desktop/bundle/src/mac.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fn bundle(out_dir: &Path, app_bin: &Path, helper_bin: &Path) -> PathBuf {
4040

4141
create_app(&app_dir, APP_ID, APP_NAME, app_bin, false);
4242

43-
for helper_type in [None, Some("GPU"), Some("Renderer"), Some("Plugin"), Some("Alerts")] {
43+
for helper_type in [None, Some("GPU"), Some("Renderer")] {
4444
let helper_id_suffix = helper_type.map(|t| format!(".{t}")).unwrap_or_default();
4545
let helper_id = format!("{APP_ID}.helper{helper_id_suffix}");
4646
let helper_name_suffix = helper_type.map(|t| format!(" ({t})")).unwrap_or_default();

desktop/src/app.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ impl App {
5656
app_event_scheduler: AppEventScheduler,
5757
launch_documents: Vec<PathBuf>,
5858
) -> Self {
59+
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
60+
ctrlc::set_handler(move || {
61+
tracing::info!("Termination signal received, exiting...");
62+
ctrlc_app_event_scheduler.schedule(AppEvent::CloseWindow);
63+
})
64+
.expect("Error setting Ctrl-C handler");
65+
5966
let rendering_app_event_scheduler = app_event_scheduler.clone();
6067
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
6168
std::thread::spawn(move || {
@@ -365,6 +372,7 @@ impl App {
365372
tracing::info!("Exiting main event loop");
366373
event_loop.exit();
367374
}
375+
#[cfg(target_os = "macos")]
368376
AppEvent::MenuEvent { id } => {
369377
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::MenuEvent { id });
370378
}

desktop/src/cef/context/builder.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use super::CefContext;
1111
use super::singlethreaded::SingleThreadedCefContext;
1212
use crate::cef::CefEventHandler;
1313
use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
14-
use crate::cef::dirs::create_instance_dir;
14+
use crate::cef::dirs::{create_instance_dir, delete_instance_dirs};
1515
use crate::cef::input::InputState;
1616
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl};
1717

@@ -85,6 +85,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
8585

8686
#[cfg(target_os = "macos")]
8787
pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result<impl CefContext, InitError> {
88+
delete_instance_dirs();
8889
let instance_dir = create_instance_dir();
8990

9091
let exe = std::env::current_exe().expect("cannot get current exe path");
@@ -105,6 +106,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
105106

106107
#[cfg(not(target_os = "macos"))]
107108
pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result<impl CefContext, InitError> {
109+
delete_instance_dirs();
108110
let instance_dir = create_instance_dir();
109111

110112
let settings = Settings {

desktop/src/cef/context/singlethreaded.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,19 @@ impl CefContext for SingleThreadedCefContext {
4343
impl Drop for SingleThreadedCefContext {
4444
fn drop(&mut self) {
4545
cef::shutdown();
46-
std::fs::remove_dir_all(&self.instance_dir).expect("Failed to remove CEF cache directory");
46+
47+
// Sometimes some CEF processes still linger at this point and hold file handles to the cache directory.
48+
// To mitigate this, we try to remove the directory multiple times with some delay.
49+
// TODO: find a better solution if possible.
50+
for _ in 0..30 {
51+
match std::fs::remove_dir_all(&self.instance_dir) {
52+
Ok(_) => break,
53+
Err(e) => {
54+
tracing::warn!("Failed to remove CEF cache directory, retrying...: {e}");
55+
std::thread::sleep(std::time::Duration::from_millis(100));
56+
}
57+
}
58+
}
4759
}
4860
}
4961

desktop/src/cef/dirs.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
use std::path::PathBuf;
22

3-
use crate::dirs::{ensure_dir_exists, graphite_data_dir};
3+
use crate::dirs::{app_data_dir, ensure_dir_exists};
44

55
static CEF_DIR_NAME: &str = "browser";
66

7+
pub(crate) fn delete_instance_dirs() {
8+
let cef_dir = app_data_dir().join(CEF_DIR_NAME);
9+
if let Ok(entries) = std::fs::read_dir(&cef_dir) {
10+
for entry in entries.flatten() {
11+
let path = entry.path();
12+
if path.is_dir() {
13+
let _ = std::fs::remove_dir_all(&path);
14+
}
15+
}
16+
}
17+
}
18+
719
pub(crate) fn create_instance_dir() -> PathBuf {
820
let instance_id: String = (0..32).map(|_| format!("{:x}", rand::random::<u8>() % 16)).collect();
9-
let path = graphite_data_dir().join(CEF_DIR_NAME).join(instance_id);
21+
let path = app_data_dir().join(CEF_DIR_NAME).join(instance_id);
1022
ensure_dir_exists(&path);
1123
path
1224
}

desktop/src/cef/input.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use cef::sys::{cef_event_flags_t, cef_key_event_type_t, cef_mouse_button_type_t};
1+
use cef::sys::{cef_key_event_type_t, cef_mouse_button_type_t};
22
use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, MouseEvent};
33
use winit::event::{ButtonSource, ElementState, MouseButton, MouseScrollDelta, WindowEvent};
44

55
mod keymap;
66
use keymap::{ToCharRepresentation, ToNativeKeycode, ToVKBits};
77

88
mod state;
9-
pub(crate) use state::InputState;
9+
pub(crate) use state::{CefModifiers, InputState};
1010

1111
use super::consts::{PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y};
1212

@@ -129,9 +129,10 @@ pub(crate) fn handle_window_event(browser: &Browser, input_state: &mut InputStat
129129
}
130130
let Some(host) = browser.host() else { return };
131131

132-
let mut mouse_event: MouseEvent = input_state.into();
133-
mouse_event.modifiers |= cef_event_flags_t::EVENTFLAG_CONTROL_DOWN.0 as u32;
134-
mouse_event.modifiers |= cef_event_flags_t::EVENTFLAG_PRECISION_SCROLLING_DELTA.0 as u32;
132+
let mouse_event = MouseEvent {
133+
modifiers: CefModifiers::PINCH_MODIFIERS.into(),
134+
..input_state.into()
135+
};
135136

136137
let delta = (delta * PINCH_ZOOM_SPEED).round() as i32;
137138

0 commit comments

Comments
 (0)