windows: Implement single instance (#15371)
This PR implements a single instance mechanism using the `CreateEventW` function to create a mutex. If the identifier name begins with `Local`, the single instance applies only to processes under the same user. If the identifier begins with `Global`, it applies to all users. Additionally, I was thinking that perhaps we should integrate the single instance functionality into `gpui`. I believe applications developed using `gpui` would benefit from this feature. Furthermore, incorporating the single instance implementation into `gpui` would facilitate the `set_dock_menu` functionality. As I mentioned in #12068, the implementation of `set_dock_menu` on Windows depends on the single instance feature. When a user clicks the "dock menu", Windows will open a new application instance. To achieve behavior similar to macOS, we need to prevent the new instance from launching and instead pass the parameters to the existing instance. Any advice and suggestions are welcome. https://github.com/user-attachments/assets/c46f7e92-4411-4fa9-830e-383798a9dd93 Release Notes: - N/A
This commit is contained in:
parent
1eec601afb
commit
3c53832141
5 changed files with 58 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -14212,6 +14212,7 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
"vim",
|
"vim",
|
||||||
"welcome",
|
"welcome",
|
||||||
|
"windows 0.58.0",
|
||||||
"winresource",
|
"winresource",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
|
|
|
@ -67,7 +67,7 @@ log.workspace = true
|
||||||
markdown_preview.workspace = true
|
markdown_preview.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
mimalloc = { version = "0.1", optional = true }
|
mimalloc = { version = "0.1", optional = true }
|
||||||
nix = {workspace = true, features = ["pthread", "signal"] }
|
nix = { workspace = true, features = ["pthread", "signal"] }
|
||||||
node_runtime.workspace = true
|
node_runtime.workspace = true
|
||||||
notifications.workspace = true
|
notifications.workspace = true
|
||||||
outline.workspace = true
|
outline.workspace = true
|
||||||
|
@ -99,7 +99,7 @@ tab_switcher.workspace = true
|
||||||
supermaven.workspace = true
|
supermaven.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
tasks_ui.workspace = true
|
tasks_ui.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry_events.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
@ -114,6 +114,9 @@ welcome.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
windows.workspace = true
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||||
winresource = "0.1"
|
winresource = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -318,6 +318,15 @@ fn init_ui(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
use zed::single_instance::*;
|
||||||
|
if !check_single_instance() {
|
||||||
|
println!("zed is already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
menu::init();
|
menu::init();
|
||||||
zed_actions::init();
|
zed_actions::init();
|
||||||
|
@ -360,7 +369,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||||
{
|
{
|
||||||
use zed::only_instance::*;
|
use zed::only_instance::*;
|
||||||
if ensure_only_instance() != IsOnlyInstance::Yes {
|
if ensure_only_instance() != IsOnlyInstance::Yes {
|
||||||
|
|
|
@ -2,9 +2,11 @@ mod app_menus;
|
||||||
pub mod inline_completion_registry;
|
pub mod inline_completion_registry;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub(crate) mod linux_prompts;
|
pub(crate) mod linux_prompts;
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||||
pub(crate) mod only_instance;
|
pub(crate) mod only_instance;
|
||||||
mod open_listener;
|
mod open_listener;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub(crate) mod single_instance;
|
||||||
|
|
||||||
pub use app_menus::*;
|
pub use app_menus::*;
|
||||||
use assistant::PromptBuilder;
|
use assistant::PromptBuilder;
|
||||||
|
|
39
crates/zed/src/zed/single_instance.rs
Normal file
39
crates/zed/src/zed/single_instance.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use release_channel::ReleaseChannel;
|
||||||
|
use windows::{
|
||||||
|
core::HSTRING,
|
||||||
|
Win32::{
|
||||||
|
Foundation::{GetLastError, ERROR_ALREADY_EXISTS},
|
||||||
|
System::Threading::CreateEventW,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn retrieve_app_instance_event_identifier() -> &'static str {
|
||||||
|
match *release_channel::RELEASE_CHANNEL {
|
||||||
|
ReleaseChannel::Dev => "Local\\Zed-Editor-Dev-Instance-Event",
|
||||||
|
ReleaseChannel::Nightly => "Local\\Zed-Editor-Nightly-Instance-Event",
|
||||||
|
ReleaseChannel::Preview => "Local\\Zed-Editor-Preview-Instance-Event",
|
||||||
|
ReleaseChannel::Stable => "Local\\Zed-Editor-Stable-Instance-Event",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_single_instance() -> bool {
|
||||||
|
if *db::ZED_STATELESS || *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_single_instance_event()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_single_instance_event() -> bool {
|
||||||
|
unsafe {
|
||||||
|
CreateEventW(
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
&HSTRING::from(retrieve_app_instance_event_identifier()),
|
||||||
|
)
|
||||||
|
.expect("Unable to create instance sync event")
|
||||||
|
};
|
||||||
|
let last_err = unsafe { GetLastError() };
|
||||||
|
last_err != ERROR_ALREADY_EXISTS
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue