wayland: Add support for wlr_output_configuration
This commit is contained in:
parent
1519942a63
commit
edbfcfa2e5
8 changed files with 543 additions and 257 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
|
@ -349,9 +349,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
|
|
@ -359,9 +359,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
|
|
@ -373,9 +373,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
|
|
@ -715,9 +715,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
version = "0.3.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
|
@ -736,9 +736,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.121"
|
||||
version = "0.2.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
|
@ -1087,18 +1087,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -1311,7 +1311,7 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
|||
[[package]]
|
||||
name = "smithay"
|
||||
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 = [
|
||||
"appendlist",
|
||||
"bitflags",
|
||||
|
|
@ -1350,9 +1350,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.15.3"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1325f292209cee78d5035530932422a30aa4c8fda1a16593ac083c1de211e68a"
|
||||
checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"calloop",
|
||||
|
|
@ -1395,9 +1395,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.90"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1534,9 +1534,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
|
|
@ -1544,9 +1544,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
|
|
@ -1559,9 +1559,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -1569,9 +1569,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1582,9 +1582,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
|
|
@ -1692,9 +1692,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.56"
|
||||
version = "0.3.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use crate::state::Fps;
|
|||
|
||||
use crate::{
|
||||
backend::render,
|
||||
config::OutputConfig,
|
||||
config::{Config, OutputConfig},
|
||||
shell::Shell,
|
||||
state::{BackendData, Common, State},
|
||||
utils::GlobalDrop,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
|
@ -32,7 +32,7 @@ use smithay::{
|
|||
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
|
||||
input::Libinput,
|
||||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||
wayland_server::{protocol::wl_output, Display},
|
||||
wayland_server::protocol::wl_output,
|
||||
},
|
||||
utils::signaling::{Linkable, Signaler},
|
||||
wayland::output::{Mode as OutputMode, Output, PhysicalProperties},
|
||||
|
|
@ -73,9 +73,9 @@ pub struct Device {
|
|||
}
|
||||
|
||||
pub struct Surface {
|
||||
surface: GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>,
|
||||
surface: Option<GbmBufferedSurface<Rc<RefCell<GbmDevice<SessionFd>>>, SessionFd>>,
|
||||
connector: connector::Handle,
|
||||
output: Output,
|
||||
_global: GlobalDrop<wl_output::WlOutput>,
|
||||
last_submit: Option<DrmEventTime>,
|
||||
refresh_rate: u32,
|
||||
vrr: bool,
|
||||
|
|
@ -241,8 +241,8 @@ impl State {
|
|||
DrmEvent::VBlank(crtc) => {
|
||||
if let Some(device) = state.backend.kms().devices.get_mut(&drm_node) {
|
||||
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
||||
match surface.surface.frame_submitted() {
|
||||
Ok(_) => {
|
||||
match surface.surface.as_mut().map(|x| x.frame_submitted()) {
|
||||
Some(Ok(_)) => {
|
||||
surface.last_submit = metadata.take().map(|data| data.time);
|
||||
surface.pending = false;
|
||||
state
|
||||
|
|
@ -254,7 +254,10 @@ impl State {
|
|||
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 mut wl_outputs = Vec::new();
|
||||
for (crtc, conn) in outputs {
|
||||
match device.setup_surface(
|
||||
&drm_node,
|
||||
crtc,
|
||||
conn,
|
||||
self.backend.kms().signaler.clone(),
|
||||
&mut self.common.display.borrow_mut(),
|
||||
&mut self.common.event_loop_handle,
|
||||
) {
|
||||
Ok(output) => self.common.shell.map_output(
|
||||
&output,
|
||||
&mut self.backend,
|
||||
&mut self.common.config,
|
||||
),
|
||||
Ok(output) => {
|
||||
self.common.shell.map_output(
|
||||
&output,
|
||||
&mut self.backend,
|
||||
&mut self.common.config,
|
||||
);
|
||||
wl_outputs.push(output);
|
||||
}
|
||||
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);
|
||||
Ok(())
|
||||
|
|
@ -336,7 +346,6 @@ impl State {
|
|||
crtc,
|
||||
conn,
|
||||
signaler.clone(),
|
||||
&mut self.common.display.borrow_mut(),
|
||||
&mut self.common.event_loop_handle,
|
||||
) {
|
||||
Ok(output) => {
|
||||
|
|
@ -347,16 +356,21 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
self.common.output_conf.remove_heads(outputs_removed.iter());
|
||||
for output in outputs_removed.into_iter() {
|
||||
self.common
|
||||
.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() {
|
||||
self.common
|
||||
.shell
|
||||
.map_output(&output, &mut self.backend, &mut self.common.config)
|
||||
}
|
||||
self.common
|
||||
.output_conf
|
||||
.update(&mut self.common.display.borrow_mut());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -377,11 +391,15 @@ impl State {
|
|||
self.common.event_loop_handle.remove(socket.token);
|
||||
}
|
||||
}
|
||||
self.common.output_conf.remove_heads(outputs_removed.iter());
|
||||
for output in outputs_removed.into_iter() {
|
||||
self.common
|
||||
.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(())
|
||||
}
|
||||
|
|
@ -402,29 +420,18 @@ impl Device {
|
|||
let surfaces = self
|
||||
.surfaces
|
||||
.iter()
|
||||
.map(|(c, s)| (*c, s.surface.current_connectors().into_iter().next()))
|
||||
.collect::<HashMap<crtc::Handle, Option<connector::Handle>>>();
|
||||
.map(|(c, s)| (*c, s.connector))
|
||||
.collect::<HashMap<crtc::Handle, connector::Handle>>();
|
||||
|
||||
let added = config
|
||||
.iter()
|
||||
.filter(|(conn, crtc)| {
|
||||
surfaces
|
||||
.get(&crtc)
|
||||
.map(|c| c.as_ref() != Some(*conn))
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.filter(|(conn, crtc)| surfaces.get(&crtc).map(|c| c != *conn).unwrap_or(true))
|
||||
.map(|(conn, crtc)| (crtc, conn))
|
||||
.map(|(crtc, conn)| (*crtc, *conn))
|
||||
.collect::<Vec<_>>();
|
||||
let removed = surfaces
|
||||
.iter()
|
||||
.filter(|(crtc, conn)| {
|
||||
if let Some(conn) = conn {
|
||||
config.get(conn).map(|c| c != *crtc).unwrap_or(true)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.filter(|(crtc, conn)| config.get(conn).map(|c| c != *crtc).unwrap_or(true))
|
||||
.map(|(crtc, _)| *crtc)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
@ -437,7 +444,6 @@ impl Device {
|
|||
crtc: crtc::Handle,
|
||||
conn: connector::Handle,
|
||||
signaler: Signaler<Signal>,
|
||||
display: &mut Display,
|
||||
loop_handle: &mut LoopHandle<'static, State>,
|
||||
) -> Result<Output> {
|
||||
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 interface = drm_helpers::interface_name(drm, conn)?;
|
||||
let edid_info = drm_helpers::edid_info(drm, conn)?;
|
||||
let mode = crtc_info.mode().unwrap_or_else(||
|
||||
conn_info.modes().iter().find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
|
||||
.copied().unwrap_or(conn_info.modes()[0])
|
||||
);
|
||||
let mode = crtc_info.mode().unwrap_or_else(|| {
|
||||
conn_info
|
||||
.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 mut surface = drm.create_surface(crtc, mode, &[conn])?;
|
||||
surface.link(signaler);
|
||||
|
|
@ -464,8 +474,7 @@ impl Device {
|
|||
refresh: refresh_rate as i32,
|
||||
};
|
||||
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));
|
||||
let (output, output_global) = Output::new(
|
||||
display,
|
||||
let output = Output::new(
|
||||
interface,
|
||||
PhysicalProperties {
|
||||
size: (phys_w as i32, phys_h as i32).into(),
|
||||
|
|
@ -476,7 +485,15 @@ impl Device {
|
|||
},
|
||||
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(
|
||||
Some(output_mode),
|
||||
// TODO: Readout property for monitor rotation
|
||||
|
|
@ -486,10 +503,7 @@ impl Device {
|
|||
);
|
||||
output.user_data().insert_if_missing(|| {
|
||||
RefCell::new(OutputConfig {
|
||||
mode: (
|
||||
(output_mode.size.w, output_mode.size.h),
|
||||
Some(format!("{}x{}@{}", mode.size().0, mode.size().1, refresh_rate)),
|
||||
),
|
||||
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
|
||||
vrr,
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -519,8 +533,8 @@ impl Device {
|
|||
|
||||
let data = Surface {
|
||||
output: output.clone(),
|
||||
_global: output_global.into(),
|
||||
surface: target,
|
||||
surface: Some(target),
|
||||
connector: conn,
|
||||
vrr,
|
||||
refresh_rate,
|
||||
last_submit: None,
|
||||
|
|
@ -545,6 +559,10 @@ impl Surface {
|
|||
target_node: &DrmNode,
|
||||
state: &mut Common,
|
||||
) -> Result<()> {
|
||||
if self.surface.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let nodes = state
|
||||
.shell
|
||||
.active_space(&self.output)
|
||||
|
|
@ -578,8 +596,8 @@ impl Surface {
|
|||
|
||||
let mut renderer = api.renderer(render_node, &target_node).unwrap();
|
||||
|
||||
let (buffer, age) = self
|
||||
.surface
|
||||
let surface = self.surface.as_mut().unwrap();
|
||||
let (buffer, age) = surface
|
||||
.next_buffer()
|
||||
.with_context(|| "Failed to allocate buffer")?;
|
||||
|
||||
|
|
@ -598,12 +616,12 @@ impl Surface {
|
|||
&mut self.fps,
|
||||
) {
|
||||
Ok(_) => {
|
||||
self.surface
|
||||
surface
|
||||
.queue_buffer()
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
}
|
||||
Err(err) => {
|
||||
self.surface.reset_buffers();
|
||||
surface.reset_buffers();
|
||||
anyhow::bail!("Rendering failed: {}", err);
|
||||
}
|
||||
};
|
||||
|
|
@ -615,67 +633,107 @@ impl KmsState {
|
|||
pub fn apply_config_for_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(device) = self
|
||||
config: &mut Config,
|
||||
shell: &mut Shell,
|
||||
test_only: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let recreated = if let Some(device) = self
|
||||
.devices
|
||||
.values_mut()
|
||||
.find(|dev| dev.surfaces.values().any(|s| s.output == *output))
|
||||
{
|
||||
let mut surface = device
|
||||
let (crtc, mut surface) = device
|
||||
.surfaces
|
||||
.values_mut()
|
||||
.find(|s| s.output == *output)
|
||||
.iter_mut()
|
||||
.find(|(_, s)| s.output == *output)
|
||||
.unwrap();
|
||||
let config = output
|
||||
let output_config = output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow();
|
||||
|
||||
let conn_info = device.drm.as_source_ref().get_connector(
|
||||
surface
|
||||
.surface
|
||||
.current_connectors()
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(
|
||||
|| surface.surface
|
||||
.pending_connectors()
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
),
|
||||
)?;
|
||||
let mode = conn_info
|
||||
.modes()
|
||||
.iter()
|
||||
.find(|mode| {
|
||||
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
|
||||
format!("{}x{}@{}", mode.size().0, mode.size().1, refresh_rate)
|
||||
== *config.mode.1.as_ref().unwrap()
|
||||
})
|
||||
.ok_or(anyhow::anyhow!("Unknown mode"))?;
|
||||
surface.surface.use_mode(*mode).unwrap();
|
||||
if !output_config.enabled {
|
||||
if !test_only {
|
||||
if surface.surface.take().is_some() {
|
||||
// just drop it
|
||||
shell.disable_output(output, config);
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
let drm = &mut *device.drm.as_source_mut();
|
||||
let conn = surface.connector;
|
||||
let conn_info = drm.get_connector(conn)?;
|
||||
let mode = conn_info
|
||||
.modes()
|
||||
.iter()
|
||||
.min_by_key(|mode| {
|
||||
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
|
||||
(output_config.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
|
||||
})
|
||||
.ok_or(anyhow::anyhow!("Unknown mode"))?;
|
||||
|
||||
if config.vrr != surface.vrr {
|
||||
surface.vrr = drm_helpers::set_vrr(
|
||||
&*device.drm.as_source_ref(),
|
||||
surface.surface.crtc(),
|
||||
conn_info.handle(),
|
||||
config.vrr,
|
||||
)?;
|
||||
if !test_only {
|
||||
if let Some(gbm_surface) = surface.surface.as_mut() {
|
||||
if output_config.vrr != surface.vrr {
|
||||
surface.vrr = drm_helpers::set_vrr(
|
||||
drm,
|
||||
*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(())
|
||||
}
|
||||
|
||||
pub fn schedule_render(&mut self, output: &Output) {
|
||||
if let Some((device, surface)) = self
|
||||
if let Some((device, crtc, surface)) = self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.flat_map(|(node, d)| d.surfaces.values_mut().map(move |s| (node, s)))
|
||||
.find(|(_, s)| s.output == *output)
|
||||
.flat_map(|(node, d)| d.surfaces.iter_mut().map(move |(c, s)| (node, c, s)))
|
||||
.find(|(_, _, s)| s.output == *output)
|
||||
{
|
||||
if surface.surface.is_none() {
|
||||
return;
|
||||
}
|
||||
if !surface.pending {
|
||||
surface.pending = true;
|
||||
let duration = surface
|
||||
|
|
@ -688,7 +746,7 @@ impl KmsState {
|
|||
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);
|
||||
let data = (*device, surface.surface.crtc());
|
||||
let data = (*device, *crtc);
|
||||
if surface.vrr {
|
||||
surface.render_timer.add_timeout(Duration::ZERO, data);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,32 @@ impl WinitState {
|
|||
|
||||
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<()> {
|
||||
|
|
@ -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(),
|
||||
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();
|
||||
output.change_current_state(
|
||||
Some(mode),
|
||||
|
|
@ -146,7 +172,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
|||
state.common.shell.unmap_output(
|
||||
&output,
|
||||
&mut state.backend,
|
||||
&state.common.config,
|
||||
&mut state.common.config,
|
||||
);
|
||||
if let Some(token) = token.take() {
|
||||
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")]
|
||||
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
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -224,10 +255,22 @@ impl State {
|
|||
size,
|
||||
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.change_current_state(Some(mode), None, None, None);
|
||||
output.set_preferred(mode);
|
||||
output.change_current_state(Some(mode), None, None, None);
|
||||
layer_map_for_output(output).arrange();
|
||||
self.common
|
||||
.output_conf
|
||||
.update(&mut *self.common.display.borrow_mut());
|
||||
render_ping.ping();
|
||||
}
|
||||
WinitEvent::Refresh => render_ping.ping(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use crate::{
|
|||
config::OutputConfig,
|
||||
input::{set_active_output, Devices},
|
||||
state::{BackendData, Common, State},
|
||||
utils::GlobalDrop,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use smithay::{
|
||||
|
|
@ -20,10 +19,7 @@ use smithay::{
|
|||
reexports::{
|
||||
calloop::{ping, EventLoop, LoopHandle},
|
||||
gbm::{Device as GbmDevice, FdWrapper},
|
||||
wayland_server::{
|
||||
protocol::wl_output::{Subpixel, WlOutput},
|
||||
Display,
|
||||
},
|
||||
wayland_server::{protocol::wl_output::Subpixel, Display},
|
||||
},
|
||||
wayland::{
|
||||
dmabuf::init_dmabuf_global,
|
||||
|
|
@ -48,11 +44,7 @@ pub struct X11State {
|
|||
}
|
||||
|
||||
impl X11State {
|
||||
pub fn add_window(
|
||||
&mut self,
|
||||
display: &mut Display,
|
||||
handle: LoopHandle<'_, State>,
|
||||
) -> Result<Output> {
|
||||
pub fn add_window(&mut self, handle: LoopHandle<'_, State>) -> Result<Output> {
|
||||
let window = WindowBuilder::new()
|
||||
.title("COSMIC")
|
||||
.build(&self.handle)
|
||||
|
|
@ -83,8 +75,7 @@ impl X11State {
|
|||
size: (size.w as i32, size.h as i32).into(),
|
||||
refresh: 60_000,
|
||||
};
|
||||
let (output, global) = Output::new(display, name, props, None);
|
||||
let _global = global.into();
|
||||
let output = Output::new(name, props, None);
|
||||
output.change_current_state(Some(mode), None, None, Some((0, 0).into()));
|
||||
output.set_preferred(mode);
|
||||
output.user_data().insert_if_missing(|| {
|
||||
|
|
@ -125,7 +116,6 @@ impl X11State {
|
|||
pending: true,
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps::default(),
|
||||
_global,
|
||||
});
|
||||
|
||||
// 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 {
|
||||
|
|
@ -152,7 +171,6 @@ pub struct Surface {
|
|||
pending: bool,
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps,
|
||||
_global: GlobalDrop<WlOutput>,
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
|
|
@ -234,8 +252,13 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
|||
let output = state
|
||||
.backend
|
||||
.x11()
|
||||
.add_window(&mut *state.common.display.borrow_mut(), event_loop.handle())
|
||||
.add_window(event_loop.handle())
|
||||
.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
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -266,7 +289,7 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
|||
state.common.shell.unmap_output(
|
||||
&output,
|
||||
&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)
|
||||
{
|
||||
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.change_current_state(Some(mode), None, None, None);
|
||||
output.set_preferred(mode);
|
||||
layer_map_for_output(output).arrange();
|
||||
state
|
||||
.common
|
||||
.output_conf
|
||||
.update(&mut *state.common.display.borrow_mut());
|
||||
surface.dirty = true;
|
||||
if !surface.pending {
|
||||
surface.render.ping();
|
||||
|
|
|
|||
|
|
@ -52,14 +52,20 @@ impl From<Output> for OutputInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fn default_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
pub struct OutputConfig {
|
||||
pub mode: ((i32, i32), Option<String>),
|
||||
pub mode: ((i32, i32), Option<u32>),
|
||||
pub vrr: bool,
|
||||
pub scale: f64,
|
||||
#[serde(with = "TransformDef")]
|
||||
pub transform: Transform,
|
||||
pub position: (i32, i32),
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for OutputConfig {
|
||||
|
|
@ -70,6 +76,7 @@ impl Default for OutputConfig {
|
|||
scale: 1.0,
|
||||
transform: Transform::Normal,
|
||||
position: (0, 0),
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,14 +86,8 @@ impl OutputConfig {
|
|||
self.mode.0.into()
|
||||
}
|
||||
|
||||
pub fn mode_refresh(&self) -> i32 {
|
||||
self.mode
|
||||
.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)
|
||||
pub fn mode_refresh(&self) -> u32 {
|
||||
self.mode.1.unwrap_or(60_000)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,11 +143,7 @@ impl FloatingLayout {
|
|||
Default::default()
|
||||
}
|
||||
|
||||
fn map_window(
|
||||
space: &mut Space,
|
||||
window: &Window,
|
||||
output: &Output,
|
||||
) {
|
||||
fn map_window(space: &mut Space, window: &Window, output: &Output) {
|
||||
let win_geo = window.bbox();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone();
|
||||
|
|
|
|||
199
src/shell/mod.rs
199
src/shell/mod.rs
|
|
@ -92,43 +92,14 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_config(
|
||||
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 {
|
||||
fn refresh_config(&mut self, backend: &mut BackendData, config: &mut Config) -> bool {
|
||||
let mut infos = self
|
||||
.outputs()
|
||||
.cloned()
|
||||
.map(Into::<crate::config::OutputInfo>::into)
|
||||
.collect::<Vec<_>>();
|
||||
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 known_good_configs = self
|
||||
.outputs
|
||||
|
|
@ -143,18 +114,20 @@ impl Shell {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (name, config) in infos
|
||||
.iter()
|
||||
.map(|o| &o.connector)
|
||||
.zip(configs.into_iter().cloned())
|
||||
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
||||
{
|
||||
let output = self.outputs.iter().find(|o| &o.name() == name).unwrap();
|
||||
let output = self
|
||||
.outputs
|
||||
.iter()
|
||||
.find(|o| &o.name() == name)
|
||||
.unwrap()
|
||||
.clone();
|
||||
*output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = config;
|
||||
if let Err(err) = Self::apply_config(output, backend) {
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, config, self) {
|
||||
slog_scope::warn!(
|
||||
"Failed to set new config for output {}: {}",
|
||||
output.name(),
|
||||
|
|
@ -166,13 +139,19 @@ impl Shell {
|
|||
}
|
||||
|
||||
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
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut() = config;
|
||||
if let Err(err) = Self::apply_config(output, backend) {
|
||||
.borrow_mut() = output_config;
|
||||
if let Err(err) = backend.apply_config_for_output(&output, false, config, self)
|
||||
{
|
||||
slog_scope::error!(
|
||||
"Failed to reset config for output {}: {}",
|
||||
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>(
|
||||
spaces: &'a mut [Workspace],
|
||||
output: &Output,
|
||||
|
|
@ -233,9 +236,49 @@ impl Shell {
|
|||
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) {
|
||||
self.outputs.push(output.clone());
|
||||
|
||||
if !self.refresh_config(backend, config) {
|
||||
let new_pos_x = self
|
||||
.outputs()
|
||||
|
|
@ -267,64 +310,32 @@ impl Shell {
|
|||
.position = new_pos;
|
||||
output.change_current_state(None, None, None, Some(new_pos.into()));
|
||||
|
||||
match self.mode {
|
||||
Mode::OutputBound => {
|
||||
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);
|
||||
self.add_output(output);
|
||||
self.save_config(config);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmap_output(&mut self, output: &Output, backend: &mut BackendData, config: &Config) {
|
||||
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 unmap_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
backend: &mut BackendData,
|
||||
config: &mut Config,
|
||||
) {
|
||||
self.remove_output(output);
|
||||
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> {
|
||||
let workspace = self.active_space(output);
|
||||
workspace
|
||||
|
|
|
|||
157
src/state.rs
157
src/state.rs
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::{
|
||||
backend::{kms::KmsState, winit::WinitState, x11::X11State},
|
||||
config::Config,
|
||||
config::{Config, OutputConfig},
|
||||
logger::LogState,
|
||||
shell::{init_shell, Shell},
|
||||
};
|
||||
|
|
@ -13,7 +13,13 @@ use smithay::{
|
|||
},
|
||||
wayland::{
|
||||
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,
|
||||
shell::xdg::ToplevelSurface,
|
||||
shm::init_shm_global,
|
||||
|
|
@ -42,6 +48,7 @@ pub struct Common {
|
|||
pub socket: OsString,
|
||||
pub event_loop_handle: LoopHandle<'static, State>,
|
||||
|
||||
pub output_conf: ConfigurationManager,
|
||||
pub shell: Shell,
|
||||
pub pending_toplevels: Vec<ToplevelSurface>,
|
||||
pub dirty_flag: Arc<AtomicBool>,
|
||||
|
|
@ -108,14 +115,45 @@ impl BackendData {
|
|||
pub fn apply_config_for_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
BackendData::Kms(ref mut state) => state.apply_config_for_output(output),
|
||||
_ => {
|
||||
// TODO: reset the mode for nested backends, because we have no control over it
|
||||
Ok(())
|
||||
test_only: bool,
|
||||
config: &mut Config,
|
||||
shell: &mut Shell,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let result = match self {
|
||||
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) {
|
||||
|
|
@ -176,6 +214,108 @@ impl State {
|
|||
default_action_chooser,
|
||||
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"))]
|
||||
let dirty_flag = Arc::new(AtomicBool::new(false));
|
||||
|
|
@ -189,6 +329,7 @@ impl State {
|
|||
socket,
|
||||
event_loop_handle: handle,
|
||||
|
||||
output_conf,
|
||||
shell,
|
||||
pending_toplevels: Vec::new(),
|
||||
dirty_flag,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue