Commit a7972ff
4
files
+65
added
-16
deleted
@@ -57,7 +57,8 @@ gnieździe nasłuchu i deskryptorze backendu. `libLuna` jest zwykłą zależnoś
57
57
`drop` do celu (z ofertą), `dnd_drop_performed`/`dnd_finished`/`cancelled` do źródła.
58
58
- **Klawiatura**: układ i wariant z otoczenia (`LUNA_KEYBOARD_LAYOUT`, `LUNA_KEYBOARD_VARIANT`,
59
59
sanityzowane), `wl_keyboard.repeat_info`.
60
-- **Realne wyjście DRM/KMS** (`SELENE_BACKEND=drm`, opcjonalne): przejęcie mastera, bufor
60
+- **Realne wyjście DRM/KMS** (`SELENE_BACKEND=drm`, opcjonalne): przejęcie mastera, wybór
61
+ trybu natywnego monitora (maks. rozdzielczość, potem najwyższe odświeżanie), bufor
61
62
„dumb”, framebuffer (`ADDFB2`), `SETCRTC` i **page-flip** na wykrytych monitorach; render
62
63
sceny wprost do buforów (podwójne buforowanie). Gdy master trzyma inna sesja — bezpieczny
63
64
fallback do nested (nie odbiera ekranu).
@@ -336,10 +336,7 @@ fn probe_connector(fd: BorrowedFd, id: u32) -> Option<OutputInfo> {
336
336
};
337
337
let c = ioctl_rw::<ModeGetConnector, DRM_IOCTL_MODE_GETCONNECTOR, _>(fd, req).ok()?;
338
338
339
- let mode = modes
340
- .iter()
341
- .find(|m| m.type_ & DRM_MODE_TYPE_PREFERRED != 0)
342
- .or_else(|| modes.first())?;
339
+ let mode = pick_best_mode(&modes)?;
343
340
344
341
Some(OutputInfo {
345
342
name: connector_name(c.connector_type, c.connector_type_id),
@@ -351,6 +348,17 @@ fn probe_connector(fd: BorrowedFd, id: u32) -> Option<OutputInfo> {
351
348
})
352
349
}
353
350
351
+/// Wybiera najlepszy tryb monitora: **największa rozdzielczość**, potem **najwyższy
352
+/// `vrefresh`**; przy remisie preferuje tryb natywny (`PREFERRED`).
353
+fn pick_best_mode(modes: &[ModeInfo]) -> Option<&ModeInfo> {
354
+ modes.iter().max_by_key(|m| {
355
+ let area = m.hdisplay as u64 * m.vdisplay as u64;
356
+ let hz = m.vrefresh as u64;
357
+ let preferred = u64::from(m.type_ & DRM_MODE_TYPE_PREFERRED != 0);
358
+ (area, hz, preferred)
359
+ })
360
+}
361
+
354
362
/// Nazwa konektora w stylu Wayland (np. `HDMI-A-1`, `eDP-1`).
355
363
fn connector_name(kind: u32, index: u32) -> String {
356
364
let base = match kind {
@@ -442,6 +450,7 @@ struct DrmOutput {
442
450
crtc_id: u32,
443
451
width: u32,
444
452
height: u32,
453
+ hz: u32,
445
454
buffers: [DumbBuffer; 2],
446
455
front: usize,
447
456
}
@@ -505,11 +514,11 @@ impl DrmBackend {
505
514
Err(last)
506
515
}
507
516
508
- /// `(nazwa, szerokość, wysokość)` każdego wyjścia.
509
- pub fn outputs(&self) -> Vec<(String, u32, u32)> {
517
+ /// `(nazwa, szerokość, wysokość, odświeżanie)` każdego wyjścia.
518
+ pub fn outputs(&self) -> Vec<(String, u32, u32, u32)> {
510
519
self.outputs
511
520
.iter()
512
- .map(|o| (o.name.clone(), o.width, o.height))
521
+ .map(|o| (o.name.clone(), o.width, o.height, o.hz))
513
522
.collect()
514
523
}
515
524
@@ -587,10 +596,7 @@ fn setup_output(fd: BorrowedFd, conn_id: u32, res: &Resources) -> Option<DrmOutp
587
596
},
588
597
)
589
598
.ok()?;
590
- let mode = *modes
591
- .iter()
592
- .find(|m| m.type_ & DRM_MODE_TYPE_PREFERRED != 0)
593
- .or_else(|| modes.first())?;
599
+ let mode = *pick_best_mode(&modes)?;
594
600
595
601
// Enkoder → CRTC (lub pierwszy możliwy).
596
602
let enc = ioctl_rw::<ModeGetEncoder, DRM_IOCTL_MODE_GETENCODER, _>(
@@ -620,6 +626,7 @@ fn setup_output(fd: BorrowedFd, conn_id: u32, res: &Resources) -> Option<DrmOutp
620
626
crtc_id,
621
627
width,
622
628
height,
629
+ hz: mode.vrefresh,
623
630
buffers: [b0, b1],
624
631
front: 0,
625
632
})
@@ -743,4 +750,38 @@ mod tests {
743
750
// 'X','R','2','4' — kontroluje kolejność bajtów przy przepisywaniu klatki.
744
751
assert_eq!(DRM_FORMAT_XRGB8888.to_le_bytes(), *b"XR24");
745
752
}
753
+
754
+ fn mode(w: u16, h: u16, hz: u32, preferred: bool) -> ModeInfo {
755
+ ModeInfo {
756
+ hdisplay: w,
757
+ vdisplay: h,
758
+ vrefresh: hz,
759
+ type_: if preferred {
760
+ DRM_MODE_TYPE_PREFERRED
761
+ } else {
762
+ 0
763
+ },
764
+ ..ModeInfo::default()
765
+ }
766
+ }
767
+
768
+ #[test]
769
+ fn picks_highest_resolution_then_refresh() {
770
+ let modes = [
771
+ mode(1280, 720, 60, false),
772
+ mode(1920, 1080, 60, false),
773
+ mode(1920, 1080, 144, false),
774
+ ];
775
+ let best = pick_best_mode(&modes).unwrap();
776
+ assert_eq!(
777
+ (best.hdisplay, best.vdisplay, best.vrefresh),
778
+ (1920, 1080, 144)
779
+ );
780
+ }
781
+
782
+ #[test]
783
+ fn preferred_mode_breaks_ties() {
784
+ let modes = [mode(1920, 1080, 60, false), mode(1920, 1080, 60, true)];
785
+ assert!(pick_best_mode(&modes).unwrap().type_ & DRM_MODE_TYPE_PREFERRED != 0);
786
+ }
746
787
}
@@ -311,7 +311,7 @@ mod tests {
311
311
312
312
#[test]
313
313
fn state_encoding_is_one_u32_per_flag() {
314
- let mut s = State::default();
314
+ let s = State::default();
315
315
s.windows.lock_or_recover().push(crate::state::Window {
316
316
x: 0.0,
317
317
y: 0.0,
@@ -111,7 +111,7 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
111
111
}
112
112
};
113
113
let infos = backend.outputs();
114
- let sizes: Vec<(u32, u32)> = infos.iter().map(|(_, w, h)| (*w, *h)).collect();
114
+ let sizes: Vec<(u32, u32)> = infos.iter().map(|(_, w, h, _)| (*w, *h)).collect();
115
115
let origins: Vec<(f64, f64)> = {
116
116
// Wyjścia ułożone poziomo (spójne z modelem monitorów).
117
117
let mut x = 0.0;
@@ -124,8 +124,15 @@ pub fn spawn_drm(model: WindowModel, cursor: CursorModel) {
124
124
})
125
125
.collect()
126
126
};
127
- for (name, w, h) in &infos {
128
- println!("{}: {} {}x{}", libluna_i18n::tr("DRM output"), name, w, h);
127
+ for (name, w, h, hz) in &infos {
128
+ println!(
129
+ "{}: {} {}x{}@{}Hz",
130
+ libluna_i18n::tr("DRM output"),
131
+ name,
132
+ w,
133
+ h,
134
+ hz
135
+ );
129
136
}
130
137
let mut renderers: Vec<SoftwareRenderer> = sizes
131
138
.iter()