gpui: Support Windows Restart Manager shutdown (#62987)
Handle the `WM_QUERYENDSESSION` and `WM_ENDSESSION` messages, which are used by Windows when shutting down an application. For example, Restart Manager uses this mechanism to gracefully close applications (e.g. when installing an update to the application). Release Notes: - N/A
This commit is contained in:
1 parent
a86d40eda5
commit
8a6aaa73ef
10 files changed
+79
-16
No files matched your search
@@ -182,6 +182,7 @@ pub struct CrashServer {
|
||||
active_gpu: Mutex<Option<system_specs::GpuSpecs>>,
|
||||
user_info: Mutex<Option<UserInfo>>,
|
||||
abort_message_location: Mutex<Option<AbortMessageLocation>>,
|
||||
shutdown: Arc<AtomicBool>,
|
||||
has_connection: Arc<AtomicBool>,
|
||||
logs_dir: PathBuf,
|
||||
}
|
||||
@@ -255,6 +256,11 @@ pub fn set_user_info(crash_client: &Arc<Client>, info: UserInfo) {
|
||||
send_crash_server_message(crash_client, CrashServerMessage::UserInfo(info));
|
||||
}
|
||||
|
||||
/// Requests an orderly exit from the crash-handler sidecar.
|
||||
pub fn shutdown_crash_handler(crash_client: &Arc<Client>) {
|
||||
send_crash_server_message(crash_client, CrashServerMessage::Shutdown);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
enum CrashServerMessage {
|
||||
Init(InitCrashHandler),
|
||||
@@ -262,6 +268,7 @@ enum CrashServerMessage {
|
||||
GPUInfo(GpuSpecs),
|
||||
UserInfo(UserInfo),
|
||||
AbortMessageLocation(AbortMessageLocation),
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
/// glibc records the diagnostic it prints just before aborting (malloc integrity
|
||||
@@ -464,6 +471,9 @@ impl minidumper::ServerHandler for CrashServer {
|
||||
CrashServerMessage::AbortMessageLocation(location) => {
|
||||
self.abort_message_location.lock().replace(location);
|
||||
}
|
||||
CrashServerMessage::Shutdown => {
|
||||
self.shutdown.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,6 +678,7 @@ pub fn crash_server(socket: &Path, logs_dir: PathBuf) {
|
||||
panic_info: Mutex::default(),
|
||||
user_info: Mutex::default(),
|
||||
abort_message_location: Mutex::default(),
|
||||
shutdown: shutdown.clone(),
|
||||
has_connection,
|
||||
active_gpu: Mutex::default(),
|
||||
logs_dir,
|
||||
|
||||
+13
-2
@@ -918,8 +918,19 @@ impl App {
|
||||
platform.on_quit(Box::new({
|
||||
let cx = Rc::downgrade(&app);
|
||||
move || {
|
||||
if let Some(cx) = cx.upgrade() {
|
||||
cx.borrow_mut().shutdown();
|
||||
let Some(cx) = cx.upgrade() else {
|
||||
return true;
|
||||
};
|
||||
match cx.try_borrow_mut() {
|
||||
Ok(mut cx) => {
|
||||
cx.shutdown();
|
||||
true
|
||||
}
|
||||
Err(_) => {
|
||||
// Quit was requested while the AppCell was borrowed, so we can't shut down synchronously.
|
||||
// The platform decides how to proceed.
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -200,7 +200,7 @@ pub trait Platform: 'static {
|
||||
fn reveal_path(&self, path: &Path);
|
||||
fn open_with_system(&self, path: &Path);
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>);
|
||||
fn on_reopen(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_system_wake(&self, callback: Box<dyn FnMut()>);
|
||||
|
||||
|
||||
@@ -454,7 +454,7 @@ impl Platform for TestPlatform {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
|
||||
fn on_quit(&self, _callback: Box<dyn FnMut() -> bool>) {}
|
||||
|
||||
fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
|
||||
unimplemented!()
|
||||
|
||||
@@ -172,7 +172,7 @@ impl Platform for VisualTestPlatform {
|
||||
self.platform.open_with_system(path)
|
||||
}
|
||||
|
||||
fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
|
||||
fn on_quit(&self, _callback: Box<dyn FnMut() -> bool>) {}
|
||||
|
||||
fn on_reopen(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ pub(crate) trait LinuxClient {
|
||||
#[derive(Default)]
|
||||
pub(crate) struct PlatformHandlers {
|
||||
pub(crate) open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
|
||||
pub(crate) quit: Option<Box<dyn FnMut()>>,
|
||||
pub(crate) quit: Option<Box<dyn FnMut() -> bool>>,
|
||||
pub(crate) reopen: Option<Box<dyn FnMut()>>,
|
||||
pub(crate) app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
pub(crate) will_open_app_menu: Option<Box<dyn FnMut()>>,
|
||||
@@ -547,7 +547,7 @@ impl<P: LinuxClient + 'static> Platform for LinuxPlatform<P> {
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.inner.with_common(|common| {
|
||||
common.callbacks.quit = Some(callback);
|
||||
});
|
||||
|
||||
@@ -178,7 +178,7 @@ pub(crate) struct MacPlatformState {
|
||||
on_thermal_state_change: Option<Box<dyn FnMut()>>,
|
||||
on_system_wake: Option<Box<dyn FnMut()>>,
|
||||
system_wake_observer_registered: bool,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
quit: Option<Box<dyn FnMut() -> bool>>,
|
||||
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
|
||||
will_open_menu: Option<Box<dyn FnMut()>>,
|
||||
@@ -940,7 +940,7 @@ impl Platform for MacPlatform {
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.0.lock().quit = Some(callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ impl std::error::Error for WebWindowError {}
|
||||
#[derive(Default)]
|
||||
struct WebPlatformCallbacks {
|
||||
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
quit: Option<Box<dyn FnMut() -> bool>>,
|
||||
reopen: Option<Box<dyn FnMut()>>,
|
||||
app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
will_open_app_menu: Option<Box<dyn FnMut()>>,
|
||||
@@ -458,7 +458,7 @@ impl Platform for WebPlatform {
|
||||
|
||||
fn open_with_system(&self, _path: &Path) {}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.callbacks.borrow_mut().quit = Some(callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
|
||||
pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6;
|
||||
pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7;
|
||||
pub(crate) const WM_GPUI_KEYDOWN: u32 = WM_USER + 8;
|
||||
pub(crate) const WM_GPUI_END_SESSION: u32 = WM_USER + 9;
|
||||
|
||||
const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
|
||||
|
||||
@@ -108,6 +109,8 @@ impl WindowsWindowInner {
|
||||
WM_PAINT => self.handle_paint_msg(handle),
|
||||
WM_CLOSE => self.handle_close_msg(),
|
||||
WM_DESTROY => self.handle_destroy_msg(handle),
|
||||
WM_QUERYENDSESSION => Some(1),
|
||||
WM_ENDSESSION => self.handle_end_session_msg(wparam),
|
||||
WM_MOUSEMOVE => self.handle_mouse_move_msg(handle, lparam, wparam),
|
||||
WM_MOUSELEAVE | WM_NCMOUSELEAVE => self.handle_mouse_leave_msg(),
|
||||
WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(handle, lparam),
|
||||
@@ -170,6 +173,20 @@ impl WindowsWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_end_session_msg(&self, wparam: WPARAM) -> Option<isize> {
|
||||
if wparam.0 != 0 {
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
self.platform_window_handle,
|
||||
WM_GPUI_END_SESSION,
|
||||
Some(WPARAM(self.validation_number)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
|
||||
let origin = logical_point(
|
||||
lparam.signed_loword() as f32,
|
||||
|
||||
@@ -79,7 +79,7 @@ pub(crate) struct WindowsPlatformState {
|
||||
#[derive(Default)]
|
||||
struct PlatformCallbacks {
|
||||
open_urls: Cell<Option<Box<dyn FnMut(Vec<String>)>>>,
|
||||
quit: Cell<Option<Box<dyn FnMut()>>>,
|
||||
quit: Cell<Option<Box<dyn FnMut() -> bool>>>,
|
||||
reopen: Cell<Option<Box<dyn FnMut()>>>,
|
||||
app_menu_action: Cell<Option<Box<dyn FnMut(&dyn Action)>>>,
|
||||
will_open_app_menu: Cell<Option<Box<dyn FnMut()>>>,
|
||||
@@ -460,8 +460,12 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
self.inner
|
||||
.with_callback(|callbacks| &callbacks.quit, |callback| callback());
|
||||
self.inner.with_callback(
|
||||
|callbacks| &callbacks.quit,
|
||||
|callback| {
|
||||
callback();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn quit(&self) {
|
||||
@@ -667,7 +671,7 @@ impl Platform for WindowsPlatform {
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.inner.state.callbacks.quit.set(Some(callback));
|
||||
}
|
||||
|
||||
@@ -997,7 +1001,8 @@ impl WindowsPlatformInner {
|
||||
| WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
|
||||
| WM_GPUI_DOCK_MENU_ACTION
|
||||
| WM_GPUI_KEYBOARD_LAYOUT_CHANGED
|
||||
| WM_GPUI_GPU_DEVICE_LOST => self.handle_gpui_events(msg, wparam, lparam),
|
||||
| WM_GPUI_GPU_DEVICE_LOST
|
||||
| WM_GPUI_END_SESSION => self.handle_gpui_events(msg, wparam, lparam),
|
||||
WM_POWERBROADCAST => self.handle_power_broadcast(wparam),
|
||||
_ => None,
|
||||
};
|
||||
@@ -1022,10 +1027,29 @@ impl WindowsPlatformInner {
|
||||
WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
|
||||
WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(),
|
||||
WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
|
||||
WM_GPUI_END_SESSION => self.handle_end_session(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_end_session(&self) -> Option<isize> {
|
||||
let mut shutdown_completed = false;
|
||||
self.with_callback(
|
||||
|callbacks| &callbacks.quit,
|
||||
|callback| shutdown_completed = callback(),
|
||||
);
|
||||
log::logger().flush();
|
||||
if shutdown_completed {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
// Shutdown couldn't run synchronously, since the AppCell is already borrowed.
|
||||
// Windows may terminate the application as soon as we return from this handler, but if we post a WM_QUIT message now,
|
||||
// we may get to gracefully shut down the app before we're terminated by the OS.
|
||||
unsafe { PostQuitMessage(0) };
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn close_one_window(&self, target_window: HWND) -> bool {
|
||||
let Some(all_windows) = self.raw_window_handles.upgrade() else {
|
||||
log::error!("Failed to upgrade raw window handles");
|
||||
|
||||
Reference in new issue
Block a user