Commit d47a99c6ac9f
tarball1 file changed+106-24
@@ -319,10 +319,7 @@ fn scan(state: &mut State) {
319319
DrmScanEvent::Changed {320320
connector,321321
crtc: Some(crtc),322
- } => {323
- disconnect(state, crtc);324
- connect(state, connector, crtc);325
- }322
+ } => changed(state, connector, crtc),326323
_ => {}327324
}328325
}@@ -360,11 +357,7 @@ fn retry_connector(state: &mut State, handle: connector::Handle) {
360357
}361358
362359
fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {363
- let name = format!(364
- "{}-{}",365
- connector.interface().as_str(),366
- connector.interface_id()367
- );360
+ let name = connector_name(&connector);368361
let config = state369362
.settings370363
.config@@ -373,21 +366,7 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
373366
.get(&name)374367
.cloned()375368
.unwrap_or_default();376
- let chosen = config.mode.as_deref().and_then(|spec| {377
- let found = pick_mode(connector.modes(), spec);378
- if found.is_none() {379
- tracing::warn!(name, spec, "no such mode, using the preferred one");380
- }381
- found382
- });383
- let Some(mode) = chosen.or_else(|| {384
- connector385
- .modes()386
- .iter()387
- .find(|m| m.mode_type().contains(ModeTypeFlags::PREFERRED))388
- .or_else(|| connector.modes().first())389
- .copied()390
- }) else {369
+ let Some(mode) = choose_mode(&connector, &name, config.mode.as_deref()) else {391370
tracing::warn!(name, "connector has no modes");392371
if let Some(drm) = state.backend.drm_mut() {393372
drm.failed.insert(connector.handle());@@ -508,6 +487,109 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
508487
);509488
}510489
490
+fn connector_name(connector: &connector::Info) -> String {491
+ format!(492
+ "{}-{}",493
+ connector.interface().as_str(),494
+ connector.interface_id()495
+ )496
+}497
+498
+/// The configured mode if the connector has it, else its preferred one.499
+fn choose_mode(500
+ connector: &connector::Info,501
+ name: &str,502
+ configured: Option<&str>,503
+) -> Option<control::Mode> {504
+ let chosen = configured.and_then(|spec| {505
+ let found = pick_mode(connector.modes(), spec);506
+ if found.is_none() {507
+ tracing::warn!(name, spec, "no such mode, using the preferred one");508
+ }509
+ found510
+ });511
+ chosen.or_else(|| {512
+ connector513
+ .modes()514
+ .iter()515
+ .find(|m| m.mode_type().contains(ModeTypeFlags::PREFERRED))516
+ .or_else(|| connector.modes().first())517
+ .copied()518
+ })519
+}520
+521
+fn changed(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {522
+ if !remode(state, &connector, crtc) {523
+ disconnect(state, crtc);524
+ connect(state, connector, crtc);525
+ }526
+}527
+528
+/// Moves a lit output to the mode its connector now calls for without529
+/// recreating the wl_output, so clients, windows, the lock surface and the530
+/// power state stay as they are. False means the output has to be rebuilt.531
+fn remode(state: &mut State, connector: &connector::Info, crtc: crtc::Handle) -> bool {532
+ let name = connector_name(connector);533
+ let State {534
+ backend: Backend::Drm(drm),535
+ renderer,536
+ wm: Wm { layout, .. },537
+ settings,538
+ ..539
+ } = state540
+ else {541
+ return false;542
+ };543
+ let Some(s) = drm.surfaces.get_mut(&crtc) else {544
+ return false;545
+ };546
+ let (mm_w, mm_h) = connector.size().unwrap_or((0, 0));547
+ if s.output.physical_properties().size != (mm_w as i32, mm_h as i32).into() {548
+ return false;549
+ }550
+ let configured = settings.config.output.named.get(&name);551
+ let Some(mode) = choose_mode(connector, &name, configured.and_then(|c| c.mode.as_deref()))552
+ else {553
+ return false;554
+ };555
+ let wl_mode = Mode::from(mode);556
+ let moved = s.output.current_mode() != Some(wl_mode);557
+ if moved {558
+ let elements = DrmOutputRenderElements::default();559
+ if let Err(e) = s.drm.use_mode::<_, Draw>(mode, renderer, &elements) {560
+ tracing::warn!(name, "could not change the mode in place: {e}");561
+ return false;562
+ }563
+ s.output564
+ .change_current_state(Some(wl_mode), None, None, None);565
+ s.refresh_ns = 1_000_000_000_000 / u64::from(wl_mode.refresh.max(1) as u32);566
+ s.last_vblank_ns = None;567
+ s.target_ns = None;568
+ s.redraw = true;569
+ let area = logical_area(570
+ s.output.current_location(),571
+ wl_mode.size,572
+ s.output.current_transform(),573
+ s.output.current_scale().fractional_scale(),574
+ );575
+ layout.set_output_area(s.id, area);576
+ tracing::info!(577
+ name,578
+ w = wl_mode.size.w,579
+ h = wl_mode.size.h,580
+ refresh = mode.vrefresh(),581
+ "output mode changed"582
+ );583
+ }584
+ s.output.set_preferred(wl_mode);585
+ if moved {586
+ reconfigure_outputs(state);587
+ state.reserve_layers();588
+ state.reconfigure_lock();589
+ }590
+ true591
+}592
+511593
/// The mode matching `WIDTHxHEIGHT` or `WIDTHxHEIGHT@HZ`, the highest rate512594
/// when none is given and the nearest one when it is.513595
fn pick_mode(modes: &[control::Mode], spec: &str) -> Option<control::Mode> {