1#![allow(dead_code)]
10
11use alloc::{collections::BTreeMap, string::String, vec::Vec};
12use core::sync::atomic::{AtomicU64, Ordering};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub enum SessionType {
21 #[default]
23 Console,
24 Desktop,
26 Wayland,
28 KdePlasma,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub enum LoginField {
39 #[default]
41 Username,
42 Password,
44 Authenticating,
46 Failed,
48 Success,
50}
51
52#[derive(Debug, Clone)]
54pub struct LoginScreen {
55 pub username_buffer: String,
57 pub password_buffer: String,
59 pub error_message: String,
61 pub state: LoginField,
63 max_username: usize,
65 max_password: usize,
67 cursor_blink: u32,
69}
70
71impl Default for LoginScreen {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl LoginScreen {
78 pub fn new() -> Self {
80 Self {
81 username_buffer: String::new(),
82 password_buffer: String::new(),
83 error_message: String::new(),
84 state: LoginField::Username,
85 max_username: 32,
86 max_password: 64,
87 cursor_blink: 0,
88 }
89 }
90
91 pub fn reset(&mut self) {
93 self.username_buffer.clear();
94 self.password_buffer.clear();
95 self.error_message.clear();
96 self.state = LoginField::Username;
97 self.cursor_blink = 0;
98 }
99
100 pub fn handle_key(&mut self, key: u8) -> bool {
104 match self.state {
105 LoginField::Username => {
106 match key {
107 b'\n' | b'\r' => {
108 self.state = LoginField::Password;
110 }
111 b'\t' => {
112 self.state = LoginField::Password;
113 }
114 0x08 | 0x7F => {
115 self.username_buffer.pop();
117 }
118 c if (0x20..0x7F).contains(&c) => {
119 if self.username_buffer.len() < self.max_username {
120 self.username_buffer.push(c as char);
121 }
122 }
123 _ => {}
124 }
125 false
126 }
127 LoginField::Password => {
128 match key {
129 b'\n' | b'\r' => {
130 self.state = LoginField::Authenticating;
132 return true;
133 }
134 0x08 | 0x7F => {
135 self.password_buffer.pop();
136 }
137 c if (0x20..0x7F).contains(&c) => {
138 if self.password_buffer.len() < self.max_password {
139 self.password_buffer.push(c as char);
140 }
141 }
142 _ => {}
143 }
144 false
145 }
146 LoginField::Failed => {
147 self.reset();
149 false
150 }
151 LoginField::Authenticating | LoginField::Success => false,
152 }
153 }
154
155 pub fn render(&mut self, buf: &mut [u32], width: u32, height: u32) {
159 let w = width as i32;
160 let h = height as i32;
161
162 for px in buf.iter_mut() {
164 *px = 0xFF1A1A2E;
165 }
166
167 let box_w: i32 = 320;
169 let box_h: i32 = 200;
170 let box_x = (w - box_w) / 2;
171 let box_y = (h - box_h) / 2;
172
173 for row in 0..box_h {
175 let dy = box_y + row;
176 if dy < 0 || dy >= h {
177 continue;
178 }
179 for col in 0..box_w {
180 let dx = box_x + col;
181 if dx < 0 || dx >= w {
182 continue;
183 }
184 buf[(dy * w + dx) as usize] = 0xFF2D2D44;
185 }
186 }
187
188 let border_color = 0xFF5555AA;
190 for col in 0..box_w {
191 let dx = box_x + col;
192 if dx >= 0 && dx < w {
193 if box_y >= 0 && box_y < h {
194 buf[(box_y * w + dx) as usize] = border_color;
195 }
196 let by = box_y + box_h - 1;
197 if by >= 0 && by < h {
198 buf[(by * w + dx) as usize] = border_color;
199 }
200 }
201 }
202 for row in 0..box_h {
203 let dy = box_y + row;
204 if dy >= 0 && dy < h {
205 if box_x >= 0 && box_x < w {
206 buf[(dy * w + box_x) as usize] = border_color;
207 }
208 let bx = box_x + box_w - 1;
209 if bx >= 0 && bx < w {
210 buf[(dy * w + bx) as usize] = border_color;
211 }
212 }
213 }
214
215 let title = b"VeridianOS Login";
217 let title_x = box_x + (box_w - title.len() as i32 * 8) / 2;
218 let title_y = box_y + 16;
219 draw_text(buf, w, h, title_x, title_y, title, 0xFFCCCCFF);
220
221 let label_x = box_x + 20;
223 let field_x = box_x + 100;
224 let user_y = box_y + 50;
225 draw_text(buf, w, h, label_x, user_y, b"User:", 0xFFAAAACC);
226 draw_field(
227 buf,
228 w,
229 h,
230 field_x,
231 user_y,
232 180,
233 &self.username_buffer,
234 self.state == LoginField::Username,
235 );
236
237 let pass_y = box_y + 80;
239 draw_text(buf, w, h, label_x, pass_y, b"Pass:", 0xFFAAAACC);
240 let mut masked = String::new();
241 for _ in 0..self.password_buffer.len() {
242 masked.push('*');
243 }
244 draw_field(
245 buf,
246 w,
247 h,
248 field_x,
249 pass_y,
250 180,
251 &masked,
252 self.state == LoginField::Password,
253 );
254
255 let btn_y = box_y + 120;
257 let btn_x = box_x + (box_w - 80) / 2;
258 let btn_color = if self.state == LoginField::Authenticating {
259 0xFF666688
260 } else {
261 0xFF4444AA
262 };
263 for row in 0..24 {
264 let dy = btn_y + row;
265 if dy < 0 || dy >= h {
266 continue;
267 }
268 for col in 0..80 {
269 let dx = btn_x + col;
270 if dx >= 0 && dx < w {
271 buf[(dy * w + dx) as usize] = btn_color;
272 }
273 }
274 }
275 let btn_label = if self.state == LoginField::Authenticating {
276 b"Wait..." as &[u8]
277 } else {
278 b"Login" as &[u8]
279 };
280 let btn_tx = btn_x + (80 - btn_label.len() as i32 * 8) / 2;
281 draw_text(buf, w, h, btn_tx, btn_y + 4, btn_label, 0xFFFFFFFF);
282
283 if !self.error_message.is_empty() {
285 let err_y = box_y + 160;
286 let err_bytes = self.error_message.as_bytes();
287 let err_x = box_x + (box_w - err_bytes.len() as i32 * 8) / 2;
288 draw_text(buf, w, h, err_x, err_y, err_bytes, 0xFFFF4444);
289 }
290
291 self.cursor_blink = self.cursor_blink.wrapping_add(1);
292 }
293
294 pub fn set_auth_result(&mut self, success: bool, message: &str) {
296 if success {
297 self.state = LoginField::Success;
298 self.error_message.clear();
299 } else {
300 self.state = LoginField::Failed;
301 self.error_message = String::from(message);
302 }
303 }
304
305 pub fn authenticate(&mut self) -> bool {
307 #[cfg(feature = "alloc")]
309 {
310 let _username = &self.username_buffer;
311 let _password = &self.password_buffer;
312 if self.username_buffer == "root" && !self.password_buffer.is_empty() {
315 self.set_auth_result(true, "");
316 return true;
317 }
318 }
319 self.set_auth_result(false, "Invalid credentials");
320 false
321 }
322}
323
324#[derive(Debug, Clone)]
330pub struct DisplaySession {
331 pub session_type: SessionType,
333 pub user_id: u32,
335 pub username: String,
337 pub login_time: u64,
339 pub idle_timeout_ticks: u64,
341 pub last_activity: u64,
343 pub locked: bool,
345}
346
347impl DisplaySession {
348 pub fn new(session_type: SessionType, user_id: u32, username: &str) -> Self {
350 Self {
351 session_type,
352 user_id,
353 username: String::from(username),
354 login_time: 0,
355 idle_timeout_ticks: 300_000, last_activity: 0,
357 locked: false,
358 }
359 }
360
361 pub fn touch(&mut self, tick: u64) {
363 self.last_activity = tick;
364 }
365
366 pub fn is_idle(&self, current_tick: u64) -> bool {
368 if self.idle_timeout_ticks == 0 {
369 return false;
370 }
371 current_tick.saturating_sub(self.last_activity) >= self.idle_timeout_ticks
372 }
373}
374
375#[derive(Debug, Clone)]
381pub struct VirtualTerminal {
382 pub id: u8,
384 pub session_type: SessionType,
386 pub active: bool,
388 pub user_id: u32,
390}
391
392impl VirtualTerminal {
393 pub fn new(id: u8) -> Self {
395 Self {
396 id,
397 session_type: SessionType::Console,
398 active: false,
399 user_id: 0,
400 }
401 }
402}
403
404static TICK_COUNTER: AtomicU64 = AtomicU64::new(0);
410
411#[derive(Debug)]
413pub struct DisplayManager {
414 pub current_session: Option<DisplaySession>,
416 pub login_screen: LoginScreen,
418 sessions: BTreeMap<u32, DisplaySession>,
420 virtual_terminals: Vec<VirtualTerminal>,
422 active_vt: u8,
424 auto_login_enabled: bool,
426 auto_login_user: String,
428 pub showing_login: bool,
430}
431
432impl Default for DisplayManager {
433 fn default() -> Self {
434 Self::new()
435 }
436}
437
438impl DisplayManager {
439 pub fn new() -> Self {
441 let mut vts = Vec::new();
442 for i in 1..=6 {
443 vts.push(VirtualTerminal::new(i));
444 }
445 if let Some(vt) = vts.first_mut() {
447 vt.active = true;
448 }
449
450 Self {
451 current_session: None,
452 login_screen: LoginScreen::new(),
453 sessions: BTreeMap::new(),
454 virtual_terminals: vts,
455 active_vt: 1,
456 auto_login_enabled: false,
457 auto_login_user: String::new(),
458 showing_login: true,
459 }
460 }
461
462 pub fn show_login(&mut self) {
464 self.login_screen.reset();
465 self.showing_login = true;
466 }
467
468 pub fn spawn_session(
470 &mut self,
471 session_type: SessionType,
472 user_id: u32,
473 username: &str,
474 ) -> bool {
475 let tick = TICK_COUNTER.load(Ordering::Relaxed);
476 let mut session = DisplaySession::new(session_type, user_id, username);
477 session.login_time = tick;
478 session.last_activity = tick;
479
480 self.sessions.insert(user_id, session.clone());
481 self.current_session = Some(session);
482 self.showing_login = false;
483
484 let vt_idx = (self.active_vt as usize).saturating_sub(1);
486 if let Some(vt) = self.virtual_terminals.get_mut(vt_idx) {
487 vt.user_id = user_id;
488 vt.session_type = session_type;
489 }
490
491 true
492 }
493
494 pub fn lock_session(&mut self) {
496 if let Some(ref mut session) = self.current_session {
497 session.locked = true;
498 self.showing_login = true;
499 self.login_screen.reset();
500 self.login_screen.username_buffer = session.username.clone();
501 self.login_screen.state = LoginField::Password;
502 }
503 }
504
505 pub fn unlock_session(&mut self) -> bool {
507 if let Some(ref mut session) = self.current_session {
508 if session.locked {
509 session.locked = false;
510 self.showing_login = false;
511 let tick = TICK_COUNTER.load(Ordering::Relaxed);
512 session.touch(tick);
513 return true;
514 }
515 }
516 false
517 }
518
519 pub fn handle_vt_switch(&mut self, vt_num: u8) -> bool {
523 if !(1..=6).contains(&vt_num) {
524 return false;
525 }
526
527 let old_idx = (self.active_vt as usize).saturating_sub(1);
529 if let Some(vt) = self.virtual_terminals.get_mut(old_idx) {
530 vt.active = false;
531 }
532
533 let new_idx = (vt_num as usize).saturating_sub(1);
535 if let Some(vt) = self.virtual_terminals.get_mut(new_idx) {
536 vt.active = true;
537 self.active_vt = vt_num;
538
539 if vt.user_id > 0 {
541 if let Some(session) = self.sessions.get(&vt.user_id) {
542 self.current_session = Some(session.clone());
543 self.showing_login = false;
544 } else {
545 self.showing_login = true;
546 }
547 } else {
548 self.showing_login = true;
549 }
550 return true;
551 }
552
553 false
554 }
555
556 pub fn check_idle(&mut self, current_tick: u64) {
558 TICK_COUNTER.store(current_tick, Ordering::Relaxed);
559 if let Some(ref session) = self.current_session {
560 if !session.locked && session.is_idle(current_tick) {
561 self.lock_session();
562 }
563 }
564 }
565
566 pub fn set_auto_login(&mut self, username: &str) {
568 self.auto_login_enabled = true;
569 self.auto_login_user = String::from(username);
570 }
571
572 pub fn auto_login(&mut self) -> bool {
574 if self.auto_login_enabled && !self.auto_login_user.is_empty() {
575 let username = self.auto_login_user.clone();
576 self.spawn_session(SessionType::Desktop, 0, &username);
577 return true;
578 }
579 false
580 }
581
582 pub fn active_vt(&self) -> u8 {
584 self.active_vt
585 }
586
587 pub fn get_vt(&self, num: u8) -> Option<&VirtualTerminal> {
589 let idx = (num as usize).saturating_sub(1);
590 self.virtual_terminals.get(idx)
591 }
592
593 pub fn session_count(&self) -> usize {
595 self.sessions.len()
596 }
597
598 pub fn handle_key(&mut self, key: u8) -> bool {
602 if !self.showing_login {
603 return false;
604 }
605
606 let submit = self.login_screen.handle_key(key);
607 if submit {
608 let success = self.login_screen.authenticate();
609 if success {
610 let username = self.login_screen.username_buffer.clone();
611 self.spawn_session(SessionType::Desktop, 0, &username);
612 return true;
613 }
614 }
615 false
616 }
617
618 pub fn render(&mut self, buf: &mut [u32], width: u32, height: u32) {
620 if self.showing_login {
621 self.login_screen.render(buf, width, height);
622 }
623 }
624}
625
626#[derive(Debug, Clone)]
632pub struct SessionInfo {
633 pub session_id: u64,
635 pub user_id: u32,
637 pub username: String,
639 pub session_type: SessionType,
641 pub vt_number: u8,
643 pub active: bool,
645 pub locked: bool,
647}
648
649impl DisplayManager {
650 pub fn create_user_session(
654 &mut self,
655 user_id: u32,
656 username: &str,
657 session_type: SessionType,
658 ) -> Option<u64> {
659 if self.sessions.len() >= 8 {
660 return None;
661 }
662
663 let session_index = self.sessions.len() as u8;
664 let vt_number = 7u8.saturating_add(session_index);
665
666 let vt_idx = (vt_number as usize).saturating_sub(1);
668 if vt_idx < self.virtual_terminals.len() {
669 let vt = &mut self.virtual_terminals[vt_idx];
670 vt.user_id = user_id;
671 vt.session_type = session_type;
672 }
673
674 let tick = TICK_COUNTER.load(Ordering::Relaxed);
675 let mut session = DisplaySession::new(session_type, user_id, username);
676 session.login_time = tick;
677 session.last_activity = tick;
678
679 self.sessions.insert(user_id, session.clone());
680
681 if self.current_session.is_none() {
683 self.current_session = Some(session);
684 self.showing_login = false;
685 }
686
687 Some(user_id as u64)
689 }
690
691 pub fn switch_to_session(&mut self, target_user_id: u32) -> bool {
696 if let Some(session) = self.sessions.get(&target_user_id) {
697 self.current_session = Some(session.clone());
698 self.showing_login = false;
699
700 for vt in self.virtual_terminals.iter_mut() {
702 if vt.user_id == target_user_id {
703 vt.active = true;
704 self.active_vt = vt.id;
705 } else {
706 vt.active = false;
707 }
708 }
709 true
710 } else {
711 false
712 }
713 }
714
715 pub fn get_sessions(&self) -> Vec<SessionInfo> {
717 let mut result = Vec::new();
718 for (uid, session) in &self.sessions {
719 let info = SessionInfo {
720 session_id: *uid as u64,
721 user_id: *uid,
722 username: session.username.clone(),
723 session_type: session.session_type,
724 vt_number: self
725 .virtual_terminals
726 .iter()
727 .find(|vt| vt.user_id == *uid)
728 .map(|vt| vt.id)
729 .unwrap_or(0),
730 active: self
731 .current_session
732 .as_ref()
733 .map(|cs| cs.user_id == *uid)
734 .unwrap_or(false),
735 locked: session.locked,
736 };
737 result.push(info);
738 }
739 result
740 }
741
742 pub fn logout_user(&mut self, user_id: u32) {
744 self.sessions.remove(&user_id);
746
747 for vt in self.virtual_terminals.iter_mut() {
749 if vt.user_id == user_id {
750 vt.user_id = 0;
751 vt.session_type = SessionType::Console;
752 }
753 }
754
755 if let Some(ref session) = self.current_session {
757 if session.user_id == user_id {
758 self.current_session = None;
759 if let Some((&next_uid, next_session)) = self.sessions.iter().next() {
761 self.current_session = Some(next_session.clone());
762 let _ = next_uid;
763 } else {
764 self.show_login();
765 }
766 }
767 }
768 }
769}
770
771fn draw_text(buf: &mut [u32], bw: i32, bh: i32, x: i32, y: i32, text: &[u8], color: u32) {
777 for (i, &ch) in text.iter().enumerate() {
778 let cx = x + (i as i32) * 8;
779 if cx + 8 <= 0 || cx >= bw || y + 16 <= 0 || y >= bh {
780 continue;
781 }
782 if !(0x20..0x7F).contains(&ch) {
783 continue;
784 }
785 for row in 2..14 {
787 let dy = y + row;
788 if dy < 0 || dy >= bh {
789 continue;
790 }
791 for col in 1..7 {
792 let dx = cx + col;
793 if dx < 0 || dx >= bw {
794 continue;
795 }
796 if row == 2 || row == 13 || col == 1 || col == 6 {
798 buf[(dy * bw + dx) as usize] = color;
799 }
800 }
801 }
802 }
803}
804
805fn draw_field(
807 buf: &mut [u32],
808 bw: i32,
809 bh: i32,
810 x: i32,
811 y: i32,
812 field_w: i32,
813 text: &str,
814 active: bool,
815) {
816 let bg = if active { 0xFF3D3D55 } else { 0xFF2D2D44 };
818 for row in 0..18 {
819 let dy = y + row;
820 if dy < 0 || dy >= bh {
821 continue;
822 }
823 for col in 0..field_w {
824 let dx = x + col;
825 if dx >= 0 && dx < bw {
826 buf[(dy * bw + dx) as usize] = bg;
827 }
828 }
829 }
830
831 let border = if active { 0xFF7777FF } else { 0xFF555577 };
833 for col in 0..field_w {
834 let dx = x + col;
835 if dx >= 0 && dx < bw {
836 if y >= 0 && y < bh {
837 buf[(y * bw + dx) as usize] = border;
838 }
839 let by = y + 17;
840 if by >= 0 && by < bh {
841 buf[(by * bw + dx) as usize] = border;
842 }
843 }
844 }
845
846 draw_text(buf, bw, bh, x + 4, y + 1, text.as_bytes(), 0xFFEEEEEE);
848}
849
850#[cfg(test)]
855mod tests {
856 #[allow(unused_imports)]
857 use alloc::vec;
858
859 use super::*;
860
861 #[test]
862 fn test_login_screen_new() {
863 let ls = LoginScreen::new();
864 assert_eq!(ls.state, LoginField::Username);
865 assert!(ls.username_buffer.is_empty());
866 }
867
868 #[test]
869 fn test_login_screen_type_username() {
870 let mut ls = LoginScreen::new();
871 ls.handle_key(b'r');
872 ls.handle_key(b'o');
873 ls.handle_key(b'o');
874 ls.handle_key(b't');
875 assert_eq!(ls.username_buffer, "root");
876 }
877
878 #[test]
879 fn test_login_screen_tab_to_password() {
880 let mut ls = LoginScreen::new();
881 ls.handle_key(b'\t');
882 assert_eq!(ls.state, LoginField::Password);
883 }
884
885 #[test]
886 fn test_login_screen_submit() {
887 let mut ls = LoginScreen::new();
888 ls.state = LoginField::Password;
889 ls.password_buffer = String::from("secret");
890 let submit = ls.handle_key(b'\n');
891 assert!(submit);
892 assert_eq!(ls.state, LoginField::Authenticating);
893 }
894
895 #[test]
896 fn test_login_screen_backspace() {
897 let mut ls = LoginScreen::new();
898 ls.handle_key(b'a');
899 ls.handle_key(b'b');
900 ls.handle_key(0x08); assert_eq!(ls.username_buffer, "a");
902 }
903
904 #[test]
905 fn test_login_screen_authenticate_root() {
906 let mut ls = LoginScreen::new();
907 ls.username_buffer = String::from("root");
908 ls.password_buffer = String::from("pass");
909 assert!(ls.authenticate());
910 assert_eq!(ls.state, LoginField::Success);
911 }
912
913 #[test]
914 fn test_login_screen_authenticate_fail() {
915 let mut ls = LoginScreen::new();
916 ls.username_buffer = String::from("nobody");
917 ls.password_buffer = String::from("wrong");
918 assert!(!ls.authenticate());
919 assert_eq!(ls.state, LoginField::Failed);
920 }
921
922 #[test]
923 fn test_display_session_idle() {
924 let mut session = DisplaySession::new(SessionType::Desktop, 1, "test");
925 session.last_activity = 0;
926 session.idle_timeout_ticks = 1000;
927 assert!(!session.is_idle(500));
928 assert!(session.is_idle(1500));
929 }
930
931 #[test]
932 fn test_display_manager_spawn_session() {
933 let mut dm = DisplayManager::new();
934 assert!(dm.showing_login);
935 dm.spawn_session(SessionType::Desktop, 1, "root");
936 assert!(!dm.showing_login);
937 assert_eq!(dm.session_count(), 1);
938 }
939
940 #[test]
941 fn test_display_manager_lock_unlock() {
942 let mut dm = DisplayManager::new();
943 dm.spawn_session(SessionType::Desktop, 1, "root");
944 dm.lock_session();
945 assert!(dm.showing_login);
946 assert!(dm.current_session.as_ref().unwrap().locked);
947 dm.unlock_session();
948 assert!(!dm.showing_login);
949 }
950
951 #[test]
952 fn test_display_manager_vt_switch() {
953 let mut dm = DisplayManager::new();
954 assert_eq!(dm.active_vt(), 1);
955 dm.handle_vt_switch(3);
956 assert_eq!(dm.active_vt(), 3);
957 }
958
959 #[test]
960 fn test_display_manager_vt_invalid() {
961 let mut dm = DisplayManager::new();
962 assert!(!dm.handle_vt_switch(0));
963 assert!(!dm.handle_vt_switch(7));
964 }
965
966 #[test]
967 fn test_display_manager_auto_login() {
968 let mut dm = DisplayManager::new();
969 dm.set_auto_login("root");
970 assert!(dm.auto_login());
971 assert!(!dm.showing_login);
972 }
973
974 #[test]
975 fn test_display_manager_check_idle() {
976 let mut dm = DisplayManager::new();
977 dm.spawn_session(SessionType::Desktop, 1, "root");
978 dm.current_session.as_mut().unwrap().idle_timeout_ticks = 100;
979 dm.current_session.as_mut().unwrap().last_activity = 0;
980 dm.check_idle(200);
981 assert!(dm.showing_login);
982 }
983
984 #[test]
985 fn test_display_manager_handle_key() {
986 let mut dm = DisplayManager::new();
987 for c in b"root" {
989 dm.handle_key(*c);
990 }
991 dm.handle_key(b'\t');
992 for c in b"pass" {
993 dm.handle_key(*c);
994 }
995 let spawned = dm.handle_key(b'\n');
996 assert!(spawned);
997 assert!(!dm.showing_login);
998 }
999
1000 #[test]
1001 fn test_login_render_no_panic() {
1002 let mut ls = LoginScreen::new();
1003 let mut buf = vec![0u32; 320 * 200];
1004 ls.render(&mut buf, 320, 200);
1005 assert!(buf.iter().any(|&p| p != 0));
1007 }
1008
1009 #[test]
1010 fn test_virtual_terminal_new() {
1011 let vt = VirtualTerminal::new(1);
1012 assert_eq!(vt.id, 1);
1013 assert!(!vt.active);
1014 assert_eq!(vt.user_id, 0);
1015 }
1016
1017 #[test]
1018 fn test_session_touch() {
1019 let mut session = DisplaySession::new(SessionType::Console, 1, "test");
1020 session.touch(12345);
1021 assert_eq!(session.last_activity, 12345);
1022 }
1023
1024 #[test]
1025 fn test_create_user_session() {
1026 let mut dm = DisplayManager::new();
1027 let sid = dm.create_user_session(1000, "alice", SessionType::Desktop);
1028 assert!(sid.is_some());
1029 assert_eq!(dm.session_count(), 1);
1030 assert!(!dm.showing_login);
1031 }
1032
1033 #[test]
1034 fn test_create_user_session_max() {
1035 let mut dm = DisplayManager::new();
1036 for i in 0..8u32 {
1037 let name = alloc::format!("user{}", i);
1038 dm.create_user_session(i, &name, SessionType::Desktop);
1039 }
1040 let overflow = dm.create_user_session(99, "overflow", SessionType::Desktop);
1041 assert!(overflow.is_none());
1042 }
1043
1044 #[test]
1045 fn test_switch_to_session() {
1046 let mut dm = DisplayManager::new();
1047 dm.create_user_session(1, "alice", SessionType::Desktop);
1048 dm.create_user_session(2, "bob", SessionType::Desktop);
1049
1050 assert!(dm.switch_to_session(2));
1051 let current = dm.current_session.as_ref().unwrap();
1052 assert_eq!(current.user_id, 2);
1053 assert_eq!(current.username, "bob");
1054 }
1055
1056 #[test]
1057 fn test_get_sessions() {
1058 let mut dm = DisplayManager::new();
1059 dm.create_user_session(1, "alice", SessionType::Desktop);
1060 dm.create_user_session(2, "bob", SessionType::Wayland);
1061
1062 let sessions = dm.get_sessions();
1063 assert_eq!(sessions.len(), 2);
1064 }
1065
1066 #[test]
1067 fn test_logout_user() {
1068 let mut dm = DisplayManager::new();
1069 dm.create_user_session(1, "alice", SessionType::Desktop);
1070 dm.create_user_session(2, "bob", SessionType::Desktop);
1071
1072 dm.logout_user(1);
1073 assert_eq!(dm.session_count(), 1);
1074 let current = dm.current_session.as_ref().unwrap();
1076 assert_eq!(current.user_id, 2);
1077 }
1078
1079 #[test]
1080 fn test_logout_last_user_shows_login() {
1081 let mut dm = DisplayManager::new();
1082 dm.create_user_session(1, "alice", SessionType::Desktop);
1083 dm.logout_user(1);
1084 assert_eq!(dm.session_count(), 0);
1085 assert!(dm.showing_login);
1086 }
1087}