Add output configuration & integer scaling support

This commit is contained in:
Ivan Molodetskikh
2023-09-21 13:48:32 +04:00
parent cfa73c153c
commit 109668fa30
6 changed files with 101 additions and 24 deletions
+8
View File
@@ -27,6 +27,14 @@ input {
} }
} }
// You can configure outputs by their name, which you can find with wayland-info(1).
// The built-in laptop monitor is usually called "eDP-1".
// Remember to uncommend the node by removing "/-"!
/-output "eDP-1" {
// Scale is a floating-point number, but at the moment only integer values work.
scale 2.0
}
binds { binds {
// Keys consist of modifiers separated by + signs, followed by an XKB key name // Keys consist of modifiers separated by + signs, followed by an XKB key name
// in the end. To find an XKB name for a particular key, you may use a program // in the end. To find an XKB name for a particular key, you may use a program
+14 -2
View File
@@ -21,7 +21,7 @@ use smithay::backend::session::libseat::LibSeatSession;
use smithay::backend::session::{Event as SessionEvent, Session}; use smithay::backend::session::{Event as SessionEvent, Session};
use smithay::backend::udev::{self, UdevBackend, UdevEvent}; use smithay::backend::udev::{self, UdevBackend, UdevEvent};
use smithay::desktop::utils::OutputPresentationFeedback; use smithay::desktop::utils::OutputPresentationFeedback;
use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpixel}; use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpixel, Scale};
use smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken}; use smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken};
use smithay::reexports::drm::control::{ use smithay::reexports::drm::control::{
connector, crtc, Mode as DrmMode, ModeFlags, ModeTypeFlags, connector, crtc, Mode as DrmMode, ModeFlags, ModeTypeFlags,
@@ -500,6 +500,15 @@ impl Tty {
); );
debug!("connecting connector: {output_name}"); debug!("connecting connector: {output_name}");
let config = self
.config
.borrow()
.outputs
.iter()
.find(|o| o.name == output_name)
.cloned()
.unwrap_or_default();
let device = self.output_device.as_mut().unwrap(); let device = self.output_device.as_mut().unwrap();
let mut mode = connector.modes().get(0); let mut mode = connector.modes().get(0);
@@ -534,6 +543,9 @@ impl Tty {
.map(|info| (info.manufacturer, info.model)) .map(|info| (info.manufacturer, info.model))
.unwrap_or_else(|| ("Unknown".into(), "Unknown".into())); .unwrap_or_else(|| ("Unknown".into(), "Unknown".into()));
let scale = config.scale.clamp(0.1, 10.);
let scale = scale.max(1.).round() as i32;
let output = Output::new( let output = Output::new(
output_name.clone(), output_name.clone(),
PhysicalProperties { PhysicalProperties {
@@ -544,7 +556,7 @@ impl Tty {
}, },
); );
let wl_mode = Mode::from(*mode); let wl_mode = Mode::from(*mode);
output.change_current_state(Some(wl_mode), None, None, Some((0, 0).into())); output.change_current_state(Some(wl_mode), None, Some(Scale::Integer(scale)), None);
output.set_preferred(wl_mode); output.set_preferred(wl_mode);
output.user_data().insert_if_missing(|| TtyOutputState { output.user_data().insert_if_missing(|| TtyOutputState {
+12 -2
View File
@@ -8,7 +8,7 @@ use smithay::backend::renderer::damage::OutputDamageTracker;
use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::{DebugFlags, Renderer}; use smithay::backend::renderer::{DebugFlags, Renderer};
use smithay::backend::winit::{self, WinitError, WinitEvent, WinitGraphicsBackend}; use smithay::backend::winit::{self, WinitError, WinitEvent, WinitGraphicsBackend};
use smithay::output::{Mode, Output, PhysicalProperties, Subpixel}; use smithay::output::{Mode, Output, PhysicalProperties, Scale, Subpixel};
use smithay::reexports::calloop::timer::{TimeoutAction, Timer}; use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
use smithay::reexports::calloop::LoopHandle; use smithay::reexports::calloop::LoopHandle;
use smithay::reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback; use smithay::reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback;
@@ -38,6 +38,16 @@ impl Winit {
.with_title("niri"); .with_title("niri");
let (backend, mut winit_event_loop) = winit::init_from_builder(builder).unwrap(); let (backend, mut winit_event_loop) = winit::init_from_builder(builder).unwrap();
let output_config = config
.borrow()
.outputs
.iter()
.find(|o| o.name == "winit")
.cloned()
.unwrap_or_default();
let scale = output_config.scale.clamp(0.1, 10.);
let scale = scale.max(1.).round() as i32;
let mode = Mode { let mode = Mode {
size: backend.window_size().physical_size, size: backend.window_size().physical_size,
refresh: 60_000, refresh: 60_000,
@@ -55,8 +65,8 @@ impl Winit {
output.change_current_state( output.change_current_state(
Some(mode), Some(mode),
Some(Transform::Flipped180), Some(Transform::Flipped180),
Some(Scale::Integer(scale)),
None, None,
Some((0, 0).into()),
); );
output.set_preferred(mode); output.set_preferred(mode);
+27
View File
@@ -11,6 +11,8 @@ use smithay::input::keyboard::Keysym;
pub struct Config { pub struct Config {
#[knuffel(child, default)] #[knuffel(child, default)]
pub input: Input, pub input: Input,
#[knuffel(children(name = "output"))]
pub outputs: Vec<Output>,
#[knuffel(child, default)] #[knuffel(child, default)]
pub binds: Binds, pub binds: Binds,
#[knuffel(child, default)] #[knuffel(child, default)]
@@ -62,6 +64,23 @@ pub struct Touchpad {
pub accel_speed: f64, pub accel_speed: f64,
} }
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
pub struct Output {
#[knuffel(argument)]
pub name: String,
#[knuffel(child, unwrap(argument), default = 1.)]
pub scale: f64,
}
impl Default for Output {
fn default() -> Self {
Self {
name: String::new(),
scale: 1.,
}
}
}
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)] #[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)]
pub struct Binds(#[knuffel(children)] pub Vec<Bind>); pub struct Binds(#[knuffel(children)] pub Vec<Bind>);
@@ -263,6 +282,10 @@ mod tests {
} }
} }
output "eDP-1" {
scale 2.0
}
binds { binds {
Mod+T { spawn "alacritty"; } Mod+T { spawn "alacritty"; }
Mod+Q { close-window; } Mod+Q { close-window; }
@@ -293,6 +316,10 @@ mod tests {
accel_speed: 0.2, accel_speed: 0.2,
}, },
}, },
outputs: vec![Output {
name: "eDP-1".to_owned(),
scale: 2.,
}],
binds: Binds(vec![ binds: Binds(vec![
Bind { Bind {
key: Key { key: Key {
+16 -7
View File
@@ -1231,6 +1231,7 @@ impl Monitor<Window> {
&self, &self,
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
) -> Vec<MonitorRenderElement<GlesRenderer>> { ) -> Vec<MonitorRenderElement<GlesRenderer>> {
let output_scale = Scale::from(self.output.current_scale().fractional_scale());
let output_transform = self.output.current_transform(); let output_transform = self.output.current_transform();
let output_mode = self.output.current_mode().unwrap(); let output_mode = self.output.current_mode().unwrap();
let output_size = output_transform.transform_size(output_mode.size); let output_size = output_transform.transform_size(output_mode.size);
@@ -1251,7 +1252,7 @@ impl Monitor<Window> {
Some(RelocateRenderElement::from_element( Some(RelocateRenderElement::from_element(
CropRenderElement::from_element( CropRenderElement::from_element(
elem, elem,
1., output_scale,
Rectangle::from_loc_and_size((0, 0), output_size), Rectangle::from_loc_and_size((0, 0), output_size),
)?, )?,
(0, -offset), (0, -offset),
@@ -1262,7 +1263,7 @@ impl Monitor<Window> {
Some(RelocateRenderElement::from_element( Some(RelocateRenderElement::from_element(
CropRenderElement::from_element( CropRenderElement::from_element(
elem, elem,
1., output_scale,
Rectangle::from_loc_and_size((0, 0), output_size), Rectangle::from_loc_and_size((0, 0), output_size),
)?, )?,
(0, -offset + output_size.h), (0, -offset + output_size.h),
@@ -1279,7 +1280,7 @@ impl Monitor<Window> {
Some(RelocateRenderElement::from_element( Some(RelocateRenderElement::from_element(
CropRenderElement::from_element( CropRenderElement::from_element(
elem, elem,
1., output_scale,
Rectangle::from_loc_and_size((0, 0), output_size), Rectangle::from_loc_and_size((0, 0), output_size),
)?, )?,
(0, 0), (0, 0),
@@ -1823,6 +1824,14 @@ impl Workspace<Window> {
return vec![]; return vec![];
} }
// FIXME: workspaces should probably cache their last used scale so they can be correctly
// rendered even with no outputs connected.
let output_scale = self
.output
.as_ref()
.map(|o| Scale::from(o.current_scale().fractional_scale()))
.unwrap_or(Scale::from(1.));
let mut rv = vec![]; let mut rv = vec![];
let view_pos = self.view_pos(); let view_pos = self.view_pos();
@@ -1840,8 +1849,8 @@ impl Workspace<Window> {
} }
rv.extend(active_win.render_elements( rv.extend(active_win.render_elements(
renderer, renderer,
win_pos.to_physical(1), win_pos.to_physical_precise_round(output_scale),
Scale::from(1.), output_scale,
1., 1.,
)); ));
@@ -1862,8 +1871,8 @@ impl Workspace<Window> {
rv.extend(win.render_elements( rv.extend(win.render_elements(
renderer, renderer,
win_pos.to_physical(1), win_pos.to_physical_precise_round(output_scale),
Scale::from(1.), output_scale,
1., 1.,
)); ));
} }
+24 -13
View File
@@ -799,6 +799,7 @@ impl Niri {
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
output: &Output, output: &Output,
) -> Vec<OutputRenderElements<GlesRenderer>> { ) -> Vec<OutputRenderElements<GlesRenderer>> {
let output_scale = Scale::from(output.current_scale().fractional_scale());
let output_pos = self.global_space.output_geometry(output).unwrap().loc; let output_pos = self.global_space.output_geometry(output).unwrap().loc;
let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64(); let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64();
@@ -825,7 +826,7 @@ impl Niri {
} else { } else {
default_hotspot default_hotspot
}; };
let pointer_pos = (pointer_pos - hotspot.to_f64()).to_physical_precise_round(1.); let pointer_pos = (pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
let mut pointer_elements = match &self.cursor_image { let mut pointer_elements = match &self.cursor_image {
CursorImageStatus::Hidden => vec![], CursorImageStatus::Hidden => vec![],
@@ -843,7 +844,7 @@ impl Niri {
renderer, renderer,
surface, surface,
pointer_pos, pointer_pos,
1., output_scale,
1., 1.,
Kind::Cursor, Kind::Cursor,
), ),
@@ -854,7 +855,7 @@ impl Niri {
renderer, renderer,
dnd_icon, dnd_icon,
pointer_pos, pointer_pos,
1., output_scale,
1., 1.,
Kind::Unspecified, Kind::Unspecified,
)); ));
@@ -871,6 +872,8 @@ impl Niri {
) -> Vec<OutputRenderElements<GlesRenderer>> { ) -> Vec<OutputRenderElements<GlesRenderer>> {
let _span = tracy_client::span!("Niri::render"); let _span = tracy_client::span!("Niri::render");
let output_scale = Scale::from(output.current_scale().fractional_scale());
// Get monitor elements. // Get monitor elements.
let mon = self.monitor_set.monitor_for_output(output).unwrap(); let mon = self.monitor_set.monitor_for_output(output).unwrap();
let monitor_elements = mon.render_elements(renderer); let monitor_elements = mon.render_elements(renderer);
@@ -901,8 +904,8 @@ impl Niri {
surface surface
.render_elements( .render_elements(
renderer, renderer,
loc.to_physical_precise_round(1.), loc.to_physical_precise_round(output_scale),
Scale::from(1.), output_scale,
1., 1.,
) )
.into_iter() .into_iter()
@@ -926,8 +929,8 @@ impl Niri {
surface surface
.render_elements( .render_elements(
renderer, renderer,
loc.to_physical_precise_round(1.), loc.to_physical_precise_round(output_scale),
Scale::from(1.), output_scale,
1., 1.,
) )
.into_iter() .into_iter()
@@ -1084,6 +1087,7 @@ impl Niri {
let _span = tracy_client::span!("Niri::send_for_screen_cast"); let _span = tracy_client::span!("Niri::send_for_screen_cast");
let size = output.current_mode().unwrap().size; let size = output.current_mode().unwrap().size;
let scale = Scale::from(output.current_scale().fractional_scale());
for cast in &mut self.casts { for cast in &mut self.casts {
if !cast.is_active.get() { if !cast.is_active.get() {
@@ -1119,7 +1123,7 @@ impl Niri {
let dmabuf = cast.dmabufs.borrow()[&fd].clone(); let dmabuf = cast.dmabufs.borrow()[&fd].clone();
// FIXME: Hidden / embedded / metadata cursor // FIXME: Hidden / embedded / metadata cursor
render_to_dmabuf(backend.renderer(), dmabuf, size, elements).unwrap(); render_to_dmabuf(backend.renderer(), dmabuf, size, scale, elements).unwrap();
let maxsize = data.as_raw().maxsize; let maxsize = data.as_raw().maxsize;
let chunk = data.chunk_mut(); let chunk = data.chunk_mut();
@@ -1139,8 +1143,9 @@ impl Niri {
let _span = tracy_client::span!("Niri::screenshot"); let _span = tracy_client::span!("Niri::screenshot");
let size = output.current_mode().unwrap().size; let size = output.current_mode().unwrap().size;
let scale = Scale::from(output.current_scale().fractional_scale());
let elements = self.render(renderer, output, true); let elements = self.render(renderer, output, true);
let pixels = render_to_vec(renderer, size, &elements)?; let pixels = render_to_vec(renderer, size, scale, &elements)?;
let path = make_screenshot_path().context("error making screenshot path")?; let path = make_screenshot_path().context("error making screenshot path")?;
debug!("saving screenshot to {path:?}"); debug!("saving screenshot to {path:?}");
@@ -1174,6 +1179,7 @@ impl Niri {
let outputs: Vec<_> = self.global_space.outputs().cloned().collect(); let outputs: Vec<_> = self.global_space.outputs().cloned().collect();
for output in outputs { for output in outputs {
let geom = self.global_space.output_geometry(&output).unwrap(); let geom = self.global_space.output_geometry(&output).unwrap();
// FIXME: this does not work when outputs can have non-1 scale.
let geom = geom.to_physical(1); let geom = geom.to_physical(1);
size.w = max(size.w, geom.loc.x + geom.size.w); size.w = max(size.w, geom.loc.x + geom.size.w);
@@ -1185,7 +1191,8 @@ impl Niri {
})); }));
} }
let pixels = render_to_vec(renderer, size, &elements)?; // FIXME: scale.
let pixels = render_to_vec(renderer, size, Scale::from(1.), &elements)?;
let path = make_screenshot_path().context("error making screenshot path")?; let path = make_screenshot_path().context("error making screenshot path")?;
debug!("saving screenshot to {path:?}"); debug!("saving screenshot to {path:?}");
@@ -1232,6 +1239,7 @@ impl ClientData for ClientState {
fn render_and_download( fn render_and_download(
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
size: Size<i32, Physical>, size: Size<i32, Physical>,
scale: Scale<f64>,
elements: &[impl RenderElement<GlesRenderer>], elements: &[impl RenderElement<GlesRenderer>],
) -> anyhow::Result<GlesMapping> { ) -> anyhow::Result<GlesMapping> {
let _span = tracy_client::span!("render_and_download"); let _span = tracy_client::span!("render_and_download");
@@ -1255,7 +1263,7 @@ fn render_and_download(
for element in elements.iter().rev() { for element in elements.iter().rev() {
let src = element.src(); let src = element.src();
let dst = element.geometry(Scale::from(1.)); let dst = element.geometry(scale);
element element
.draw(&mut frame, src, dst, &[output_rect]) .draw(&mut frame, src, dst, &[output_rect])
.context("error drawing element")?; .context("error drawing element")?;
@@ -1273,11 +1281,13 @@ fn render_and_download(
fn render_to_vec( fn render_to_vec(
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
size: Size<i32, Physical>, size: Size<i32, Physical>,
scale: Scale<f64>,
elements: &[impl RenderElement<GlesRenderer>], elements: &[impl RenderElement<GlesRenderer>],
) -> anyhow::Result<Vec<u8>> { ) -> anyhow::Result<Vec<u8>> {
let _span = tracy_client::span!("render_to_vec"); let _span = tracy_client::span!("render_to_vec");
let mapping = render_and_download(renderer, size, elements).context("error rendering")?; let mapping =
render_and_download(renderer, size, scale, elements).context("error rendering")?;
let copy = renderer let copy = renderer
.map_texture(&mapping) .map_texture(&mapping)
.context("error mapping texture")?; .context("error mapping texture")?;
@@ -1288,6 +1298,7 @@ fn render_to_dmabuf(
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
dmabuf: Dmabuf, dmabuf: Dmabuf,
size: Size<i32, Physical>, size: Size<i32, Physical>,
scale: Scale<f64>,
elements: &[OutputRenderElements<GlesRenderer>], elements: &[OutputRenderElements<GlesRenderer>],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let _span = tracy_client::span!("render_to_dmabuf"); let _span = tracy_client::span!("render_to_dmabuf");
@@ -1305,7 +1316,7 @@ fn render_to_dmabuf(
for element in elements.iter().rev() { for element in elements.iter().rev() {
let src = element.src(); let src = element.src();
let dst = element.geometry(Scale::from(1.)); let dst = element.geometry(scale);
element element
.draw(&mut frame, src, dst, &[output_rect]) .draw(&mut frame, src, dst, &[output_rect])
.context("error drawing element")?; .context("error drawing element")?;