1
0
Fork 0
forked from wry/wry

tree: make scale and position of outputs persistent

This commit is contained in:
Julian Orth 2024-03-16 01:36:04 +01:00
parent bc9b1c3638
commit 3eb0f61ec1
16 changed files with 135 additions and 84 deletions

View file

@ -602,10 +602,10 @@ impl MetalConnector {
&self.state, &self.state,
Some(output.global.pos.get()), Some(output.global.pos.get()),
Some(rr), Some(rr),
output.global.preferred_scale.get(), output.global.persistent.scale.get(),
render_hw_cursor, render_hw_cursor,
output.has_fullscreen(), output.has_fullscreen(),
output.global.transform.get(), output.global.persistent.transform.get(),
); );
let try_direct_scanout = try_direct_scanout let try_direct_scanout = try_direct_scanout
&& self.direct_scanout_enabled() && self.direct_scanout_enabled()

View file

@ -16,7 +16,10 @@ use {
dbus::Dbus, dbus::Dbus,
forker, forker,
globals::Globals, globals::Globals,
ifs::{wl_output::WlOutputGlobal, wl_surface::NoneSurfaceExt}, ifs::{
wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
wl_surface::NoneSurfaceExt,
},
io_uring::{IoUring, IoUringError}, io_uring::{IoUring, IoUringError},
leaks, leaks,
logger::Logger, logger::Logger,
@ -204,7 +207,7 @@ fn start_compositor2(
dma_buf_ids: Default::default(), dma_buf_ids: Default::default(),
drm_feedback_ids: Default::default(), drm_feedback_ids: Default::default(),
direct_scanout_enabled: Cell::new(true), 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_interval_usec: Cell::new(400 * 1000),
double_click_distance: Cell::new(5), double_click_distance: Cell::new(5),
create_default_seat: Cell::new(true), create_default_seat: Cell::new(true),
@ -364,6 +367,17 @@ fn init_fd_limit() {
} }
fn create_dummy_output(state: &Rc<State>) { fn create_dummy_output(state: &Rc<State>) {
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 { let dummy_output = Rc::new(OutputNode {
id: state.node_ids.next(), id: state.node_ids.next(),
global: Rc::new(WlOutputGlobal::new( global: Rc::new(WlOutputGlobal::new(
@ -379,18 +393,16 @@ fn create_dummy_output(state: &Rc<State>) {
drm_dev: None, drm_dev: None,
async_event: Default::default(), async_event: Default::default(),
}), }),
0,
Vec::new(), Vec::new(),
&backend::Mode { &backend::Mode {
width: 0, width: 0,
height: 0, height: 0,
refresh_rate_millihz: 0, refresh_rate_millihz: 0,
}, },
"jay",
"dummy-output",
"0",
0, 0,
0, 0,
&output_id,
&persistent_state,
)), )),
jay_outputs: Default::default(), jay_outputs: Default::default(),
workspaces: Default::default(), workspaces: Default::default(),

View file

@ -793,7 +793,7 @@ impl ConfigProxyHandler {
fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> { fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?; let connector = self.get_output(connector)?;
self.respond(Response::ConnectorGetScale { self.respond(Response::ConnectorGetScale {
scale: connector.node.global.preferred_scale.get().to_f64(), scale: connector.node.global.persistent.scale.get().to_f64(),
}); });
Ok(()) Ok(())
} }

View file

@ -316,7 +316,7 @@ impl dyn GfxFramebuffer {
scale, scale,
render_hardware_cursor, render_hardware_cursor,
node.has_fullscreen(), node.has_fullscreen(),
node.global.transform.get(), node.global.persistent.transform.get(),
) )
} }

View file

@ -92,12 +92,12 @@ impl JayRandr {
let pos = global.pos.get(); let pos = global.pos.get();
self.client.event(Output { self.client.event(Output {
self_id: self.id, self_id: self.id,
scale: global.preferred_scale.get().to_wl(), scale: global.persistent.scale.get().to_wl(),
width: pos.width(), width: pos.width(),
height: pos.height(), height: pos.height(),
x: pos.x1(), x: pos.x1(),
y: pos.y1(), y: pos.y1(),
transform: global.transform.get().to_wl(), transform: global.persistent.transform.get().to_wl(),
manufacturer: &output.monitor_info.manufacturer, manufacturer: &output.monitor_info.manufacturer,
product: &output.monitor_info.product, product: &output.monitor_info.product,
serial_number: &output.monitor_info.serial_number, serial_number: &output.monitor_info.serial_number,

View file

@ -181,7 +181,7 @@ impl JayScreencast {
x_off, x_off,
y_off, y_off,
size, size,
on.global.transform.get(), on.global.persistent.transform.get(),
); );
self.client.event(Ready { self.client.event(Ready {
self_id: self.id, self_id: self.id,

View file

@ -13,7 +13,7 @@ use {
rect::Rect, rect::Rect,
state::{ConnectorData, State}, state::{ConnectorData, State},
time::Time, time::Time,
tree::OutputNode, tree::{calculate_logical_size, OutputNode},
utils::{ utils::{
buffd::{MsgParser, MsgParserError}, buffd::{MsgParser, MsgParserError},
clonecell::CloneCell, clonecell::CloneCell,
@ -75,12 +75,18 @@ pub struct WlOutputGlobal {
pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>, pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
pub destroyed: Cell<bool>, pub destroyed: Cell<bool>,
pub legacy_scale: Cell<u32>, pub legacy_scale: Cell<u32>,
pub preferred_scale: Cell<crate::scale::Scale>, pub persistent: Rc<PersistentOutputState>,
}
pub struct PersistentOutputState {
pub transform: Cell<Transform>, pub transform: Cell<Transform>,
pub scale: Cell<crate::scale::Scale>,
pub pos: Cell<(i32, i32)>,
} }
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash)]
pub struct OutputId { pub struct OutputId {
pub connector: String,
pub manufacturer: String, pub manufacturer: String,
pub model: String, pub model: String,
pub serial_number: String, pub serial_number: String,
@ -96,33 +102,26 @@ impl WlOutputGlobal {
name: GlobalName, name: GlobalName,
state: &Rc<State>, state: &Rc<State>,
connector: &Rc<ConnectorData>, connector: &Rc<ConnectorData>,
x1: i32,
modes: Vec<backend::Mode>, modes: Vec<backend::Mode>,
mode: &backend::Mode, mode: &backend::Mode,
manufacturer: &str,
product: &str,
serial_number: &str,
width_mm: i32, width_mm: i32,
height_mm: i32, height_mm: i32,
output_id: &Rc<OutputId>,
persistent_state: &Rc<PersistentOutputState>,
) -> Self { ) -> Self {
let output_id = Rc::new(OutputId { let (x, y) = persistent_state.pos.get();
manufacturer: manufacturer.to_string(), let scale = persistent_state.scale.get();
model: product.to_string(), let (width, height) = calculate_logical_size(
serial_number: serial_number.to_string(), (mode.width, mode.height),
}); persistent_state.transform.get(),
let transform = state scale,
.output_transforms );
.borrow()
.get(&output_id)
.copied()
.unwrap_or(Transform::None);
let (width, height) = transform.maybe_swap((mode.width, mode.height));
Self { Self {
name, name,
state: state.clone(), state: state.clone(),
connector: connector.clone(), connector: connector.clone(),
pos: Cell::new(Rect::new_sized(x1, 0, width, height).unwrap()), pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
output_id, output_id: output_id.clone(),
mode: Cell::new(*mode), mode: Cell::new(*mode),
modes, modes,
node: Default::default(), node: Default::default(),
@ -132,9 +131,8 @@ impl WlOutputGlobal {
unused_captures: Default::default(), unused_captures: Default::default(),
pending_captures: Default::default(), pending_captures: Default::default(),
destroyed: Cell::new(false), destroyed: Cell::new(false),
legacy_scale: Cell::new(1), legacy_scale: Cell::new(scale.round_up()),
preferred_scale: Cell::new(crate::scale::Scale::from_int(1)), persistent: persistent_state.clone(),
transform: Cell::new(transform),
} }
} }
@ -289,7 +287,10 @@ impl WlOutputGlobal {
pub fn pixel_size(&self) -> (i32, i32) { pub fn pixel_size(&self) -> (i32, i32) {
let mode = self.mode.get(); 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, subpixel: SP_UNKNOWN,
make: &self.global.output_id.manufacturer, make: &self.global.output_id.manufacturer,
model: &self.global.output_id.model, 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); self.client.event(event);
} }

View file

@ -272,9 +272,9 @@ impl WlSeatGlobal {
let (x, y) = self.get_position(); let (x, y) = self.get_position();
for output in self.state.root.outputs.lock().values() { for output in self.state.root.outputs.lock().values() {
if let Some(hc) = output.hardware_cursor.get() { 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 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 extents = cursor.extents_at_scale(scale);
let (hc_width, hc_height) = hc.size(); let (hc_width, hc_height) = hc.size();
if render { if render {

View file

@ -357,10 +357,10 @@ impl WlSurface {
} }
output.global.send_enter(self); output.global.send_enter(self);
old.global.send_leave(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(); 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(); self.send_preferred_buffer_transform();
} }
let children = self.children.borrow_mut(); let children = self.children.borrow_mut();
@ -459,7 +459,7 @@ impl WlSurface {
if self.version >= TRANSFORM_SINCE { if self.version >= TRANSFORM_SINCE {
self.client.event(PreferredBufferTransform { self.client.event(PreferredBufferTransform {
self_id: self.id, 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 _,
}); });
} }
} }

View file

@ -44,7 +44,8 @@ impl WpFractionalScaleV1 {
.output .output
.get() .get()
.global .global
.preferred_scale .persistent
.scale
.get() .get()
.to_wl(), .to_wl(),
}); });

View file

@ -100,7 +100,7 @@ impl ZwlrScreencopyManagerV1 {
let mode = output.global.mode.get(); let mode = output.global.mode.get();
let mut rect = Rect::new_sized(0, 0, mode.width, mode.height).unwrap(); let mut rect = Rect::new_sized(0, 0, mode.width, mode.height).unwrap();
if let Some(region) = region { 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 x1 = (region.x1() as f64 * scale).round() as i32;
let y1 = (region.y1() 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; let x2 = (region.x2() as f64 * scale).round() as i32;

View file

@ -148,7 +148,7 @@ impl Renderer<'_> {
let c = theme.colors.attention_requested_background.get(); let c = theme.colors.attention_requested_background.get();
self.base self.base
.fill_boxes2(&rd.attention_requested_workspaces, &c, x, y); .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 { for title in &rd.titles {
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y); let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
self.base self.base

View file

@ -7,6 +7,12 @@ const BASEF: f64 = BASE as f64;
#[repr(transparent)] #[repr(transparent)]
pub struct Scale(u32); pub struct Scale(u32);
impl Default for Scale {
fn default() -> Self {
Scale::from_int(1)
}
}
impl Scale { impl Scale {
pub fn from_int(f: u32) -> Self { pub fn from_int(f: u32) -> Self {
Self(f.saturating_mul(BASE)) Self(f.saturating_mul(BASE))

View file

@ -27,7 +27,7 @@ use {
jay_seat_events::JaySeatEvents, jay_seat_events::JaySeatEvents,
jay_workspace_watcher::JayWorkspaceWatcher, jay_workspace_watcher::JayWorkspaceWatcher,
wl_drm::WlDrmGlobal, wl_drm::WlDrmGlobal,
wl_output::OutputId, wl_output::{OutputId, PersistentOutputState},
wl_seat::{SeatIds, WlSeatGlobal}, wl_seat::{SeatIds, WlSeatGlobal},
wl_surface::{ wl_surface::{
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
@ -153,7 +153,7 @@ pub struct State {
pub dma_buf_ids: DmaBufIds, pub dma_buf_ids: DmaBufIds,
pub drm_feedback_ids: DrmFeedbackIds, pub drm_feedback_ids: DrmFeedbackIds,
pub direct_scanout_enabled: Cell<bool>, pub direct_scanout_enabled: Cell<bool>,
pub output_transforms: RefCell<AHashMap<Rc<OutputId>, Transform>>, pub persistent_output_states: CopyHashMap<Rc<OutputId>, Rc<PersistentOutputState>>,
pub double_click_interval_usec: Cell<u64>, pub double_click_interval_usec: Cell<u64>,
pub double_click_distance: Cell<i32>, pub double_click_distance: Cell<i32>,
pub create_default_seat: Cell<bool>, pub create_default_seat: Cell<bool>,
@ -772,7 +772,7 @@ impl State {
self, self,
Some(output.global.pos.get()), Some(output.global.pos.get()),
Some(rr), Some(rr),
output.global.preferred_scale.get(), output.global.persistent.scale.get(),
render_hw_cursor, render_hw_cursor,
); );
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None); output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);

View file

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo}, backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
ifs::wl_output::WlOutputGlobal, ifs::wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
state::{ConnectorData, OutputData, State}, state::{ConnectorData, OutputData, State},
tree::{OutputNode, OutputRenderData}, tree::{OutputNode, OutputRenderData},
utils::{asyncevent::AsyncEvent, clonecell::CloneCell}, utils::{asyncevent::AsyncEvent, clonecell::CloneCell},
@ -80,27 +80,45 @@ impl ConnectorHandler {
log::info!("Connector {} connected", self.data.connector.kernel_id()); log::info!("Connector {} connected", self.data.connector.kernel_id());
self.data.connected.set(true); self.data.connected.set(true);
let name = self.state.globals.name(); let name = self.state.globals.name();
let x1 = self let output_id = Rc::new(OutputId {
.state connector: self.data.name.clone(),
.root manufacturer: info.manufacturer.clone(),
.outputs model: info.product.clone(),
.lock() serial_number: info.serial_number.clone(),
.values() });
.map(|o| o.global.pos.get().x2()) let desired_state = match self.state.persistent_output_states.get(&output_id) {
.max() Some(ds) => ds,
.unwrap_or(0); _ => {
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( let global = Rc::new(WlOutputGlobal::new(
name, name,
&self.state, &self.state,
&self.data, &self.data,
x1,
info.modes.clone(), info.modes.clone(),
&info.initial_mode, &info.initial_mode,
&info.manufacturer,
&info.product,
&info.serial_number,
info.width_mm, info.width_mm,
info.height_mm, info.height_mm,
&output_id,
&desired_state,
)); ));
let on = Rc::new(OutputNode { let on = Rc::new(OutputNode {
id: self.state.node_ids.next(), id: self.state.node_ids.next(),
@ -130,8 +148,8 @@ impl ConnectorHandler {
update_render_data_scheduled: Cell::new(false), update_render_data_scheduled: Cell::new(false),
hardware_cursor_needs_render: Cell::new(false), hardware_cursor_needs_render: Cell::new(false),
}); });
self.state.add_output_scale(on.global.preferred_scale.get()); self.state
let mode = info.initial_mode; .add_output_scale(on.global.persistent.scale.get());
let output_data = Rc::new(OutputData { let output_data = Rc::new(OutputData {
connector: self.data.clone(), connector: self.data.clone(),
monitor_info: info, monitor_info: info,
@ -140,8 +158,11 @@ impl ConnectorHandler {
self.state.outputs.set(self.id, output_data); self.state.outputs.set(self.id, output_data);
if self.state.outputs.len() == 1 { if self.state.outputs.len() == 1 {
let seats = self.state.globals.seats.lock(); 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() { 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())); global.node.set(Some(on.clone()));
@ -277,7 +298,7 @@ impl ConnectorHandler {
dev.connectors.remove(&self.id); dev.connectors.remove(&self.id);
} }
self.state self.state
.remove_output_scale(on.global.preferred_scale.get()); .remove_output_scale(on.global.persistent.scale.get());
let _ = self.state.remove_global(&*global); let _ = self.state.remove_global(&*global);
} }
} }

View file

@ -30,7 +30,7 @@ use {
}, },
utils::{ utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
linkedlist::LinkedList, scroller::Scroller, linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt,
}, },
wire::{JayOutputId, JayScreencastId}, wire::{JayOutputId, JayScreencastId},
}, },
@ -120,7 +120,7 @@ impl OutputNode {
} }
pub fn set_preferred_scale(self: &Rc<Self>, scale: Scale) { pub fn set_preferred_scale(self: &Rc<Self>, scale: Scale) {
let old_scale = self.global.preferred_scale.replace(scale); let old_scale = self.global.persistent.scale.replace(scale);
if scale == old_scale { if scale == old_scale {
return; return;
} }
@ -161,7 +161,7 @@ impl OutputNode {
let font = self.state.theme.font.borrow_mut(); let font = self.state.theme.font.borrow_mut();
let theme = &self.state.theme; let theme = &self.state.theme;
let th = theme.sizes.title_height.get(); 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 { let scale = if scale != 1 {
Some(scale.to_f64()) Some(scale.to_f64())
} else { } else {
@ -388,7 +388,7 @@ impl OutputNode {
} }
pub fn update_mode(self: &Rc<Self>, mode: Mode) { pub fn update_mode(self: &Rc<Self>, 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<Self>, transform: Transform) { pub fn update_transform(self: &Rc<Self>, transform: Transform) {
@ -397,17 +397,13 @@ impl OutputNode {
pub fn update_mode_and_transform(self: &Rc<Self>, mode: Mode, transform: Transform) { pub fn update_mode_and_transform(self: &Rc<Self>, mode: Mode, transform: Transform) {
let old_mode = self.global.mode.get(); 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) { if (old_mode, old_transform) == (mode, transform) {
return; return;
} }
let (old_width, old_height) = self.global.pixel_size(); let (old_width, old_height) = self.global.pixel_size();
self.global.mode.set(mode); self.global.mode.set(mode);
self.state self.global.persistent.transform.set(transform);
.output_transforms
.borrow_mut()
.insert(self.global.output_id.clone(), transform);
self.global.transform.set(transform);
let (new_width, new_height) = self.global.pixel_size(); let (new_width, new_height) = self.global.pixel_size();
self.change_extents_(&self.calculate_extents()); self.change_extents_(&self.calculate_extents());
@ -436,18 +432,18 @@ impl OutputNode {
} }
fn calculate_extents(&self) -> Rect { fn calculate_extents(&self) -> Rect {
let (mut width, mut height) = self.global.pixel_size(); let mode = self.global.mode.get();
let scale = self.global.preferred_scale.get(); let (width, height) = calculate_logical_size(
if scale != 1 { (mode.width, mode.height),
let scale = scale.to_f64(); self.global.persistent.transform.get(),
width = (width as f64 / scale).round() as _; self.global.persistent.scale.get(),
height = (height as f64 / scale).round() as _; );
}
let pos = self.global.pos.get(); let pos = self.global.pos.get();
pos.with_size(width, height).unwrap() pos.with_size(width, height).unwrap()
} }
fn change_extents_(self: &Rc<Self>, rect: &Rect) { fn change_extents_(self: &Rc<Self>, rect: &Rect) {
self.global.persistent.pos.set((rect.x1(), rect.y1()));
self.global.pos.set(*rect); self.global.pos.set(*rect);
self.state.root.update_extents(); self.state.root.update_extents();
self.schedule_update_render_data(); self.schedule_update_render_data();
@ -766,3 +762,17 @@ impl Node for OutputNode {
true 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)
}