wayland: Add support for wlr_output_configuration

This commit is contained in:
Victoria Brekenfeld 2022-04-13 22:59:14 +02:00
parent 1519942a63
commit edbfcfa2e5
8 changed files with 543 additions and 257 deletions

62
Cargo.lock generated
View file

@ -349,9 +349,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.13.2" version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@ -359,9 +359,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.13.2" version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989" checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
@ -373,9 +373,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.13.2" version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
@ -715,9 +715,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.56" version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -736,9 +736,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.121" version = "0.2.123"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -1087,18 +1087,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.17" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1311,7 +1311,7 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]] [[package]]
name = "smithay" name = "smithay"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/pop-os/smithay?branch=main#d8bf081cb5d2c6c666daaf238e2a6360ca963716" source = "git+https://github.com/pop-os/smithay?branch=main#a9aea2275645cc4b2fc290103cc2072f9a2bfbb1"
dependencies = [ dependencies = [
"appendlist", "appendlist",
"bitflags", "bitflags",
@ -1350,9 +1350,9 @@ dependencies = [
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"
version = "0.15.3" version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1325f292209cee78d5035530932422a30aa4c8fda1a16593ac083c1de211e68a" checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"calloop", "calloop",
@ -1395,9 +1395,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.90" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1534,9 +1534,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.79" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -1544,9 +1544,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.79" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@ -1559,9 +1559,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.79" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -1569,9 +1569,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.79" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1582,9 +1582,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.79" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"
@ -1692,9 +1692,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.56" version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

View file

@ -5,9 +5,9 @@ use crate::state::Fps;
use crate::{ use crate::{
backend::render, backend::render,
config::OutputConfig, config::{Config, OutputConfig},
shell::Shell,
state::{BackendData, Common, State}, state::{BackendData, Common, State},
utils::GlobalDrop,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -32,7 +32,7 @@ use smithay::{
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags}, drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
input::Libinput, input::Libinput,
nix::{fcntl::OFlag, sys::stat::dev_t}, nix::{fcntl::OFlag, sys::stat::dev_t},
wayland_server::{protocol::wl_output, Display}, wayland_server::protocol::wl_output,
}, },
utils::signaling::{Linkable, Signaler}, utils::signaling::{Linkable, Signaler},
wayland::output::{Mode as OutputMode, Output, PhysicalProperties}, wayland::output::{Mode as OutputMode, Output, PhysicalProperties},
@ -73,9 +73,9 @@ pub struct Device {
} }
pub struct Surface { pub struct Surface {
surface: GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>, surface: Option<GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>>,
connector: connector::Handle,
output: Output, output: Output,
_global: GlobalDrop<wl_output::WlOutput>,
last_submit: Option<DrmEventTime>, last_submit: Option<DrmEventTime>,
refresh_rate: u32, refresh_rate: u32,
vrr: bool, vrr: bool,
@ -241,8 +241,8 @@ impl State {
DrmEvent::VBlank(crtc) => { DrmEvent::VBlank(crtc) => {
if let Some(device) = state.backend.kms().devices.get_mut(&drm_node) { if let Some(device) = state.backend.kms().devices.get_mut(&drm_node) {
if let Some(surface) = device.surfaces.get_mut(&crtc) { if let Some(surface) = device.surfaces.get_mut(&crtc) {
match surface.surface.frame_submitted() { match surface.surface.as_mut().map(|x| x.frame_submitted()) {
Ok(_) => { Some(Ok(_)) => {
surface.last_submit = metadata.take().map(|data| data.time); surface.last_submit = metadata.take().map(|data| data.time);
surface.pending = false; surface.pending = false;
state state
@ -254,7 +254,10 @@ impl State {
state.common.start_time.elapsed().as_millis() as u32 state.common.start_time.elapsed().as_millis() as u32
); );
} }
Err(err) => slog_scope::warn!("Failed to submit frame: {}", err), Some(Err(err)) => {
slog_scope::warn!("Failed to submit frame: {}", err)
}
None => {} // got disabled
}; };
} }
} }
@ -293,23 +296,30 @@ impl State {
}; };
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
let mut wl_outputs = Vec::new();
for (crtc, conn) in outputs { for (crtc, conn) in outputs {
match device.setup_surface( match device.setup_surface(
&drm_node, &drm_node,
crtc, crtc,
conn, conn,
self.backend.kms().signaler.clone(), self.backend.kms().signaler.clone(),
&mut self.common.display.borrow_mut(),
&mut self.common.event_loop_handle, &mut self.common.event_loop_handle,
) { ) {
Ok(output) => self.common.shell.map_output( Ok(output) => {
&output, self.common.shell.map_output(
&mut self.backend, &output,
&mut self.common.config, &mut self.backend,
), &mut self.common.config,
);
wl_outputs.push(output);
}
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err), Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
}; };
} }
self.common.output_conf.add_heads(wl_outputs.iter());
self.common
.output_conf
.update(&mut *self.common.display.borrow_mut());
self.backend.kms().devices.insert(drm_node, device); self.backend.kms().devices.insert(drm_node, device);
Ok(()) Ok(())
@ -336,7 +346,6 @@ impl State {
crtc, crtc,
conn, conn,
signaler.clone(), signaler.clone(),
&mut self.common.display.borrow_mut(),
&mut self.common.event_loop_handle, &mut self.common.event_loop_handle,
) { ) {
Ok(output) => { Ok(output) => {
@ -347,16 +356,21 @@ impl State {
} }
} }
self.common.output_conf.remove_heads(outputs_removed.iter());
for output in outputs_removed.into_iter() { for output in outputs_removed.into_iter() {
self.common self.common
.shell .shell
.unmap_output(&output, &mut self.backend, &self.common.config); .unmap_output(&output, &mut self.backend, &mut self.common.config);
} }
self.common.output_conf.add_heads(outputs_added.iter());
for output in outputs_added.into_iter() { for output in outputs_added.into_iter() {
self.common self.common
.shell .shell
.map_output(&output, &mut self.backend, &mut self.common.config) .map_output(&output, &mut self.backend, &mut self.common.config)
} }
self.common
.output_conf
.update(&mut self.common.display.borrow_mut());
Ok(()) Ok(())
} }
@ -377,11 +391,15 @@ impl State {
self.common.event_loop_handle.remove(socket.token); self.common.event_loop_handle.remove(socket.token);
} }
} }
self.common.output_conf.remove_heads(outputs_removed.iter());
for output in outputs_removed.into_iter() { for output in outputs_removed.into_iter() {
self.common self.common
.shell .shell
.unmap_output(&output, &mut self.backend, &self.common.config); .unmap_output(&output, &mut self.backend, &mut self.common.config);
} }
self.common
.output_conf
.update(&mut *self.common.display.borrow_mut());
Ok(()) Ok(())
} }
@ -402,29 +420,18 @@ impl Device {
let surfaces = self let surfaces = self
.surfaces .surfaces
.iter() .iter()
.map(|(c, s)| (*c, s.surface.current_connectors().into_iter().next())) .map(|(c, s)| (*c, s.connector))
.collect::<HashMap<crtc::Handle, Option<connector::Handle>>>(); .collect::<HashMap<crtc::Handle, connector::Handle>>();
let added = config let added = config
.iter() .iter()
.filter(|(conn, crtc)| { .filter(|(conn, crtc)| surfaces.get(&crtc).map(|c| c != *conn).unwrap_or(true))
surfaces
.get(&crtc)
.map(|c| c.as_ref() != Some(*conn))
.unwrap_or(true)
})
.map(|(conn, crtc)| (crtc, conn)) .map(|(conn, crtc)| (crtc, conn))
.map(|(crtc, conn)| (*crtc, *conn)) .map(|(crtc, conn)| (*crtc, *conn))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let removed = surfaces let removed = surfaces
.iter() .iter()
.filter(|(crtc, conn)| { .filter(|(crtc, conn)| config.get(conn).map(|c| c != *crtc).unwrap_or(true))
if let Some(conn) = conn {
config.get(conn).map(|c| c != *crtc).unwrap_or(true)
} else {
true
}
})
.map(|(crtc, _)| *crtc) .map(|(crtc, _)| *crtc)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -437,7 +444,6 @@ impl Device {
crtc: crtc::Handle, crtc: crtc::Handle,
conn: connector::Handle, conn: connector::Handle,
signaler: Signaler<Signal>, signaler: Signaler<Signal>,
display: &mut Display,
loop_handle: &mut LoopHandle<'static, State>, loop_handle: &mut LoopHandle<'static, State>,
) -> Result<Output> { ) -> Result<Output> {
let drm = &mut *self.drm.as_source_mut(); let drm = &mut *self.drm.as_source_mut();
@ -446,10 +452,14 @@ impl Device {
let vrr = drm_helpers::set_vrr(drm, crtc, conn, true).unwrap_or(false); let vrr = drm_helpers::set_vrr(drm, crtc, conn, true).unwrap_or(false);
let interface = drm_helpers::interface_name(drm, conn)?; let interface = drm_helpers::interface_name(drm, conn)?;
let edid_info = drm_helpers::edid_info(drm, conn)?; let edid_info = drm_helpers::edid_info(drm, conn)?;
let mode = crtc_info.mode().unwrap_or_else(|| let mode = crtc_info.mode().unwrap_or_else(|| {
conn_info.modes().iter().find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED)) conn_info
.copied().unwrap_or(conn_info.modes()[0]) .modes()
); .iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.copied()
.unwrap_or(conn_info.modes()[0])
});
let refresh_rate = drm_helpers::calculate_refresh_rate(mode); let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
let mut surface = drm.create_surface(crtc, mode, &[conn])?; let mut surface = drm.create_surface(crtc, mode, &[conn])?;
surface.link(signaler); surface.link(signaler);
@ -464,8 +474,7 @@ impl Device {
refresh: refresh_rate as i32, refresh: refresh_rate as i32,
}; };
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0)); let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));
let (output, output_global) = Output::new( let output = Output::new(
display,
interface, interface,
PhysicalProperties { PhysicalProperties {
size: (phys_w as i32, phys_h as i32).into(), size: (phys_w as i32, phys_h as i32).into(),
@ -476,7 +485,15 @@ impl Device {
}, },
None, None,
); );
output.set_preferred(output_mode.clone()); for mode in conn_info.modes() {
let refresh_rate = drm_helpers::calculate_refresh_rate(*mode);
let mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32,
};
output.add_mode(mode);
}
output.set_preferred(output_mode);
output.change_current_state( output.change_current_state(
Some(output_mode), Some(output_mode),
// TODO: Readout property for monitor rotation // TODO: Readout property for monitor rotation
@ -486,10 +503,7 @@ impl Device {
); );
output.user_data().insert_if_missing(|| { output.user_data().insert_if_missing(|| {
RefCell::new(OutputConfig { RefCell::new(OutputConfig {
mode: ( mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
(output_mode.size.w, output_mode.size.h),
Some(format!("{}x{}@{}", mode.size().0, mode.size().1, refresh_rate)),
),
vrr, vrr,
..Default::default() ..Default::default()
}) })
@ -519,8 +533,8 @@ impl Device {
let data = Surface { let data = Surface {
output: output.clone(), output: output.clone(),
_global: output_global.into(), surface: Some(target),
surface: target, connector: conn,
vrr, vrr,
refresh_rate, refresh_rate,
last_submit: None, last_submit: None,
@ -545,6 +559,10 @@ impl Surface {
target_node: &DrmNode, target_node: &DrmNode,
state: &mut Common, state: &mut Common,
) -> Result<()> { ) -> Result<()> {
if self.surface.is_none() {
return Ok(());
}
let nodes = state let nodes = state
.shell .shell
.active_space(&self.output) .active_space(&self.output)
@ -578,8 +596,8 @@ impl Surface {
let mut renderer = api.renderer(render_node, &target_node).unwrap(); let mut renderer = api.renderer(render_node, &target_node).unwrap();
let (buffer, age) = self let surface = self.surface.as_mut().unwrap();
.surface let (buffer, age) = surface
.next_buffer() .next_buffer()
.with_context(|| "Failed to allocate buffer")?; .with_context(|| "Failed to allocate buffer")?;
@ -598,12 +616,12 @@ impl Surface {
&mut self.fps, &mut self.fps,
) { ) {
Ok(_) => { Ok(_) => {
self.surface surface
.queue_buffer() .queue_buffer()
.with_context(|| "Failed to submit buffer for display")?; .with_context(|| "Failed to submit buffer for display")?;
} }
Err(err) => { Err(err) => {
self.surface.reset_buffers(); surface.reset_buffers();
anyhow::bail!("Rendering failed: {}", err); anyhow::bail!("Rendering failed: {}", err);
} }
}; };
@ -615,67 +633,107 @@ impl KmsState {
pub fn apply_config_for_output( pub fn apply_config_for_output(
&mut self, &mut self,
output: &Output, output: &Output,
) -> Result<(), Box<dyn std::error::Error>> { config: &mut Config,
if let Some(device) = self shell: &mut Shell,
test_only: bool,
) -> Result<(), anyhow::Error> {
let recreated = if let Some(device) = self
.devices .devices
.values_mut() .values_mut()
.find(|dev| dev.surfaces.values().any(|s| s.output == *output)) .find(|dev| dev.surfaces.values().any(|s| s.output == *output))
{ {
let mut surface = device let (crtc, mut surface) = device
.surfaces .surfaces
.values_mut() .iter_mut()
.find(|s| s.output == *output) .find(|(_, s)| s.output == *output)
.unwrap(); .unwrap();
let config = output let output_config = output
.user_data() .user_data()
.get::<RefCell<OutputConfig>>() .get::<RefCell<OutputConfig>>()
.unwrap() .unwrap()
.borrow(); .borrow();
let conn_info = device.drm.as_source_ref().get_connector( if !output_config.enabled {
surface if !test_only {
.surface if surface.surface.take().is_some() {
.current_connectors() // just drop it
.into_iter() shell.disable_output(output, config);
.next() }
.unwrap_or_else( }
|| surface.surface false
.pending_connectors() } else {
.into_iter() let drm = &mut *device.drm.as_source_mut();
.next() let conn = surface.connector;
.unwrap() let conn_info = drm.get_connector(conn)?;
), let mode = conn_info
)?; .modes()
let mode = conn_info .iter()
.modes() .min_by_key(|mode| {
.iter() let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
.find(|mode| { (output_config.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode); })
format!("{}x{}@{}", mode.size().0, mode.size().1, refresh_rate) .ok_or(anyhow::anyhow!("Unknown mode"))?;
== *config.mode.1.as_ref().unwrap()
})
.ok_or(anyhow::anyhow!("Unknown mode"))?;
surface.surface.use_mode(*mode).unwrap();
if config.vrr != surface.vrr { if !test_only {
surface.vrr = drm_helpers::set_vrr( if let Some(gbm_surface) = surface.surface.as_mut() {
&*device.drm.as_source_ref(), if output_config.vrr != surface.vrr {
surface.surface.crtc(), surface.vrr = drm_helpers::set_vrr(
conn_info.handle(), drm,
config.vrr, *crtc,
)?; conn_info.handle(),
output_config.vrr,
)?;
}
gbm_surface.use_mode(*mode).unwrap();
false
} else {
surface.vrr = drm_helpers::set_vrr(drm, *crtc, conn, output_config.vrr)
.unwrap_or(false);
surface.refresh_rate = drm_helpers::calculate_refresh_rate(*mode);
let mut drm_surface = drm.create_surface(*crtc, *mode, &[conn])?;
drm_surface.link(self.signaler.clone());
let target = GbmBufferedSurface::new(
drm_surface,
device.allocator.clone(),
device.formats.clone(),
None,
)
.with_context(|| {
format!(
"Failed to initialize Gbm surface for {}",
drm_helpers::interface_name(drm, conn)
.unwrap_or_else(|_| String::from("Unknown"))
)
})?;
surface.surface = Some(target);
shell.enable_output(output, config);
true
}
} else {
false
}
} }
} else {
false
};
if recreated {
self.schedule_render(output);
} }
Ok(()) Ok(())
} }
pub fn schedule_render(&mut self, output: &Output) { pub fn schedule_render(&mut self, output: &Output) {
if let Some((device, surface)) = self if let Some((device, crtc, surface)) = self
.devices .devices
.iter_mut() .iter_mut()
.flat_map(|(node, d)| d.surfaces.values_mut().map(move |s| (node, s))) .flat_map(|(node, d)| d.surfaces.iter_mut().map(move |(c, s)| (node, c, s)))
.find(|(_, s)| s.output == *output) .find(|(_, _, s)| s.output == *output)
{ {
if surface.surface.is_none() {
return;
}
if !surface.pending { if !surface.pending {
surface.pending = true; surface.pending = true;
let duration = surface let duration = surface
@ -688,7 +746,7 @@ impl KmsState {
DrmEventTime::Realtime(time) => time.duration_since(SystemTime::now()).ok(), DrmEventTime::Realtime(time) => time.duration_since(SystemTime::now()).ok(),
}) })
.unwrap_or(Duration::ZERO); // + Duration::from_secs_f64((1.0 / surface.refresh_rate as f64) - 20.0); .unwrap_or(Duration::ZERO); // + Duration::from_secs_f64((1.0 / surface.refresh_rate as f64) - 20.0);
let data = (*device, surface.surface.crtc()); let data = (*device, *crtc);
if surface.vrr { if surface.vrr {
surface.render_timer.add_timeout(Duration::ZERO, data); surface.render_timer.add_timeout(Duration::ZERO, data);
} else { } else {

View file

@ -72,6 +72,32 @@ impl WinitState {
Ok(()) Ok(())
} }
pub fn apply_config_for_output(
&mut self,
output: &Output,
test_only: bool,
) -> Result<(), anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't ignore config.enabled
// reset size
let size = self.backend.borrow().window_size();
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
if dbg!(config.mode.0) != dbg!((size.physical_size.w as i32, size.physical_size.h as i32)) {
if !test_only {
config.mode = (
(size.physical_size.w as i32, size.physical_size.h as i32),
None,
);
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
}
}
} }
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> { pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
@ -93,7 +119,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
size: (size.physical_size.w as i32, size.physical_size.h as i32).into(), size: (size.physical_size.w as i32, size.physical_size.h as i32).into(),
refresh: 60_000, refresh: 60_000,
}; };
let (output, _global) = Output::new(&mut *state.common.display.borrow_mut(), name, props, None); let output = Output::new(name, props, None);
//let _global = global.into(); //let _global = global.into();
output.change_current_state( output.change_current_state(
Some(mode), Some(mode),
@ -146,7 +172,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
state.common.shell.unmap_output( state.common.shell.unmap_output(
&output, &output,
&mut state.backend, &mut state.backend,
&state.common.config, &mut state.common.config,
); );
if let Some(token) = token.take() { if let Some(token) = token.take() {
event_loop_handle.remove(token); event_loop_handle.remove(token);
@ -163,6 +189,11 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
fps: Fps::default(), fps: Fps::default(),
}); });
state.common.output_conf.add_heads(std::iter::once(&output));
state
.common
.output_conf
.update(&mut *state.common.display.borrow_mut());
state state
.common .common
.shell .shell
@ -224,10 +255,22 @@ impl State {
size, size,
refresh: 60_000, refresh: 60_000,
}; };
{
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
config.mode.0 = size.into();
}
output.delete_mode(output.current_mode().unwrap()); output.delete_mode(output.current_mode().unwrap());
output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode); output.set_preferred(mode);
output.change_current_state(Some(mode), None, None, None);
layer_map_for_output(output).arrange(); layer_map_for_output(output).arrange();
self.common
.output_conf
.update(&mut *self.common.display.borrow_mut());
render_ping.ping(); render_ping.ping();
} }
WinitEvent::Refresh => render_ping.ping(), WinitEvent::Refresh => render_ping.ping(),

View file

@ -5,7 +5,6 @@ use crate::{
config::OutputConfig, config::OutputConfig,
input::{set_active_output, Devices}, input::{set_active_output, Devices},
state::{BackendData, Common, State}, state::{BackendData, Common, State},
utils::GlobalDrop,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use smithay::{ use smithay::{
@ -20,10 +19,7 @@ use smithay::{
reexports::{ reexports::{
calloop::{ping, EventLoop, LoopHandle}, calloop::{ping, EventLoop, LoopHandle},
gbm::{Device as GbmDevice, FdWrapper}, gbm::{Device as GbmDevice, FdWrapper},
wayland_server::{ wayland_server::{protocol::wl_output::Subpixel, Display},
protocol::wl_output::{Subpixel, WlOutput},
Display,
},
}, },
wayland::{ wayland::{
dmabuf::init_dmabuf_global, dmabuf::init_dmabuf_global,
@ -48,11 +44,7 @@ pub struct X11State {
} }
impl X11State { impl X11State {
pub fn add_window( pub fn add_window(&mut self, handle: LoopHandle<'_, State>) -> Result<Output> {
&mut self,
display: &mut Display,
handle: LoopHandle<'_, State>,
) -> Result<Output> {
let window = WindowBuilder::new() let window = WindowBuilder::new()
.title("COSMIC") .title("COSMIC")
.build(&self.handle) .build(&self.handle)
@ -83,8 +75,7 @@ impl X11State {
size: (size.w as i32, size.h as i32).into(), size: (size.w as i32, size.h as i32).into(),
refresh: 60_000, refresh: 60_000,
}; };
let (output, global) = Output::new(display, name, props, None); let output = Output::new(name, props, None);
let _global = global.into();
output.change_current_state(Some(mode), None, None, Some((0, 0).into())); output.change_current_state(Some(mode), None, None, Some((0, 0).into()));
output.set_preferred(mode); output.set_preferred(mode);
output.user_data().insert_if_missing(|| { output.user_data().insert_if_missing(|| {
@ -125,7 +116,6 @@ impl X11State {
pending: true, pending: true,
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
fps: Fps::default(), fps: Fps::default(),
_global,
}); });
// schedule first render // schedule first render
@ -141,6 +131,35 @@ impl X11State {
} }
} }
} }
pub fn apply_config_for_output(
&mut self,
output: &Output,
test_only: bool,
) -> Result<(), anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't ignore config.enabled
// reset size
let size = self
.surfaces
.iter()
.find(|s| s.output == *output)
.unwrap()
.window
.size();
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
if config.mode.0 != (size.w as i32, size.h as i32) {
if !test_only {
config.mode = ((size.w as i32, size.h as i32), None);
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
}
}
} }
pub struct Surface { pub struct Surface {
@ -152,7 +171,6 @@ pub struct Surface {
pending: bool, pending: bool,
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
fps: Fps, fps: Fps,
_global: GlobalDrop<WlOutput>,
} }
impl Surface { impl Surface {
@ -234,8 +252,13 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
let output = state let output = state
.backend .backend
.x11() .x11()
.add_window(&mut *state.common.display.borrow_mut(), event_loop.handle()) .add_window(event_loop.handle())
.with_context(|| "Failed to create wl_output")?; .with_context(|| "Failed to create wl_output")?;
state.common.output_conf.add_heads(std::iter::once(&output));
state
.common
.output_conf
.update(&mut *state.common.display.borrow_mut());
state state
.common .common
.shell .shell
@ -266,7 +289,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
state.common.shell.unmap_output( state.common.shell.unmap_output(
&output, &output,
&mut state.backend, &mut state.backend,
&state.common.config, &mut state.common.config,
); );
} }
} }
@ -287,10 +310,23 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
.find(|s| s.window.id() == window_id) .find(|s| s.window.id() == window_id)
{ {
let output = &surface.output; let output = &surface.output;
{
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
config.mode.0 = size.into();
}
output.delete_mode(output.current_mode().unwrap()); output.delete_mode(output.current_mode().unwrap());
output.change_current_state(Some(mode), None, None, None); output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode); output.set_preferred(mode);
layer_map_for_output(output).arrange(); layer_map_for_output(output).arrange();
state
.common
.output_conf
.update(&mut *state.common.display.borrow_mut());
surface.dirty = true; surface.dirty = true;
if !surface.pending { if !surface.pending {
surface.render.ping(); surface.render.ping();

View file

@ -52,14 +52,20 @@ impl From<Output> for OutputInfo {
} }
} }
fn default_enabled() -> bool {
true
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct OutputConfig { pub struct OutputConfig {
pub mode: ((i32, i32), Option<String>), pub mode: ((i32, i32), Option<u32>),
pub vrr: bool, pub vrr: bool,
pub scale: f64, pub scale: f64,
#[serde(with = "TransformDef")] #[serde(with = "TransformDef")]
pub transform: Transform, pub transform: Transform,
pub position: (i32, i32), pub position: (i32, i32),
#[serde(default = "default_enabled")]
pub enabled: bool,
} }
impl Default for OutputConfig { impl Default for OutputConfig {
@ -70,6 +76,7 @@ impl Default for OutputConfig {
scale: 1.0, scale: 1.0,
transform: Transform::Normal, transform: Transform::Normal,
position: (0, 0), position: (0, 0),
enabled: true,
} }
} }
} }
@ -79,14 +86,8 @@ impl OutputConfig {
self.mode.0.into() self.mode.0.into()
} }
pub fn mode_refresh(&self) -> i32 { pub fn mode_refresh(&self) -> u32 {
self.mode self.mode.1.unwrap_or(60_000)
.1
.as_deref()
.and_then(|x| x.split("@").nth(1))
.and_then(|x| str::parse::<f64>(x).ok())
.map(|x| x.round() as i32)
.unwrap_or(60)
} }
} }

View file

@ -143,11 +143,7 @@ impl FloatingLayout {
Default::default() Default::default()
} }
fn map_window( fn map_window(space: &mut Space, window: &Window, output: &Output) {
space: &mut Space,
window: &Window,
output: &Output,
) {
let win_geo = window.bbox(); let win_geo = window.bbox();
let layers = layer_map_for_output(&output); let layers = layer_map_for_output(&output);
let geometry = layers.non_exclusive_zone(); let geometry = layers.non_exclusive_zone();

View file

@ -92,43 +92,14 @@ impl Shell {
} }
} }
fn apply_config( fn refresh_config(&mut self, backend: &mut BackendData, config: &mut Config) -> bool {
output: &Output,
backend: &mut BackendData,
) -> Result<(), Box<dyn std::error::Error>> {
backend.apply_config_for_output(output)?;
let final_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let mode = Some(OutputMode {
size: final_config.mode_size(),
refresh: final_config.mode_refresh(),
})
.filter(|m| match output.current_mode() {
None => true,
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
});
let transform =
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
let scale = Some(final_config.scale.ceil() as i32).filter(|x| *x != output.current_scale());
let location =
Some(final_config.position.into()).filter(|x| *x != output.current_location());
output.change_current_state(mode, transform, scale, location);
Ok(())
}
fn refresh_config(&mut self, backend: &mut BackendData, config: &Config) -> bool {
let mut infos = self let mut infos = self
.outputs() .outputs()
.cloned() .cloned()
.map(Into::<crate::config::OutputInfo>::into) .map(Into::<crate::config::OutputInfo>::into)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
infos.sort(); infos.sort();
if let Some(configs) = config.dynamic_conf.outputs().config.get(&infos) { if let Some(configs) = config.dynamic_conf.outputs().config.get(&infos).cloned() {
let mut reset = false; let mut reset = false;
let known_good_configs = self let known_good_configs = self
.outputs .outputs
@ -143,18 +114,20 @@ impl Shell {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (name, config) in infos for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
.iter()
.map(|o| &o.connector)
.zip(configs.into_iter().cloned())
{ {
let output = self.outputs.iter().find(|o| &o.name() == name).unwrap(); let output = self
.outputs
.iter()
.find(|o| &o.name() == name)
.unwrap()
.clone();
*output *output
.user_data() .user_data()
.get::<RefCell<OutputConfig>>() .get::<RefCell<OutputConfig>>()
.unwrap() .unwrap()
.borrow_mut() = config; .borrow_mut() = output_config;
if let Err(err) = Self::apply_config(output, backend) { if let Err(err) = backend.apply_config_for_output(&output, false, config, self) {
slog_scope::warn!( slog_scope::warn!(
"Failed to set new config for output {}: {}", "Failed to set new config for output {}: {}",
output.name(), output.name(),
@ -166,13 +139,19 @@ impl Shell {
} }
if reset { if reset {
for (output, config) in self.outputs.iter().zip(known_good_configs.into_iter()) { for (output, output_config) in self
.outputs
.clone()
.into_iter()
.zip(known_good_configs.into_iter())
{
*output *output
.user_data() .user_data()
.get::<RefCell<OutputConfig>>() .get::<RefCell<OutputConfig>>()
.unwrap() .unwrap()
.borrow_mut() = config; .borrow_mut() = output_config;
if let Err(err) = Self::apply_config(output, backend) { if let Err(err) = backend.apply_config_for_output(&output, false, config, self)
{
slog_scope::error!( slog_scope::error!(
"Failed to reset config for output {}: {}", "Failed to reset config for output {}: {}",
output.name(), output.name(),
@ -212,6 +191,30 @@ impl Shell {
} }
} }
pub fn save_config(&self, config: &mut Config) {
let mut infos = self
.outputs()
.cloned()
.map(|o| {
(
Into::<crate::config::OutputInfo>::into(o.clone()),
o.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone(),
)
})
.collect::<Vec<(OutputInfo, OutputConfig)>>();
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
let (infos, configs) = infos.into_iter().unzip();
config
.dynamic_conf
.outputs_mut()
.config
.insert(infos, configs);
}
fn assign_next_free_output<'a>( fn assign_next_free_output<'a>(
spaces: &'a mut [Workspace], spaces: &'a mut [Workspace],
output: &Output, output: &Output,
@ -233,9 +236,49 @@ impl Shell {
workspace workspace
} }
fn add_output(&mut self, output: &Output) {
let config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
match self.mode {
Mode::OutputBound => {
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
workspace.space.map_output(output, config.scale, (0, 0));
}
Mode::Global { active } => {
let workspace = &mut self.spaces[active];
workspace
.space
.map_output(output, config.scale, config.position);
}
}
}
fn remove_output(&mut self, output: &Output) {
match self.mode {
Mode::OutputBound => {
if let Some(idx) = output
.user_data()
.get::<ActiveWorkspace>()
.and_then(|a| a.get())
{
self.spaces[idx].space.unmap_output(output);
self.outputs.retain(|o| o != output);
}
}
Mode::Global { active } => {
self.spaces[active].space.unmap_output(output);
self.outputs.retain(|o| o != output);
// TODO move windows and outputs farther on the right / or load save config for remaining monitors
}
}
}
pub fn map_output(&mut self, output: &Output, backend: &mut BackendData, config: &mut Config) { pub fn map_output(&mut self, output: &Output, backend: &mut BackendData, config: &mut Config) {
self.outputs.push(output.clone()); self.outputs.push(output.clone());
if !self.refresh_config(backend, config) { if !self.refresh_config(backend, config) {
let new_pos_x = self let new_pos_x = self
.outputs() .outputs()
@ -267,64 +310,32 @@ impl Shell {
.position = new_pos; .position = new_pos;
output.change_current_state(None, None, None, Some(new_pos.into())); output.change_current_state(None, None, None, Some(new_pos.into()));
match self.mode { self.add_output(output);
Mode::OutputBound => { self.save_config(config);
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
workspace.space.map_output(output, 1.0, (0, 0));
}
Mode::Global { active } => {
// just put new outputs on the right of the previous ones.
// in the future we will only need that as a fallback and need to read saved configurations here
let workspace = &mut self.spaces[active];
workspace.space.map_output(output, 1.0, new_pos);
}
}
let mut infos = self
.outputs()
.cloned()
.map(|o| {
(
Into::<crate::config::OutputInfo>::into(o.clone()),
o.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow()
.clone(),
)
})
.collect::<Vec<(OutputInfo, OutputConfig)>>();
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
let (infos, configs) = infos.into_iter().unzip();
config
.dynamic_conf
.outputs_mut()
.config
.insert(infos, configs);
} }
} }
pub fn unmap_output(&mut self, output: &Output, backend: &mut BackendData, config: &Config) { pub fn unmap_output(
match self.mode { &mut self,
Mode::OutputBound => { output: &Output,
if let Some(idx) = output backend: &mut BackendData,
.user_data() config: &mut Config,
.get::<ActiveWorkspace>() ) {
.and_then(|a| a.get()) self.remove_output(output);
{
self.spaces[idx].space.unmap_output(output);
self.outputs.retain(|o| o != output);
}
}
Mode::Global { active } => {
self.spaces[active].space.unmap_output(output);
self.outputs.retain(|o| o != output);
// TODO move windows and outputs farther on the right / or load save config for remaining monitors
}
}
self.refresh_config(backend, config); self.refresh_config(backend, config);
} }
pub fn enable_output(&mut self, output: &Output, config: &mut Config) {
self.outputs.push(output.clone());
self.add_output(output);
self.save_config(config);
}
pub fn disable_output(&mut self, output: &Output, config: &mut Config) {
self.remove_output(output);
self.save_config(config);
}
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> { pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
let workspace = self.active_space(output); let workspace = self.active_space(output);
workspace workspace

View file

@ -2,7 +2,7 @@
use crate::{ use crate::{
backend::{kms::KmsState, winit::WinitState, x11::X11State}, backend::{kms::KmsState, winit::WinitState, x11::X11State},
config::Config, config::{Config, OutputConfig},
logger::LogState, logger::LogState,
shell::{init_shell, Shell}, shell::{init_shell, Shell},
}; };
@ -13,7 +13,13 @@ use smithay::{
}, },
wayland::{ wayland::{
data_device::{default_action_chooser, init_data_device, DataDeviceEvent}, data_device::{default_action_chooser, init_data_device, DataDeviceEvent},
output::{xdg::init_xdg_output_manager, Output}, output::{
wlr_configuration::{
self, init_wlr_output_configuration, ConfigurationManager, ModeConfiguration,
},
xdg::init_xdg_output_manager,
Mode as OutputMode, Output,
},
seat::Seat, seat::Seat,
shell::xdg::ToplevelSurface, shell::xdg::ToplevelSurface,
shm::init_shm_global, shm::init_shm_global,
@ -42,6 +48,7 @@ pub struct Common {
pub socket: OsString, pub socket: OsString,
pub event_loop_handle: LoopHandle<'static, State>, pub event_loop_handle: LoopHandle<'static, State>,
pub output_conf: ConfigurationManager,
pub shell: Shell, pub shell: Shell,
pub pending_toplevels: Vec<ToplevelSurface>, pub pending_toplevels: Vec<ToplevelSurface>,
pub dirty_flag: Arc<AtomicBool>, pub dirty_flag: Arc<AtomicBool>,
@ -108,14 +115,45 @@ impl BackendData {
pub fn apply_config_for_output( pub fn apply_config_for_output(
&mut self, &mut self,
output: &Output, output: &Output,
) -> Result<(), Box<dyn std::error::Error>> { test_only: bool,
match self { config: &mut Config,
BackendData::Kms(ref mut state) => state.apply_config_for_output(output), shell: &mut Shell,
_ => { ) -> Result<(), anyhow::Error> {
// TODO: reset the mode for nested backends, because we have no control over it let result = match self {
Ok(()) BackendData::Kms(ref mut state) => {
state.apply_config_for_output(output, config, shell, test_only)
} }
BackendData::Winit(ref mut state) => state.apply_config_for_output(output, test_only),
BackendData::X11(ref mut state) => state.apply_config_for_output(output, test_only),
_ => unreachable!("No backend set when applying output config"),
};
if result.is_ok() {
// apply to Output
let final_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow();
let mode = Some(OutputMode {
size: final_config.mode_size(),
refresh: final_config.mode_refresh() as i32,
})
.filter(|m| match output.current_mode() {
None => true,
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
});
let transform =
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
let scale =
Some(final_config.scale.ceil() as i32).filter(|x| *x != output.current_scale());
let location =
Some(final_config.position.into()).filter(|x| *x != output.current_location());
output.change_current_state(mode, transform, scale, location);
shell.save_config(config);
} }
result
} }
pub fn schedule_render(&mut self, output: &Output) { pub fn schedule_render(&mut self, output: &Output) {
@ -176,6 +214,108 @@ impl State {
default_action_chooser, default_action_chooser,
None, None,
); );
let (output_conf, _) = init_wlr_output_configuration(
&mut display,
|_| true,
|conf, test_only, mut ddata| {
let state = ddata.get::<State>().unwrap();
if conf.iter().all(|(_, conf)| conf.is_none()) {
return false; // we don't allow the user to accidentally disable all their outputs
}
let mut backups = Vec::new();
for (output, conf) in &conf {
{
let mut current_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
backups.push((output, current_config.clone()));
if let Some(conf) = conf {
match conf.mode {
Some(ModeConfiguration::Mode(mode)) => {
current_config.mode =
((mode.size.w, mode.size.h), Some(mode.refresh as u32));
}
Some(ModeConfiguration::Custom { size, refresh }) => {
current_config.mode =
((size.w, size.h), refresh.map(|x| x as u32));
}
_ => {}
}
if let Some(scale) = conf.scale {
current_config.scale = scale;
}
if let Some(transform) = conf.transform {
current_config.transform = transform;
}
if let Some(position) = conf.position {
current_config.position = position.into();
}
current_config.enabled = true;
} else {
current_config.enabled = false;
}
}
if let Err(err) = state.backend.apply_config_for_output(
output,
test_only,
&mut state.common.config,
&mut state.common.shell,
) {
slog_scope::warn!(
"Failed to apply config to {}: {}. Resetting",
output.name(),
err
);
for (output, backup) in backups {
{
let mut current_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
*current_config = backup;
}
if !test_only {
if let Err(err) = state.backend.apply_config_for_output(
output,
false,
&mut state.common.config,
&mut state.common.shell,
) {
slog_scope::error!(
"Failed to reset output config for {}: {}",
output.name(),
err
);
}
}
}
return false;
}
}
for output in conf.iter().filter(|(_, c)| c.is_some()).map(|(o, _)| o) {
wlr_configuration::enable_head(output);
}
for output in conf.iter().filter(|(_, c)| c.is_none()).map(|(o, _)| o) {
wlr_configuration::disable_head(output);
}
state.common.event_loop_handle.insert_idle(move |state| {
state
.common
.output_conf
.update(&mut *state.common.display.borrow_mut());
});
true
},
None,
);
#[cfg(not(feature = "debug"))] #[cfg(not(feature = "debug"))]
let dirty_flag = Arc::new(AtomicBool::new(false)); let dirty_flag = Arc::new(AtomicBool::new(false));
@ -189,6 +329,7 @@ impl State {
socket, socket,
event_loop_handle: handle, event_loop_handle: handle,
output_conf,
shell, shell,
pending_toplevels: Vec::new(), pending_toplevels: Vec::new(),
dirty_flag, dirty_flag,