Commit 90ad3d5
5
files
+75
added
-12
deleted
@@ -111,6 +111,10 @@ założył sesji), Selene wybiera katalog sam: najpierw `/run/user/<uid>`, a gdy
111
111
istnieje — tworzy prywatny katalog `0700` pod `$TMPDIR`. Skrypt `luna-session` robi ten
112
112
sam wybór wcześniej, żeby zmienną dziedziczyli także klienci.
113
113
114
+Log uruchomienia sesji trafia do stałego pliku `~/.local/state/luna/session.log`
115
+(LightDM trzyma tylko dwa pliki `.xsession-errors` i obraca je przy każdej sesji, więc
116
+bez tego diagnostyka przepada).
117
+
114
118
Tryb nested (domyślny) uruchamia okno na nadrzędnym kompozytorze. **Realne wyjście DRM**
115
119
(gdy masz wolnego mastera, np. z osobnej konsoli):
116
120
@@ -446,6 +446,17 @@ struct DrmOutput {
446
446
front: usize,
447
447
}
448
448
449
+/// Powód, dla którego nie udało się otworzyć wyjścia DRM (do diagnostyki).
450
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
451
+pub enum DrmError {
452
+ /// Inna sesja trzyma mastera DRM — nie odbieramy ekranu.
453
+ MasterBusy,
454
+ /// Nie udało się odczytać zasobów karty (`GETRESOURCES`).
455
+ Resources,
456
+ /// Nie udało się ustawić trybu na żadnym podłączonym monitorze.
457
+ NoOutputs,
458
+}
459
+
449
460
/// Backend wyjścia DRM/KMS (jedna karta, wiele monitorów).
450
461
pub struct DrmBackend {
451
462
fd: OwnedFd,
@@ -462,17 +473,20 @@ impl Drop for DrmBackend {
462
473
impl DrmBackend {
463
474
/// Otwiera kartę, przejmuje mastera i ustawia tryb na wszystkich podłączonych monitorach.
464
475
/// Zwraca `None`, gdy DRM jest niedostępne lub ktoś inny trzyma mastera (np. sesja użytkownika).
465
- pub fn open() -> Option<Self> {
476
+ pub fn open() -> Result<Self, DrmError> {
477
+ let mut last = DrmError::NoOutputs;
466
478
for card in card_paths() {
467
479
let Ok(fd) = rustix::fs::open(&card, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty())
468
480
else {
469
481
continue;
470
482
};
471
483
if ioctl_none::<DRM_IOCTL_SET_MASTER, _>(&fd).is_err() {
472
- continue; // inny master (uruchomiona sesja) — nie odbieramy ekranu
484
+ last = DrmError::MasterBusy; // inna sesja trzyma mastera
485
+ continue;
473
486
}
474
487
let Some(res) = fetch_resources(fd.as_fd()) else {
475
488
let _ = ioctl_none::<DRM_IOCTL_DROP_MASTER, _>(&fd);
489
+ last = DrmError::Resources;
476
490
continue;
477
491
};
478
492
let mut outputs = Vec::new();
@@ -483,11 +497,12 @@ impl DrmBackend {
483
497
}
484
498
if outputs.is_empty() {
485
499
let _ = ioctl_none::<DRM_IOCTL_DROP_MASTER, _>(&fd);
500
+ last = DrmError::NoOutputs;
486
501
continue;
487
502
}
488
- return Some(Self { fd, outputs });
503
+ return Ok(Self { fd, outputs });
489
504
}
490
- None
505
+ Err(last)
491
506
}
492
507
493
508
/// `(nazwa, szerokość, wysokość)` każdego wyjścia.
@@ -12,7 +12,7 @@ use libluna_core::{Color, Point, Rect, Size, ThemeSettings};
12
12
use libluna_platform::{run, AppConfig, Frame};
13
13
use libluna_render::{Scene, SoftwareRenderer, TextRun};
14
14
15
-use crate::drm::DrmBackend;
15
+use crate::drm::{DrmBackend, DrmError};
16
16
use crate::input::{self, Events};
17
17
use crate::monitors::Outputs;
18
18
use crate::state::{Window, WindowModel};
@@ -68,12 +68,24 @@ pub fn spawn(
68
68
/// (wywołujący powinien wtedy użyć wyjścia nested).
69
69
pub fn spawn_drm(model: WindowModel) {
70
70
std::thread::spawn(move || {
71
- let Some(mut backend) = DrmBackend::open() else {
72
- eprintln!(
73
- "{}",
74
- libluna_i18n::tr("DRM backend unavailable (no master)")
75
- );
76
- return;
71
+ let mut backend = match DrmBackend::open() {
72
+ Ok(backend) => backend,
73
+ Err(reason) => {
74
+ // Rozróżniamy powód — inaczej „brak mastera” maskuje błędy inicjalizacji.
75
+ let msg = match reason {
76
+ DrmError::MasterBusy => {
77
+ libluna_i18n::tr("DRM backend unavailable (master held by another session)")
78
+ }
79
+ DrmError::Resources => {
80
+ libluna_i18n::tr("DRM backend unavailable (cannot read card resources)")
81
+ }
82
+ DrmError::NoOutputs => {
83
+ libluna_i18n::tr("DRM backend unavailable (no monitor could be set up)")
84
+ }
85
+ };
86
+ eprintln!("{msg}");
87
+ return;
88
+ }
77
89
};
78
90
let infos = backend.outputs();
79
91
let sizes: Vec<(u32, u32)> = infos.iter().map(|(_, w, h)| (*w, *h)).collect();
@@ -98,6 +110,8 @@ pub fn spawn_drm(model: WindowModel) {
98
110
.collect();
99
111
let bg = Color::from_rgba8(0x1e, 0x20, 0x28, 0xff);
100
112
113
+ // Gdy page-flip zawodzi (np. sterownik/karta), ekran zostaje czarny — logujemy raz.
114
+ let mut flip_error_logged = false;
101
115
loop {
102
116
for i in 0..sizes.len() {
103
117
let (w, h) = sizes[i];
@@ -105,7 +119,13 @@ pub fn spawn_drm(model: WindowModel) {
105
119
let mut scene = Scene::new();
106
120
build_scene(&mut scene, Size::new(w as f64, h as f64), ox, oy, &model);
107
121
renderers[i].draw(&scene, 1.0, bg);
108
- backend.present(i, renderers[i].data());
122
+ if !backend.present(i, renderers[i].data()) && !flip_error_logged {
123
+ eprintln!(
124
+ "{}",
125
+ libluna_i18n::tr("DRM page-flip failed (screen may stay blank)")
126
+ );
127
+ flip_error_logged = true;
128
+ }
109
129
backend.wait_flip();
110
130
}
111
131
// Bez wejścia w trybie DRM odświeżamy w stałym rytmie (~60 Hz).
@@ -28,4 +28,16 @@ if [ "$SELENE_BACKEND" = "drm" ]; then
28
28
export SELENE_INPUT
29
29
fi
30
30
31
+# Trwały log sesji: LightDM trzyma tylko 2 pliki `.xsession-errors` i obraca je przy
32
+# każdym starcie sesji, przez co logi Luny przepadają. Piszemy do stałego pliku w
33
+# katalogu stanu użytkownika, żeby każdą próbę dało się obejrzeć po fakcie.
34
+log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/luna"
35
+if mkdir -p "$log_dir" 2>/dev/null; then
36
+ log="$log_dir/session.log"
37
+ printf '=== luna-session %s ===\nXDG_RUNTIME_DIR=%s SELENE_BACKEND=%s SELENE_INPUT=%s\n' \
38
+ "$(date -Is)" "${XDG_RUNTIME_DIR:-<brak>}" "$SELENE_BACKEND" "${SELENE_INPUT:-<brak>}" \
39
+ >>"$log" 2>/dev/null || true
40
+ exec >>"$log" 2>&1
41
+fi
42
+
31
43
exec selene
@@ -28,4 +28,16 @@ if [ "$SELENE_BACKEND" = "drm" ]; then
28
28
export SELENE_INPUT
29
29
fi
30
30
31
+# Trwały log sesji: LightDM trzyma tylko 2 pliki `.xsession-errors` i obraca je przy
32
+# każdym starcie sesji, przez co logi Luny przepadają. Piszemy do stałego pliku w
33
+# katalogu stanu użytkownika, żeby każdą próbę dało się obejrzeć po fakcie.
34
+log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/luna"
35
+if mkdir -p "$log_dir" 2>/dev/null; then
36
+ log="$log_dir/session.log"
37
+ printf '=== luna-session %s ===\nXDG_RUNTIME_DIR=%s SELENE_BACKEND=%s SELENE_INPUT=%s\n' \
38
+ "$(date -Is)" "${XDG_RUNTIME_DIR:-<brak>}" "$SELENE_BACKEND" "${SELENE_INPUT:-<brak>}" \
39
+ >>"$log" 2>/dev/null || true
40
+ exec >>"$log" 2>&1
41
+fi
42
+
31
43
exec selene