shell: load/update output configuration
This commit is contained in:
parent
a9d6b8c3d7
commit
ec861fadd4
11 changed files with 481 additions and 152 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -349,9 +349,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.13.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
|
checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e"
|
||||||
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.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
|
checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
|
|
@ -373,9 +373,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.13.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
|
checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -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#f3e6921298e061ed4289c68bbbec798a2534d132"
|
source = "git+https://github.com/pop-os/smithay?branch=main#d8bf081cb5d2c6c666daaf238e2a6360ca963716"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::state::Fps;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render,
|
backend::render,
|
||||||
|
config::OutputConfig,
|
||||||
state::{BackendData, Common, State},
|
state::{BackendData, Common, State},
|
||||||
utils::GlobalDrop,
|
utils::GlobalDrop,
|
||||||
};
|
};
|
||||||
|
|
@ -28,7 +29,7 @@ use smithay::{
|
||||||
timer::{Timer, TimerHandle},
|
timer::{Timer, TimerHandle},
|
||||||
Dispatcher, EventLoop, LoopHandle, RegistrationToken,
|
Dispatcher, EventLoop, LoopHandle, RegistrationToken,
|
||||||
},
|
},
|
||||||
drm::control::{connector, crtc, Device as ControlDevice},
|
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, Display},
|
||||||
|
|
@ -301,7 +302,11 @@ impl State {
|
||||||
&mut self.common.display.borrow_mut(),
|
&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(&output, &mut self.backend, &self.common.config),
|
Ok(output) => self.common.shell.map_output(
|
||||||
|
&output,
|
||||||
|
&mut self.backend,
|
||||||
|
&mut self.common.config,
|
||||||
|
),
|
||||||
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -313,6 +318,8 @@ impl State {
|
||||||
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
|
fn device_changed(&mut self, dev: dev_t) -> Result<()> {
|
||||||
let drm_node = DrmNode::from_dev_id(dev)?;
|
let drm_node = DrmNode::from_dev_id(dev)?;
|
||||||
let signaler = self.backend.kms().signaler.clone();
|
let signaler = self.backend.kms().signaler.clone();
|
||||||
|
let mut outputs_removed = Vec::new();
|
||||||
|
let mut outputs_added = Vec::new();
|
||||||
if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) {
|
if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) {
|
||||||
let changes = device.enumerate_surfaces()?;
|
let changes = device.enumerate_surfaces()?;
|
||||||
for crtc in changes.removed {
|
for crtc in changes.removed {
|
||||||
|
|
@ -320,7 +327,7 @@ impl State {
|
||||||
if let Some(token) = surface.render_timer_token.take() {
|
if let Some(token) = surface.render_timer_token.take() {
|
||||||
self.common.event_loop_handle.remove(token);
|
self.common.event_loop_handle.remove(token);
|
||||||
}
|
}
|
||||||
self.common.shell.unmap_output(&surface.output);
|
outputs_removed.push(surface.output.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (crtc, conn) in changes.added {
|
for (crtc, conn) in changes.added {
|
||||||
|
|
@ -332,22 +339,36 @@ impl State {
|
||||||
&mut self.common.display.borrow_mut(),
|
&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(&output, &mut self.backend, &self.common.config),
|
Ok(output) => {
|
||||||
|
outputs_added.push(output);
|
||||||
|
}
|
||||||
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
Err(err) => slog_scope::warn!("Failed to initialize output: {}", err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for output in outputs_removed.into_iter() {
|
||||||
|
self.common
|
||||||
|
.shell
|
||||||
|
.unmap_output(&output, &mut self.backend, &self.common.config);
|
||||||
|
}
|
||||||
|
for output in outputs_added.into_iter() {
|
||||||
|
self.common
|
||||||
|
.shell
|
||||||
|
.map_output(&output, &mut self.backend, &mut self.common.config)
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_removed(&mut self, dev: dev_t) -> Result<()> {
|
fn device_removed(&mut self, dev: dev_t) -> Result<()> {
|
||||||
let drm_node = DrmNode::from_dev_id(dev)?;
|
let drm_node = DrmNode::from_dev_id(dev)?;
|
||||||
if let Some(device) = self.backend.kms().devices.get_mut(&drm_node) {
|
let mut outputs_removed = Vec::new();
|
||||||
|
if let Some(mut device) = self.backend.kms().devices.remove(&drm_node) {
|
||||||
for surface in device.surfaces.values_mut() {
|
for surface in device.surfaces.values_mut() {
|
||||||
if let Some(token) = surface.render_timer_token.take() {
|
if let Some(token) = surface.render_timer_token.take() {
|
||||||
self.common.event_loop_handle.remove(token);
|
self.common.event_loop_handle.remove(token);
|
||||||
}
|
}
|
||||||
self.common.shell.unmap_output(&surface.output);
|
outputs_removed.push(surface.output.clone());
|
||||||
}
|
}
|
||||||
if let Some(token) = device.event_token.take() {
|
if let Some(token) = device.event_token.take() {
|
||||||
self.common.event_loop_handle.remove(token);
|
self.common.event_loop_handle.remove(token);
|
||||||
|
|
@ -356,6 +377,12 @@ impl State {
|
||||||
self.common.event_loop_handle.remove(socket.token);
|
self.common.event_loop_handle.remove(socket.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for output in outputs_removed.into_iter() {
|
||||||
|
self.common
|
||||||
|
.shell
|
||||||
|
.unmap_output(&output, &mut self.backend, &self.common.config);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -419,7 +446,11 @@ 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(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])?;
|
let mut surface = drm.create_surface(crtc, mode, &[conn])?;
|
||||||
surface.link(signaler);
|
surface.link(signaler);
|
||||||
|
|
||||||
|
|
@ -430,7 +461,7 @@ impl Device {
|
||||||
//let is_nvidia = driver(drm.device_id()).ok().flatten().map(|x| x == "nvidia").unwrap_or(false);
|
//let is_nvidia = driver(drm.device_id()).ok().flatten().map(|x| x == "nvidia").unwrap_or(false);
|
||||||
let output_mode = OutputMode {
|
let output_mode = OutputMode {
|
||||||
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
|
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
|
||||||
refresh: (mode.vrefresh() * 1000) 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_global) = Output::new(
|
||||||
|
|
@ -453,6 +484,16 @@ impl Device {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
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)),
|
||||||
|
),
|
||||||
|
vrr,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let timer = Timer::new()?;
|
let timer = Timer::new()?;
|
||||||
let timer_handle = timer.handle();
|
let timer_handle = timer.handle();
|
||||||
|
|
@ -481,7 +522,7 @@ impl Device {
|
||||||
_global: output_global.into(),
|
_global: output_global.into(),
|
||||||
surface: target,
|
surface: target,
|
||||||
vrr,
|
vrr,
|
||||||
refresh_rate: drm_helpers::calculate_refresh_rate(mode),
|
refresh_rate,
|
||||||
last_submit: None,
|
last_submit: None,
|
||||||
pending: true,
|
pending: true,
|
||||||
render_timer: timer_handle,
|
render_timer: timer_handle,
|
||||||
|
|
@ -571,10 +612,63 @@ impl Surface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KmsState {
|
impl KmsState {
|
||||||
pub fn apply_config_for_output(&mut self, output: &Output) -> Result<(), impl std::error::Error> {
|
pub fn apply_config_for_output(
|
||||||
|
&mut self,
|
||||||
|
output: &Output,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if let Some(device) = self
|
||||||
|
.devices
|
||||||
|
.values_mut()
|
||||||
|
.find(|dev| dev.surfaces.values().any(|s| s.output == *output))
|
||||||
|
{
|
||||||
|
let mut surface = device
|
||||||
|
.surfaces
|
||||||
|
.values_mut()
|
||||||
|
.find(|s| s.output == *output)
|
||||||
|
.unwrap();
|
||||||
|
let 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 config.vrr != surface.vrr {
|
||||||
|
surface.vrr = drm_helpers::set_vrr(
|
||||||
|
&*device.drm.as_source_ref(),
|
||||||
|
surface.surface.crtc(),
|
||||||
|
conn_info.handle(),
|
||||||
|
config.vrr,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, surface)) = self
|
||||||
.devices
|
.devices
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render,
|
backend::render,
|
||||||
|
config::OutputConfig,
|
||||||
input::{set_active_output, Devices},
|
input::{set_active_output, Devices},
|
||||||
state::{BackendData, Common, State},
|
state::{BackendData, Common, State},
|
||||||
};
|
};
|
||||||
|
|
@ -101,6 +102,16 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
Some((0, 0).into()),
|
Some((0, 0).into()),
|
||||||
);
|
);
|
||||||
output.set_preferred(mode);
|
output.set_preferred(mode);
|
||||||
|
output.user_data().insert_if_missing(|| {
|
||||||
|
RefCell::new(OutputConfig {
|
||||||
|
mode: (
|
||||||
|
(size.physical_size.w as i32, size.physical_size.h as i32),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
transform: Transform::Flipped180.into(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let (event_ping, event_source) =
|
let (event_ping, event_source) =
|
||||||
ping::make_ping().with_context(|| "Failed to init eventloop timer for winit")?;
|
ping::make_ping().with_context(|| "Failed to init eventloop timer for winit")?;
|
||||||
|
|
@ -131,8 +142,12 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
render_ping_handle.ping();
|
render_ping_handle.ping();
|
||||||
}
|
}
|
||||||
Err(winit::WinitError::WindowClosed) => {
|
Err(winit::WinitError::WindowClosed) => {
|
||||||
let winit_state = state.backend.winit();
|
let output = state.backend.winit().output.clone();
|
||||||
state.common.shell.unmap_output(&winit_state.output);
|
state.common.shell.unmap_output(
|
||||||
|
&output,
|
||||||
|
&mut state.backend,
|
||||||
|
&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);
|
||||||
}
|
}
|
||||||
|
|
@ -144,11 +159,14 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
|
|
||||||
state.backend = BackendData::Winit(WinitState {
|
state.backend = BackendData::Winit(WinitState {
|
||||||
backend,
|
backend,
|
||||||
output,
|
output: output.clone(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
fps: Fps::default(),
|
fps: Fps::default(),
|
||||||
});
|
});
|
||||||
state.common.shell.map_output(&output, &mut state.backend, &state.common.config);
|
state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.map_output(&output, &mut state.backend, &mut state.common.config);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render,
|
backend::render,
|
||||||
|
config::OutputConfig,
|
||||||
input::{set_active_output, Devices},
|
input::{set_active_output, Devices},
|
||||||
state::{BackendData, Common, State},
|
state::{BackendData, Common, State},
|
||||||
utils::GlobalDrop,
|
utils::GlobalDrop,
|
||||||
|
|
@ -86,6 +87,12 @@ impl X11State {
|
||||||
let _global = global.into();
|
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(|| {
|
||||||
|
RefCell::new(OutputConfig {
|
||||||
|
mode: ((size.w as i32, size.h as i32), None),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let output_ref = output.clone();
|
let output_ref = output.clone();
|
||||||
let (ping, source) =
|
let (ping, source) =
|
||||||
|
|
@ -229,13 +236,17 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
.x11()
|
.x11()
|
||||||
.add_window(&mut *state.common.display.borrow_mut(), event_loop.handle())
|
.add_window(&mut *state.common.display.borrow_mut(), event_loop.handle())
|
||||||
.with_context(|| "Failed to create wl_output")?;
|
.with_context(|| "Failed to create wl_output")?;
|
||||||
state.common.shell.map_output(&output, &mut state.backend, &state.common.config);
|
state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.map_output(&output, &mut state.backend, &mut state.common.config);
|
||||||
|
|
||||||
event_loop
|
event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(backend, |event, _, state| match event {
|
.insert_source(backend, |event, _, state| match event {
|
||||||
X11Event::CloseRequested { window_id } => {
|
X11Event::CloseRequested { window_id } => {
|
||||||
// TODO: drain_filter
|
// TODO: drain_filter
|
||||||
|
let mut outputs_removed = Vec::new();
|
||||||
for surface in state
|
for surface in state
|
||||||
.backend
|
.backend
|
||||||
.x11()
|
.x11()
|
||||||
|
|
@ -244,13 +255,20 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
.filter(|s| s.window.id() == window_id)
|
.filter(|s| s.window.id() == window_id)
|
||||||
{
|
{
|
||||||
surface.window.unmap();
|
surface.window.unmap();
|
||||||
state.common.shell.unmap_output(&surface.output);
|
outputs_removed.push(surface.output.clone());
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
.backend
|
.backend
|
||||||
.x11()
|
.x11()
|
||||||
.surfaces
|
.surfaces
|
||||||
.retain(|s| s.window.id() != window_id);
|
.retain(|s| s.window.id() != window_id);
|
||||||
|
for output in outputs_removed.into_iter() {
|
||||||
|
state.common.shell.unmap_output(
|
||||||
|
&output,
|
||||||
|
&mut state.backend,
|
||||||
|
&state.common.config,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
X11Event::Resized {
|
X11Event::Resized {
|
||||||
new_size,
|
new_size,
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use serde::{Deserialize, Serialize};
|
||||||
use smithay::wayland::seat::Keysym;
|
use smithay::wayland::seat::Keysym;
|
||||||
pub use smithay::{
|
pub use smithay::{
|
||||||
backend::input::KeyState,
|
backend::input::KeyState,
|
||||||
|
utils::{Logical, Physical, Point, Size, Transform},
|
||||||
wayland::{
|
wayland::{
|
||||||
output::Output,
|
output::Output,
|
||||||
seat::{keysyms as KeySyms, ModifiersState as KeyModifiers},
|
seat::{keysyms as KeySyms, ModifiersState as KeyModifiers},
|
||||||
},
|
},
|
||||||
utils::{Point, Size, Logical, Physical, Transform},
|
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, fs::OpenOptions, path::PathBuf};
|
use std::{collections::HashMap, fs::OpenOptions, path::PathBuf};
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
@ -57,18 +57,36 @@ pub struct OutputConfig {
|
||||||
pub mode: ((i32, i32), Option<String>),
|
pub mode: ((i32, i32), Option<String>),
|
||||||
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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for OutputConfig {
|
||||||
|
fn default() -> OutputConfig {
|
||||||
|
OutputConfig {
|
||||||
|
mode: ((0, 0), None),
|
||||||
|
vrr: false,
|
||||||
|
scale: 1.0,
|
||||||
|
transform: Transform::Normal,
|
||||||
|
position: (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OutputConfig {
|
impl OutputConfig {
|
||||||
fn mode_size(&self) -> Size<i32, Physical> {
|
pub fn mode_size(&self) -> Size<i32, Physical> {
|
||||||
self.mode.0.into()
|
self.mode.0.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mode_refresh(&self) -> i32 {
|
pub fn mode_refresh(&self) -> i32 {
|
||||||
self.mode.1.as_deref().map(|x| str::parse::<f64>(x).unwrap().round() as i32).unwrap_or(60)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +146,8 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
||||||
let output_path = xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
let output_path =
|
||||||
|
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
||||||
let outputs = Self::load_outputs(&output_path);
|
let outputs = Self::load_outputs(&output_path);
|
||||||
|
|
||||||
DynamicConfig {
|
DynamicConfig {
|
||||||
|
|
@ -143,7 +162,9 @@ impl Config {
|
||||||
Ok(config) => return config,
|
Ok(config) => return config,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
slog_scope::warn!("Failed to read output_config ({}), resetting..", err);
|
||||||
std::fs::remove_file(path);
|
if let Err(err) = std::fs::remove_file(path) {
|
||||||
|
slog_scope::error!("Failed to remove output_config {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -173,13 +194,21 @@ impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
||||||
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(path) = self.0.as_ref() {
|
if let Some(path) = self.0.as_ref() {
|
||||||
let writer = match OpenOptions::new().write(true).open(path) {
|
let writer = match OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
Ok(writer) => writer,
|
Ok(writer) => writer,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ron::ser::to_writer(writer, &self.1);
|
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
||||||
|
slog_scope::warn!("Failed to persist {}: {}", path.display(), err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,8 @@ impl Common {
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we can handle global shortcuts and the like
|
// here we can handle global shortcuts and the like
|
||||||
for (binding, action) in self.config.static_conf.key_bindings.iter() {
|
for (binding, action) in self.config.static_conf.key_bindings.iter()
|
||||||
|
{
|
||||||
if state == KeyState::Pressed
|
if state == KeyState::Pressed
|
||||||
&& binding.modifiers == *modifiers
|
&& binding.modifiers == *modifiers
|
||||||
&& handle.raw_syms().contains(&binding.key)
|
&& handle.raw_syms().contains(&binding.key)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ use smithay::{
|
||||||
mod grabs;
|
mod grabs;
|
||||||
pub use self::grabs::*;
|
pub use self::grabs::*;
|
||||||
|
|
||||||
pub struct FloatingLayout;
|
#[derive(Debug, Default)]
|
||||||
|
pub struct FloatingLayout {
|
||||||
|
pending_windows: Vec<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Layout for FloatingLayout {
|
impl Layout for FloatingLayout {
|
||||||
fn map_window<'a>(
|
fn map_window<'a>(
|
||||||
|
|
@ -26,36 +29,26 @@ impl Layout for FloatingLayout {
|
||||||
seat: &Seat,
|
seat: &Seat,
|
||||||
_focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
_focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
||||||
) {
|
) {
|
||||||
let output = super::output_from_seat(Some(seat), space);
|
if let Some(output) = super::output_from_seat(Some(seat), space) {
|
||||||
let win_geo = window.bbox();
|
Self::map_window(space, window, &output)
|
||||||
let layers = layer_map_for_output(&output);
|
} else {
|
||||||
let geometry = layers.non_exclusive_zone();
|
self.pending_windows.push(window.clone());
|
||||||
|
|
||||||
let position = (
|
|
||||||
-geometry.loc.x + (geometry.size.w / 2) - (win_geo.size.w / 2),
|
|
||||||
-geometry.loc.y + (geometry.size.h / 2) - (win_geo.size.h / 2),
|
|
||||||
);
|
|
||||||
#[allow(irrefutable_let_patterns)]
|
|
||||||
if let Kind::Xdg(xdg) = &window.toplevel() {
|
|
||||||
let ret = xdg.with_pending_state(|state| {
|
|
||||||
state.states.unset(XdgState::TiledLeft);
|
|
||||||
state.states.unset(XdgState::TiledRight);
|
|
||||||
state.states.unset(XdgState::TiledTop);
|
|
||||||
state.states.unset(XdgState::TiledBottom);
|
|
||||||
});
|
|
||||||
if ret.is_ok() {
|
|
||||||
xdg.send_configure();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
space.map_window(&window, position, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self, _space: &mut Space) {
|
fn refresh(&mut self, space: &mut Space) {
|
||||||
|
self.pending_windows.retain(|w| w.toplevel().alive());
|
||||||
|
if let Some(output) = super::output_from_seat(None, space) {
|
||||||
|
for window in self.pending_windows.drain(..) {
|
||||||
|
Self::map_window(space, &window, &output);
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO make sure all windows are still visible on any output or move them
|
// TODO make sure all windows are still visible on any output or move them
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmap_window(&mut self, space: &mut Space, window: &Window) {
|
fn unmap_window(&mut self, space: &mut Space, window: &Window) {
|
||||||
space.unmap_window(window)
|
space.unmap_window(window);
|
||||||
|
self.pending_windows.retain(|w| w != window);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) {
|
fn maximize_request(&mut self, space: &mut Space, window: &Window, output: &Output) {
|
||||||
|
|
@ -144,3 +137,37 @@ impl Layout for FloatingLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FloatingLayout {
|
||||||
|
pub fn new() -> FloatingLayout {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
let position = (
|
||||||
|
-geometry.loc.x + (geometry.size.w / 2) - (win_geo.size.w / 2),
|
||||||
|
-geometry.loc.y + (geometry.size.h / 2) - (win_geo.size.h / 2),
|
||||||
|
);
|
||||||
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
||||||
|
let ret = xdg.with_pending_state(|state| {
|
||||||
|
state.states.unset(XdgState::TiledLeft);
|
||||||
|
state.states.unset(XdgState::TiledRight);
|
||||||
|
state.states.unset(XdgState::TiledTop);
|
||||||
|
state.states.unset(XdgState::TiledBottom);
|
||||||
|
});
|
||||||
|
if ret.is_ok() {
|
||||||
|
xdg.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
space.map_window(&window, position, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ pub trait Layout {
|
||||||
pub fn new_default_layout() -> Box<dyn Layout> {
|
pub fn new_default_layout() -> Box<dyn Layout> {
|
||||||
Box::new(combined::Combined::new(
|
Box::new(combined::Combined::new(
|
||||||
tiling::TilingLayout::new(),
|
tiling::TilingLayout::new(),
|
||||||
floating::FloatingLayout,
|
floating::FloatingLayout::new(),
|
||||||
|window| {
|
|window| {
|
||||||
if let Some(surface) = window.toplevel().get_surface() {
|
if let Some(surface) = window.toplevel().get_surface() {
|
||||||
with_states(surface, |states| {
|
with_states(surface, |states| {
|
||||||
|
|
@ -128,7 +128,7 @@ pub fn new_default_layout() -> Box<dyn Layout> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Output {
|
fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Option<Output> {
|
||||||
seat.and_then(|seat| {
|
seat.and_then(|seat| {
|
||||||
seat.user_data()
|
seat.user_data()
|
||||||
.get::<ActiveOutput>()
|
.get::<ActiveOutput>()
|
||||||
|
|
@ -139,5 +139,5 @@ fn output_from_seat(seat: Option<&Seat>, space: &Space) -> Output {
|
||||||
.cloned()
|
.cloned()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| space.outputs().next().cloned().unwrap())
|
.or_else(|| space.outputs().next().cloned())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ impl Layout for TilingLayout {
|
||||||
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
||||||
) -> Option<Window> {
|
) -> Option<Window> {
|
||||||
let output = super::output_from_seat(Some(seat), space);
|
let output = super::output_from_seat(Some(seat), space);
|
||||||
let idx = space.outputs().position(|o| *o == output).unwrap_or(0);
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
||||||
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
||||||
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
||||||
let mut node_id = last_active;
|
let mut node_id = last_active;
|
||||||
|
|
@ -141,7 +141,7 @@ impl Layout for TilingLayout {
|
||||||
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
||||||
) {
|
) {
|
||||||
let output = super::output_from_seat(Some(seat), space);
|
let output = super::output_from_seat(Some(seat), space);
|
||||||
let idx = space.outputs().position(|o| *o == output).unwrap_or(0);
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
||||||
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
||||||
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
||||||
if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) {
|
if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) {
|
||||||
|
|
@ -288,7 +288,7 @@ impl TilingLayout {
|
||||||
focus_stack: Option<Box<dyn Iterator<Item = &'a Window> + 'a>>,
|
focus_stack: Option<Box<dyn Iterator<Item = &'a Window> + 'a>>,
|
||||||
) {
|
) {
|
||||||
let output = super::output_from_seat(seat, space);
|
let output = super::output_from_seat(seat, space);
|
||||||
let idx = space.outputs().position(|o| *o == output).unwrap_or(0);
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
||||||
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
||||||
let new_window = Node::new(Data::Window(window.clone()));
|
let new_window = Node::new(Data::Window(window.clone()));
|
||||||
|
|
||||||
|
|
|
||||||
307
src/shell/mod.rs
307
src/shell/mod.rs
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, OutputInfo, OutputConfig},
|
config::{Config, OutputConfig, OutputInfo},
|
||||||
input::active_output,
|
input::active_output,
|
||||||
state::{BackendData, Common},
|
state::{BackendData, Common},
|
||||||
};
|
};
|
||||||
|
|
@ -10,8 +10,11 @@ pub use smithay::{
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
utils::{Logical, Point, Rectangle, Size},
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::with_states, output::{Mode as OutputMode, Output}, seat::Seat,
|
compositor::with_states,
|
||||||
shell::xdg::XdgToplevelSurfaceRoleAttributes, Serial, SERIAL_COUNTER,
|
output::{Mode as OutputMode, Output},
|
||||||
|
seat::Seat,
|
||||||
|
shell::xdg::XdgToplevelSurfaceRoleAttributes,
|
||||||
|
Serial, SERIAL_COUNTER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -89,46 +92,74 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_config(output: &Output, backend: &mut BackendData) -> Result<(), impl std::error::Error> {
|
fn apply_config(
|
||||||
|
output: &Output,
|
||||||
|
backend: &mut BackendData,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
backend.apply_config_for_output(output)?;
|
backend.apply_config_for_output(output)?;
|
||||||
|
|
||||||
let final_config = output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow();
|
let final_config = output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
let mode = Some(OutputMode {
|
let mode = Some(OutputMode {
|
||||||
size: final_config.mode_size(),
|
size: final_config.mode_size(),
|
||||||
refresh: final_config.mode_refresh(),
|
refresh: final_config.mode_refresh(),
|
||||||
}).filter(|m| match output.current_mode() {
|
})
|
||||||
|
.filter(|m| match output.current_mode() {
|
||||||
None => true,
|
None => true,
|
||||||
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
|
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 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 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());
|
let location =
|
||||||
output.change_current_state(
|
Some(final_config.position.into()).filter(|x| *x != output.current_location());
|
||||||
mode,
|
output.change_current_state(mode, transform, scale, location);
|
||||||
transform,
|
|
||||||
scale,
|
|
||||||
location,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_output(&mut self, output: &Output, backend: &mut BackendData, config: &Config) {
|
fn refresh_config(&mut self, backend: &mut BackendData, config: &Config) -> bool {
|
||||||
self.outputs.push(output.clone());
|
let mut infos = self
|
||||||
|
.outputs()
|
||||||
let mut infos = self.outputs().cloned().map(Into::<crate::config::OutputInfo>::into).collect::<Vec<_>>();
|
.cloned()
|
||||||
|
.map(Into::<crate::config::OutputInfo>::into)
|
||||||
|
.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) {
|
||||||
let mut reset = false;
|
let mut reset = false;
|
||||||
let known_good_configs = self.outputs.iter().map(|output| {
|
let known_good_configs = self
|
||||||
output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow().clone()
|
.outputs
|
||||||
}).collect::<Vec<_>>();
|
.iter()
|
||||||
|
.map(|output| {
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for (name, config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter().cloned()) {
|
for (name, config) in infos
|
||||||
|
.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();
|
||||||
*output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow_mut() = config;
|
*output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut() = config;
|
||||||
if let Err(err) = Self::apply_config(output, backend) {
|
if let Err(err) = Self::apply_config(output, backend) {
|
||||||
slog_scope::warn!("Failed to set new config for output {}: {}", output.name(), err);
|
slog_scope::warn!(
|
||||||
|
"Failed to set new config for output {}: {}",
|
||||||
|
output.name(),
|
||||||
|
err
|
||||||
|
);
|
||||||
reset = true;
|
reset = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -136,10 +167,17 @@ impl Shell {
|
||||||
|
|
||||||
if reset {
|
if reset {
|
||||||
for (output, config) in self.outputs.iter().zip(known_good_configs.into_iter()) {
|
for (output, config) in self.outputs.iter().zip(known_good_configs.into_iter()) {
|
||||||
*output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow_mut() = config;
|
*output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut() = config;
|
||||||
if let Err(err) = Self::apply_config(output, backend) {
|
if let Err(err) = Self::apply_config(output, backend) {
|
||||||
slog_scope::error!("Failed to reset config for output {}: {}", output.name(), err);
|
slog_scope::error!(
|
||||||
self.unmap_output(output);
|
"Failed to reset config for output {}: {}",
|
||||||
|
output.name(),
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,37 +185,91 @@ impl Shell {
|
||||||
if let Mode::Global { active } = self.mode {
|
if let Mode::Global { active } = self.mode {
|
||||||
let workspace = &mut self.spaces[active];
|
let workspace = &mut self.spaces[active];
|
||||||
for output in self.outputs.iter() {
|
for output in self.outputs.iter() {
|
||||||
let config = output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow();
|
let config = output
|
||||||
workspace.space.map_output(output, config.scale, config.position);
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
|
workspace
|
||||||
|
.space
|
||||||
|
.map_output(output, config.scale, config.position);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for output in self.outputs.iter() {
|
||||||
|
let config = output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
|
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
|
||||||
|
workspace.space.map_output(output, config.scale, (0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
let new_pos_x = self.outputs().map(|o| {
|
false
|
||||||
let logical_size = self.active_space(o).space.output_geometry(o).map(|x| x.size).unwrap_or((0, 0).into());
|
}
|
||||||
o.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow().position.0 + logical_size.w
|
}
|
||||||
}).max().unwrap_or(0);
|
|
||||||
|
fn assign_next_free_output<'a>(
|
||||||
|
spaces: &'a mut [Workspace],
|
||||||
|
output: &Output,
|
||||||
|
) -> &'a mut Workspace {
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.insert_if_missing(|| ActiveWorkspace::new());
|
||||||
|
let (idx, workspace) = spaces
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, x)| x.space.outputs().next().is_none())
|
||||||
|
.expect("More then 10 outputs?");
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.get::<ActiveWorkspace>()
|
||||||
|
.unwrap()
|
||||||
|
.set(idx);
|
||||||
|
|
||||||
|
workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
.take(self.outputs.len() - 1)
|
||||||
|
.map(|o| {
|
||||||
|
let logical_size = self
|
||||||
|
.active_space(o)
|
||||||
|
.space
|
||||||
|
.output_geometry(o)
|
||||||
|
.map(|x| x.size)
|
||||||
|
.unwrap_or((0, 0).into());
|
||||||
|
o.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.position
|
||||||
|
.0
|
||||||
|
+ logical_size.w
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
let new_pos = (new_pos_x, 0);
|
let new_pos = (new_pos_x, 0);
|
||||||
output.user_data().get::<RefCell<OutputConfig>>().unwrap().borrow_mut().position = new_pos;
|
output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.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 {
|
match self.mode {
|
||||||
Mode::OutputBound => {
|
Mode::OutputBound => {
|
||||||
output
|
let workspace = Self::assign_next_free_output(&mut self.spaces, output);
|
||||||
.user_data()
|
|
||||||
.insert_if_missing(|| ActiveWorkspace::new());
|
|
||||||
|
|
||||||
let (idx, workspace) = self
|
|
||||||
.spaces
|
|
||||||
.iter_mut()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, x)| x.space.outputs().next().is_none())
|
|
||||||
.expect("More then 10 outputs?");
|
|
||||||
output
|
|
||||||
.user_data()
|
|
||||||
.get::<ActiveWorkspace>()
|
|
||||||
.unwrap()
|
|
||||||
.set(idx);
|
|
||||||
workspace.space.map_output(output, 1.0, (0, 0));
|
workspace.space.map_output(output, 1.0, (0, 0));
|
||||||
}
|
}
|
||||||
Mode::Global { active } => {
|
Mode::Global { active } => {
|
||||||
|
|
@ -187,10 +279,32 @@ impl Shell {
|
||||||
workspace.space.map_output(output, 1.0, new_pos);
|
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) {
|
pub fn unmap_output(&mut self, output: &Output, backend: &mut BackendData, config: &Config) {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::OutputBound => {
|
Mode::OutputBound => {
|
||||||
if let Some(idx) = output
|
if let Some(idx) = output
|
||||||
|
|
@ -208,6 +322,7 @@ impl Shell {
|
||||||
// TODO move windows and outputs farther on the right / or load save config for remaining monitors
|
// TODO move windows and outputs farther on the right / or load save config for remaining monitors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.refresh_config(backend, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
|
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
|
||||||
|
|
@ -220,12 +335,16 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn global_space(&self) -> Rectangle<i32, Logical> {
|
pub fn global_space(&self) -> Rectangle<i32, Logical> {
|
||||||
let size = self.outputs.iter().fold((0, 0), |(w, h), output| {
|
self.outputs
|
||||||
let size = self.output_size(output);
|
.iter()
|
||||||
(w + size.w, std::cmp::max(h, size.h))
|
.fold(
|
||||||
});
|
Option::<Rectangle<i32, Logical>>::None,
|
||||||
|
|maybe_geo, output| match maybe_geo {
|
||||||
Rectangle::from_loc_and_size((0, 0), size)
|
Some(rect) => Some(rect.merge(self.output_geometry(output))),
|
||||||
|
None => Some(self.output_geometry(output)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn space_relative_output_geometry<C: smithay::utils::Coordinate>(
|
pub fn space_relative_output_geometry<C: smithay::utils::Coordinate>(
|
||||||
|
|
@ -246,17 +365,12 @@ impl Shell {
|
||||||
// due to our different modes, we cannot just ask the space for the global output coordinates,
|
// due to our different modes, we cannot just ask the space for the global output coordinates,
|
||||||
// because for `Mode::OutputBound` the origin will always be (0, 0)
|
// because for `Mode::OutputBound` the origin will always be (0, 0)
|
||||||
|
|
||||||
// TODO: Add a proper grid like structure, for now the outputs just extend to the right
|
let config = output
|
||||||
let pos =
|
.user_data()
|
||||||
self.outputs
|
.get::<RefCell<OutputConfig>>()
|
||||||
.iter()
|
.unwrap()
|
||||||
.take_while(|o| o != &output)
|
.borrow();
|
||||||
.fold((0, 0), |(x, y), output| {
|
Rectangle::from_loc_and_size(config.position, self.output_size(output))
|
||||||
let size = self.output_size(output);
|
|
||||||
(x + size.w, y)
|
|
||||||
});
|
|
||||||
|
|
||||||
Rectangle::from_loc_and_size(pos, self.output_size(output))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(&mut self, seat: &Seat, output: &Output, idx: usize) {
|
pub fn activate(&mut self, seat: &Seat, output: &Output, idx: usize) {
|
||||||
|
|
@ -294,7 +408,14 @@ impl Shell {
|
||||||
if let Some(old_idx) = active.set(idx) {
|
if let Some(old_idx) = active.set(idx) {
|
||||||
self.spaces[old_idx].space.unmap_output(output);
|
self.spaces[old_idx].space.unmap_output(output);
|
||||||
}
|
}
|
||||||
self.spaces[idx].space.map_output(output, 1.0, (0, 0));
|
let config = output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
|
self.spaces[idx]
|
||||||
|
.space
|
||||||
|
.map_output(output, config.scale, (0, 0));
|
||||||
self.spaces[idx].refresh();
|
self.spaces[idx].refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -302,9 +423,15 @@ impl Shell {
|
||||||
let old = *active;
|
let old = *active;
|
||||||
*active = idx;
|
*active = idx;
|
||||||
for output in &self.outputs {
|
for output in &self.outputs {
|
||||||
let loc = self.spaces[old].space.output_geometry(output).unwrap().loc;
|
|
||||||
self.spaces[old].space.unmap_output(output);
|
self.spaces[old].space.unmap_output(output);
|
||||||
self.spaces[*active].space.map_output(output, 1.0, loc);
|
let config = output
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
|
self.spaces[*active]
|
||||||
|
.space
|
||||||
|
.map_output(output, config.scale, config.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -347,7 +474,6 @@ impl Shell {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let mut x = 0;
|
|
||||||
|
|
||||||
for output in &self.outputs {
|
for output in &self.outputs {
|
||||||
let old_active = output
|
let old_active = output
|
||||||
|
|
@ -356,32 +482,45 @@ impl Shell {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clear()
|
.clear()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let width = self.spaces[old_active]
|
let config = output
|
||||||
.space
|
.user_data()
|
||||||
.output_geometry(output)
|
.get::<RefCell<OutputConfig>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.size
|
.borrow();
|
||||||
.w;
|
|
||||||
self.spaces[old_active].space.unmap_output(output);
|
self.spaces[old_active].space.unmap_output(output);
|
||||||
self.spaces[active].space.map_output(output, 1.0, (x, 0));
|
self.spaces[active]
|
||||||
x += width;
|
.space
|
||||||
|
.map_output(output, config.scale, config.position);
|
||||||
|
self.spaces[active].refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mode = Mode::Global { active };
|
self.mode = Mode::Global { active };
|
||||||
// TODO move windows into new bounds
|
|
||||||
}
|
}
|
||||||
(Mode::Global { active }, new @ Mode::OutputBound) => {
|
(Mode::Global { active }, new @ Mode::OutputBound) => {
|
||||||
for output in &self.outputs {
|
for output in &self.outputs {
|
||||||
self.spaces[*active].space.unmap_output(output);
|
self.spaces[*active].space.unmap_output(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut active = Some(active.clone());
|
||||||
self.mode = new;
|
self.mode = new;
|
||||||
let outputs = self.outputs.drain(..).collect::<Vec<_>>();
|
for output in &self.outputs {
|
||||||
for output in &outputs {
|
let config = output
|
||||||
self.map_output(output);
|
.user_data()
|
||||||
|
.get::<RefCell<OutputConfig>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow();
|
||||||
|
let workspace = if let Some(a) = active.take() {
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.insert_if_missing(|| ActiveWorkspace::new());
|
||||||
|
output.user_data().get::<ActiveWorkspace>().unwrap().set(a);
|
||||||
|
&mut self.spaces[a]
|
||||||
|
} else {
|
||||||
|
Self::assign_next_free_output(&mut self.spaces, output)
|
||||||
|
};
|
||||||
|
workspace.space.map_output(output, config.scale, (0, 0));
|
||||||
|
workspace.refresh();
|
||||||
}
|
}
|
||||||
// TODO move windows into new bounds
|
|
||||||
// TODO active should probably be mapped somewhere
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{kms::KmsState, winit::WinitState, x11::X11State},
|
backend::{kms::KmsState, winit::WinitState, x11::X11State},
|
||||||
config::{Config, OutputConfig},
|
config::Config,
|
||||||
logger::LogState,
|
logger::LogState,
|
||||||
shell::{init_shell, Shell},
|
shell::{init_shell, Shell},
|
||||||
};
|
};
|
||||||
|
|
@ -105,13 +105,16 @@ impl BackendData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_config_for_output(&mut self, output: &Output) -> Result<(), impl std::error::Error> {
|
pub fn apply_config_for_output(
|
||||||
|
&mut self,
|
||||||
|
output: &Output,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match self {
|
match self {
|
||||||
BackendData::Kms(ref mut state) => state.apply_config_for_output(output),
|
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
|
// TODO: reset the mode for nested backends, because we have no control over it
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue