Commit 599f8f3cc5f6
tarball2 files changed+71-8
@@ -21,8 +21,8 @@ use smithay::backend::drm::{
2121
DrmDevice, DrmDeviceFd, DrmEvent, DrmEventMetadata, DrmEventTime, DrmNode, NodeType,2222
};2323
use smithay::backend::input::{24
- AbsolutePositionEvent, Axis, AxisSource, Event, InputEvent, KeyboardKeyEvent, PointerAxisEvent,25
- PointerButtonEvent, PointerMotionEvent,24
+ AbsolutePositionEvent, Axis, AxisSource, Event, InputEvent, InputTime, KeyboardKeyEvent,25
+ PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,2626
};2727
use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};2828
use smithay::backend::session::libseat::LibSeatSession;@@ -94,6 +94,8 @@ struct Surface {
9494
queued_at: u64,9595
/// The panel is lit. Off outputs are not rendered to.9696
powered: bool,97
+ /// Out of the layout and Space, but still driven so it can relight.98
+ parked: bool,9799
/// Fires when a queued frame's vblank never arrives.98100
watchdog: Option<RegistrationToken>,99101
/// Consecutive frames that failed to render or queue.@@ -466,6 +468,7 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
466468
queued: false,467469
queued_at: 0,468470
powered: true,471
+ parked: false,469472
watchdog: None,470473
failures: 0,471474
redraw: true,@@ -636,8 +639,14 @@ pub fn reconfigure_outputs(state: &mut State) {
636639
else {637640
return;638641
};639
- let mut surfaces: Vec<&mut Surface> = drm.surfaces.values_mut().collect();640
- surfaces.sort_by_key(|s| s.output.current_location().x);642
+ let mut surfaces: Vec<&mut Surface> = drm.surfaces.values_mut().filter(|s| !s.parked).collect();643
+ surfaces.sort_by_cached_key(|s| {644
+ let name = s.output.name();645
+ (646
+ s.output.current_location().x,647
+ !crate::power::internal(&name),648
+ )649
+ });641650
let mut right = 0;642651
let mut changed = false;643652
for s in surfaces {@@ -727,7 +736,54 @@ fn disconnect(state: &mut State, crtc: crtc::Handle) {
727736
state.protocols.output_power.output_removed(&surface.output);728737
}729738
739
+/// Takes the output called `name` out of the layout and Space, or puts it740
+/// back. Its windows move to the other outputs and return when it does.741
+pub fn set_parked(state: &mut State, name: &str, parked: bool) {742
+ let State {743
+ backend: Backend::Drm(drm),744
+ wm: Wm { layout, space, .. },745
+ ..746
+ } = state747
+ else {748
+ return;749
+ };750
+ let Some(s) = drm.surfaces.values_mut().find(|s| s.output.name() == name) else {751
+ return;752
+ };753
+ if s.parked == parked {754
+ return;755
+ }756
+ s.parked = parked;757
+ tracing::info!(name, parked, "output parked");758
+ if parked {759
+ space.unmap_output(&s.output);760
+ layout.remove_output(s.id);761
+ } else if let Some(mode) = s.output.current_mode() {762
+ let position = s.output.current_location();763
+ space.map_output(&s.output, position);764
+ let scale = s.output.current_scale().fractional_scale();765
+ let area = logical_area(position, mode.size, s.output.current_transform(), scale);766
+ s.id = layout.add_output(name, area);767
+ s.redraw = true;768
+ }769
+ let pos = state.input.pointer_pos;770
+ let held = clamp(state, pos);771
+ if held != pos {772
+ state.warp_pointer(held, InputTime::now());773
+ }774
+ if !parked {775
+ reconfigure_outputs(state);776
+ }777
+ state.reserve_layers();778
+ state.reconfigure_lock();779
+ state.apply_layout();780
+}781
+730782
impl Drm {783
+ pub fn names(&self) -> Vec<String> {784
+ self.surfaces.values().map(|s| s.output.name()).collect()785
+ }786
+731787
/// Lights or darkens the output called `name`. Dark outputs get DPMS off732788
/// and no frames, and the next frame after turning on is a full modeset.733789
pub fn set_powered(&mut self, name: &str, on: bool) {@@ -25,8 +25,8 @@ pub struct Power {
2525
lid_closed: bool,2626
/// The lid close action already ran for this closing.2727
lid_acted: bool,28
- /// The internal panel is dark because the lid is shut and something else29
- /// is connected.28
+ /// The internal panel is dark and out of the layout because the lid is29
+ /// shut and something else is connected.3030
panel_off: bool,3131
last_activity: u64,3232
timer: Option<RegistrationToken>,@@ -48,7 +48,7 @@ impl Power {
4848
}4949
}5050
51
-fn internal(name: &str) -> bool {51
+pub fn internal(name: &str) -> bool {5252
["eDP-", "LVDS-", "DSI-"]5353
.iter()5454
.any(|p| name.starts_with(p))@@ -100,14 +100,21 @@ impl State {
100100
101101
/// Pushes the wanted power state of every output to the backend.102102
pub fn apply_power(&mut self) {103
- let names: Vec<String> = self.wm.space.outputs().map(|o| o.name()).collect();103
+ let names: Vec<String> = match self.backend.drm() {104
+ Some(drm) => drm.names(),105
+ None => self.wm.space.outputs().map(|o| o.name()).collect(),106
+ };104107
let power = &mut self.services.power;105108
power.panel_off = power.lid_closed && names.iter().any(|n| !internal(n));109
+ let parked = power.panel_off;106110
for name in names {107111
let on = self.output_powered(&name);108112
if let Some(drm) = self.backend.drm_mut() {109113
drm.set_powered(&name, on);110114
}115
+ if internal(&name) {116
+ crate::drm::set_parked(self, &name, parked);117
+ }111118
}112119
let power = &self.services.power;113120
self.protocols.output_power.sync(|o| !power.dark(&o.name()));