diff --git a/Cargo.toml b/Cargo.toml index 6bcbfd2069..4100ea2979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -494,10 +494,5 @@ non_canonical_partial_ord_impl = "allow" reversed_empty_ranges = "allow" type_complexity = "allow" -[workspace.lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = [ - 'cfg(gles)', # used in gpui -] } - [workspace.metadata.cargo-machete] ignored = ["bindgen", "cbindgen", "prost_build", "serde"] diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index b62fe2522f..26e8b109b7 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -28,7 +28,6 @@ "ctrl-0": "zed::ResetBufferFontSize", "ctrl-,": "zed::OpenSettings", "ctrl-q": "zed::Quit", - "alt-f9": "zed::Hide", "f11": "zed::ToggleFullScreen" } }, diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 2c1a221c89..06390b8b9e 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -99,7 +99,6 @@ objc = "0.2" [target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies] flume = "0.11" -#TODO: use these on all platforms blade-graphics.workspace = true blade-macros.workspace = true blade-util.workspace = true diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index e04d526c38..0db83599e4 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -29,10 +29,11 @@ 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, PathPromptOptions, Pixels, Platform, PlatformDisplay, - Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, - SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, - Window, WindowAppearance, WindowContext, WindowHandle, WindowId, + 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; @@ -1167,6 +1168,11 @@ impl AppContext { self.platform.set_menus(menus, &self.keymap.borrow()); } + /// Sets the menu bar for this application. This will replace any existing menu bar. + pub fn get_menus(&self) -> Option> { + self.platform.get_menus() + } + /// Sets the right click menu for the app icon in the dock pub fn set_dock_menu(&mut self, menus: Vec) { self.platform.set_dock_menu(menus, &self.keymap.borrow()); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index a806109287..a932ac8513 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -1,5 +1,3 @@ -// todo(linux): remove -#![cfg_attr(target_os = "linux", allow(dead_code))] // todo(windows): remove #![cfg_attr(windows, allow(dead_code))] @@ -135,6 +133,10 @@ pub(crate) trait Platform: 'static { fn on_reopen(&self, callback: Box); fn set_menus(&self, menus: Vec, keymap: &Keymap); + fn get_menus(&self) -> Option> { + None + } + fn set_dock_menu(&self, menu: Vec, keymap: &Keymap); fn add_recent_document(&self, _path: &Path) {} fn on_app_menu_action(&self, callback: Box); @@ -203,7 +205,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn content_size(&self) -> Size; fn scale_factor(&self) -> f32; fn appearance(&self) -> WindowAppearance; - fn display(&self) -> Rc; + fn display(&self) -> Option>; fn mouse_position(&self) -> Point; fn modifiers(&self) -> Modifiers; fn set_input_handler(&mut self, input_handler: PlatformInputHandler); @@ -413,6 +415,7 @@ impl PlatformInputHandler { .flatten() } + #[cfg_attr(target_os = "linux", allow(dead_code))] fn text_for_range(&mut self, range_utf16: Range) -> Option { self.cx .update(|cx| self.handler.text_for_range(range_utf16, cx)) @@ -573,13 +576,17 @@ pub(crate) struct WindowParams { pub titlebar: Option, /// The kind of window to create + #[cfg_attr(target_os = "linux", allow(dead_code))] pub kind: WindowKind, /// Whether the window should be movable by the user + #[cfg_attr(target_os = "linux", allow(dead_code))] pub is_movable: bool, + #[cfg_attr(target_os = "linux", allow(dead_code))] pub focus: bool, + #[cfg_attr(target_os = "linux", allow(dead_code))] pub show: bool, pub display_id: Option, @@ -797,10 +804,6 @@ pub enum CursorStyle { /// corresponds to the CSS curosr value `row-resize` ResizeRow, - /// A cursor indicating that something will disappear if moved here - /// Does not correspond to a CSS cursor value - DisappearingItem, - /// A text input cursor for vertical layout /// corresponds to the CSS cursor value `vertical-text` IBeamCursorForVerticalLayout, @@ -865,6 +868,7 @@ impl ClipboardItem { .and_then(|m| serde_json::from_str(m).ok()) } + #[cfg_attr(target_os = "linux", allow(dead_code))] pub(crate) fn text_hash(text: &str) -> u64 { let mut hasher = SeaHasher::new(); text.hash(&mut hasher); diff --git a/crates/gpui/src/platform/app_menu.rs b/crates/gpui/src/platform/app_menu.rs index 91fe358931..abe169d8bd 100644 --- a/crates/gpui/src/platform/app_menu.rs +++ b/crates/gpui/src/platform/app_menu.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::{Action, AppContext, Platform}; use util::ResultExt; @@ -10,6 +12,16 @@ pub struct Menu<'a> { pub items: Vec>, } +impl<'a> Menu<'a> { + /// Create an OwnedMenu from this Menu + pub fn owned(self) -> OwnedMenu { + OwnedMenu { + name: self.name.to_string().into(), + items: self.items.into_iter().map(|item| item.owned()).collect(), + } + } +} + /// The different kinds of items that can be in a menu pub enum MenuItem<'a> { /// A separator between items @@ -60,6 +72,73 @@ impl<'a> MenuItem<'a> { os_action: Some(os_action), } } + + /// Create an OwnedMenuItem from this MenuItem + pub fn owned(self) -> OwnedMenuItem { + match self { + MenuItem::Separator => OwnedMenuItem::Separator, + MenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.owned()), + MenuItem::Action { + name, + action, + os_action, + } => OwnedMenuItem::Action { + name: name.into(), + action, + os_action, + }, + } + } +} + +/// A menu of the application, either a main menu or a submenu +#[derive(Clone)] +pub struct OwnedMenu { + /// The name of the menu + pub name: Cow<'static, str>, + + /// The items in the menu + pub items: Vec, +} + +/// The different kinds of items that can be in a menu +pub enum OwnedMenuItem { + /// A separator between items + Separator, + + /// A submenu + Submenu(OwnedMenu), + + /// An action that can be performed + Action { + /// The name of this menu item + name: String, + + /// the action to perform when this menu item is selected + action: Box, + + /// The OS Action that corresponds to this action, if any + /// See [`OsAction`] for more information + os_action: Option, + }, +} + +impl Clone for OwnedMenuItem { + fn clone(&self) -> Self { + match self { + OwnedMenuItem::Separator => OwnedMenuItem::Separator, + OwnedMenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.clone()), + OwnedMenuItem::Action { + name, + action, + os_action, + } => OwnedMenuItem::Action { + name: name.clone(), + action: action.boxed_clone(), + os_action: *os_action, + }, + } + } } // TODO: As part of the global selections refactor, these should diff --git a/crates/gpui/src/platform/blade/blade_renderer.rs b/crates/gpui/src/platform/blade/blade_renderer.rs index f8d1b8b983..761466495e 100644 --- a/crates/gpui/src/platform/blade/blade_renderer.rs +++ b/crates/gpui/src/platform/blade/blade_renderer.rs @@ -20,7 +20,9 @@ use std::{mem, sync::Arc}; const MAX_FRAME_TIME_MS: u32 = 1000; +#[cfg(target_os = "macos")] pub type Context = (); +#[cfg(target_os = "macos")] pub type Renderer = BladeRenderer; #[cfg(target_os = "macos")] diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index 3f6804b4a3..fe2f08296c 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,6 +1,3 @@ -// todo(linux): remove -#![allow(unused)] - mod dispatcher; mod headless; mod platform; diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index 8be712d274..0047b113a8 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -1,9 +1,3 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -// todo(linux): remove -#![allow(unused_variables)] - use crate::{PlatformDispatcher, TaskLabel}; use async_task::Runnable; use calloop::{ @@ -63,7 +57,7 @@ impl LinuxDispatcher { timer_handle .insert_source( calloop::timer::Timer::from_duration(timer.duration), - move |e, _, _| { + move |_, _, _| { if let Some(runnable) = runnable.take() { runnable.run(); } diff --git a/crates/gpui/src/platform/linux/headless/client.rs b/crates/gpui/src/platform/linux/headless/client.rs index f41a02784e..86c15243f9 100644 --- a/crates/gpui/src/platform/linux/headless/client.rs +++ b/crates/gpui/src/platform/linux/headless/client.rs @@ -1,27 +1,16 @@ use std::cell::RefCell; -use std::ops::Deref; use std::rc::Rc; -use std::time::{Duration, Instant}; use calloop::{EventLoop, LoopHandle}; -use collections::HashMap; use util::ResultExt; use crate::platform::linux::LinuxClient; use crate::platform::{LinuxCommon, PlatformWindow}; -use crate::{ - px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, Modifiers, ModifiersChangedEvent, Pixels, - PlatformDisplay, PlatformInput, Point, ScrollDelta, Size, TouchPhase, WindowParams, -}; - -use calloop::{ - generic::{FdWrapper, Generic}, - RegistrationToken, -}; +use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams}; pub struct HeadlessClientState { - pub(crate) loop_handle: LoopHandle<'static, HeadlessClient>, + pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>, pub(crate) event_loop: Option>, pub(crate) common: LinuxCommon, } @@ -37,15 +26,17 @@ impl HeadlessClient { let handle = event_loop.handle(); - handle.insert_source(main_receiver, |event, _, _: &mut HeadlessClient| { - if let calloop::channel::Event::Msg(runnable) = event { - runnable.run(); - } - }); + handle + .insert_source(main_receiver, |event, _, _: &mut HeadlessClient| { + if let calloop::channel::Event::Msg(runnable) = event { + runnable.run(); + } + }) + .ok(); HeadlessClient(Rc::new(RefCell::new(HeadlessClientState { event_loop: Some(event_loop), - loop_handle: handle, + _loop_handle: handle, common, }))) } @@ -64,7 +55,7 @@ impl LinuxClient for HeadlessClient { None } - fn display(&self, id: DisplayId) -> Option> { + fn display(&self, _id: DisplayId) -> Option> { None } @@ -72,10 +63,14 @@ impl LinuxClient for HeadlessClient { 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 { + None + } + fn open_window( &self, _handle: AnyWindowHandle, - params: WindowParams, + _params: WindowParams, ) -> Box { unimplemented!() } @@ -84,9 +79,9 @@ impl LinuxClient for HeadlessClient { fn open_uri(&self, _uri: &str) {} - fn write_to_primary(&self, item: crate::ClipboardItem) {} + fn write_to_primary(&self, _item: crate::ClipboardItem) {} - fn write_to_clipboard(&self, item: crate::ClipboardItem) {} + fn write_to_clipboard(&self, _item: crate::ClipboardItem) {} fn read_from_primary(&self) -> Option { None diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 6974eb7fbd..d6c041b65d 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -38,9 +38,9 @@ use crate::platform::linux::xdg_desktop_portal::{should_auto_hide_scrollbars, wi use crate::{ px, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CosmicTextSystem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, Keystroke, LinuxDispatcher, Menu, MenuItem, Modifiers, - PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler, PlatformTextSystem, - PlatformWindow, Point, PromptLevel, Result, SemanticVersion, Size, Task, WindowAppearance, - WindowOptions, WindowParams, + OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler, + PlatformTextSystem, PlatformWindow, Point, PromptLevel, Result, SemanticVersion, Size, Task, + WindowAppearance, WindowOptions, WindowParams, }; use super::x11::X11Client; @@ -72,6 +72,7 @@ pub trait LinuxClient { fn write_to_clipboard(&self, item: ClipboardItem); fn read_from_primary(&self) -> Option; fn read_from_clipboard(&self) -> Option; + fn active_window(&self) -> Option; fn run(&self); } @@ -93,6 +94,7 @@ pub(crate) struct LinuxCommon { pub(crate) auto_hide_scrollbars: bool, pub(crate) callbacks: PlatformHandlers, pub(crate) signal: LoopSignal, + pub(crate) menus: Vec, } impl LinuxCommon { @@ -118,6 +120,7 @@ impl LinuxCommon { auto_hide_scrollbars, callbacks, signal, + menus: Vec::new(), }; (common, main_receiver) @@ -210,18 +213,21 @@ impl Platform for P { } } - // todo(linux) - fn activate(&self, ignoring_other_apps: bool) {} - - // todo(linux) - fn hide(&self) {} - - fn hide_other_apps(&self) { - log::warn!("hide_other_apps is not implemented on Linux, ignoring the call") + fn activate(&self, ignoring_other_apps: bool) { + log::info!("activate is not implemented on Linux, ignoring the call") } - // todo(linux) - fn unhide_other_apps(&self) {} + fn hide(&self) { + log::info!("hide is not implemented on Linux, ignoring the call") + } + + fn hide_other_apps(&self) { + log::info!("hide_other_apps is not implemented on Linux, ignoring the call") + } + + fn unhide_other_apps(&self) { + log::info!("unhide_other_apps is not implemented on Linux, ignoring the call") + } fn primary_display(&self) -> Option> { self.primary_display() @@ -231,9 +237,8 @@ impl Platform for P { self.displays() } - // todo(linux) fn active_window(&self) -> Option { - None + self.active_window() } fn open_window( @@ -387,15 +392,22 @@ impl Platform for P { Ok(exe_path) } - // todo(linux) - fn set_menus(&self, menus: Vec, keymap: &Keymap) {} + fn set_menus(&self, menus: Vec, _keymap: &Keymap) { + self.with_common(|common| { + common.menus = menus.into_iter().map(|menu| menu.owned()).collect(); + }) + } + + fn get_menus(&self) -> Option> { + self.with_common(|common| Some(common.menus.clone())) + } + fn set_dock_menu(&self, menu: Vec, keymap: &Keymap) {} fn local_timezone(&self) -> UtcOffset { UtcOffset::UTC } - //todo(linux) fn path_for_auxiliary_executable(&self, name: &str) -> Result { Err(anyhow::Error::msg( "Platform::path_for_auxiliary_executable is not implemented yet", @@ -549,7 +561,6 @@ impl CursorStyle { CursorStyle::ResizeUpDown => Shape::NsResize, CursorStyle::ResizeColumn => Shape::ColResize, CursorStyle::ResizeRow => Shape::RowResize, - CursorStyle::DisappearingItem => Shape::Grabbing, // todo(linux) - couldn't find equivalent icon in linux CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText, CursorStyle::OperationNotAllowed => Shape::NotAllowed, CursorStyle::DragLink => Shape::Alias, @@ -577,7 +588,6 @@ impl CursorStyle { CursorStyle::ResizeUpDown => "ns-resize", CursorStyle::ResizeColumn => "col-resize", CursorStyle::ResizeRow => "row-resize", - CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux CursorStyle::IBeamCursorForVerticalLayout => "vertical-text", CursorStyle::OperationNotAllowed => "not-allowed", CursorStyle::DragLink => "alias", diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index b6152807e6..464a8ada30 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -1,14 +1,11 @@ -use core::hash; use std::cell::{RefCell, RefMut}; use std::ffi::OsString; -use std::ops::{Deref, DerefMut}; +use std::hash::Hash; use std::os::fd::{AsRawFd, BorrowedFd}; use std::path::PathBuf; use std::rc::{Rc, Weak}; -use std::sync::Arc; use std::time::{Duration, Instant}; -use async_task::Runnable; use calloop::timer::{TimeoutAction, Timer}; use calloop::{EventLoop, LoopHandle}; use calloop_wayland_source::WaylandSource; @@ -16,7 +13,7 @@ use collections::HashMap; use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary}; use copypasta::ClipboardProvider; use filedescriptor::Pipe; -use parking_lot::Mutex; + use smallvec::SmallVec; use util::ResultExt; use wayland_backend::client::ObjectId; @@ -26,9 +23,8 @@ use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContent use wayland_client::protocol::wl_callback::{self, WlCallback}; use wayland_client::protocol::wl_data_device_manager::DndAction; use wayland_client::protocol::wl_pointer::AxisSource; -use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::protocol::{ - wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output, wl_region, + wl_data_device, wl_data_device_manager, wl_data_offer, wl_output, wl_region, }; use wayland_client::{ delegate_noop, @@ -38,7 +34,6 @@ use wayland_client::{ }, Connection, Dispatch, Proxy, QueueHandle, }; -use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape; use wayland_protocols::wp::cursor_shape::v1::client::{ wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1, }; @@ -62,7 +57,8 @@ use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1; use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS}; use super::super::{open_uri_internal, read_fd, DOUBLE_CLICK_INTERVAL}; -use super::window::{ImeInput, WaylandWindowState, WaylandWindowStatePtr}; +use super::display::WaylandDisplay; +use super::window::{ImeInput, WaylandWindowStatePtr}; use crate::platform::linux::is_within_click_distance; use crate::platform::linux::wayland::cursor::Cursor; use crate::platform::linux::wayland::serial::{SerialKind, SerialTracker}; @@ -71,7 +67,7 @@ use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSour use crate::platform::linux::LinuxClient; use crate::platform::PlatformWindow; use crate::{ - point, px, Bounds, FileDropEvent, ForegroundExecutor, MouseExitEvent, WindowAppearance, + point, px, size, Bounds, DevicePixels, FileDropEvent, ForegroundExecutor, MouseExitEvent, Size, SCROLL_LINES, }; use crate::{ @@ -143,11 +139,49 @@ impl Globals { } } +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InProgressOutput { + scale: Option, + position: Option>, + size: Option>, +} + +impl InProgressOutput { + fn complete(&self) -> Option { + if let Some((position, size)) = self.position.zip(self.size) { + let scale = self.scale.unwrap_or(1); + Some(Output { + scale, + bounds: Bounds::new(position, size), + }) + } else { + None + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct Output { + pub scale: i32, + pub bounds: Bounds, +} + +impl Hash for Output { + fn hash(&self, state: &mut H) { + state.write_i32(self.scale); + state.write_i32(self.bounds.origin.x.0); + state.write_i32(self.bounds.origin.y.0); + state.write_i32(self.bounds.size.width.0); + state.write_i32(self.bounds.size.height.0); + } +} + pub(crate) struct WaylandClientState { serial_tracker: SerialTracker, globals: Globals, - wl_seat: wl_seat::WlSeat, // todo(linux): multi-seat support + wl_seat: wl_seat::WlSeat, // TODO: Multi seat support wl_pointer: Option, + wl_keyboard: Option, cursor_shape_device: Option, data_device: Option, text_input: Option, @@ -156,15 +190,16 @@ pub(crate) struct WaylandClientState { // Surface to Window mapping windows: HashMap, // Output to scale mapping - output_scales: HashMap, + outputs: HashMap, + in_progress_outputs: HashMap, keymap_state: Option, compose_state: Option, drag: DragState, click: ClickState, repeat: KeyRepeat, - modifiers: Modifiers, + pub modifiers: Modifiers, axis_source: AxisSource, - mouse_location: Option>, + pub mouse_location: Option>, continuous_scroll_delta: Option>, discrete_scroll_delta: Option>, vertical_modifier: f32, @@ -210,7 +245,7 @@ pub(crate) struct KeyRepeat { pub struct WaylandClientStatePtr(Weak>); impl WaylandClientStatePtr { - fn get_client(&self) -> Rc> { + pub fn get_client(&self) -> Rc> { self.0 .upgrade() .expect("The pointer should always be valid when dispatching in wayland") @@ -328,7 +363,7 @@ impl WaylandClient { let qh = event_queue.handle(); let mut seat: Option = None; - let mut outputs = HashMap::default(); + let mut in_progress_outputs = HashMap::default(); globals.contents().with_list(|list| { for global in list { match &global.interface[..] { @@ -347,7 +382,7 @@ impl WaylandClient { &qh, (), ); - outputs.insert(output.id(), 1); + in_progress_outputs.insert(output.id(), InProgressOutput::default()); } _ => {} } @@ -361,11 +396,13 @@ impl WaylandClient { let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); let handle = event_loop.handle(); - handle.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| { - if let calloop::channel::Event::Msg(runnable) = event { - runnable.run(); - } - }); + handle + .insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| { + if let calloop::channel::Event::Msg(runnable) = event { + runnable.run(); + } + }) + .unwrap(); let seat = seat.unwrap(); let globals = Globals::new( @@ -384,33 +421,37 @@ impl WaylandClient { let cursor = Cursor::new(&conn, &globals, 24); - handle.insert_source(XDPEventSource::new(&common.background_executor), { - move |event, _, client| match event { - XDPEvent::WindowAppearance(appearance) => { - if let Some(client) = client.0.upgrade() { - let mut client = client.borrow_mut(); + handle + .insert_source(XDPEventSource::new(&common.background_executor), { + move |event, _, client| match event { + XDPEvent::WindowAppearance(appearance) => { + if let Some(client) = client.0.upgrade() { + let mut client = client.borrow_mut(); - client.common.appearance = appearance; + client.common.appearance = appearance; - for (_, window) in &mut client.windows { - window.set_appearance(appearance); + for (_, window) in &mut client.windows { + window.set_appearance(appearance); + } } } } - } - }); + }) + .unwrap(); let mut state = Rc::new(RefCell::new(WaylandClientState { serial_tracker: SerialTracker::new(), globals, wl_seat: seat, wl_pointer: None, + wl_keyboard: None, cursor_shape_device: None, data_device, text_input: None, pre_edit_text: None, composing: false, - output_scales: outputs, + outputs: HashMap::default(), + in_progress_outputs, windows: HashMap::default(), common, keymap_state: None, @@ -459,7 +500,9 @@ impl WaylandClient { pending_open_uri: None, })); - WaylandSource::new(conn, event_queue).insert(handle); + WaylandSource::new(conn, event_queue) + .insert(handle) + .unwrap(); Self(state) } @@ -467,11 +510,32 @@ impl WaylandClient { impl LinuxClient for WaylandClient { fn displays(&self) -> Vec> { - Vec::new() + self.0 + .borrow() + .outputs + .iter() + .map(|(id, output)| { + Rc::new(WaylandDisplay { + id: id.clone(), + bounds: output.bounds, + }) as Rc + }) + .collect() } fn display(&self, id: DisplayId) -> Option> { - unimplemented!() + self.0 + .borrow() + .outputs + .iter() + .find_map(|(object_id, output)| { + (object_id.protocol_id() == id.0).then(|| { + Rc::new(WaylandDisplay { + id: object_id.clone(), + bounds: output.bounds, + }) as Rc + }) + }) } fn primary_display(&self) -> Option> { @@ -486,6 +550,7 @@ impl LinuxClient for WaylandClient { let mut state = self.0.borrow_mut(); let (window, surface_id) = WaylandWindow::new( + handle, state.globals.clone(), WaylandClientStatePtr(Rc::downgrade(&self.0)), params, @@ -566,7 +631,8 @@ impl LinuxClient for WaylandClient { .primary .as_mut() .unwrap() - .set_contents(item.text); + .set_contents(item.text) + .ok(); } fn write_to_clipboard(&self, item: crate::ClipboardItem) { @@ -575,7 +641,8 @@ impl LinuxClient for WaylandClient { .clipboard .as_mut() .unwrap() - .set_contents(item.text); + .set_contents(item.text) + .ok(); } fn read_from_primary(&self) -> Option { @@ -605,6 +672,14 @@ impl LinuxClient for WaylandClient { metadata: None, }) } + + fn active_window(&self) -> Option { + self.0 + .borrow_mut() + .keyboard_focused_window + .as_ref() + .map(|window| window.handle()) + } } impl Dispatch for WaylandClientStatePtr { @@ -626,18 +701,33 @@ impl Dispatch for WaylandClientStat version, } => match &interface[..] { "wl_seat" => { - state.wl_pointer = None; - registry.bind::(name, wl_seat_version(version), qh, ()); + if let Some(wl_pointer) = state.wl_pointer.take() { + wl_pointer.release(); + } + if let Some(wl_keyboard) = state.wl_keyboard.take() { + wl_keyboard.release(); + } + state.wl_seat.release(); + state.wl_seat = registry.bind::( + name, + wl_seat_version(version), + qh, + (), + ); } "wl_output" => { let output = registry.bind::(name, WL_OUTPUT_VERSION, qh, ()); - state.output_scales.insert(output.id(), 1); + state + .in_progress_outputs + .insert(output.id(), InProgressOutput::default()); } _ => {} }, - wl_registry::Event::GlobalRemove { name: _ } => {} + wl_registry::Event::GlobalRemove { name: _ } => { + // TODO: handle global removal + } _ => {} } } @@ -667,7 +757,7 @@ impl Dispatch for WaylandClientStatePtr { event: wl_callback::Event, surface_id: &ObjectId, _: &Connection, - qh: &QueueHandle, + _: &QueueHandle, ) { let client = state.get_client(); let mut state = client.borrow_mut(); @@ -677,7 +767,7 @@ impl Dispatch for WaylandClientStatePtr { drop(state); match event { - wl_callback::Event::Done { callback_data } => { + wl_callback::Event::Done { .. } => { window.frame(true); } _ => {} @@ -707,10 +797,10 @@ impl Dispatch for WaylandClientStatePtr { let Some(window) = get_window(&mut state, &surface.id()) else { return; }; - let scales = state.output_scales.clone(); + let outputs = state.outputs.clone(); drop(state); - window.handle_surface_event(event, scales); + window.handle_surface_event(event, outputs); } } @@ -726,13 +816,25 @@ impl Dispatch for WaylandClientStatePtr { let mut client = this.get_client(); let mut state = client.borrow_mut(); - let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else { + let Some(mut in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else { return; }; match event { wl_output::Event::Scale { factor } => { - *output_scale = factor; + in_progress_output.scale = Some(factor); + } + wl_output::Event::Geometry { x, y, .. } => { + in_progress_output.position = Some(point(DevicePixels(x), DevicePixels(y))) + } + wl_output::Event::Mode { width, height, .. } => { + in_progress_output.size = Some(size(DevicePixels(width), DevicePixels(height))) + } + wl_output::Event::Done => { + if let Some(complete) = in_progress_output.complete() { + state.outputs.insert(output.id(), complete); + } + state.in_progress_outputs.remove(&output.id()); } _ => {} } @@ -742,7 +844,7 @@ impl Dispatch for WaylandClientStatePtr { impl Dispatch for WaylandClientStatePtr { fn event( state: &mut Self, - xdg_surface: &xdg_surface::XdgSurface, + _: &xdg_surface::XdgSurface, event: xdg_surface::Event, surface_id: &ObjectId, _: &Connection, @@ -761,7 +863,7 @@ impl Dispatch for WaylandClientStatePtr { impl Dispatch for WaylandClientStatePtr { fn event( this: &mut Self, - xdg_toplevel: &xdg_toplevel::XdgToplevel, + _: &xdg_toplevel::XdgToplevel, event: ::Event, surface_id: &ObjectId, _: &Connection, @@ -824,8 +926,8 @@ impl Dispatch for WaylandClientStatePtr { state: &mut Self, seat: &wl_seat::WlSeat, event: wl_seat::Event, - data: &(), - conn: &Connection, + _: &(), + _: &Connection, qh: &QueueHandle, ) { if let wl_seat::Event::Capabilities { @@ -835,12 +937,19 @@ impl Dispatch for WaylandClientStatePtr { let client = state.get_client(); let mut state = client.borrow_mut(); if capabilities.contains(wl_seat::Capability::Keyboard) { - seat.get_keyboard(qh, ()); + let keyboard = seat.get_keyboard(qh, ()); + state.text_input = state .globals .text_input_manager .as_ref() .map(|text_input_manager| text_input_manager.get_text_input(&seat, qh, ())); + + if let Some(wl_keyboard) = &state.wl_keyboard { + wl_keyboard.release(); + } + + state.wl_keyboard = Some(keyboard); } if capabilities.contains(wl_seat::Capability::Pointer) { let pointer = seat.get_pointer(qh, ()); @@ -849,6 +958,11 @@ impl Dispatch for WaylandClientStatePtr { .cursor_shape_manager .as_ref() .map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ())); + + if let Some(wl_pointer) = &state.wl_pointer { + wl_pointer.release(); + } + state.wl_pointer = Some(pointer); } } @@ -858,11 +972,11 @@ impl Dispatch for WaylandClientStatePtr { impl Dispatch for WaylandClientStatePtr { fn event( this: &mut Self, - keyboard: &wl_keyboard::WlKeyboard, + _: &wl_keyboard::WlKeyboard, event: wl_keyboard::Event, - data: &(), - conn: &Connection, - qh: &QueueHandle, + _: &(), + _: &Connection, + _: &QueueHandle, ) { let mut client = this.get_client(); let mut state = client.borrow_mut(); @@ -1018,8 +1132,8 @@ impl Dispatch for WaylandClientStatePtr { state.compose_state = Some(compose); } let input = PlatformInput::KeyDown(KeyDownEvent { - keystroke: keystroke, - is_held: false, // todo(linux) + keystroke: keystroke.clone(), + is_held: false, }); state.repeat.current_id += 1; @@ -1030,8 +1144,11 @@ impl Dispatch for WaylandClientStatePtr { state .loop_handle .insert_source(Timer::from_duration(state.repeat.delay), { - let input = input.clone(); - move |event, _metadata, this| { + let input = PlatformInput::KeyDown(KeyDownEvent { + keystroke, + is_held: true, + }); + move |_event, _metadata, this| { let mut client = this.get_client(); let mut state = client.borrow_mut(); let is_repeating = id == state.repeat.current_id @@ -1080,18 +1197,18 @@ impl Dispatch for WaylandClientStatePtr { this: &mut Self, text_input: &zwp_text_input_v3::ZwpTextInputV3, event: ::Event, - data: &(), - conn: &Connection, - qhandle: &QueueHandle, + _: &(), + _: &Connection, + _: &QueueHandle, ) { let client = this.get_client(); let mut state = client.borrow_mut(); match event { - zwp_text_input_v3::Event::Enter { surface } => { + zwp_text_input_v3::Event::Enter { .. } => { drop(state); this.enable_ime(); } - zwp_text_input_v3::Event::Leave { surface } => { + zwp_text_input_v3::Event::Leave { .. } => { drop(state); this.disable_ime(); } @@ -1119,11 +1236,7 @@ impl Dispatch for WaylandClientStatePtr { } } } - zwp_text_input_v3::Event::PreeditString { - text, - cursor_begin, - cursor_end, - } => { + zwp_text_input_v3::Event::PreeditString { text, .. } => { state.composing = true; state.pre_edit_text = text; } @@ -1183,9 +1296,9 @@ impl Dispatch for WaylandClientStatePtr { this: &mut Self, wl_pointer: &wl_pointer::WlPointer, event: wl_pointer::Event, - data: &(), - conn: &Connection, - qh: &QueueHandle, + _: &(), + _: &Connection, + _: &QueueHandle, ) { let mut client = this.get_client(); let mut state = client.borrow_mut(); @@ -1220,7 +1333,7 @@ impl Dispatch for WaylandClientStatePtr { window.set_focused(true); } } - wl_pointer::Event::Leave { surface, .. } => { + wl_pointer::Event::Leave { .. } => { if let Some(focused_window) = state.mouse_focused_window.clone() { let input = PlatformInput::MouseExited(MouseExitEvent { position: state.mouse_location.unwrap(), @@ -1237,7 +1350,6 @@ impl Dispatch for WaylandClientStatePtr { } } wl_pointer::Event::Motion { - time, surface_x, surface_y, .. @@ -1280,7 +1392,6 @@ impl Dispatch for WaylandClientStatePtr { wl_pointer::ButtonState::Pressed => { if let Some(window) = state.keyboard_focused_window.clone() { if state.composing && state.text_input.is_some() { - let text_input = state.text_input.as_ref().unwrap(); drop(state); // text_input_v3 don't have something like a reset function this.disable_ime(); @@ -1351,7 +1462,6 @@ impl Dispatch for WaylandClientStatePtr { state.axis_source = axis_source; } wl_pointer::Event::Axis { - time, axis: WEnum::Value(axis), value, .. @@ -1364,13 +1474,10 @@ impl Dispatch for WaylandClientStatePtr { wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier, _ => 1.0, }; - let supports_relative_direction = - wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE; state.scroll_event_received = true; let scroll_delta = state .continuous_scroll_delta .get_or_insert(point(px(0.0), px(0.0))); - // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings let modifier = 3.0; match axis { wl_pointer::Axis::VerticalScroll => { diff --git a/crates/gpui/src/platform/linux/wayland/display.rs b/crates/gpui/src/platform/linux/wayland/display.rs index 2e61770d02..7017daed08 100644 --- a/crates/gpui/src/platform/linux/wayland/display.rs +++ b/crates/gpui/src/platform/linux/wayland/display.rs @@ -1,31 +1,36 @@ -use std::fmt::Debug; +use std::{ + fmt::Debug, + hash::{Hash, Hasher}, +}; use uuid::Uuid; +use wayland_backend::client::ObjectId; -use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Size}; +use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay}; -#[derive(Debug)] -pub(crate) struct WaylandDisplay {} +#[derive(Debug, Clone)] +pub(crate) struct WaylandDisplay { + /// The ID of the wl_output object + pub id: ObjectId, + pub bounds: Bounds, +} -impl PlatformDisplay for WaylandDisplay { - // todo(linux) - fn id(&self) -> DisplayId { - DisplayId(123) // return some fake data so it doesn't panic - } - - // todo(linux) - fn uuid(&self) -> anyhow::Result { - Ok(Uuid::from_bytes([0; 16])) // return some fake data so it doesn't panic - } - - // todo(linux) - fn bounds(&self) -> Bounds { - Bounds { - origin: Default::default(), - size: Size { - width: DevicePixels(1000), - height: DevicePixels(500), - }, - } // return some fake data so it doesn't panic +impl Hash for WaylandDisplay { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl PlatformDisplay for WaylandDisplay { + fn id(&self) -> DisplayId { + DisplayId(self.id.protocol_id()) + } + + fn uuid(&self) -> anyhow::Result { + Err(anyhow::anyhow!("Display UUID is not supported on Wayland")) + } + + fn bounds(&self) -> Bounds { + self.bounds } } diff --git a/crates/gpui/src/platform/linux/wayland/serial.rs b/crates/gpui/src/platform/linux/wayland/serial.rs index ff647615d1..eadc7a9ca9 100644 --- a/crates/gpui/src/platform/linux/wayland/serial.rs +++ b/crates/gpui/src/platform/linux/wayland/serial.rs @@ -1,5 +1,3 @@ -use std::time::Instant; - use collections::HashMap; #[derive(Debug, Hash, PartialEq, Eq)] @@ -14,15 +12,11 @@ pub(crate) enum SerialKind { #[derive(Debug)] struct SerialData { serial: u32, - time: Instant, } impl SerialData { fn new(value: u32) -> Self { - Self { - serial: value, - time: Instant::now(), - } + Self { serial: value } } } @@ -52,41 +46,4 @@ impl SerialTracker { .map(|serial_data| serial_data.serial) .unwrap_or(0) } - - /// Returns the newest serial of any of the provided [`SerialKind`] - pub fn get_newest_of(&self, kinds: &[SerialKind]) -> u32 { - kinds - .iter() - .filter_map(|kind| self.serials.get(&kind)) - .max_by_key(|serial_data| serial_data.time) - .map(|serial_data| serial_data.serial) - .unwrap_or(0) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_serial_tracker() { - let mut tracker = SerialTracker::new(); - - tracker.update(SerialKind::KeyPress, 100); - tracker.update(SerialKind::MousePress, 50); - tracker.update(SerialKind::MouseEnter, 300); - - assert_eq!( - tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]), - 50 - ); - assert_eq!(tracker.get(SerialKind::DataDevice), 0); - - tracker.update(SerialKind::KeyPress, 2000); - assert_eq!(tracker.get(SerialKind::KeyPress), 2000); - assert_eq!( - tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]), - 2000 - ); - } } diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index b1f2c5f34b..4b2f41e938 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -1,27 +1,24 @@ -use std::any::Any; use std::cell::{Ref, RefCell, RefMut}; use std::ffi::c_void; use std::num::NonZeroU32; -use std::ops::{Deref, Range}; use std::ptr::NonNull; -use std::rc::{Rc, Weak}; +use std::rc::Rc; use std::sync::Arc; use blade_graphics as gpu; -use collections::{HashMap, HashSet}; +use collections::HashMap; use futures::channel::oneshot::Receiver; -use parking_lot::Mutex; + use raw_window_handle as rwh; use wayland_backend::client::ObjectId; -use wayland_client::protocol::wl_region::WlRegion; use wayland_client::WEnum; use wayland_client::{protocol::wl_surface, Proxy}; use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1; use wayland_protocols::wp::viewporter::client::wp_viewport; use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1; use wayland_protocols::xdg::shell::client::xdg_surface; -use wayland_protocols::xdg::shell::client::xdg_toplevel::{self, WmCapabilities}; -use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager}; +use wayland_protocols::xdg::shell::client::xdg_toplevel::{self}; +use wayland_protocols_plasma::blur::client::org_kde_kwin_blur; use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig}; use crate::platform::linux::wayland::display::WaylandDisplay; @@ -29,9 +26,9 @@ use crate::platform::linux::wayland::serial::SerialKind; use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow}; use crate::scene::Scene; use crate::{ - px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput, - Point, PromptLevel, Size, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowParams, + px, size, AnyWindowHandle, Bounds, DevicePixels, Globals, Modifiers, Output, Pixels, + PlatformDisplay, PlatformInput, Point, PromptLevel, Size, WaylandClientStatePtr, + WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowParams, }; #[derive(Default)] @@ -75,7 +72,8 @@ pub struct WaylandWindowState { blur: Option, toplevel: xdg_toplevel::XdgToplevel, viewport: Option, - outputs: HashSet, + outputs: HashMap, + display: Option<(ObjectId, Output)>, globals: Globals, renderer: BladeRenderer, bounds: Bounds, @@ -86,7 +84,8 @@ pub struct WaylandWindowState { restore_bounds: Bounds, maximized: bool, client: WaylandClientStatePtr, - callbacks: Callbacks, + handle: AnyWindowHandle, + active: bool, } #[derive(Clone)] @@ -98,6 +97,7 @@ pub struct WaylandWindowStatePtr { impl WaylandWindowState { #[allow(clippy::too_many_arguments)] pub(crate) fn new( + handle: AnyWindowHandle, surface: wl_surface::WlSurface, xdg_surface: xdg_surface::XdgSurface, toplevel: xdg_toplevel::XdgToplevel, @@ -150,7 +150,8 @@ impl WaylandWindowState { toplevel, viewport, globals, - outputs: HashSet::default(), + outputs: HashMap::default(), + display: None, renderer: BladeRenderer::new(gpu, config), bounds, scale: 1.0, @@ -159,9 +160,10 @@ impl WaylandWindowState { fullscreen: false, restore_bounds: Bounds::default(), maximized: false, - callbacks: Callbacks::default(), client, appearance, + handle, + active: false, } } } @@ -217,6 +219,7 @@ impl WaylandWindow { } pub fn new( + handle: AnyWindowHandle, globals: Globals, client: WaylandClientStatePtr, params: WindowParams, @@ -253,6 +256,7 @@ impl WaylandWindow { let this = Self(WaylandWindowStatePtr { state: Rc::new(RefCell::new(WaylandWindowState::new( + handle, surface.clone(), xdg_surface, toplevel, @@ -274,6 +278,10 @@ impl WaylandWindow { } impl WaylandWindowStatePtr { + pub fn handle(&self) -> AnyWindowHandle { + self.state.borrow().handle + } + pub fn surface(&self) -> wl_surface::WlSurface { self.state.borrow().surface.clone() } @@ -381,7 +389,7 @@ impl WaylandWindowStatePtr { pub fn handle_surface_event( &self, event: wl_surface::Event, - output_scales: HashMap, + outputs: HashMap, ) { let mut state = self.state.borrow_mut(); @@ -396,15 +404,15 @@ impl WaylandWindowStatePtr { if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE { return; } + let id = output.id(); - state.outputs.insert(output.id()); + let Some(output) = outputs.get(&id) else { + return; + }; - let mut scale = 1; - for output in state.outputs.iter() { - if let Some(s) = output_scales.get(output) { - scale = scale.max(*s) - } - } + state.outputs.insert(id, *output); + + let scale = primary_output_scale(&mut state); state.surface.set_buffer_scale(scale); drop(state); @@ -418,12 +426,7 @@ impl WaylandWindowStatePtr { state.outputs.remove(&output.id()); - let mut scale = 1; - for output in state.outputs.iter() { - if let Some(s) = output_scales.get(output) { - scale = scale.max(*s) - } - } + let scale = primary_output_scale(&mut state); state.surface.set_buffer_scale(scale); drop(state); @@ -577,6 +580,7 @@ impl WaylandWindowStatePtr { } pub fn set_focused(&self, focus: bool) { + self.state.borrow_mut().active = focus; if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change { fun(focus); } @@ -592,6 +596,23 @@ impl WaylandWindowStatePtr { } } +fn primary_output_scale(state: &mut RefMut) -> i32 { + let mut scale = 1; + let mut current_output = state.display.take(); + for (id, output) in state.outputs.iter() { + if let Some((_, output_data)) = ¤t_output { + if output.scale > output_data.scale { + current_output = Some((id.clone(), *output)); + } + } else { + current_output = Some((id.clone(), *output)); + } + scale = scale.max(output.scale); + } + state.display = current_output; + scale +} + impl rwh::HasWindowHandle for WaylandWindow { fn window_handle(&self) -> Result, rwh::HandleError> { unimplemented!() @@ -612,8 +633,6 @@ impl PlatformWindow for WaylandWindow { self.borrow().maximized } - // todo(linux) - // check if it is right fn window_bounds(&self) -> WindowBounds { let state = self.borrow(); if state.fullscreen { @@ -641,19 +660,26 @@ impl PlatformWindow for WaylandWindow { self.borrow().appearance } - // todo(linux) - fn display(&self) -> Rc { - Rc::new(WaylandDisplay {}) + fn display(&self) -> Option> { + self.borrow().display.as_ref().map(|(id, display)| { + Rc::new(WaylandDisplay { + id: id.clone(), + bounds: display.bounds, + }) as Rc + }) } - // todo(linux) fn mouse_position(&self) -> Point { - Point::default() + self.borrow() + .client + .get_client() + .borrow() + .mouse_location + .unwrap_or_default() } - // todo(linux) fn modifiers(&self) -> Modifiers { - crate::Modifiers::default() + self.borrow().client.get_client().borrow().modifiers } fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { @@ -666,21 +692,20 @@ impl PlatformWindow for WaylandWindow { fn prompt( &self, - level: PromptLevel, - msg: &str, - detail: Option<&str>, - answers: &[&str], + _level: PromptLevel, + _msg: &str, + _detail: Option<&str>, + _answers: &[&str], ) -> Option> { None } fn activate(&self) { - // todo(linux) + log::info!("Wayland does not support this API"); } - // todo(linux) fn is_active(&self) -> bool { - false + self.borrow().active } fn set_title(&mut self, title: &str) { @@ -712,8 +737,8 @@ impl PlatformWindow for WaylandWindow { } if let Some(ref blur_manager) = state.globals.blur_manager { - if (background_appearance == WindowBackgroundAppearance::Blurred) { - if (state.blur.is_none()) { + if background_appearance == WindowBackgroundAppearance::Blurred { + if state.blur.is_none() { let blur = blur_manager.create(&state.surface, &state.globals.qh, ()); blur.set_region(Some(®ion)); state.blur = Some(blur); @@ -731,12 +756,12 @@ impl PlatformWindow for WaylandWindow { region.destroy(); } - fn set_edited(&mut self, edited: bool) { - // todo(linux) + fn set_edited(&mut self, _edited: bool) { + log::info!("ignoring macOS specific set_edited"); } fn show_character_palette(&self) { - // todo(linux) + log::info!("ignoring macOS specific show_character_palette"); } fn minimize(&self) { diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index b61a07114f..ae06ec8cc7 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::ffi::OsString; use std::ops::Deref; use std::rc::{Rc, Weak}; -use std::sync::OnceLock; use std::time::{Duration, Instant}; use calloop::generic::{FdWrapper, Generic}; @@ -11,30 +10,29 @@ use calloop::{channel, EventLoop, LoopHandle, RegistrationToken}; use collections::HashMap; use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext}; use copypasta::ClipboardProvider; -use parking_lot::Mutex; use util::ResultExt; use x11rb::connection::{Connection, RequestConnection}; use x11rb::cursor; use x11rb::errors::ConnectionError; use x11rb::protocol::randr::ConnectionExt as _; -use x11rb::protocol::xinput::{ConnectionExt, ScrollClass}; +use x11rb::protocol::xinput::ConnectionExt; use x11rb::protocol::xkb::ConnectionExt as _; use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _}; use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event}; use x11rb::resource_manager::Database; use x11rb::xcb_ffi::XCBConnection; use xim::{x11rb::X11rbClient, Client}; -use xim::{AHashMap, AttributeName, ClientHandler, InputStyle}; +use xim::{AttributeName, InputStyle}; use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION}; use xkbcommon::xkb as xkbc; use crate::platform::linux::LinuxClient; -use crate::platform::{LinuxCommon, PlatformWindow, WaylandClientState}; +use crate::platform::{LinuxCommon, PlatformWindow}; use crate::{ modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, - ForegroundExecutor, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, - PlatformInput, Point, ScrollDelta, Size, TouchPhase, WindowAppearance, WindowParams, X11Window, + Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, Point, + ScrollDelta, Size, TouchPhase, WindowParams, X11Window, }; use super::{ @@ -54,6 +52,12 @@ pub(crate) struct WindowRef { refresh_event_token: RegistrationToken, } +impl WindowRef { + pub fn handle(&self) -> AnyWindowHandle { + self.window.state.borrow().handle + } +} + impl Deref for WindowRef { type Target = X11WindowStatePtr; @@ -104,13 +108,14 @@ pub struct X11ClientState { pub(crate) xcb_connection: Rc, pub(crate) x_root_index: usize, - pub(crate) resource_database: Database, + pub(crate) _resource_database: Database, pub(crate) atoms: XcbAtoms, pub(crate) windows: HashMap, pub(crate) focused_window: Option, pub(crate) xkb: xkbc::State, pub(crate) ximc: Option>>, pub(crate) xim_handler: Option, + pub modifiers: Modifiers, pub(crate) compose_state: xkbc::compose::State, pub(crate) pre_edit_text: Option, @@ -159,11 +164,13 @@ impl X11Client { let handle = event_loop.handle(); - handle.insert_source(main_receiver, |event, _, _: &mut X11Client| { - if let calloop::channel::Event::Msg(runnable) = event { - runnable.run(); - } - }); + handle + .insert_source(main_receiver, |event, _, _: &mut X11Client| { + if let calloop::channel::Event::Msg(runnable) = event { + runnable.run(); + } + }) + .unwrap(); let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap(); xcb_connection @@ -248,23 +255,7 @@ impl X11Client { xkbc::compose::State::new(&table, xkbc::compose::STATE_NO_FLAGS) }; - let screen = xcb_connection.setup().roots.get(x_root_index).unwrap(); - - // Values from `Database::GET_RESOURCE_DATABASE` - let resource_manager = xcb_connection - .get_property( - false, - screen.root, - xproto::AtomEnum::RESOURCE_MANAGER, - xproto::AtomEnum::STRING, - 0, - 100_000_000, - ) - .unwrap(); - let resource_manager = resource_manager.reply().unwrap(); - - // todo(linux): read hostname - let resource_database = Database::new_from_default(&resource_manager, "HOSTNAME".into()); + let resource_database = x11rb::resource_manager::new_from_default(&xcb_connection).unwrap(); let scale_factor = resource_database .get_value("Xft.dpi", "Xft.dpi") @@ -345,7 +336,7 @@ impl X11Client { .insert_source(xim_rx, { move |chan_event, _, client| match chan_event { channel::Event::Msg(xim_event) => { - match (xim_event) { + match xim_event { XimCallbackEvent::XimXEvent(event) => { client.handle_event(event); } @@ -363,18 +354,21 @@ impl X11Client { } }) .expect("Failed to initialize XIM event source"); - handle.insert_source(XDPEventSource::new(&common.background_executor), { - move |event, _, client| match event { - XDPEvent::WindowAppearance(appearance) => { - client.with_common(|common| common.appearance = appearance); - for (_, window) in &mut client.0.borrow_mut().windows { - window.window.set_appearance(appearance); + handle + .insert_source(XDPEventSource::new(&common.background_executor), { + move |event, _, client| match event { + XDPEvent::WindowAppearance(appearance) => { + client.with_common(|common| common.appearance = appearance); + for (_, window) in &mut client.0.borrow_mut().windows { + window.window.set_appearance(appearance); + } } } - } - }); + }) + .unwrap(); X11Client(Rc::new(RefCell::new(X11ClientState { + modifiers: Modifiers::default(), event_loop: Some(event_loop), loop_handle: handle, common, @@ -385,7 +379,7 @@ impl X11Client { xcb_connection, x_root_index, - resource_database, + _resource_database: resource_database, atoms, windows: HashMap::default(), focused_window: None, @@ -446,7 +440,8 @@ impl X11Client { }); } } - ximc.create_ic(xim_handler.im_id, ic_attributes.build()); + ximc.create_ic(xim_handler.im_id, ic_attributes.build()) + .ok(); state = self.0.borrow_mut(); state.xim_handler = Some(xim_handler); state.ximc = Some(ximc); @@ -457,7 +452,7 @@ impl X11Client { state.composing = false; if let Some(mut ximc) = state.ximc.take() { let xim_handler = state.xim_handler.as_ref().unwrap(); - ximc.destroy_ic(xim_handler.im_id, xim_handler.ic_id); + ximc.destroy_ic(xim_handler.im_id, xim_handler.ic_id).ok(); state.ximc = Some(ximc); } } @@ -535,6 +530,7 @@ impl X11Client { ); let modifiers = Modifiers::from_xkb(&state.xkb); let focused_window_id = state.focused_window?; + state.modifiers = modifiers; drop(state); let focused_window = self.get_window(focused_window_id)?; @@ -547,6 +543,8 @@ impl X11Client { let mut state = self.0.borrow_mut(); let modifiers = modifiers_from_state(event.state); + state.modifiers = modifiers; + let keystroke = { let code = event.detail.into(); let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code); @@ -600,6 +598,8 @@ impl X11Client { let mut state = self.0.borrow_mut(); let modifiers = modifiers_from_state(event.state); + state.modifiers = modifiers; + let keystroke = { let code = event.detail.into(); let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code); @@ -618,6 +618,8 @@ impl X11Client { let mut state = self.0.borrow_mut(); let modifiers = modifiers_from_xinput_info(event.mods); + state.modifiers = modifiers; + let position = point( px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), @@ -664,8 +666,10 @@ impl X11Client { } Event::XinputButtonRelease(event) => { let window = self.get_window(event.event)?; - let state = self.0.borrow(); + let mut state = self.0.borrow_mut(); let modifiers = modifiers_from_xinput_info(event.mods); + state.modifiers = modifiers; + let position = point( px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), @@ -683,14 +687,15 @@ impl X11Client { } Event::XinputMotion(event) => { let window = self.get_window(event.event)?; - let state = self.0.borrow(); + let mut state = self.0.borrow_mut(); let pressed_button = pressed_button_from_mask(event.button_mask[0]); let position = point( px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); - drop(state); let modifiers = modifiers_from_xinput_info(event.mods); + state.modifiers = modifiers; + drop(state); let axisvalues = event .axisvalues @@ -766,13 +771,14 @@ impl X11Client { self.0.borrow_mut().scroll_y = None; let window = self.get_window(event.event)?; - let state = self.0.borrow(); + let mut state = self.0.borrow_mut(); let pressed_button = pressed_button_from_mask(event.buttons[0]); let position = point( px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); let modifiers = modifiers_from_xinput_info(event.mods); + state.modifiers = modifiers; drop(state); window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent { @@ -855,7 +861,8 @@ impl X11Client { ); }) .build(); - ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes); + ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes) + .ok(); } let mut state = self.0.borrow_mut(); state.ximc = Some(ximc); @@ -904,13 +911,14 @@ impl LinuxClient for X11Client { fn open_window( &self, - _handle: AnyWindowHandle, + handle: AnyWindowHandle, params: WindowParams, ) -> Box { let mut state = self.0.borrow_mut(); let x_window = state.xcb_connection.generate_id().unwrap(); let window = X11Window::new( + handle, X11ClientStatePtr(Rc::downgrade(&self.0)), state.common.foreground_executor.clone(), params, @@ -1034,11 +1042,11 @@ impl LinuxClient for X11Client { } fn write_to_primary(&self, item: crate::ClipboardItem) { - self.0.borrow_mut().primary.set_contents(item.text); + self.0.borrow_mut().primary.set_contents(item.text).ok(); } fn write_to_clipboard(&self, item: crate::ClipboardItem) { - self.0.borrow_mut().clipboard.set_contents(item.text); + self.0.borrow_mut().clipboard.set_contents(item.text).ok(); } fn read_from_primary(&self) -> Option { @@ -1075,6 +1083,16 @@ impl LinuxClient for X11Client { event_loop.run(None, &mut self.clone(), |_| {}).log_err(); } + + fn active_window(&self) -> Option { + let state = self.0.borrow(); + state.focused_window.and_then(|focused_window| { + state + .windows + .get(&focused_window) + .map(|window| window.handle()) + }) + } } // Adatpted from: diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 1e628f2806..8181aa0d62 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -1,40 +1,29 @@ -// todo(linux): remove -#![allow(unused)] - use crate::{ platform::blade::{BladeRenderer, BladeSurfaceConfig}, - size, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, Platform, PlatformAtlas, - PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel, - Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowOptions, - WindowParams, X11Client, X11ClientState, X11ClientStatePtr, + size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, + PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, + PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds, + WindowParams, X11ClientStatePtr, }; use blade_graphics as gpu; -use parking_lot::Mutex; use raw_window_handle as rwh; use util::ResultExt; use x11rb::{ - connection::{Connection as _, RequestConnection as _}, + connection::Connection, protocol::{ - render, xinput::{self, ConnectionExt as _}, xproto::{ - self, Atom, ClientMessageEvent, ConnectionExt as _, CreateWindowAux, EventMask, - TranslateCoordinatesReply, + self, ClientMessageEvent, ConnectionExt as _, EventMask, TranslateCoordinatesReply, }, }, - resource_manager::Database, wrapper::ConnectionExt as _, xcb_ffi::XCBConnection, }; -use std::ops::Deref; -use std::rc::Weak; use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashMap, + cell::RefCell, ffi::c_void, - iter::Zip, mem, num::NonZeroU32, ops::Div, @@ -165,29 +154,29 @@ pub struct Callbacks { appearance_changed: Option>, } -pub(crate) struct X11WindowState { +pub struct X11WindowState { client: X11ClientStatePtr, executor: ForegroundExecutor, atoms: XcbAtoms, x_root_window: xproto::Window, - raw: RawWindow, + _raw: RawWindow, bounds: Bounds, scale_factor: f32, renderer: BladeRenderer, display: Rc, input_handler: Option, appearance: WindowAppearance, + pub handle: AnyWindowHandle, } #[derive(Clone)] pub(crate) struct X11WindowStatePtr { - pub(crate) state: Rc>, + pub state: Rc>, pub(crate) callbacks: Rc>, xcb_connection: Rc, x_window: xproto::Window, } -// todo(linux): Remove other RawWindowHandle implementation impl rwh::HasWindowHandle for RawWindow { fn window_handle(&self) -> Result { let non_zero = NonZeroU32::new(self.window_id).unwrap(); @@ -218,6 +207,7 @@ impl rwh::HasDisplayHandle for X11Window { impl X11WindowState { #[allow(clippy::too_many_arguments)] pub fn new( + handle: AnyWindowHandle, client: X11ClientStatePtr, executor: ForegroundExecutor, params: WindowParams, @@ -372,7 +362,7 @@ impl X11WindowState { client, executor, display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()), - raw, + _raw: raw, x_root_window: visual_set.root, bounds: params.bounds.map(|v| v.0), scale_factor, @@ -380,6 +370,7 @@ impl X11WindowState { atoms: *atoms, input_handler: None, appearance, + handle, } } @@ -420,14 +411,15 @@ impl Drop for X11Window { } enum WmHintPropertyState { - Remove = 0, - Add = 1, + // Remove = 0, + // Add = 1, Toggle = 2, } impl X11Window { #[allow(clippy::too_many_arguments)] pub fn new( + handle: AnyWindowHandle, client: X11ClientStatePtr, executor: ForegroundExecutor, params: WindowParams, @@ -440,6 +432,7 @@ impl X11Window { ) -> Self { Self(X11WindowStatePtr { state: Rc::new(RefCell::new(X11WindowState::new( + handle, client, executor, params, @@ -622,8 +615,6 @@ impl X11WindowStatePtr { let mut state = self.state.borrow_mut(); let old_bounds = mem::replace(&mut state.bounds, bounds); do_move = old_bounds.origin != bounds.origin; - // todo(linux): use normal GPUI types here, refactor out the double - // viewport check and extra casts ( ) let gpu_size = query_render_extent(&self.xcb_connection, self.x_window); if state.renderer.viewport_size() != gpu_size { state @@ -698,8 +689,8 @@ impl PlatformWindow for X11Window { self.0.state.borrow().appearance } - fn display(&self) -> Rc { - self.0.state.borrow().display.clone() + fn display(&self) -> Option> { + Some(self.0.state.borrow().display.clone()) } fn mouse_position(&self) -> Point { @@ -713,9 +704,15 @@ impl PlatformWindow for X11Window { Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into()) } - // todo(linux) fn modifiers(&self) -> Modifiers { - Modifiers::default() + self.0 + .state + .borrow() + .client + .0 + .upgrade() + .map(|ref_cell| ref_cell.borrow().modifiers) + .unwrap_or_default() } fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { @@ -792,8 +789,9 @@ impl PlatformWindow for X11Window { .unwrap(); } - // todo(linux) - fn set_edited(&mut self, edited: bool) {} + fn set_edited(&mut self, _edited: bool) { + log::info!("ignoring macOS specific set_edited"); + } fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) { let mut inner = self.0.state.borrow_mut(); @@ -801,14 +799,8 @@ impl PlatformWindow for X11Window { inner.renderer.update_transparency(transparent); } - // todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS, - // but it looks like the equivalent for Linux is GTK specific: - // - // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html - // - // This API might need to change, or we might need to build an emoji picker into GPUI fn show_character_palette(&self) { - unimplemented!() + log::info!("ignoring macOS specific show_character_palette"); } fn minimize(&self) { diff --git a/crates/gpui/src/platform/linux/x11/xim_handler.rs b/crates/gpui/src/platform/linux/x11/xim_handler.rs index b05cf5b0c3..5900a19020 100644 --- a/crates/gpui/src/platform/linux/x11/xim_handler.rs +++ b/crates/gpui/src/platform/linux/x11/xim_handler.rs @@ -1,13 +1,9 @@ -use std::cell::RefCell; use std::default::Default; -use std::rc::Rc; use calloop::channel; use x11rb::protocol::{xproto, Event}; -use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle, Point}; - -use crate::{Keystroke, PlatformInput, X11ClientState}; +use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle}; pub enum XimCallbackEvent { XimXEvent(x11rb::protocol::Event), @@ -84,10 +80,12 @@ impl> ClientHandler for XimHandler _input_context_id: u16, text: &str, ) -> Result<(), ClientError> { - self.xim_tx.send(XimCallbackEvent::XimCommitEvent( - self.window, - String::from(text), - )); + self.xim_tx + .send(XimCallbackEvent::XimCommitEvent( + self.window, + String::from(text), + )) + .ok(); Ok(()) } @@ -99,14 +97,16 @@ impl> ClientHandler for XimHandler _flag: xim::ForwardEventFlag, xev: C::XEvent, ) -> Result<(), ClientError> { - match (xev.response_type) { + match xev.response_type { x11rb::protocol::xproto::KEY_PRESS_EVENT => { self.xim_tx - .send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev))); + .send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev))) + .ok(); } x11rb::protocol::xproto::KEY_RELEASE_EVENT => { self.xim_tx - .send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev))); + .send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev))) + .ok(); } _ => {} } @@ -145,10 +145,12 @@ impl> ClientHandler for XimHandler // XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified, // but interchangeable as above // Currently there's no way to support these. - let mark_range = self.xim_tx.send(XimCallbackEvent::XimPreeditEvent( - self.window, - String::from(preedit_string), - )); + self.xim_tx + .send(XimCallbackEvent::XimPreeditEvent( + self.window, + String::from(preedit_string), + )) + .ok(); Ok(()) } } diff --git a/crates/gpui/src/platform/linux/xdg_desktop_portal.rs b/crates/gpui/src/platform/linux/xdg_desktop_portal.rs index 6711572793..ef0430de8a 100644 --- a/crates/gpui/src/platform/linux/xdg_desktop_portal.rs +++ b/crates/gpui/src/platform/linux/xdg_desktop_portal.rs @@ -6,7 +6,6 @@ use std::future::Future; use ashpd::desktop::settings::{ColorScheme, Settings}; use calloop::channel::{Channel, Sender}; use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory}; -use parking_lot::Mutex; use smol::stream::StreamExt; use util::ResultExt; @@ -115,6 +114,7 @@ impl WindowAppearance { } } + #[cfg_attr(target_os = "linux", allow(dead_code))] fn set_native(&mut self, cs: ColorScheme) { *self = Self::from_native(cs); } diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 1ce29ba43c..81aacb3369 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -826,9 +826,6 @@ impl Platform for MacPlatform { CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor], CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], CursorStyle::ResizeRow => msg_send![class!(NSCursor), resizeUpDownCursor], - CursorStyle::DisappearingItem => { - msg_send![class!(NSCursor), disappearingItemCursor] - } CursorStyle::IBeamCursorForVerticalLayout => { msg_send![class!(NSCursor), IBeamCursorForVerticalLayout] } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index c4b8cb9f8c..d34a25b720 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -799,7 +799,7 @@ impl PlatformWindow for MacWindow { } } - fn display(&self) -> Rc { + fn display(&self) -> Option> { unsafe { let screen = self.0.lock().native_window.screen(); let device_description: id = msg_send![screen, deviceDescription]; @@ -810,7 +810,7 @@ impl PlatformWindow for MacWindow { let screen_number: u32 = msg_send![screen_number, unsignedIntValue]; - Rc::new(MacDisplay(screen_number)) + Some(Rc::new(MacDisplay(screen_number))) } } diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index c602335928..d17739239e 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -1,7 +1,6 @@ mod dispatcher; mod display; mod platform; -mod text_system; mod window; pub(crate) use dispatcher::*; diff --git a/crates/gpui/src/platform/test/text_system.rs b/crates/gpui/src/platform/test/text_system.rs deleted file mode 100644 index bec559a0f7..0000000000 --- a/crates/gpui/src/platform/test/text_system.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{ - Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, GlyphId, LineLayout, Pixels, - PlatformTextSystem, RenderGlyphParams, Size, -}; -use anyhow::Result; -use std::borrow::Cow; - -pub(crate) struct TestTextSystem {} - -// todo(linux) -#[allow(unused)] -impl PlatformTextSystem for TestTextSystem { - fn add_fonts(&self, fonts: Vec>) -> Result<()> { - unimplemented!() - } - fn all_font_names(&self) -> Vec { - unimplemented!() - } - fn all_font_families(&self) -> Vec { - unimplemented!() - } - fn font_id(&self, descriptor: &Font) -> Result { - unimplemented!() - } - fn font_metrics(&self, font_id: FontId) -> FontMetrics { - unimplemented!() - } - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - unimplemented!() - } - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - unimplemented!() - } - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - unimplemented!() - } - fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - unimplemented!() - } - fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)> { - unimplemented!() - } - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { - unimplemented!() - } -} diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index cd7196a291..822fdd714b 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -132,8 +132,8 @@ impl PlatformWindow for TestWindow { WindowAppearance::Light } - fn display(&self) -> std::rc::Rc { - self.0.lock().display.clone() + fn display(&self) -> Option> { + Some(self.0.lock().display.clone()) } fn mouse_position(&self) -> Point { diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 26fe9d63ce..21230d0356 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -379,8 +379,8 @@ impl PlatformWindow for WindowsWindow { WindowAppearance::Dark } - fn display(&self) -> Rc { - Rc::new(self.0.state.borrow().display) + fn display(&self) -> Option> { + Some(Rc::new(self.0.state.borrow().display)) } fn mouse_position(&self) -> Point { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7c2ce490d4..7024b666cf 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -488,7 +488,7 @@ pub struct Window { pub(crate) handle: AnyWindowHandle, pub(crate) removed: bool, pub(crate) platform_window: Box, - display_id: DisplayId, + display_id: Option, sprite_atlas: Arc, text_system: Arc, rem_size: Pixels, @@ -634,7 +634,7 @@ impl Window { window_background, }, ); - let display_id = platform_window.display().id(); + let display_id = platform_window.display().map(|display| display.id()); let sprite_atlas = platform_window.sprite_atlas(); let mouse_position = platform_window.mouse_position(); let modifiers = platform_window.modifiers(); @@ -1099,7 +1099,12 @@ impl<'a> WindowContext<'a> { fn bounds_changed(&mut self) { self.window.scale_factor = self.window.platform_window.scale_factor(); self.window.viewport_size = self.window.platform_window.content_size(); - self.window.display_id = self.window.platform_window.display().id(); + self.window.display_id = self + .window + .platform_window + .display() + .map(|display| display.id()); + self.refresh(); self.window @@ -1191,7 +1196,7 @@ impl<'a> WindowContext<'a> { self.platform .displays() .into_iter() - .find(|display| display.id() == self.window.display_id) + .find(|display| Some(display.id()) == self.window.display_id) } /// Show the platform character palette. diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6150ca383f..c20a21e7b9 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -76,8 +76,11 @@ actions!( ); pub fn init(cx: &mut AppContext) { + #[cfg(target_os = "macos")] cx.on_action(|_: &Hide, cx| cx.hide()); + #[cfg(target_os = "macos")] cx.on_action(|_: &HideOthers, cx| cx.hide_other_apps()); + #[cfg(target_os = "macos")] cx.on_action(|_: &ShowAll, cx| cx.unhide_other_apps()); cx.on_action(quit); }