feat: revamp login screen, fix printing bug
This commit is contained in:
@@ -289,7 +289,11 @@ static int _path_is_directory(const char *path) {
|
||||
return (int)_zos_syscall3(SYS_READDIR, (long)path, (long)names, 1L) >= 0;
|
||||
}
|
||||
|
||||
static int _path_prefix_skip(const char *path) {
|
||||
static int _path_local_prefix(const char *path, char *buf, size_t size) {
|
||||
if (buf == NULL || size == 0) return 0;
|
||||
buf[0] = '\0';
|
||||
if (path == NULL) return 0;
|
||||
|
||||
const char *local = path;
|
||||
for (int i = 0; local[i]; i++) {
|
||||
if (local[i] == ':') {
|
||||
@@ -299,9 +303,15 @@ static int _path_prefix_skip(const char *path) {
|
||||
}
|
||||
}
|
||||
|
||||
int prefix_len = (int)strlen(local);
|
||||
if (prefix_len > 0 && local[prefix_len - 1] != '/') prefix_len++;
|
||||
return prefix_len;
|
||||
size_t len = strlen(local);
|
||||
while (len > 0 && local[len - 1] == '/') len--;
|
||||
if (len == 0) return 0;
|
||||
|
||||
if (len >= size - 1) len = size - 2;
|
||||
memcpy(buf, local, len);
|
||||
buf[len++] = '/';
|
||||
buf[len] = '\0';
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
static int _find_env_slot(const char *name) {
|
||||
@@ -2076,10 +2086,12 @@ DIR *opendir(const char *name) {
|
||||
dir->count = count;
|
||||
dir->index = 0;
|
||||
|
||||
int prefix_len = _path_prefix_skip(name);
|
||||
char local_prefix[256];
|
||||
int prefix_len = _path_local_prefix(name, local_prefix, sizeof(local_prefix));
|
||||
for (int i = 0; i < count && i < 256; i++) {
|
||||
const char *entry = raw_names[i];
|
||||
if ((int)strlen(entry) >= prefix_len) {
|
||||
if (prefix_len > 0 &&
|
||||
strncmp(entry, local_prefix, (size_t)prefix_len) == 0) {
|
||||
entry += prefix_len;
|
||||
}
|
||||
strncpy(dir->names[i], entry, NAME_MAX);
|
||||
|
||||
Binary file not shown.
+245
-237
@@ -213,54 +213,163 @@ static bool load_login_wallpaper(LoginState* ls) {
|
||||
// Drawing helpers
|
||||
// ============================================================================
|
||||
|
||||
static constexpr Color BG_COLOR = Color::from_rgb(0x2B, 0x3E, 0x50);
|
||||
static constexpr Color CARD_BG = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||
static constexpr Color FIELD_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
|
||||
static constexpr Color FIELD_BORDER = Color::from_rgb(0xCC, 0xCC, 0xCC);
|
||||
static constexpr Color FIELD_ACTIVE = Color::from_rgb(0x36, 0x7B, 0xF0);
|
||||
static constexpr Color BTN_COLOR = Color::from_rgb(0x36, 0x7B, 0xF0);
|
||||
static constexpr Color BTN_TEXT = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||
static constexpr Color TEXT_COLOR = Color::from_rgb(0x33, 0x33, 0x33);
|
||||
static constexpr Color BG_COLOR = Color::from_rgb(0x00, 0x00, 0x00);
|
||||
static constexpr Color CARD_BG = colors::WINDOW_BG;
|
||||
static constexpr Color CARD_BORDER = colors::BORDER;
|
||||
static constexpr Color TITLEBAR_BG = colors::TITLEBAR_BG;
|
||||
static constexpr Color FOOTER_BG = Color::from_rgb(0xF7, 0xF7, 0xF7);
|
||||
static constexpr Color FIELD_BG = colors::WHITE;
|
||||
static constexpr Color FIELD_BG_ACTIVE = Color::from_rgb(0xFA, 0xFC, 0xFF);
|
||||
static constexpr Color FIELD_BORDER = colors::BORDER;
|
||||
static constexpr Color FIELD_HOVER = Color::from_rgb(0xB7, 0xC7, 0xE8);
|
||||
static constexpr Color FIELD_ACTIVE = colors::ACCENT;
|
||||
static constexpr Color BTN_COLOR = colors::ACCENT;
|
||||
static constexpr Color BTN_HOVER = Color::from_rgb(0x2B, 0x6B, 0xE0);
|
||||
static constexpr Color BTN_TEXT = colors::WHITE;
|
||||
static constexpr Color SECONDARY_BTN_BG = Color::from_rgb(0xE8, 0xE8, 0xE8);
|
||||
static constexpr Color SECONDARY_BTN_HOVER = Color::from_rgb(0xDC, 0xDC, 0xDC);
|
||||
static constexpr Color SECONDARY_BTN_BORDER = colors::BORDER;
|
||||
static constexpr Color TEXT_COLOR = colors::TEXT_COLOR;
|
||||
static constexpr Color LABEL_COLOR = Color::from_rgb(0x66, 0x66, 0x66);
|
||||
static constexpr Color ERROR_COLOR = Color::from_rgb(0xE0, 0x40, 0x40);
|
||||
static constexpr Color TITLE_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||
static constexpr Color ERROR_COLOR = Color::from_rgb(0xC0, 0x33, 0x33);
|
||||
|
||||
static constexpr int CARD_W = 360;
|
||||
static constexpr int FIELD_H = 36;
|
||||
static constexpr int CARD_W = 432;
|
||||
static constexpr int TITLEBAR_H = 30;
|
||||
static constexpr int FOOTER_H = 58;
|
||||
static constexpr int FIELD_H = 34;
|
||||
static constexpr int FIELD_PAD = 10;
|
||||
static constexpr int BTN_H = 40;
|
||||
static constexpr int BTN_H = 30;
|
||||
static constexpr int LABEL_GAP = 4;
|
||||
static constexpr int POWER_ICON_SZ = 32;
|
||||
static constexpr int POWER_GAP = 48; // horizontal spacing between icon centers
|
||||
static constexpr int POWER_TOP_PAD = 24; // padding below error message area
|
||||
static constexpr int CONTENT_PAD_X = 24;
|
||||
static constexpr int CONTENT_TOP_PAD = 18;
|
||||
static constexpr int BODY_BOTTOM_PAD = 18;
|
||||
static constexpr int FIELD_GAP = 12;
|
||||
static constexpr int POWER_ICON_SZ = 16;
|
||||
static constexpr int POWER_BTN_W = 104;
|
||||
static constexpr int POWER_BTN_GAP = 8;
|
||||
static constexpr int FOOTER_PAD = 16;
|
||||
|
||||
static void draw_field(Framebuffer& fb, int x, int y, int w, const char* text,
|
||||
bool is_password, bool active) {
|
||||
// Background
|
||||
fb.fill_rect(x, y, w, FIELD_H, FIELD_BG);
|
||||
struct LoginLayout {
|
||||
Rect card;
|
||||
Rect titlebar;
|
||||
Rect footer;
|
||||
Rect username_field;
|
||||
Rect display_name_field;
|
||||
Rect password_field;
|
||||
Rect confirm_field;
|
||||
Rect submit_button;
|
||||
Rect shutdown_button;
|
||||
Rect reboot_button;
|
||||
int content_x;
|
||||
int content_w;
|
||||
int heading_y;
|
||||
int subtitle_y;
|
||||
int error_y;
|
||||
const char* window_title;
|
||||
const char* heading;
|
||||
const char* subtitle;
|
||||
const char* submit_label;
|
||||
};
|
||||
|
||||
// Border
|
||||
Color border = active ? FIELD_ACTIVE : FIELD_BORDER;
|
||||
// Top
|
||||
for (int i = 0; i < w; i++) fb.put_pixel(x + i, y, border);
|
||||
// Bottom
|
||||
for (int i = 0; i < w; i++) fb.put_pixel(x + i, y + FIELD_H - 1, border);
|
||||
// Left
|
||||
for (int i = 0; i < FIELD_H; i++) fb.put_pixel(x, y + i, border);
|
||||
// Right
|
||||
for (int i = 0; i < FIELD_H; i++) fb.put_pixel(x + w - 1, y + i, border);
|
||||
static Rect offset_rect(Rect rect, int dx, int dy) {
|
||||
rect.x += dx;
|
||||
rect.y += dy;
|
||||
return rect;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
// Draw 2px border for active field
|
||||
for (int i = 0; i < w; i++) fb.put_pixel(x + i, y + 1, border);
|
||||
for (int i = 0; i < w; i++) fb.put_pixel(x + i, y + FIELD_H - 2, border);
|
||||
for (int i = 0; i < FIELD_H; i++) fb.put_pixel(x + 1, y + i, border);
|
||||
for (int i = 0; i < FIELD_H; i++) fb.put_pixel(x + w - 2, y + i, border);
|
||||
static void draw_rounded_frame(Framebuffer& fb, const Rect& rect, int radius,
|
||||
Color border, Color fill) {
|
||||
fill_rounded_rect(fb, rect.x, rect.y, rect.w, rect.h, radius, border);
|
||||
if (rect.w > 2 && rect.h > 2) {
|
||||
int inner_radius = radius > 0 ? radius - 1 : 0;
|
||||
fill_rounded_rect(fb, rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2,
|
||||
inner_radius, fill);
|
||||
}
|
||||
}
|
||||
|
||||
static LoginLayout layout_login_screen(LoginState* ls) {
|
||||
LoginLayout lo = {};
|
||||
int sfh = system_font_height();
|
||||
|
||||
lo.window_title = ls->mode == MODE_FIRST_BOOT ? "MontaukOS Setup" : "MontaukOS";
|
||||
lo.heading = ls->mode == MODE_FIRST_BOOT ? "Create Administrator Account" : "Log In";
|
||||
lo.subtitle = ls->mode == MODE_FIRST_BOOT
|
||||
? "Create the first administrator account."
|
||||
: "Sign in to continue to the desktop.";
|
||||
lo.submit_label = ls->mode == MODE_FIRST_BOOT ? "Create Account" : "Log In";
|
||||
|
||||
lo.content_x = CONTENT_PAD_X;
|
||||
lo.content_w = CARD_W - CONTENT_PAD_X * 2;
|
||||
|
||||
int y = TITLEBAR_H + CONTENT_TOP_PAD;
|
||||
lo.heading_y = y;
|
||||
y += sfh + 6;
|
||||
lo.subtitle_y = y;
|
||||
y += sfh + 18;
|
||||
|
||||
lo.username_field = {lo.content_x, y + sfh + LABEL_GAP, lo.content_w, FIELD_H};
|
||||
y = lo.username_field.y + FIELD_H + FIELD_GAP;
|
||||
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
lo.display_name_field = {lo.content_x, y + sfh + LABEL_GAP, lo.content_w, FIELD_H};
|
||||
y = lo.display_name_field.y + FIELD_H + FIELD_GAP;
|
||||
|
||||
lo.password_field = {lo.content_x, y + sfh + LABEL_GAP, lo.content_w, FIELD_H};
|
||||
y = lo.password_field.y + FIELD_H + FIELD_GAP;
|
||||
|
||||
lo.confirm_field = {lo.content_x, y + sfh + LABEL_GAP, lo.content_w, FIELD_H};
|
||||
y = lo.confirm_field.y + FIELD_H + FIELD_GAP;
|
||||
} else {
|
||||
lo.password_field = {lo.content_x, y + sfh + LABEL_GAP, lo.content_w, FIELD_H};
|
||||
y = lo.password_field.y + FIELD_H + FIELD_GAP;
|
||||
}
|
||||
|
||||
// Text (clipped to field width, showing trailing portion)
|
||||
int ty = y + (FIELD_H - system_font_height()) / 2;
|
||||
int max_text_w = w - FIELD_PAD * 2 - 2; // left pad, right pad, cursor
|
||||
if (ls->show_error) {
|
||||
lo.error_y = y;
|
||||
y += sfh + 12;
|
||||
} else {
|
||||
lo.error_y = -1;
|
||||
}
|
||||
|
||||
int footer_y = y + BODY_BOTTOM_PAD;
|
||||
int card_h = footer_y + FOOTER_H;
|
||||
int card_x = (ls->screen_w - CARD_W) / 2;
|
||||
int card_y = (ls->screen_h - card_h) / 2;
|
||||
|
||||
lo.card = {card_x, card_y, CARD_W, card_h};
|
||||
lo.titlebar = {card_x, card_y, CARD_W, TITLEBAR_H};
|
||||
lo.footer = {card_x, card_y + footer_y, CARD_W, FOOTER_H};
|
||||
|
||||
int submit_w = text_width(lo.submit_label) + 32;
|
||||
if (submit_w < 100) submit_w = 100;
|
||||
int footer_btn_y = footer_y + (FOOTER_H - BTN_H) / 2;
|
||||
lo.shutdown_button = {FOOTER_PAD, footer_btn_y, POWER_BTN_W, BTN_H};
|
||||
lo.reboot_button = {FOOTER_PAD + POWER_BTN_W + POWER_BTN_GAP, footer_btn_y, POWER_BTN_W, BTN_H};
|
||||
lo.submit_button = {CARD_W - FOOTER_PAD - submit_w, footer_btn_y, submit_w, BTN_H};
|
||||
|
||||
lo.username_field = offset_rect(lo.username_field, card_x, card_y);
|
||||
lo.display_name_field = offset_rect(lo.display_name_field, card_x, card_y);
|
||||
lo.password_field = offset_rect(lo.password_field, card_x, card_y);
|
||||
lo.confirm_field = offset_rect(lo.confirm_field, card_x, card_y);
|
||||
lo.shutdown_button = offset_rect(lo.shutdown_button, card_x, card_y);
|
||||
lo.reboot_button = offset_rect(lo.reboot_button, card_x, card_y);
|
||||
lo.submit_button = offset_rect(lo.submit_button, card_x, card_y);
|
||||
|
||||
lo.content_x += card_x;
|
||||
lo.heading_y += card_y;
|
||||
lo.subtitle_y += card_y;
|
||||
if (lo.error_y >= 0) lo.error_y += card_y;
|
||||
return lo;
|
||||
}
|
||||
|
||||
static void draw_field(Framebuffer& fb, const Rect& rect, const char* text,
|
||||
bool is_password, bool active, bool hovered) {
|
||||
Color border = active ? FIELD_ACTIVE : (hovered ? FIELD_HOVER : FIELD_BORDER);
|
||||
Color bg = active ? FIELD_BG_ACTIVE : FIELD_BG;
|
||||
draw_rounded_frame(fb, rect, 6, border, bg);
|
||||
|
||||
int ty = rect.y + (rect.h - system_font_height()) / 2;
|
||||
int max_text_w = rect.w - FIELD_PAD * 2 - 2;
|
||||
if (is_password) {
|
||||
int len = montauk::slen(text);
|
||||
if (len > 64) len = 64;
|
||||
@@ -274,32 +383,65 @@ static void draw_field(Framebuffer& fb, int x, int y, int w, const char* text,
|
||||
char masked[65];
|
||||
for (int i = 0; i < vis; i++) masked[i] = '*';
|
||||
masked[vis] = '\0';
|
||||
draw_text(fb, x + FIELD_PAD, ty, masked, TEXT_COLOR);
|
||||
draw_text(fb, rect.x + FIELD_PAD, ty, masked, TEXT_COLOR);
|
||||
if (active) {
|
||||
int cx = x + FIELD_PAD + text_width(masked);
|
||||
int cx = rect.x + FIELD_PAD + text_width(masked);
|
||||
fb.fill_rect(cx, ty, 2, system_font_height(), FIELD_ACTIVE);
|
||||
}
|
||||
} else {
|
||||
int len = montauk::slen(text);
|
||||
// Show only trailing portion that fits
|
||||
int offset = 0;
|
||||
while (text_width(text + offset) > max_text_w && offset < len)
|
||||
offset++;
|
||||
draw_text(fb, x + FIELD_PAD, ty, text + offset, TEXT_COLOR);
|
||||
draw_text(fb, rect.x + FIELD_PAD, ty, text + offset, TEXT_COLOR);
|
||||
if (active) {
|
||||
int cx = x + FIELD_PAD + text_width(text + offset);
|
||||
int cx = rect.x + FIELD_PAD + text_width(text + offset);
|
||||
fb.fill_rect(cx, ty, 2, system_font_height(), FIELD_ACTIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_button(Framebuffer& fb, int x, int y, int w, int h,
|
||||
const char* label, Color bg) {
|
||||
fb.fill_rect(x, y, w, h, bg);
|
||||
static void draw_labeled_field(Framebuffer& fb, const Rect& rect, const char* label,
|
||||
const char* text, bool is_password,
|
||||
bool active, bool hovered) {
|
||||
draw_text(fb, rect.x, rect.y - LABEL_GAP - system_font_height(), label, LABEL_COLOR);
|
||||
draw_field(fb, rect, text, is_password, active, hovered);
|
||||
}
|
||||
|
||||
static void draw_button(Framebuffer& fb, const Rect& rect, const char* label,
|
||||
bool hovered, bool primary) {
|
||||
if (primary) {
|
||||
fill_rounded_rect(fb, rect.x, rect.y, rect.w, rect.h, 4,
|
||||
hovered ? BTN_HOVER : BTN_COLOR);
|
||||
} else {
|
||||
draw_rounded_frame(fb, rect, 4,
|
||||
hovered ? FIELD_HOVER : SECONDARY_BTN_BORDER,
|
||||
hovered ? SECONDARY_BTN_HOVER : SECONDARY_BTN_BG);
|
||||
}
|
||||
int tw = text_width(label);
|
||||
int tx = x + (w - tw) / 2;
|
||||
int ty = y + (h - system_font_height()) / 2;
|
||||
draw_text(fb, tx, ty, label, BTN_TEXT);
|
||||
int tx = rect.x + (rect.w - tw) / 2;
|
||||
int ty = rect.y + (rect.h - system_font_height()) / 2;
|
||||
draw_text(fb, tx, ty, label, primary ? BTN_TEXT : TEXT_COLOR);
|
||||
}
|
||||
|
||||
static void draw_power_button(Framebuffer& fb, const Rect& rect, const SvgIcon& icon,
|
||||
const char* label, bool hovered) {
|
||||
draw_rounded_frame(fb, rect, 4,
|
||||
hovered ? FIELD_HOVER : SECONDARY_BTN_BORDER,
|
||||
hovered ? SECONDARY_BTN_HOVER : SECONDARY_BTN_BG);
|
||||
|
||||
int icon_w = icon.pixels ? icon.width : 0;
|
||||
int gap = icon_w > 0 ? 8 : 0;
|
||||
int tw = text_width(label);
|
||||
int total_w = icon_w + gap + tw;
|
||||
int sx = rect.x + (rect.w - total_w) / 2;
|
||||
if (icon.pixels) {
|
||||
int iy = rect.y + (rect.h - icon.height) / 2;
|
||||
fb.blit_alpha(sx, iy, icon.width, icon.height, icon.pixels);
|
||||
sx += icon.width + gap;
|
||||
}
|
||||
int ty = rect.y + (rect.h - system_font_height()) / 2;
|
||||
draw_text(fb, sx, ty, label, TEXT_COLOR);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -308,130 +450,60 @@ static void draw_button(Framebuffer& fb, int x, int y, int w, int h,
|
||||
|
||||
static void draw_login_screen(LoginState* ls) {
|
||||
Framebuffer& fb = ls->fb;
|
||||
LoginLayout lo = layout_login_screen(ls);
|
||||
int sfh = system_font_height();
|
||||
|
||||
// Background
|
||||
if (ls->has_wallpaper) {
|
||||
fb.blit(0, 0, ls->bg_wallpaper_w, ls->bg_wallpaper_h, ls->bg_wallpaper);
|
||||
fb.fill_rect_alpha(0, 0, ls->screen_w, ls->screen_h, Color::from_rgba(0, 0, 0, 0x38));
|
||||
} else {
|
||||
fb.clear(BG_COLOR);
|
||||
}
|
||||
|
||||
int card_h;
|
||||
const char* title;
|
||||
int sfh = system_font_height();
|
||||
int power_section_h = POWER_ICON_SZ + sfh + 6 + 4; // icon + label + gap + padding
|
||||
int error_section_h = ls->show_error ? sfh + 8 : 0; // error text + padding
|
||||
draw_shadow(fb, lo.card.x, lo.card.y, lo.card.w, lo.card.h, 4, colors::SHADOW);
|
||||
fb.fill_rect(lo.card.x, lo.card.y, lo.card.w, lo.card.h, CARD_BG);
|
||||
fb.fill_rect(lo.titlebar.x, lo.titlebar.y, lo.titlebar.w, lo.titlebar.h, TITLEBAR_BG);
|
||||
fb.fill_rect(lo.footer.x, lo.footer.y, lo.footer.w, lo.footer.h, FOOTER_BG);
|
||||
draw_rect(fb, lo.card.x, lo.card.y, lo.card.w, lo.card.h, CARD_BORDER);
|
||||
draw_hline(fb, lo.titlebar.x, lo.titlebar.y + lo.titlebar.h - 1, lo.titlebar.w, CARD_BORDER);
|
||||
draw_hline(fb, lo.footer.x, lo.footer.y, lo.footer.w, CARD_BORDER);
|
||||
|
||||
int window_tw = text_width(lo.window_title);
|
||||
draw_text(fb, lo.titlebar.x + (lo.titlebar.w - window_tw) / 2,
|
||||
lo.titlebar.y + (TITLEBAR_H - sfh) / 2, lo.window_title, TEXT_COLOR);
|
||||
draw_text(fb, lo.content_x, lo.heading_y, lo.heading, TEXT_COLOR);
|
||||
draw_text(fb, lo.content_x, lo.subtitle_y, lo.subtitle, LABEL_COLOR);
|
||||
|
||||
draw_labeled_field(fb, lo.username_field, "Username", ls->username, false,
|
||||
ls->active_field == 0,
|
||||
lo.username_field.contains(ls->mouse.x, ls->mouse.y));
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
card_h = 420 + error_section_h + power_section_h;
|
||||
title = "Create Administrator Account";
|
||||
draw_labeled_field(fb, lo.display_name_field, "Display Name", ls->display_name, false,
|
||||
ls->active_field == 1,
|
||||
lo.display_name_field.contains(ls->mouse.x, ls->mouse.y));
|
||||
draw_labeled_field(fb, lo.password_field, "Password", ls->password, true,
|
||||
ls->active_field == 2,
|
||||
lo.password_field.contains(ls->mouse.x, ls->mouse.y));
|
||||
draw_labeled_field(fb, lo.confirm_field, "Confirm Password", ls->confirm, true,
|
||||
ls->active_field == 3,
|
||||
lo.confirm_field.contains(ls->mouse.x, ls->mouse.y));
|
||||
} else {
|
||||
card_h = 284 + error_section_h + power_section_h;
|
||||
title = "Log In";
|
||||
draw_labeled_field(fb, lo.password_field, "Password", ls->password, true,
|
||||
ls->active_field == 1,
|
||||
lo.password_field.contains(ls->mouse.x, ls->mouse.y));
|
||||
}
|
||||
|
||||
int card_x = (ls->screen_w - CARD_W) / 2;
|
||||
int card_y = (ls->screen_h - card_h) / 2;
|
||||
|
||||
// Card background
|
||||
fb.fill_rect(card_x, card_y, CARD_W, card_h, CARD_BG);
|
||||
|
||||
int x = card_x + 24;
|
||||
int content_w = CARD_W - 48;
|
||||
int y = card_y + 20;
|
||||
|
||||
// Card title
|
||||
{
|
||||
int tw = text_width(title);
|
||||
draw_text(fb, card_x + (CARD_W - tw) / 2, y, title, TEXT_COLOR);
|
||||
y += sfh + 16;
|
||||
if (ls->show_error && lo.error_y >= 0) {
|
||||
draw_text(fb, lo.content_x, lo.error_y, ls->error_msg, ERROR_COLOR);
|
||||
}
|
||||
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
// Username field
|
||||
draw_text(fb, x, y, "Username", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->username, false, ls->active_field == 0);
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Display name field
|
||||
draw_text(fb, x, y, "Display Name", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->display_name, false, ls->active_field == 1);
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Password field
|
||||
draw_text(fb, x, y, "Password", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->password, true, ls->active_field == 2);
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Confirm password field
|
||||
draw_text(fb, x, y, "Confirm Password", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->confirm, true, ls->active_field == 3);
|
||||
y += FIELD_H + 16;
|
||||
|
||||
// Create button
|
||||
draw_button(fb, x, y, content_w, BTN_H, "Create Account", BTN_COLOR);
|
||||
y += BTN_H;
|
||||
} else {
|
||||
// Username field
|
||||
draw_text(fb, x, y, "Username", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->username, false, ls->active_field == 0);
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Password field
|
||||
draw_text(fb, x, y, "Password", LABEL_COLOR);
|
||||
y += sfh + LABEL_GAP;
|
||||
draw_field(fb, x, y, content_w, ls->password, true, ls->active_field == 1);
|
||||
y += FIELD_H + 16;
|
||||
|
||||
// Login button
|
||||
draw_button(fb, x, y, content_w, BTN_H, "Log In", BTN_COLOR);
|
||||
y += BTN_H;
|
||||
}
|
||||
|
||||
// Error message (inside card, between button and separator)
|
||||
if (ls->show_error) {
|
||||
y += 8;
|
||||
int tw = text_width(ls->error_msg);
|
||||
draw_text(fb, card_x + (CARD_W - tw) / 2, y, ls->error_msg, ERROR_COLOR);
|
||||
y += sfh;
|
||||
}
|
||||
|
||||
// Separator line
|
||||
y += 16;
|
||||
fb.fill_rect(x, y, content_w, 1, FIELD_BORDER);
|
||||
y += 16;
|
||||
|
||||
// Power options (inside card, centered)
|
||||
{
|
||||
int total_w = POWER_ICON_SZ + POWER_GAP + POWER_ICON_SZ;
|
||||
int start_x = card_x + (CARD_W - total_w) / 2;
|
||||
|
||||
// Shutdown icon
|
||||
if (ls->icon_shutdown.pixels) {
|
||||
int ix = start_x;
|
||||
fb.blit_alpha(ix, y, ls->icon_shutdown.width, ls->icon_shutdown.height,
|
||||
ls->icon_shutdown.pixels);
|
||||
const char* lbl = "Shut Down";
|
||||
int lw = text_width(lbl);
|
||||
draw_text(fb, ix + (POWER_ICON_SZ - lw) / 2, y + POWER_ICON_SZ + 6,
|
||||
lbl, LABEL_COLOR);
|
||||
}
|
||||
|
||||
// Reboot icon
|
||||
if (ls->icon_reboot.pixels) {
|
||||
int ix = start_x + POWER_ICON_SZ + POWER_GAP;
|
||||
fb.blit_alpha(ix, y, ls->icon_reboot.width, ls->icon_reboot.height,
|
||||
ls->icon_reboot.pixels);
|
||||
const char* lbl = "Restart";
|
||||
int lw = text_width(lbl);
|
||||
draw_text(fb, ix + (POWER_ICON_SZ - lw) / 2, y + POWER_ICON_SZ + 6,
|
||||
lbl, LABEL_COLOR);
|
||||
}
|
||||
}
|
||||
draw_power_button(fb, lo.shutdown_button, ls->icon_shutdown, "Shut Down",
|
||||
lo.shutdown_button.contains(ls->mouse.x, ls->mouse.y));
|
||||
draw_power_button(fb, lo.reboot_button, ls->icon_reboot, "Restart",
|
||||
lo.reboot_button.contains(ls->mouse.x, ls->mouse.y));
|
||||
draw_button(fb, lo.submit_button, lo.submit_label,
|
||||
lo.submit_button.contains(ls->mouse.x, ls->mouse.y), true);
|
||||
|
||||
// Mouse cursor (shared with desktop)
|
||||
draw_cursor(fb, ls->mouse.x, ls->mouse.y);
|
||||
@@ -603,60 +675,27 @@ static void handle_mouse(LoginState* ls) {
|
||||
bool left_pressed = (buttons & 0x01) && !(prev & 0x01);
|
||||
|
||||
if (!left_pressed) return;
|
||||
LoginLayout lo = layout_login_screen(ls);
|
||||
|
||||
int sfh = system_font_height();
|
||||
int power_section_h = POWER_ICON_SZ + sfh + 6 + 8;
|
||||
int error_section_h = ls->show_error ? sfh + 8 : 0;
|
||||
int card_h;
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
card_h = 420 + error_section_h + power_section_h;
|
||||
} else {
|
||||
card_h = 284 + error_section_h + power_section_h;
|
||||
}
|
||||
int card_x = (ls->screen_w - CARD_W) / 2;
|
||||
int card_y = (ls->screen_h - card_h) / 2;
|
||||
int x = card_x + 24;
|
||||
int content_w = CARD_W - 48;
|
||||
int y = card_y + 20;
|
||||
|
||||
// Skip title
|
||||
y += sfh + 16;
|
||||
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
// Username label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
if (lo.username_field.contains(mx, my)) {
|
||||
ls->active_field = 0;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Display name label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
if (ls->mode == MODE_FIRST_BOOT) {
|
||||
if (lo.display_name_field.contains(mx, my)) {
|
||||
ls->active_field = 1;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Password label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
if (lo.password_field.contains(mx, my)) {
|
||||
ls->active_field = 2;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Confirm label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
if (lo.confirm_field.contains(mx, my)) {
|
||||
ls->active_field = 3;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 16;
|
||||
|
||||
// Create button
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + BTN_H) {
|
||||
if (lo.submit_button.contains(mx, my)) {
|
||||
if (try_first_boot_submit(ls)) {
|
||||
ls->mode = MODE_LOGIN;
|
||||
ls->active_field = 0;
|
||||
@@ -666,26 +705,12 @@ static void handle_mouse(LoginState* ls) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
y += BTN_H;
|
||||
} else {
|
||||
// Username label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
ls->active_field = 0;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 12;
|
||||
|
||||
// Password label + field
|
||||
y += sfh + LABEL_GAP;
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + FIELD_H) {
|
||||
if (lo.password_field.contains(mx, my)) {
|
||||
ls->active_field = 1;
|
||||
return;
|
||||
}
|
||||
y += FIELD_H + 16;
|
||||
|
||||
// Login button
|
||||
if (mx >= x && mx < x + content_w && my >= y && my < y + BTN_H) {
|
||||
if (lo.submit_button.contains(mx, my)) {
|
||||
if (try_login(ls)) {
|
||||
int pid = montauk::spawn("0:/os/desktop.elf", ls->username);
|
||||
if (pid >= 0) {
|
||||
@@ -699,32 +724,15 @@ static void handle_mouse(LoginState* ls) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
y += BTN_H;
|
||||
}
|
||||
|
||||
// Skip error section if visible
|
||||
if (ls->show_error) y += error_section_h;
|
||||
|
||||
// Power options (inside card, after separator)
|
||||
y += 16 + 1 + 16; // padding + separator + padding
|
||||
{
|
||||
int total_w = POWER_ICON_SZ + POWER_GAP + POWER_ICON_SZ;
|
||||
int start_x = card_x + (CARD_W - total_w) / 2;
|
||||
int hit_h = POWER_ICON_SZ + sfh + 6;
|
||||
|
||||
// Shutdown
|
||||
if (mx >= start_x && mx < start_x + POWER_ICON_SZ &&
|
||||
my >= y && my < y + hit_h) {
|
||||
if (lo.shutdown_button.contains(mx, my)) {
|
||||
montauk::shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reboot
|
||||
int rx = start_x + POWER_ICON_SZ + POWER_GAP;
|
||||
if (mx >= rx && mx < rx + POWER_ICON_SZ &&
|
||||
my >= y && my < y + hit_h) {
|
||||
if (lo.reboot_button.contains(mx, my)) {
|
||||
montauk::reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -746,7 +754,7 @@ extern "C" void _start() {
|
||||
montauk::set_mouse_bounds(ls->screen_w - 1, ls->screen_h - 1);
|
||||
|
||||
// Load power option icons
|
||||
Color icon_color = Color::from_rgb(0x66, 0x66, 0x66);
|
||||
Color icon_color = colors::ICON_COLOR;
|
||||
ls->icon_shutdown = svg_load("0:/icons/system-shutdown.svg", POWER_ICON_SZ, POWER_ICON_SZ, icon_color);
|
||||
ls->icon_reboot = svg_load("0:/icons/system-reboot.svg", POWER_ICON_SZ, POWER_ICON_SZ, icon_color);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user