Pull app / OS info out of GPUI, add Linux information, make fallible window initialization (#12869)
TODO: - [x] Finish GPUI changes on other operating systems This is a largely internal change to how we report data to our diagnostics and telemetry. This PR also includes an update to our blade backend which allows us to report errors in a more useful way when failing to initialize blade. Release Notes: - N/A --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
ec9e700e70
commit
80c14c9198
50 changed files with 571 additions and 550 deletions
|
@ -27,13 +27,12 @@ use util::ResultExt;
|
|||
|
||||
use crate::{
|
||||
current_platform, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
|
||||
AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
||||
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
||||
Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
|
||||
PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||
RenderablePromptHandle, Reservation, SharedString, SubscriberSet, Subscription, SvgRenderer,
|
||||
Task, TextSystem, View, ViewContext, Window, WindowAppearance, WindowContext, WindowHandle,
|
||||
WindowId,
|
||||
AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
|
||||
Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId,
|
||||
Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point,
|
||||
PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
|
||||
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext,
|
||||
Window, WindowAppearance, WindowContext, WindowHandle, WindowId,
|
||||
};
|
||||
|
||||
mod async_context;
|
||||
|
@ -169,11 +168,6 @@ impl App {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns metadata associated with the application
|
||||
pub fn metadata(&self) -> AppMetadata {
|
||||
self.0.borrow().app_metadata.clone()
|
||||
}
|
||||
|
||||
/// Returns a handle to the [`BackgroundExecutor`] associated with this app, which can be used to spawn futures in the background.
|
||||
pub fn background_executor(&self) -> BackgroundExecutor {
|
||||
self.0.borrow().background_executor.clone()
|
||||
|
@ -208,7 +202,6 @@ type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
|
|||
pub struct AppContext {
|
||||
pub(crate) this: Weak<AppCell>,
|
||||
pub(crate) platform: Rc<dyn Platform>,
|
||||
app_metadata: AppMetadata,
|
||||
text_system: Arc<TextSystem>,
|
||||
flushing_effects: bool,
|
||||
pending_updates: usize,
|
||||
|
@ -261,17 +254,10 @@ impl AppContext {
|
|||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||
let entities = EntityMap::new();
|
||||
|
||||
let app_metadata = AppMetadata {
|
||||
os_name: platform.os_name(),
|
||||
os_version: platform.os_version().ok(),
|
||||
app_version: platform.app_version().ok(),
|
||||
};
|
||||
|
||||
let app = Rc::new_cyclic(|this| AppCell {
|
||||
app: RefCell::new(AppContext {
|
||||
this: this.clone(),
|
||||
platform: platform.clone(),
|
||||
app_metadata,
|
||||
text_system,
|
||||
actions: Rc::new(ActionRegistry::default()),
|
||||
flushing_effects: false,
|
||||
|
@ -346,11 +332,6 @@ impl AppContext {
|
|||
self.platform.quit();
|
||||
}
|
||||
|
||||
/// Get metadata about the app and platform.
|
||||
pub fn app_metadata(&self) -> AppMetadata {
|
||||
self.app_metadata.clone()
|
||||
}
|
||||
|
||||
/// Schedules all windows in the application to be redrawn. This can be called
|
||||
/// multiple times in an update cycle and still result in a single redraw.
|
||||
pub fn refresh(&mut self) {
|
||||
|
@ -490,26 +471,26 @@ impl AppContext {
|
|||
&mut self,
|
||||
options: crate::WindowOptions,
|
||||
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
|
||||
) -> WindowHandle<V> {
|
||||
) -> anyhow::Result<WindowHandle<V>> {
|
||||
self.update(|cx| {
|
||||
let id = cx.windows.insert(None);
|
||||
let handle = WindowHandle::new(id);
|
||||
let mut window = Window::new(handle.into(), options, cx);
|
||||
let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
|
||||
window.root_view.replace(root_view.into());
|
||||
cx.window_handles.insert(id, window.handle);
|
||||
cx.windows.get_mut(id).unwrap().replace(window);
|
||||
handle
|
||||
match Window::new(handle.into(), options, cx) {
|
||||
Ok(mut window) => {
|
||||
let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
|
||||
window.root_view.replace(root_view.into());
|
||||
cx.window_handles.insert(id, window.handle);
|
||||
cx.windows.get_mut(id).unwrap().replace(window);
|
||||
Ok(handle)
|
||||
}
|
||||
Err(e) => {
|
||||
cx.windows.remove(id);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns Ok() if the platform supports opening windows.
|
||||
/// This returns false (for example) on linux when we could
|
||||
/// not establish a connection to X or Wayland.
|
||||
pub fn can_open_windows(&self) -> anyhow::Result<()> {
|
||||
self.platform.can_open_windows()
|
||||
}
|
||||
|
||||
/// Instructs the platform to activate the application by bringing it to the foreground.
|
||||
pub fn activate(&self, ignoring_other_apps: bool) {
|
||||
self.platform.activate(ignoring_other_apps);
|
||||
|
@ -616,6 +597,12 @@ impl AppContext {
|
|||
self.platform.app_path()
|
||||
}
|
||||
|
||||
/// On Linux, returns the name of the compositor in use.
|
||||
/// Is blank on other platforms.
|
||||
pub fn compositor_name(&self) -> &'static str {
|
||||
self.platform.compositor_name()
|
||||
}
|
||||
|
||||
/// Returns the file URL of the executable with the specified name in the application bundle
|
||||
pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
|
||||
self.platform.path_for_auxiliary_executable(name)
|
||||
|
|
|
@ -151,7 +151,7 @@ impl AsyncAppContext {
|
|||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.open_window(options, build_root_view))
|
||||
lock.open_window(options, build_root_view)
|
||||
}
|
||||
|
||||
/// Schedule a future to be polled in the background.
|
||||
|
|
|
@ -193,19 +193,22 @@ impl TestAppContext {
|
|||
},
|
||||
|cx| cx.new_view(build_window),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Adds a new window with no content.
|
||||
pub fn add_empty_window(&mut self) -> &mut VisualTestContext {
|
||||
let mut cx = self.app.borrow_mut();
|
||||
let bounds = Bounds::maximized(None, &mut cx);
|
||||
let window = cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|cx| cx.new_view(|_| Empty),
|
||||
);
|
||||
let window = cx
|
||||
.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|cx| cx.new_view(|_| Empty),
|
||||
)
|
||||
.unwrap();
|
||||
drop(cx);
|
||||
let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
|
||||
cx.run_until_parked();
|
||||
|
@ -222,13 +225,15 @@ impl TestAppContext {
|
|||
{
|
||||
let mut cx = self.app.borrow_mut();
|
||||
let bounds = Bounds::maximized(None, &mut cx);
|
||||
let window = cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|cx| cx.new_view(build_root_view),
|
||||
);
|
||||
let window = cx
|
||||
.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|cx| cx.new_view(build_root_view),
|
||||
)
|
||||
.unwrap();
|
||||
drop(cx);
|
||||
let view = window.root_view(self).unwrap();
|
||||
let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
|
||||
|
|
|
@ -486,6 +486,7 @@ mod test {
|
|||
focus_handle: cx.focus_handle(),
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
cx.update(|cx| {
|
||||
|
|
|
@ -70,6 +70,19 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
|||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
||||
match guess_compositor() {
|
||||
"Wayland" => Rc::new(WaylandClient::new()),
|
||||
"X11" => Rc::new(X11Client::new()),
|
||||
"Headless" => Rc::new(HeadlessClient::new()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return which compositor we're guessing we'll use.
|
||||
/// Does not attempt to connect to the given compositor
|
||||
#[cfg(target_os = "linux")]
|
||||
#[inline]
|
||||
pub fn guess_compositor() -> &'static str {
|
||||
let wayland_display = std::env::var_os("WAYLAND_DISPLAY");
|
||||
let x11_display = std::env::var_os("DISPLAY");
|
||||
|
||||
|
@ -77,13 +90,14 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
|||
let use_x11 = x11_display.is_some_and(|display| !display.is_empty());
|
||||
|
||||
if use_wayland {
|
||||
Rc::new(WaylandClient::new())
|
||||
"Wayland"
|
||||
} else if use_x11 {
|
||||
Rc::new(X11Client::new())
|
||||
"X11"
|
||||
} else {
|
||||
Rc::new(HeadlessClient::new())
|
||||
"Headless"
|
||||
}
|
||||
}
|
||||
|
||||
// todo("windows")
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
||||
|
@ -106,14 +120,12 @@ pub(crate) trait Platform: 'static {
|
|||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
|
||||
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
|
||||
fn active_window(&self) -> Option<AnyWindowHandle>;
|
||||
fn can_open_windows(&self) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow>;
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>>;
|
||||
|
||||
/// Returns the appearance of the application's windows.
|
||||
fn window_appearance(&self) -> WindowAppearance;
|
||||
|
@ -143,9 +155,9 @@ pub(crate) trait Platform: 'static {
|
|||
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
|
||||
|
||||
fn os_name(&self) -> &'static str;
|
||||
fn os_version(&self) -> Result<SemanticVersion>;
|
||||
fn app_version(&self) -> Result<SemanticVersion>;
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
fn app_path(&self) -> Result<PathBuf>;
|
||||
fn local_timezone(&self) -> UtcOffset;
|
||||
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
|
||||
|
@ -288,19 +300,6 @@ pub(crate) trait PlatformTextSystem: Send + Sync {
|
|||
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
|
||||
}
|
||||
|
||||
/// Basic metadata about the current application and operating system.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppMetadata {
|
||||
/// The name of the current operating system
|
||||
pub os_name: &'static str,
|
||||
|
||||
/// The operating system's version
|
||||
pub os_version: Option<SemanticVersion>,
|
||||
|
||||
/// The current version of the application
|
||||
pub app_version: Option<SemanticVersion>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub(crate) enum AtlasKey {
|
||||
Glyph(RenderGlyphParams),
|
||||
|
|
|
@ -59,10 +59,6 @@ impl LinuxClient for HeadlessClient {
|
|||
None
|
||||
}
|
||||
|
||||
fn can_open_windows(&self) -> anyhow::Result<()> {
|
||||
return Err(anyhow::anyhow!("neither DISPLAY, nor WAYLAND_DISPLAY found. You can still run zed for remote development with --dev-server-token."));
|
||||
}
|
||||
|
||||
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||
None
|
||||
}
|
||||
|
@ -71,8 +67,14 @@ impl LinuxClient for HeadlessClient {
|
|||
&self,
|
||||
_handle: AnyWindowHandle,
|
||||
_params: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
unimplemented!()
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>> {
|
||||
Err(anyhow::anyhow!(
|
||||
"neither DISPLAY nor WAYLAND_DISPLAY is set. You can run in headless mode"
|
||||
))
|
||||
}
|
||||
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
"headless"
|
||||
}
|
||||
|
||||
fn set_cursor_style(&self, _style: CursorStyle) {}
|
||||
|
|
|
@ -39,8 +39,8 @@ use crate::{
|
|||
px, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CosmicTextSystem, CursorStyle,
|
||||
DisplayId, ForegroundExecutor, Keymap, Keystroke, LinuxDispatcher, Menu, MenuItem, Modifiers,
|
||||
OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler,
|
||||
PlatformTextSystem, PlatformWindow, Point, PromptLevel, Result, SemanticVersion, Size, Task,
|
||||
WindowAppearance, WindowOptions, WindowParams,
|
||||
PlatformTextSystem, PlatformWindow, Point, PromptLevel, Result, SemanticVersion, SharedString,
|
||||
Size, Task, WindowAppearance, WindowOptions, WindowParams,
|
||||
};
|
||||
|
||||
use super::x11::X11Client;
|
||||
|
@ -54,18 +54,17 @@ pub(crate) const DOUBLE_CLICK_DISTANCE: Pixels = px(5.0);
|
|||
pub(crate) const KEYRING_LABEL: &str = "zed-github-account";
|
||||
|
||||
pub trait LinuxClient {
|
||||
fn compositor_name(&self) -> &'static str;
|
||||
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R;
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
|
||||
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
|
||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
|
||||
fn can_open_windows(&self) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow>;
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>>;
|
||||
fn set_cursor_style(&self, style: CursorStyle);
|
||||
fn open_uri(&self, uri: &str);
|
||||
fn write_to_primary(&self, item: ClipboardItem);
|
||||
|
@ -152,14 +151,14 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
});
|
||||
}
|
||||
|
||||
fn can_open_windows(&self) -> anyhow::Result<()> {
|
||||
self.can_open_windows()
|
||||
}
|
||||
|
||||
fn quit(&self) {
|
||||
self.with_common(|common| common.signal.stop());
|
||||
}
|
||||
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
self.compositor_name()
|
||||
}
|
||||
|
||||
fn restart(&self, binary_path: Option<PathBuf>) {
|
||||
use std::os::unix::process::CommandExt as _;
|
||||
|
||||
|
@ -245,7 +244,7 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>> {
|
||||
self.open_window(handle, options)
|
||||
}
|
||||
|
||||
|
@ -369,23 +368,6 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
});
|
||||
}
|
||||
|
||||
fn os_name(&self) -> &'static str {
|
||||
"Linux"
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<SemanticVersion> {
|
||||
Ok(SemanticVersion::new(1, 0, 0))
|
||||
}
|
||||
|
||||
fn app_version(&self) -> Result<SemanticVersion> {
|
||||
const VERSION: Option<&str> = option_env!("RELEASE_VERSION");
|
||||
if let Some(version) = VERSION {
|
||||
version.parse()
|
||||
} else {
|
||||
Ok(SemanticVersion::new(1, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
// get the path of the executable of the current process
|
||||
let exe_path = std::env::current_exe()?;
|
||||
|
@ -510,6 +492,8 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.read_from_clipboard()
|
||||
}
|
||||
|
||||
fn add_recent_document(&self, _path: &Path) {}
|
||||
}
|
||||
|
||||
pub(super) fn open_uri_internal(uri: &str, activation_token: Option<&str>) {
|
||||
|
|
|
@ -559,7 +559,7 @@ impl LinuxClient for WaylandClient {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
params: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>> {
|
||||
let mut state = self.0.borrow_mut();
|
||||
|
||||
let (window, surface_id) = WaylandWindow::new(
|
||||
|
@ -568,10 +568,10 @@ impl LinuxClient for WaylandClient {
|
|||
WaylandClientStatePtr(Rc::downgrade(&self.0)),
|
||||
params,
|
||||
state.common.appearance,
|
||||
);
|
||||
)?;
|
||||
state.windows.insert(surface_id, window.0.clone());
|
||||
|
||||
Box::new(window)
|
||||
Ok(Box::new(window))
|
||||
}
|
||||
|
||||
fn set_cursor_style(&self, style: CursorStyle) {
|
||||
|
@ -693,6 +693,10 @@ impl LinuxClient for WaylandClient {
|
|||
.as_ref()
|
||||
.map(|window| window.handle())
|
||||
}
|
||||
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
"Wayland"
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
|
||||
|
|
|
@ -107,7 +107,7 @@ impl WaylandWindowState {
|
|||
client: WaylandClientStatePtr,
|
||||
globals: Globals,
|
||||
options: WindowParams,
|
||||
) -> Self {
|
||||
) -> anyhow::Result<Self> {
|
||||
let bounds = options.bounds.map(|p| p.0 as u32);
|
||||
|
||||
let raw = RawWindow {
|
||||
|
@ -130,7 +130,7 @@ impl WaylandWindowState {
|
|||
},
|
||||
)
|
||||
}
|
||||
.unwrap(),
|
||||
.map_err(|e| anyhow::anyhow!("{:?}", e))?,
|
||||
);
|
||||
let config = BladeSurfaceConfig {
|
||||
size: gpu::Extent {
|
||||
|
@ -141,7 +141,7 @@ impl WaylandWindowState {
|
|||
transparent: options.window_background != WindowBackgroundAppearance::Opaque,
|
||||
};
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
xdg_surface,
|
||||
acknowledged_first_configure: false,
|
||||
surface,
|
||||
|
@ -164,7 +164,7 @@ impl WaylandWindowState {
|
|||
appearance,
|
||||
handle,
|
||||
active: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ impl WaylandWindow {
|
|||
client: WaylandClientStatePtr,
|
||||
params: WindowParams,
|
||||
appearance: WindowAppearance,
|
||||
) -> (Self, ObjectId) {
|
||||
) -> anyhow::Result<(Self, ObjectId)> {
|
||||
let surface = globals.compositor.create_surface(&globals.qh, ());
|
||||
let xdg_surface = globals
|
||||
.wm_base
|
||||
|
@ -267,14 +267,14 @@ impl WaylandWindow {
|
|||
client,
|
||||
globals,
|
||||
params,
|
||||
))),
|
||||
)?)),
|
||||
callbacks: Rc::new(RefCell::new(Callbacks::default())),
|
||||
});
|
||||
|
||||
// Kick things off
|
||||
surface.commit();
|
||||
|
||||
(this, surface.id())
|
||||
Ok((this, surface.id()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -889,6 +889,10 @@ impl X11Client {
|
|||
}
|
||||
|
||||
impl LinuxClient for X11Client {
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
"X11"
|
||||
}
|
||||
|
||||
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
|
||||
f(&mut self.0.borrow_mut().common)
|
||||
}
|
||||
|
@ -929,7 +933,7 @@ impl LinuxClient for X11Client {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
params: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
) -> anyhow::Result<Box<dyn PlatformWindow>> {
|
||||
let mut state = self.0.borrow_mut();
|
||||
let x_window = state.xcb_connection.generate_id().unwrap();
|
||||
|
||||
|
@ -944,7 +948,7 @@ impl LinuxClient for X11Client {
|
|||
&state.atoms,
|
||||
state.scale_factor,
|
||||
state.common.appearance,
|
||||
);
|
||||
)?;
|
||||
|
||||
let screen_resources = state
|
||||
.xcb_connection
|
||||
|
@ -1012,7 +1016,7 @@ impl LinuxClient for X11Client {
|
|||
};
|
||||
|
||||
state.windows.insert(x_window, window_ref);
|
||||
Box::new(window)
|
||||
Ok(Box::new(window))
|
||||
}
|
||||
|
||||
fn set_cursor_style(&self, style: CursorStyle) {
|
||||
|
|
|
@ -217,7 +217,7 @@ impl X11WindowState {
|
|||
atoms: &XcbAtoms,
|
||||
scale_factor: f32,
|
||||
appearance: WindowAppearance,
|
||||
) -> Self {
|
||||
) -> anyhow::Result<Self> {
|
||||
let x_screen_index = params
|
||||
.display_id
|
||||
.map_or(x_main_screen_index, |did| did.0 as usize);
|
||||
|
@ -249,8 +249,7 @@ impl X11WindowState {
|
|||
xcb_connection
|
||||
.create_colormap(xproto::ColormapAlloc::NONE, id, visual_set.root, visual.id)
|
||||
.unwrap()
|
||||
.check()
|
||||
.unwrap();
|
||||
.check()?;
|
||||
id
|
||||
};
|
||||
|
||||
|
@ -282,8 +281,7 @@ impl X11WindowState {
|
|||
&win_aux,
|
||||
)
|
||||
.unwrap()
|
||||
.check()
|
||||
.unwrap();
|
||||
.check()?;
|
||||
|
||||
if let Some(titlebar) = params.titlebar {
|
||||
if let Some(title) = titlebar.title {
|
||||
|
@ -346,7 +344,7 @@ impl X11WindowState {
|
|||
},
|
||||
)
|
||||
}
|
||||
.unwrap(),
|
||||
.map_err(|e| anyhow::anyhow!("{:?}", e))?,
|
||||
);
|
||||
|
||||
let config = BladeSurfaceConfig {
|
||||
|
@ -356,7 +354,7 @@ impl X11WindowState {
|
|||
transparent: params.window_background != WindowBackgroundAppearance::Opaque,
|
||||
};
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
client,
|
||||
executor,
|
||||
display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
|
||||
|
@ -370,7 +368,7 @@ impl X11WindowState {
|
|||
appearance,
|
||||
handle,
|
||||
destroyed: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
|
@ -432,8 +430,8 @@ impl X11Window {
|
|||
atoms: &XcbAtoms,
|
||||
scale_factor: f32,
|
||||
appearance: WindowAppearance,
|
||||
) -> Self {
|
||||
Self(X11WindowStatePtr {
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self(X11WindowStatePtr {
|
||||
state: Rc::new(RefCell::new(X11WindowState::new(
|
||||
handle,
|
||||
client,
|
||||
|
@ -445,11 +443,11 @@ impl X11Window {
|
|||
atoms,
|
||||
scale_factor,
|
||||
appearance,
|
||||
))),
|
||||
)?)),
|
||||
callbacks: Rc::new(RefCell::new(Callbacks::default())),
|
||||
xcb_connection: xcb_connection.clone(),
|
||||
x_window,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn set_wm_hints(&self, wm_hint_property_state: WmHintPropertyState, prop1: u32, prop2: u32) {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task,
|
||||
WindowAppearance, WindowParams,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use anyhow::anyhow;
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
|
@ -367,6 +367,18 @@ impl MacPlatform {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<SemanticVersion> {
|
||||
unsafe {
|
||||
let process_info = NSProcessInfo::processInfo(nil);
|
||||
let version = process_info.operatingSystemVersion();
|
||||
Ok(SemanticVersion::new(
|
||||
version.majorVersion as usize,
|
||||
version.minorVersion as usize,
|
||||
version.patchVersion as usize,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for MacPlatform {
|
||||
|
@ -504,16 +516,16 @@ impl Platform for MacPlatform {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
) -> Result<Box<dyn PlatformWindow>> {
|
||||
// Clippy thinks that this evaluates to `()`, for some reason.
|
||||
#[allow(clippy::unit_arg, clippy::clone_on_copy)]
|
||||
let renderer_context = self.0.lock().renderer_context.clone();
|
||||
Box::new(MacWindow::open(
|
||||
Ok(Box::new(MacWindow::open(
|
||||
handle,
|
||||
options,
|
||||
self.foreground_executor(),
|
||||
renderer_context,
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
fn window_appearance(&self) -> WindowAppearance {
|
||||
|
@ -705,40 +717,6 @@ impl Platform for MacPlatform {
|
|||
self.0.lock().validate_menu_command = Some(callback);
|
||||
}
|
||||
|
||||
fn os_name(&self) -> &'static str {
|
||||
"macOS"
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<SemanticVersion> {
|
||||
unsafe {
|
||||
let process_info = NSProcessInfo::processInfo(nil);
|
||||
let version = process_info.operatingSystemVersion();
|
||||
Ok(SemanticVersion::new(
|
||||
version.majorVersion as usize,
|
||||
version.minorVersion as usize,
|
||||
version.patchVersion as usize,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn app_version(&self) -> Result<SemanticVersion> {
|
||||
unsafe {
|
||||
let bundle: id = NSBundle::mainBundle();
|
||||
if bundle.is_null() {
|
||||
Err(anyhow!("app is not running inside a bundle"))
|
||||
} else {
|
||||
let version: id = msg_send![bundle, objectForInfoDictionaryKey: ns_string("CFBundleShortVersionString")];
|
||||
if version.is_null() {
|
||||
bail!("bundle does not have version");
|
||||
}
|
||||
let len = msg_send![version, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
|
||||
let bytes = version.UTF8String() as *const u8;
|
||||
let version = str::from_utf8(slice::from_raw_parts(bytes, len)).unwrap();
|
||||
version.parse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
unsafe {
|
||||
let bundle: id = NSBundle::mainBundle();
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow, WindowAppearance,
|
||||
WindowParams,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::Result;
|
||||
use collections::VecDeque;
|
||||
use futures::channel::oneshot;
|
||||
use parking_lot::Mutex;
|
||||
|
@ -187,14 +187,14 @@ impl Platform for TestPlatform {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
params: WindowParams,
|
||||
) -> Box<dyn crate::PlatformWindow> {
|
||||
) -> anyhow::Result<Box<dyn crate::PlatformWindow>> {
|
||||
let window = TestWindow::new(
|
||||
handle,
|
||||
params,
|
||||
self.weak.clone(),
|
||||
self.active_display.clone(),
|
||||
);
|
||||
Box::new(window)
|
||||
Ok(Box::new(window))
|
||||
}
|
||||
|
||||
fn window_appearance(&self) -> WindowAppearance {
|
||||
|
@ -249,18 +249,6 @@ impl Platform for TestPlatform {
|
|||
|
||||
fn on_validate_app_menu_command(&self, _callback: Box<dyn FnMut(&dyn crate::Action) -> bool>) {}
|
||||
|
||||
fn os_name(&self) -> &'static str {
|
||||
"test"
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<crate::SemanticVersion> {
|
||||
Err(anyhow!("os_version called on TestPlatform"))
|
||||
}
|
||||
|
||||
fn app_version(&self) -> Result<crate::SemanticVersion> {
|
||||
Err(anyhow!("app_version called on TestPlatform"))
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<std::path::PathBuf> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
|
@ -16,17 +16,14 @@ use copypasta::{ClipboardContext, ClipboardProvider};
|
|||
use futures::channel::oneshot::{self, Receiver};
|
||||
use itertools::Itertools;
|
||||
use parking_lot::RwLock;
|
||||
use semantic_version::SemanticVersion;
|
||||
use smallvec::SmallVec;
|
||||
use time::UtcOffset;
|
||||
use windows::{
|
||||
core::*,
|
||||
Wdk::System::SystemServices::*,
|
||||
Win32::{
|
||||
Foundation::*,
|
||||
Graphics::Gdi::*,
|
||||
Security::Credentials::*,
|
||||
Storage::FileSystem::*,
|
||||
System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*, Threading::*, Time::*},
|
||||
UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
|
||||
},
|
||||
|
@ -287,7 +284,7 @@ impl Platform for WindowsPlatform {
|
|||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
) -> Result<Box<dyn PlatformWindow>> {
|
||||
let lock = self.state.borrow();
|
||||
let window = WindowsWindow::new(
|
||||
handle,
|
||||
|
@ -300,7 +297,7 @@ impl Platform for WindowsPlatform {
|
|||
let handle = window.get_raw_handle();
|
||||
self.raw_window_handles.write().push(handle);
|
||||
|
||||
Box::new(window)
|
||||
Ok(Box::new(window))
|
||||
}
|
||||
|
||||
// todo(windows)
|
||||
|
@ -464,121 +461,6 @@ impl Platform for WindowsPlatform {
|
|||
self.state.borrow_mut().callbacks.validate_app_menu_command = Some(callback);
|
||||
}
|
||||
|
||||
fn os_name(&self) -> &'static str {
|
||||
"Windows"
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<SemanticVersion> {
|
||||
let mut info = unsafe { std::mem::zeroed() };
|
||||
let status = unsafe { RtlGetVersion(&mut info) };
|
||||
if status.is_ok() {
|
||||
Ok(SemanticVersion::new(
|
||||
info.dwMajorVersion as _,
|
||||
info.dwMinorVersion as _,
|
||||
info.dwBuildNumber as _,
|
||||
))
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"unable to get Windows version: {}",
|
||||
std::io::Error::last_os_error()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn app_version(&self) -> Result<SemanticVersion> {
|
||||
let mut file_name_buffer = vec![0u16; MAX_PATH as usize];
|
||||
let file_name = {
|
||||
let mut file_name_buffer_capacity = MAX_PATH as usize;
|
||||
let mut file_name_length;
|
||||
loop {
|
||||
file_name_length =
|
||||
unsafe { GetModuleFileNameW(None, &mut file_name_buffer) } as usize;
|
||||
if file_name_length < file_name_buffer_capacity {
|
||||
break;
|
||||
}
|
||||
// buffer too small
|
||||
file_name_buffer_capacity *= 2;
|
||||
file_name_buffer = vec![0u16; file_name_buffer_capacity];
|
||||
}
|
||||
PCWSTR::from_raw(file_name_buffer[0..(file_name_length + 1)].as_ptr())
|
||||
};
|
||||
|
||||
let version_info_block = {
|
||||
let mut version_handle = 0;
|
||||
let version_info_size =
|
||||
unsafe { GetFileVersionInfoSizeW(file_name, Some(&mut version_handle)) } as usize;
|
||||
if version_info_size == 0 {
|
||||
log::error!(
|
||||
"unable to get version info size: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
return Err(anyhow!("unable to get version info size"));
|
||||
}
|
||||
let mut version_data = vec![0u8; version_info_size + 2];
|
||||
unsafe {
|
||||
GetFileVersionInfoW(
|
||||
file_name,
|
||||
version_handle,
|
||||
version_info_size as u32,
|
||||
version_data.as_mut_ptr() as _,
|
||||
)
|
||||
}
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"unable to retrieve version info: {}",
|
||||
std::io::Error::last_os_error()
|
||||
)
|
||||
})?;
|
||||
version_data
|
||||
};
|
||||
|
||||
let version_info_raw = {
|
||||
let mut buffer = unsafe { std::mem::zeroed() };
|
||||
let mut size = 0;
|
||||
let entry = "\\".encode_utf16().chain(Some(0)).collect_vec();
|
||||
if !unsafe {
|
||||
VerQueryValueW(
|
||||
version_info_block.as_ptr() as _,
|
||||
PCWSTR::from_raw(entry.as_ptr()),
|
||||
&mut buffer,
|
||||
&mut size,
|
||||
)
|
||||
}
|
||||
.as_bool()
|
||||
{
|
||||
log::error!(
|
||||
"unable to query version info data: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
return Err(anyhow!("the specified resource is not valid"));
|
||||
}
|
||||
if size == 0 {
|
||||
log::error!(
|
||||
"unable to query version info data: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
return Err(anyhow!("no value is available for the specified name"));
|
||||
}
|
||||
buffer
|
||||
};
|
||||
|
||||
let version_info = unsafe { &*(version_info_raw as *mut VS_FIXEDFILEINFO) };
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
|
||||
if version_info.dwSignature == 0xFEEF04BD {
|
||||
return Ok(SemanticVersion::new(
|
||||
((version_info.dwProductVersionMS >> 16) & 0xFFFF) as usize,
|
||||
(version_info.dwProductVersionMS & 0xFFFF) as usize,
|
||||
((version_info.dwProductVersionLS >> 16) & 0xFFFF) as usize,
|
||||
));
|
||||
} else {
|
||||
log::error!(
|
||||
"no version info present: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
return Err(anyhow!("no version info present"));
|
||||
}
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
Ok(std::env::current_exe()?)
|
||||
}
|
||||
|
|
|
@ -605,7 +605,7 @@ impl Window {
|
|||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
cx: &mut AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
let WindowOptions {
|
||||
window_bounds,
|
||||
titlebar,
|
||||
|
@ -633,7 +633,7 @@ impl Window {
|
|||
display_id,
|
||||
window_background,
|
||||
},
|
||||
);
|
||||
)?;
|
||||
let display_id = platform_window.display().map(|display| display.id());
|
||||
let sprite_atlas = platform_window.sprite_atlas();
|
||||
let mouse_position = platform_window.mouse_position();
|
||||
|
@ -761,7 +761,7 @@ impl Window {
|
|||
platform_window.set_app_id(&app_id);
|
||||
}
|
||||
|
||||
Window {
|
||||
Ok(Window {
|
||||
handle,
|
||||
removed: false,
|
||||
platform_window,
|
||||
|
@ -807,7 +807,7 @@ impl Window {
|
|||
focus_enabled: true,
|
||||
pending_input: None,
|
||||
prompt: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
fn new_focus_listener(
|
||||
&mut self,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue