From 3eb0f61ec18413395a538d225f8cb03efaa6eaf4 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 16 Mar 2024 01:36:04 +0100 Subject: [PATCH] tree: make scale and position of outputs persistent --- src/backends/metal/video.rs | 4 +- src/compositor.rs | 24 ++++++--- src/config/handler.rs | 2 +- src/gfx_api.rs | 2 +- src/ifs/jay_randr.rs | 4 +- src/ifs/jay_screencast.rs | 2 +- src/ifs/wl_output.rs | 51 +++++++++--------- src/ifs/wl_seat.rs | 4 +- src/ifs/wl_surface.rs | 6 +-- src/ifs/wl_surface/wp_fractional_scale_v1.rs | 3 +- src/ifs/zwlr_screencopy_manager_v1.rs | 2 +- src/renderer.rs | 2 +- src/scale.rs | 6 +++ src/state.rs | 6 +-- src/tasks/connector.rs | 57 +++++++++++++------- src/tree/output.rs | 44 +++++++++------ 16 files changed, 135 insertions(+), 84 deletions(-) diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 17290f12..d04f696f 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -602,10 +602,10 @@ impl MetalConnector { &self.state, Some(output.global.pos.get()), Some(rr), - output.global.preferred_scale.get(), + output.global.persistent.scale.get(), render_hw_cursor, output.has_fullscreen(), - output.global.transform.get(), + output.global.persistent.transform.get(), ); let try_direct_scanout = try_direct_scanout && self.direct_scanout_enabled() diff --git a/src/compositor.rs b/src/compositor.rs index a226d1d7..8f48948c 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -16,7 +16,10 @@ use { dbus::Dbus, forker, globals::Globals, - ifs::{wl_output::WlOutputGlobal, wl_surface::NoneSurfaceExt}, + ifs::{ + wl_output::{OutputId, PersistentOutputState, WlOutputGlobal}, + wl_surface::NoneSurfaceExt, + }, io_uring::{IoUring, IoUringError}, leaks, logger::Logger, @@ -204,7 +207,7 @@ fn start_compositor2( dma_buf_ids: Default::default(), drm_feedback_ids: Default::default(), direct_scanout_enabled: Cell::new(true), - output_transforms: Default::default(), + persistent_output_states: Default::default(), double_click_interval_usec: Cell::new(400 * 1000), double_click_distance: Cell::new(5), create_default_seat: Cell::new(true), @@ -364,6 +367,17 @@ fn init_fd_limit() { } fn create_dummy_output(state: &Rc) { + let output_id = Rc::new(OutputId { + connector: "jay-dummy-connector".to_string(), + manufacturer: "jay".to_string(), + model: "jay-dummy-output".to_string(), + serial_number: "".to_string(), + }); + let persistent_state = Rc::new(PersistentOutputState { + transform: Default::default(), + scale: Default::default(), + pos: Default::default(), + }); let dummy_output = Rc::new(OutputNode { id: state.node_ids.next(), global: Rc::new(WlOutputGlobal::new( @@ -379,18 +393,16 @@ fn create_dummy_output(state: &Rc) { drm_dev: None, async_event: Default::default(), }), - 0, Vec::new(), &backend::Mode { width: 0, height: 0, refresh_rate_millihz: 0, }, - "jay", - "dummy-output", - "0", 0, 0, + &output_id, + &persistent_state, )), jay_outputs: Default::default(), workspaces: Default::default(), diff --git a/src/config/handler.rs b/src/config/handler.rs index baeb69f7..5866186b 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -793,7 +793,7 @@ impl ConfigProxyHandler { fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> { let connector = self.get_output(connector)?; self.respond(Response::ConnectorGetScale { - scale: connector.node.global.preferred_scale.get().to_f64(), + scale: connector.node.global.persistent.scale.get().to_f64(), }); Ok(()) } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 8d2716c7..a9f5d280 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -316,7 +316,7 @@ impl dyn GfxFramebuffer { scale, render_hardware_cursor, node.has_fullscreen(), - node.global.transform.get(), + node.global.persistent.transform.get(), ) } diff --git a/src/ifs/jay_randr.rs b/src/ifs/jay_randr.rs index dffe241e..735f65a8 100644 --- a/src/ifs/jay_randr.rs +++ b/src/ifs/jay_randr.rs @@ -92,12 +92,12 @@ impl JayRandr { let pos = global.pos.get(); self.client.event(Output { self_id: self.id, - scale: global.preferred_scale.get().to_wl(), + scale: global.persistent.scale.get().to_wl(), width: pos.width(), height: pos.height(), x: pos.x1(), y: pos.y1(), - transform: global.transform.get().to_wl(), + transform: global.persistent.transform.get().to_wl(), manufacturer: &output.monitor_info.manufacturer, product: &output.monitor_info.product, serial_number: &output.monitor_info.serial_number, diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index 77541294..f3a7a791 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -181,7 +181,7 @@ impl JayScreencast { x_off, y_off, size, - on.global.transform.get(), + on.global.persistent.transform.get(), ); self.client.event(Ready { self_id: self.id, diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index d93f3b50..8fb3f1cc 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -13,7 +13,7 @@ use { rect::Rect, state::{ConnectorData, State}, time::Time, - tree::OutputNode, + tree::{calculate_logical_size, OutputNode}, utils::{ buffd::{MsgParser, MsgParserError}, clonecell::CloneCell, @@ -75,12 +75,18 @@ pub struct WlOutputGlobal { pub pending_captures: LinkedList>, pub destroyed: Cell, pub legacy_scale: Cell, - pub preferred_scale: Cell, + pub persistent: Rc, +} + +pub struct PersistentOutputState { pub transform: Cell, + pub scale: Cell, + pub pos: Cell<(i32, i32)>, } #[derive(Eq, PartialEq, Hash)] pub struct OutputId { + pub connector: String, pub manufacturer: String, pub model: String, pub serial_number: String, @@ -96,33 +102,26 @@ impl WlOutputGlobal { name: GlobalName, state: &Rc, connector: &Rc, - x1: i32, modes: Vec, mode: &backend::Mode, - manufacturer: &str, - product: &str, - serial_number: &str, width_mm: i32, height_mm: i32, + output_id: &Rc, + persistent_state: &Rc, ) -> Self { - let output_id = Rc::new(OutputId { - manufacturer: manufacturer.to_string(), - model: product.to_string(), - serial_number: serial_number.to_string(), - }); - let transform = state - .output_transforms - .borrow() - .get(&output_id) - .copied() - .unwrap_or(Transform::None); - let (width, height) = transform.maybe_swap((mode.width, mode.height)); + let (x, y) = persistent_state.pos.get(); + let scale = persistent_state.scale.get(); + let (width, height) = calculate_logical_size( + (mode.width, mode.height), + persistent_state.transform.get(), + scale, + ); Self { name, state: state.clone(), connector: connector.clone(), - pos: Cell::new(Rect::new_sized(x1, 0, width, height).unwrap()), - output_id, + pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()), + output_id: output_id.clone(), mode: Cell::new(*mode), modes, node: Default::default(), @@ -132,9 +131,8 @@ impl WlOutputGlobal { unused_captures: Default::default(), pending_captures: Default::default(), destroyed: Cell::new(false), - legacy_scale: Cell::new(1), - preferred_scale: Cell::new(crate::scale::Scale::from_int(1)), - transform: Cell::new(transform), + legacy_scale: Cell::new(scale.round_up()), + persistent: persistent_state.clone(), } } @@ -289,7 +287,10 @@ impl WlOutputGlobal { pub fn pixel_size(&self) -> (i32, i32) { let mode = self.mode.get(); - self.transform.get().maybe_swap((mode.width, mode.height)) + self.persistent + .transform + .get() + .maybe_swap((mode.width, mode.height)) } } @@ -336,7 +337,7 @@ impl WlOutput { subpixel: SP_UNKNOWN, make: &self.global.output_id.manufacturer, model: &self.global.output_id.model, - transform: self.global.transform.get().to_wl(), + transform: self.global.persistent.transform.get().to_wl(), }; self.client.event(event); } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index d027602e..3edf241b 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -272,9 +272,9 @@ impl WlSeatGlobal { let (x, y) = self.get_position(); for output in self.state.root.outputs.lock().values() { if let Some(hc) = output.hardware_cursor.get() { - let transform = output.global.transform.get(); + let transform = output.global.persistent.transform.get(); let render = render | output.hardware_cursor_needs_render.take(); - let scale = output.global.preferred_scale.get(); + let scale = output.global.persistent.scale.get(); let extents = cursor.extents_at_scale(scale); let (hc_width, hc_height) = hc.size(); if render { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 35f69d5b..ca4079cf 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -357,10 +357,10 @@ impl WlSurface { } output.global.send_enter(self); old.global.send_leave(self); - if old.global.preferred_scale.get() != output.global.preferred_scale.get() { + if old.global.persistent.scale.get() != output.global.persistent.scale.get() { self.on_scale_change(); } - if old.global.transform.get() != output.global.transform.get() { + if old.global.persistent.transform.get() != output.global.persistent.transform.get() { self.send_preferred_buffer_transform(); } let children = self.children.borrow_mut(); @@ -459,7 +459,7 @@ impl WlSurface { if self.version >= TRANSFORM_SINCE { self.client.event(PreferredBufferTransform { self_id: self.id, - transform: self.output.get().global.transform.get().to_wl() as _, + transform: self.output.get().global.persistent.transform.get().to_wl() as _, }); } } diff --git a/src/ifs/wl_surface/wp_fractional_scale_v1.rs b/src/ifs/wl_surface/wp_fractional_scale_v1.rs index 51e7f404..6f86fdcc 100644 --- a/src/ifs/wl_surface/wp_fractional_scale_v1.rs +++ b/src/ifs/wl_surface/wp_fractional_scale_v1.rs @@ -44,7 +44,8 @@ impl WpFractionalScaleV1 { .output .get() .global - .preferred_scale + .persistent + .scale .get() .to_wl(), }); diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 3e653c28..63704fa3 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -100,7 +100,7 @@ impl ZwlrScreencopyManagerV1 { let mode = output.global.mode.get(); let mut rect = Rect::new_sized(0, 0, mode.width, mode.height).unwrap(); if let Some(region) = region { - let scale = output.global.preferred_scale.get().to_f64(); + let scale = output.global.persistent.scale.get().to_f64(); let x1 = (region.x1() as f64 * scale).round() as i32; let y1 = (region.y1() as f64 * scale).round() as i32; let x2 = (region.x2() as f64 * scale).round() as i32; diff --git a/src/renderer.rs b/src/renderer.rs index 5231c8b9..e9999c21 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -148,7 +148,7 @@ impl Renderer<'_> { let c = theme.colors.attention_requested_background.get(); self.base .fill_boxes2(&rd.attention_requested_workspaces, &c, x, y); - let scale = output.global.preferred_scale.get(); + let scale = output.global.persistent.scale.get(); for title in &rd.titles { let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y); self.base diff --git a/src/scale.rs b/src/scale.rs index d481766b..fb16ba37 100644 --- a/src/scale.rs +++ b/src/scale.rs @@ -7,6 +7,12 @@ const BASEF: f64 = BASE as f64; #[repr(transparent)] pub struct Scale(u32); +impl Default for Scale { + fn default() -> Self { + Scale::from_int(1) + } +} + impl Scale { pub fn from_int(f: u32) -> Self { Self(f.saturating_mul(BASE)) diff --git a/src/state.rs b/src/state.rs index 277a1918..31837998 100644 --- a/src/state.rs +++ b/src/state.rs @@ -27,7 +27,7 @@ use { jay_seat_events::JaySeatEvents, jay_workspace_watcher::JayWorkspaceWatcher, wl_drm::WlDrmGlobal, - wl_output::OutputId, + wl_output::{OutputId, PersistentOutputState}, wl_seat::{SeatIds, WlSeatGlobal}, wl_surface::{ zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, @@ -153,7 +153,7 @@ pub struct State { pub dma_buf_ids: DmaBufIds, pub drm_feedback_ids: DrmFeedbackIds, pub direct_scanout_enabled: Cell, - pub output_transforms: RefCell, Transform>>, + pub persistent_output_states: CopyHashMap, Rc>, pub double_click_interval_usec: Cell, pub double_click_distance: Cell, pub create_default_seat: Cell, @@ -772,7 +772,7 @@ impl State { self, Some(output.global.pos.get()), Some(rr), - output.global.preferred_scale.get(), + output.global.persistent.scale.get(), render_hw_cursor, ); output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None); diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 5c1df848..6d78b454 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -1,7 +1,7 @@ use { crate::{ backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo}, - ifs::wl_output::WlOutputGlobal, + ifs::wl_output::{OutputId, PersistentOutputState, WlOutputGlobal}, state::{ConnectorData, OutputData, State}, tree::{OutputNode, OutputRenderData}, utils::{asyncevent::AsyncEvent, clonecell::CloneCell}, @@ -80,27 +80,45 @@ impl ConnectorHandler { log::info!("Connector {} connected", self.data.connector.kernel_id()); self.data.connected.set(true); let name = self.state.globals.name(); - let x1 = self - .state - .root - .outputs - .lock() - .values() - .map(|o| o.global.pos.get().x2()) - .max() - .unwrap_or(0); + let output_id = Rc::new(OutputId { + connector: self.data.name.clone(), + manufacturer: info.manufacturer.clone(), + model: info.product.clone(), + serial_number: info.serial_number.clone(), + }); + let desired_state = match self.state.persistent_output_states.get(&output_id) { + Some(ds) => ds, + _ => { + let x1 = self + .state + .root + .outputs + .lock() + .values() + .map(|o| o.global.pos.get().x2()) + .max() + .unwrap_or(0); + let ds = Rc::new(PersistentOutputState { + transform: Default::default(), + scale: Default::default(), + pos: Cell::new((x1, 0)), + }); + self.state + .persistent_output_states + .set(output_id.clone(), ds.clone()); + ds + } + }; let global = Rc::new(WlOutputGlobal::new( name, &self.state, &self.data, - x1, info.modes.clone(), &info.initial_mode, - &info.manufacturer, - &info.product, - &info.serial_number, info.width_mm, info.height_mm, + &output_id, + &desired_state, )); let on = Rc::new(OutputNode { id: self.state.node_ids.next(), @@ -130,8 +148,8 @@ impl ConnectorHandler { update_render_data_scheduled: Cell::new(false), hardware_cursor_needs_render: Cell::new(false), }); - self.state.add_output_scale(on.global.preferred_scale.get()); - let mode = info.initial_mode; + self.state + .add_output_scale(on.global.persistent.scale.get()); let output_data = Rc::new(OutputData { connector: self.data.clone(), monitor_info: info, @@ -140,8 +158,11 @@ impl ConnectorHandler { self.state.outputs.set(self.id, output_data); if self.state.outputs.len() == 1 { let seats = self.state.globals.seats.lock(); + let pos = global.pos.get(); + let x = (pos.x1() + pos.x2()) / 2; + let y = (pos.y1() + pos.y2()) / 2; for seat in seats.values() { - seat.set_position(x1 + mode.width / 2, mode.height / 2); + seat.set_position(x, y); } } global.node.set(Some(on.clone())); @@ -277,7 +298,7 @@ impl ConnectorHandler { dev.connectors.remove(&self.id); } self.state - .remove_output_scale(on.global.preferred_scale.get()); + .remove_output_scale(on.global.persistent.scale.get()); let _ = self.state.remove_global(&*global); } } diff --git a/src/tree/output.rs b/src/tree/output.rs index c81a0c7b..b1f0c7d8 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -30,7 +30,7 @@ use { }, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, - linkedlist::LinkedList, scroller::Scroller, + linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt, }, wire::{JayOutputId, JayScreencastId}, }, @@ -120,7 +120,7 @@ impl OutputNode { } pub fn set_preferred_scale(self: &Rc, scale: Scale) { - let old_scale = self.global.preferred_scale.replace(scale); + let old_scale = self.global.persistent.scale.replace(scale); if scale == old_scale { return; } @@ -161,7 +161,7 @@ impl OutputNode { let font = self.state.theme.font.borrow_mut(); let theme = &self.state.theme; let th = theme.sizes.title_height.get(); - let scale = self.global.preferred_scale.get(); + let scale = self.global.persistent.scale.get(); let scale = if scale != 1 { Some(scale.to_f64()) } else { @@ -388,7 +388,7 @@ impl OutputNode { } pub fn update_mode(self: &Rc, mode: Mode) { - self.update_mode_and_transform(mode, self.global.transform.get()); + self.update_mode_and_transform(mode, self.global.persistent.transform.get()); } pub fn update_transform(self: &Rc, transform: Transform) { @@ -397,17 +397,13 @@ impl OutputNode { pub fn update_mode_and_transform(self: &Rc, mode: Mode, transform: Transform) { let old_mode = self.global.mode.get(); - let old_transform = self.global.transform.get(); + let old_transform = self.global.persistent.transform.get(); if (old_mode, old_transform) == (mode, transform) { return; } let (old_width, old_height) = self.global.pixel_size(); self.global.mode.set(mode); - self.state - .output_transforms - .borrow_mut() - .insert(self.global.output_id.clone(), transform); - self.global.transform.set(transform); + self.global.persistent.transform.set(transform); let (new_width, new_height) = self.global.pixel_size(); self.change_extents_(&self.calculate_extents()); @@ -436,18 +432,18 @@ impl OutputNode { } fn calculate_extents(&self) -> Rect { - let (mut width, mut height) = self.global.pixel_size(); - let scale = self.global.preferred_scale.get(); - if scale != 1 { - let scale = scale.to_f64(); - width = (width as f64 / scale).round() as _; - height = (height as f64 / scale).round() as _; - } + let mode = self.global.mode.get(); + let (width, height) = calculate_logical_size( + (mode.width, mode.height), + self.global.persistent.transform.get(), + self.global.persistent.scale.get(), + ); let pos = self.global.pos.get(); pos.with_size(width, height).unwrap() } fn change_extents_(self: &Rc, rect: &Rect) { + self.global.persistent.pos.set((rect.x1(), rect.y1())); self.global.pos.set(*rect); self.state.root.update_extents(); self.schedule_update_render_data(); @@ -766,3 +762,17 @@ impl Node for OutputNode { true } } + +pub fn calculate_logical_size( + mode: (i32, i32), + transform: Transform, + scale: crate::scale::Scale, +) -> (i32, i32) { + let (mut width, mut height) = transform.maybe_swap(mode); + if scale != 1 { + let scale = scale.to_f64(); + width = (width as f64 / scale).round() as _; + height = (height as f64 / scale).round() as _; + } + (width, height) +}