gpui_runtime/application.rs
1//! The [`Application`] type: building an app, configuring it before launch, and
2//! starting the platform run loop that drives it.
3
4use anyhow::Result;
5use gpui_authoring::{
6 App, AppCell, AssetSource, AsyncApp, BackgroundExecutor, ForegroundExecutor, FramePipeline,
7 GpuPreference, LayoutEngine, Platform, QuitMode, TextSystem, WindowId, WindowingRequest,
8 http_client::HttpClient,
9};
10use std::{ffi::OsString, path::PathBuf, rc::Rc, sync::Arc};
11
12/// A reference to a GPUI application, typically constructed in the `main` function of your app.
13/// You won't interact with this type much outside of initial configuration and startup.
14pub struct Application(Rc<AppCell>);
15
16/// A strong handle to an [`Application`] started with [`Application::run_embedded`].
17///
18/// Dropping this handle releases the app, so an embedder must hold it for as long as the
19/// app should run. While held, it is the embedder's entry point back into GPUI each time
20/// the external run loop gives it control.
21pub struct ApplicationHandle {
22 app: Rc<AppCell>,
23}
24
25impl ApplicationHandle {
26 /// Invoke `f` with the app context. Must not be called re-entrantly from code that
27 /// is already inside an update; the app state is a `RefCell` and will panic on a
28 /// double borrow.
29 pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
30 let cx = &mut *self.app.borrow_mut();
31 f(cx)
32 }
33
34 /// An [`AsyncApp`] for use across await points. It holds the app weakly; keeping the
35 /// app alive remains this handle's job.
36 pub fn to_async(&self) -> AsyncApp {
37 self.update(|cx| cx.to_async())
38 }
39}
40
41/// Represents an application before it is fully launched. Once your app is
42/// configured, you'll start the app with `App::run`.
43impl Application {
44 /// Builds an app with a caller-provided platform implementation.
45 pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
46 Self(App::new_app_for_platform(platform))
47 }
48
49 /// Builds an app with accessibility (AccessKit) integration forcibly
50 /// disabled.
51 ///
52 /// In this mode, accessibility APIs (e.g.
53 /// [`div().role()`][gpui_authoring::StatefulInteractiveElement::role])
54 /// silently no-op.
55 ///
56 /// See the [accessibility guide](gpui_authoring::_accessibility) for an
57 /// overview of the features this disables.
58 pub fn new_inaccessible(platform: Rc<dyn Platform>) -> Self {
59 let this = Self::with_platform(platform);
60 this.0.borrow_mut().set_accessibility_force_disabled(true);
61 this
62 }
63
64 /// Assigns the source of assets for the application.
65 pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
66 self.0.borrow_mut().set_asset_source(Arc::new(asset_source));
67 self
68 }
69
70 /// Configures arguments to pass when restarting the application.
71 pub fn with_restart_arguments(self, arguments: Vec<OsString>) -> Self {
72 self.0.borrow_mut().set_restart_arguments(arguments);
73 self
74 }
75
76 /// Sets the HTTP client for the application.
77 pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
78 self.0.borrow_mut().set_http_client(http_client);
79 self
80 }
81
82 /// Configures when the application should automatically quit.
83 /// By default, [`QuitMode::Default`] is used.
84 pub fn with_quit_mode(self, mode: QuitMode) -> Self {
85 self.0.borrow_mut().set_quit_mode(mode);
86 self
87 }
88
89 /// Sets the windowing mode the app starts in. See [`App::request_windowing`].
90 ///
91 /// Defaults to windowed. On Linux, the default environment is the process's own, and the app
92 /// starts headless if that names no allowed display server. On macOS, headless means the app
93 /// starts without a Dock icon or menu bar (`ActivationPolicy::Accessory`). Has no effect on
94 /// other platforms.
95 pub fn with_windowing(self, request: WindowingRequest) -> Self {
96 self.0.borrow_mut().set_initial_windowing(request);
97 self
98 }
99
100 /// States which GPU the renderer should run on.
101 ///
102 /// The device is process-wide and chosen before the first window, so set this before
103 /// [`run`](Self::run). When it is not stated, each backend keeps its own default: `LowPower` on
104 /// Linux, and the adapter order the platform already used elsewhere. See decisions `0008` and
105 /// `0009` in the project record.
106 pub fn with_gpu_preference(self, preference: GpuPreference) -> Self {
107 self.0.borrow().platform().set_gpu_preference(preference);
108 self
109 }
110
111 /// Sets the factory that creates each window's layout engine.
112 ///
113 /// Defaults to the engine bundled with GPUI. Supply a different
114 /// implementation to swap the layout engine without touching windows.
115 pub fn with_layout_engine(
116 self,
117 layout_engine: impl Fn() -> Box<dyn LayoutEngine> + 'static,
118 ) -> Self {
119 self.0
120 .borrow_mut()
121 .set_layout_engine_factory(Rc::new(layout_engine));
122 self
123 }
124
125 /// Sets the factory that creates each window's frame pipeline.
126 ///
127 /// Defaults to the immediate-mode pipeline bundled with GPUI, which
128 /// re-evaluates the view tree every frame. Supply a different implementation
129 /// to change how a window draws its frames.
130 pub fn with_frame_pipeline(
131 self,
132 frame_pipeline: impl Fn(WindowId) -> Box<dyn FramePipeline> + 'static,
133 ) -> Self {
134 self.0
135 .borrow_mut()
136 .set_frame_pipeline_factory(Rc::new(frame_pipeline));
137 self
138 }
139
140 /// Sets the text system used to shape and lay out text.
141 ///
142 /// Defaults to the text system bundled with GPUI. Supply a different
143 /// implementation to swap the shaper and line layout engine.
144 pub fn with_text_system(self, text_system: Arc<dyn TextSystem>) -> Self {
145 self.0.borrow_mut().set_text_system(text_system);
146 self
147 }
148
149 /// Start the application. The provided callback will be called once the
150 /// app is fully launched.
151 ///
152 /// On WebAssembly, this returns immediately and retains the app for the lifetime
153 /// of the Wasm instance. Use [`Self::run_embedded`] to control its lifetime explicitly.
154 pub fn run<F>(self, on_finish_launching: F)
155 where
156 F: 'static + FnOnce(&mut App),
157 {
158 let this = self.0.clone();
159 let platform = self.0.borrow().platform();
160 platform.run(Box::new(move || {
161 let cx = &mut *this.borrow_mut();
162 on_finish_launching(cx);
163 }));
164
165 #[cfg(target_family = "wasm")]
166 std::mem::forget(self);
167 }
168
169 /// Start the application for an embedder that drives the run loop itself.
170 ///
171 /// On ordinary platforms `Platform::run` blocks for the lifetime of the app, and the
172 /// app state is kept alive by [`Application::run`]'s stack frame. Embedded platforms —
173 /// where the run loop belongs to someone else, e.g. GPUI compiled into a Wasm guest,
174 /// or a GPUI view hosted inside a foreign native application — implement
175 /// `Platform::run` to invoke the launch callback and return immediately. This method
176 /// supports that shape: it returns an [`ApplicationHandle`] that keeps the app alive
177 /// and lets the embedder re-enter it whenever the external run loop yields control.
178 pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
179 where
180 F: 'static + FnOnce(&mut App),
181 {
182 let this = self.0.clone();
183 let platform = self.0.borrow().platform();
184 platform.run(Box::new(move || {
185 let cx = &mut *this.borrow_mut();
186 on_finish_launching(cx);
187 }));
188 ApplicationHandle { app: self.0 }
189 }
190
191 /// Register a handler to be invoked when the platform instructs the application
192 /// to open one or more URLs.
193 pub fn on_open_urls<F>(&self, callback: F) -> &Self
194 where
195 F: 'static + FnMut(Vec<String>),
196 {
197 let platform = self.0.borrow().platform();
198 platform.on_open_urls(Box::new(callback));
199 self
200 }
201
202 /// Invokes a handler when an already-running application is launched.
203 /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock.
204 pub fn on_reopen<F>(&self, mut callback: F) -> &Self
205 where
206 F: 'static + FnMut(&mut App),
207 {
208 let this = Rc::downgrade(&self.0);
209 let platform = self.0.borrow().platform();
210 platform.on_reopen(Box::new(move || {
211 if let Some(app) = this.upgrade() {
212 callback(&mut app.borrow_mut());
213 }
214 }));
215 self
216 }
217
218 /// Returns a handle to the [`BackgroundExecutor`] associated with this app, which can be used to spawn futures in the background.
219 pub fn background_executor(&self) -> BackgroundExecutor {
220 self.0.borrow().background_executor().clone()
221 }
222
223 /// Returns a handle to the [`ForegroundExecutor`] associated with this app, which can be used to spawn futures in the foreground.
224 pub fn foreground_executor(&self) -> ForegroundExecutor {
225 self.0.borrow().foreground_executor().clone()
226 }
227
228 /// Returns the app's [`TextSystem`].
229 pub fn text_system(&self) -> Arc<dyn TextSystem> {
230 self.0.borrow().text_system().clone()
231 }
232
233 /// Returns the file URL of the executable with the specified name in the application bundle
234 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
235 self.0.borrow().path_for_auxiliary_executable(name)
236 }
237}