diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index 75c0fb107c..95bb18b61d 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -712,7 +712,7 @@ impl Size { /// assert_eq!(bounds.origin, origin); /// assert_eq!(bounds.size, size); /// ``` -#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] +#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq, Hash)] #[refineable(Debug)] #[repr(C)] pub struct Bounds { diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 464a8ada30..2dfd878755 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -139,8 +139,9 @@ impl Globals { } } -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] pub struct InProgressOutput { + name: Option, scale: Option, position: Option>, size: Option>, @@ -151,6 +152,7 @@ impl InProgressOutput { if let Some((position, size)) = self.position.zip(self.size) { let scale = self.scale.unwrap_or(1); Some(Output { + name: self.name.clone(), scale, bounds: Bounds::new(position, size), }) @@ -160,22 +162,13 @@ impl InProgressOutput { } } -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct Output { + pub name: Option, 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, @@ -337,7 +330,6 @@ impl Drop for WaylandClient { } const WL_DATA_DEVICE_MANAGER_VERSION: u32 = 3; -const WL_OUTPUT_VERSION: u32 = 2; fn wl_seat_version(version: u32) -> u32 { // We rely on the wl_pointer.frame event @@ -354,6 +346,20 @@ fn wl_seat_version(version: u32) -> u32 { version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION) } +fn wl_output_version(version: u32) -> u32 { + const WL_OUTPUT_MIN_VERSION: u32 = 2; + const WL_OUTPUT_MAX_VERSION: u32 = 4; + + if version < WL_OUTPUT_MIN_VERSION { + panic!( + "wl_output below required version: {} < {}", + version, WL_OUTPUT_MIN_VERSION + ); + } + + version.clamp(WL_OUTPUT_MIN_VERSION, WL_OUTPUT_MAX_VERSION) +} + impl WaylandClient { pub(crate) fn new() -> Self { let conn = Connection::connect_to_env().unwrap(); @@ -378,7 +384,7 @@ impl WaylandClient { "wl_output" => { let output = globals.registry().bind::( global.name, - WL_OUTPUT_VERSION, + wl_output_version(global.version), &qh, (), ); @@ -517,6 +523,7 @@ impl LinuxClient for WaylandClient { .map(|(id, output)| { Rc::new(WaylandDisplay { id: id.clone(), + name: output.name.clone(), bounds: output.bounds, }) as Rc }) @@ -532,6 +539,7 @@ impl LinuxClient for WaylandClient { (object_id.protocol_id() == id.0).then(|| { Rc::new(WaylandDisplay { id: object_id.clone(), + name: output.name.clone(), bounds: output.bounds, }) as Rc }) @@ -716,8 +724,12 @@ impl Dispatch for WaylandClientStat ); } "wl_output" => { - let output = - registry.bind::(name, WL_OUTPUT_VERSION, qh, ()); + let output = registry.bind::( + name, + wl_output_version(version), + qh, + (), + ); state .in_progress_outputs @@ -821,6 +833,9 @@ impl Dispatch for WaylandClientStatePtr { }; match event { + wl_output::Event::Name { name } => { + in_progress_output.name = Some(name); + } wl_output::Event::Scale { factor } => { in_progress_output.scale = Some(factor); } diff --git a/crates/gpui/src/platform/linux/wayland/display.rs b/crates/gpui/src/platform/linux/wayland/display.rs index 7017daed08..34a54ad133 100644 --- a/crates/gpui/src/platform/linux/wayland/display.rs +++ b/crates/gpui/src/platform/linux/wayland/display.rs @@ -12,6 +12,7 @@ use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay}; pub(crate) struct WaylandDisplay { /// The ID of the wl_output object pub id: ObjectId, + pub name: Option, pub bounds: Bounds, } @@ -27,7 +28,11 @@ impl PlatformDisplay for WaylandDisplay { } fn uuid(&self) -> anyhow::Result { - Err(anyhow::anyhow!("Display UUID is not supported on Wayland")) + if let Some(name) = &self.name { + Ok(Uuid::new_v5(&Uuid::NAMESPACE_DNS, name.as_bytes())) + } else { + Err(anyhow::anyhow!("Wayland display does not have a name")) + } } fn bounds(&self) -> Bounds { diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 4b2f41e938..972a32e0e4 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -81,8 +81,8 @@ pub struct WaylandWindowState { input_handler: Option, decoration_state: WaylandDecorationState, fullscreen: bool, - restore_bounds: Bounds, maximized: bool, + windowed_bounds: Bounds, client: WaylandClientStatePtr, handle: AnyWindowHandle, active: bool, @@ -158,8 +158,8 @@ impl WaylandWindowState { input_handler: None, decoration_state: WaylandDecorationState::Client, fullscreen: false, - restore_bounds: Bounds::default(), maximized: false, + windowed_bounds: options.bounds, client, appearance, handle, @@ -230,6 +230,7 @@ impl WaylandWindow { .wm_base .get_xdg_surface(&surface, &globals.qh, surface.id()); let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id()); + toplevel.set_min_size(200, 200); if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() { fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id()); @@ -348,23 +349,32 @@ impl WaylandWindowStatePtr { pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool { match event { xdg_toplevel::Event::Configure { - width, - height, + mut width, + mut height, states, } => { - let width = NonZeroU32::new(width as u32); - let height = NonZeroU32::new(height as u32); let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8)); let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8)); + let mut state = self.state.borrow_mut(); - state.maximized = maximized; + let got_unmaximized = state.maximized && !maximized; state.fullscreen = fullscreen; - if fullscreen || maximized { - state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32)); + state.maximized = maximized; + + if got_unmaximized { + width = state.windowed_bounds.size.width.0; + height = state.windowed_bounds.size.height.0; + } else if width != 0 && height != 0 && !fullscreen && !maximized { + state.windowed_bounds = Bounds { + origin: Point::default(), + size: size(width.into(), height.into()), + }; } + + let width = NonZeroU32::new(width as u32); + let height = NonZeroU32::new(height as u32); drop(state); self.resize(width, height); - self.set_fullscreen(fullscreen); false } @@ -393,49 +403,44 @@ impl WaylandWindowStatePtr { ) { let mut state = self.state.borrow_mut(); - // We use `WpFractionalScale` instead to set the scale if it's available - if state.globals.fractional_scale_manager.is_some() { - return; - } - match event { wl_surface::Event::Enter { output } => { - // We use `PreferredBufferScale` instead to set the scale if it's available - if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE { - return; - } let id = output.id(); let Some(output) = outputs.get(&id) else { return; }; - state.outputs.insert(id, *output); + state.outputs.insert(id, output.clone()); let scale = primary_output_scale(&mut state); - state.surface.set_buffer_scale(scale); - drop(state); - self.rescale(scale as f32); + // We use `PreferredBufferScale` instead to set the scale if it's available + if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE { + state.surface.set_buffer_scale(scale); + drop(state); + self.rescale(scale as f32); + } } wl_surface::Event::Leave { output } => { - // We use `PreferredBufferScale` instead to set the scale if it's available - if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE { - return; - } - state.outputs.remove(&output.id()); let scale = primary_output_scale(&mut state); - state.surface.set_buffer_scale(scale); - drop(state); - self.rescale(scale as f32); + // We use `PreferredBufferScale` instead to set the scale if it's available + if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE { + state.surface.set_buffer_scale(scale); + drop(state); + self.rescale(scale as f32); + } } wl_surface::Event::PreferredBufferScale { factor } => { - state.surface.set_buffer_scale(factor); - drop(state); - self.rescale(factor as f32); + // We use `WpFractionalScale` instead to set the scale if it's available + if state.globals.fractional_scale_manager.is_none() { + state.surface.set_buffer_scale(factor); + drop(state); + self.rescale(factor as f32); + } } _ => {} } @@ -537,11 +542,6 @@ impl WaylandWindowStatePtr { self.set_size_and_scale(None, None, Some(scale)); } - pub fn set_fullscreen(&self, fullscreen: bool) { - let mut state = self.state.borrow_mut(); - state.fullscreen = fullscreen; - } - /// Notifies the window of the state of the decorations. /// /// # Note @@ -602,10 +602,10 @@ fn primary_output_scale(state: &mut RefMut) -> i32 { 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)); + current_output = Some((id.clone(), output.clone())); } } else { - current_output = Some((id.clone(), *output)); + current_output = Some((id.clone(), output.clone())); } scale = scale.max(output.scale); } @@ -636,9 +636,9 @@ impl PlatformWindow for WaylandWindow { fn window_bounds(&self) -> WindowBounds { let state = self.borrow(); if state.fullscreen { - WindowBounds::Fullscreen(state.restore_bounds) + WindowBounds::Fullscreen(state.windowed_bounds) } else if state.maximized { - WindowBounds::Maximized(state.restore_bounds) + WindowBounds::Maximized(state.windowed_bounds) } else { WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p as i32))) } @@ -664,6 +664,7 @@ impl PlatformWindow for WaylandWindow { self.borrow().display.as_ref().map(|(id, display)| { Rc::new(WaylandDisplay { id: id.clone(), + name: display.name.clone(), bounds: display.bounds, }) as Rc }) @@ -779,7 +780,6 @@ impl PlatformWindow for WaylandWindow { fn toggle_fullscreen(&self) { let mut state = self.borrow_mut(); - state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32)); if !state.fullscreen { state.toplevel.set_fullscreen(None); } else {