Skip to content

Commit

Permalink
windows: Implement single instance (zed-industries#15371)
Browse files Browse the repository at this point in the history
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 zed-industries#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
  • Loading branch information
JunkuiZhang authored Aug 29, 2024
1 parent 1eec601 commit 3c53832
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions crates/zed/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ log.workspace = true
markdown_preview.workspace = true
menu.workspace = true
mimalloc = { version = "0.1", optional = true }
nix = {workspace = true, features = ["pthread", "signal"] }
nix = { workspace = true, features = ["pthread", "signal"] }
node_runtime.workspace = true
notifications.workspace = true
outline.workspace = true
Expand Down Expand Up @@ -99,7 +99,7 @@ tab_switcher.workspace = true
supermaven.workspace = true
task.workspace = true
tasks_ui.workspace = true
time.workspace = true
time.workspace = true
telemetry_events.workspace = true
terminal_view.workspace = true
theme.workspace = true
Expand All @@ -114,6 +114,9 @@ welcome.workspace = true
workspace.workspace = true
zed_actions.workspace = true

[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true

[target.'cfg(target_os = "windows")'.build-dependencies]
winresource = "0.1"

Expand Down
11 changes: 10 additions & 1 deletion crates/zed/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ fn init_ui(
}

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();
menu::init();
zed_actions::init();
Expand Down Expand Up @@ -360,7 +369,7 @@ fn main() {
}
}
}
#[cfg(not(target_os = "linux"))]
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
{
use zed::only_instance::*;
if ensure_only_instance() != IsOnlyInstance::Yes {
Expand Down
4 changes: 3 additions & 1 deletion crates/zed/src/zed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ mod app_menus;
pub mod inline_completion_registry;
#[cfg(target_os = "linux")]
pub(crate) mod linux_prompts;
#[cfg(not(target_os = "linux"))]
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
pub(crate) mod only_instance;
mod open_listener;
#[cfg(target_os = "windows")]
pub(crate) mod single_instance;

pub use app_menus::*;
use assistant::PromptBuilder;
Expand Down
39 changes: 39 additions & 0 deletions crates/zed/src/zed/single_instance.rs
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 3c53832

Please sign in to comment.