/* * main.cpp * MontaukOS graphical login screen * Copyright (c) 2026 Daniel Hammer */ #include "login.hpp" inline void* operator new(unsigned long, void* p) { return p; } static void maybe_run_setup_session() { auto doc = montauk::config::load("setup"); const char* mode = doc.get_string("environment.mode", ""); if (!montauk::streq(mode, "setup")) { doc.destroy(); return; } const char* user = doc.get_string("session.username", "liveuser"); const char* display = doc.get_string("session.display_name", "Live User"); const char* role = doc.get_string("session.role", "admin"); montauk::fmkdir("0:/users"); montauk::user::create_user(user, display, "", role); montauk::user::set_session(user); doc.destroy(); int pid = montauk::spawn("0:/os/desktop.elf", user); if (pid >= 0) { montauk::setuser(pid, user); montauk::waitpid(pid); } montauk::user::clear_session(); } static void initialize_login_mode(LoginState* ls) { int fh = montauk::open("0:/config/users.toml"); if (fh < 0) { ls->mode = MODE_FIRST_BOOT; ls->active_field = 0; montauk::strcpy(ls->username, "admin"); ls->username_len = 5; gui::mtk::text_input_set_cursor(ls->username_input, ls->username_len, ls->username_len, false); return; } montauk::close(fh); ls->mode = MODE_LOGIN; ls->active_field = 0; } extern "C" void _start() { LoginState* ls = (LoginState*)montauk::malloc(sizeof(LoginState)); montauk::memset(ls, 0, sizeof(LoginState)); new (&ls->fb) gui::Framebuffer(); ls->screen_w = ls->fb.width(); ls->screen_h = ls->fb.height(); gui::fonts::init(); montauk::set_mouse_bounds(ls->screen_w - 1, ls->screen_h - 1); load_login_wallpaper(ls); // MTK theme (picks up the system accent). The compose buffer is only // needed when the framebuffer pitch is not tightly packed; otherwise the // renderer draws straight into the framebuffer back buffer. ls->theme = gui::mtk::make_theme(); if (ls->fb.pitch() != ls->screen_w * (int)sizeof(uint32_t)) { ls->compose = (uint32_t*)montauk::alloc( (uint64_t)ls->screen_w * ls->screen_h * sizeof(uint32_t)); } maybe_run_setup_session(); initialize_login_mode(ls); bool first_frame = true; uint64_t input_serial = montauk::input_wait(0, 0); for (;;) { bool mouse_changed = false; bool key_changed = false; bool fast_mouse_path = false; int prev_mouse_x = ls->mouse.x; int prev_mouse_y = ls->mouse.y; uint8_t prev_mouse_buttons = ls->mouse.buttons; ls->prev_buttons = ls->mouse.buttons; montauk::mouse_state(&ls->mouse); mouse_changed = ls->mouse.x != prev_mouse_x || ls->mouse.y != prev_mouse_y || ls->mouse.buttons != prev_mouse_buttons || ls->mouse.scrollDelta != 0; fast_mouse_path = mouse_changed || ls->mouse.buttons != 0; while (montauk::is_key_available()) { montauk::abi::KeyEvent key; montauk::getkey(&key); // Deliberately untranslated: the login screen is US-English only // until it grows its own layout switcher. There is no user yet, so // there is no per-user layout to apply, and silently picking one // would leave no way to type a password in a different layout. handle_key(ls, key); key_changed = true; } if (mouse_changed) handle_mouse(ls); if (first_frame || mouse_changed || key_changed) { draw_login_screen(ls); first_frame = false; } // Event-driven wait: input_wait returns the instant the mouse or // keyboard ISR reports activity, so the cursor tracks the device with // no fixed-timer latency. timeoutMs is only an upper bound. Matches // the desktop's run loop; a plain sleep_ms() here cannot wake early // and makes every gesture start ~16 ms late. // // The login screen has no periodic UI (no clock, no caret blink, no // animation), so when idle it never needs to wake without input: pass // UINT64_MAX to block indefinitely and consume 0% CPU until the next // mouse/keyboard event, instead of waking ~62x/sec for nothing. uint64_t wait_ms = fast_mouse_path ? 1 : ((mouse_changed || key_changed) ? 4 : UINT64_MAX); input_serial = montauk::input_wait(input_serial, wait_ms); } }