← luna

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()