From 1678af87b810719a39bd965d5254119c13d81c40 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Sat, 18 Apr 2026 15:56:15 +0800 Subject: [PATCH] menuconfig --- clks/kernel/kmain.c | 12 +- .../__pycache__/menuconfig.cpython-312.pyc | Bin 0 -> 35863 bytes scripts/menuconfig.py | 325 ++++++++++++++++-- 3 files changed, 295 insertions(+), 42 deletions(-) create mode 100644 scripts/__pycache__/menuconfig.cpython-312.pyc diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 81f7ccd..563b9a4 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -74,6 +74,7 @@ #define CLKS_CFG_USRD_TASK 1 #endif +#if CLKS_CFG_KLOGD_TASK static void clks_task_klogd(u64 tick) { static u64 last_emit = 0ULL; @@ -84,7 +85,9 @@ static void clks_task_klogd(u64 tick) { last_emit = tick; } } +#endif +#if CLKS_CFG_KWORKER_TASK static void clks_task_kworker(u64 tick) { static u32 phase = 0U; @@ -107,16 +110,16 @@ static void clks_task_kworker(u64 tick) { phase = (phase + 1U) & 3U; } +#endif -static void clks_task_kelfd(u64 tick) { #if CLKS_CFG_KELF +static void clks_task_kelfd(u64 tick) { clks_service_heartbeat(CLKS_SERVICE_KELF, tick); clks_kelf_tick(tick); -#else - (void)tick; -#endif } +#endif +#if CLKS_CFG_USRD_TASK static void clks_task_usrd(u64 tick) { clks_service_heartbeat(CLKS_SERVICE_USER, tick); clks_exec_tick(tick); @@ -127,6 +130,7 @@ static void clks_task_usrd(u64 tick) { clks_tty_tick(tick); clks_shell_tick(tick); } +#endif void clks_kernel_main(void) { const struct limine_framebuffer *boot_fb; diff --git a/scripts/__pycache__/menuconfig.cpython-312.pyc b/scripts/__pycache__/menuconfig.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e93bca78723a940d4262080f6b0dbae28123e91d GIT binary patch literal 35863 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 None: pass +def _safe_addch(stdscr, y: int, x: int, ch, attr: int = 0) -> None: + h, w = stdscr.getmaxyx() + if y < 0 or y >= h or x < 0 or x >= w: + return + try: + stdscr.addch(y, x, ch, attr) + except Exception: + pass + + +def _curses_theme() -> Dict[str, int]: + # Reasonable monochrome fallback first. + theme = { + "header": curses.A_BOLD, + "subtitle": curses.A_DIM, + "panel_border": curses.A_DIM, + "panel_title": curses.A_BOLD, + "selected": curses.A_REVERSE | curses.A_BOLD, + "enabled": curses.A_BOLD, + "disabled": curses.A_DIM, + "value_key": curses.A_DIM, + "value_label": curses.A_BOLD, + "help": curses.A_DIM, + "status_ok": curses.A_BOLD, + "status_warn": curses.A_BOLD, + "progress_on": curses.A_REVERSE, + "progress_off": curses.A_DIM, + "scroll_track": curses.A_DIM, + "scroll_thumb": curses.A_BOLD, + } + + if not curses.has_colors(): + return theme + + try: + curses.start_color() + except Exception: + return theme + + try: + curses.use_default_colors() + except Exception: + pass + + # Pair index map + # 1: Header + # 2: Subtitle + # 3: Panel border/title + # 4: Selected row + # 5: Enabled accent + # 6: Disabled accent + # 7: Footer/help + # 8: Success/status + # 9: Warning/status + # 10: Scroll thumb + try: + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN) + curses.init_pair(2, curses.COLOR_CYAN, -1) + curses.init_pair(3, curses.COLOR_BLUE, -1) + curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLUE) + curses.init_pair(5, curses.COLOR_GREEN, -1) + curses.init_pair(6, curses.COLOR_RED, -1) + curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_WHITE) + curses.init_pair(8, curses.COLOR_BLACK, curses.COLOR_GREEN) + curses.init_pair(9, curses.COLOR_BLACK, curses.COLOR_YELLOW) + curses.init_pair(10, curses.COLOR_MAGENTA, -1) + except Exception: + return theme + + theme.update( + { + "header": curses.color_pair(1) | curses.A_BOLD, + "subtitle": curses.color_pair(2) | curses.A_DIM, + "panel_border": curses.color_pair(3), + "panel_title": curses.color_pair(3) | curses.A_BOLD, + "selected": curses.color_pair(4) | curses.A_BOLD, + "enabled": curses.color_pair(5) | curses.A_BOLD, + "disabled": curses.color_pair(6) | curses.A_DIM, + "value_key": curses.color_pair(2) | curses.A_DIM, + "value_label": curses.A_BOLD, + "help": curses.color_pair(7), + "status_ok": curses.color_pair(8) | curses.A_BOLD, + "status_warn": curses.color_pair(9) | curses.A_BOLD, + "progress_on": curses.color_pair(5) | curses.A_REVERSE, + "progress_off": curses.A_DIM, + "scroll_track": curses.A_DIM, + "scroll_thumb": curses.color_pair(10) | curses.A_BOLD, + } + ) + return theme + + +def _draw_box(stdscr, y: int, x: int, h: int, w: int, title: str, border_attr: int, title_attr: int) -> None: + if h < 2 or w < 4: + return + right = x + w - 1 + bottom = y + h - 1 + + _safe_addch(stdscr, y, x, curses.ACS_ULCORNER, border_attr) + _safe_addch(stdscr, y, right, curses.ACS_URCORNER, border_attr) + _safe_addch(stdscr, bottom, x, curses.ACS_LLCORNER, border_attr) + _safe_addch(stdscr, bottom, right, curses.ACS_LRCORNER, border_attr) + + for col in range(x + 1, right): + _safe_addch(stdscr, y, col, curses.ACS_HLINE, border_attr) + _safe_addch(stdscr, bottom, col, curses.ACS_HLINE, border_attr) + for row in range(y + 1, bottom): + _safe_addch(stdscr, row, x, curses.ACS_VLINE, border_attr) + _safe_addch(stdscr, row, right, curses.ACS_VLINE, border_attr) + + if title: + _safe_addnstr(stdscr, y, x + 2, f" {title} ", title_attr) + + +def _draw_progress_bar( + stdscr, + y: int, + x: int, + width: int, + enabled_count: int, + total_count: int, + on_attr: int, + off_attr: int, +) -> None: + if width < 8 or total_count <= 0: + return + bar_w = width - 8 + if bar_w < 4: + return + fill = int((enabled_count * bar_w) / total_count) + for i in range(bar_w): + ch = "#" if i < fill else "-" + attr = on_attr if i < fill else off_attr + _safe_addch(stdscr, y, x + i, ch, attr) + _safe_addnstr(stdscr, y, x + bar_w + 1, f"{enabled_count:>3}/{total_count:<3}", off_attr | curses.A_BOLD) + + def _option_enabled(values: Dict[str, bool], item: OptionItem) -> bool: return values.get(item.key, item.default) @@ -237,7 +374,30 @@ def _set_all(values: Dict[str, bool], options: List[OptionItem], enabled: bool) values[item.key] = enabled -def _run_ncurses_section(stdscr, title: str, options: List[OptionItem], values: Dict[str, bool]) -> None: +def _draw_scrollbar(stdscr, y: int, x: int, height: int, total: int, top: int, visible: int, track_attr: int, thumb_attr: int) -> None: + if height <= 0: + return + for r in range(height): + _safe_addch(stdscr, y + r, x, "|", track_attr) + + if total <= 0 or visible <= 0 or total <= visible: + for r in range(height): + _safe_addch(stdscr, y + r, x, "|", thumb_attr) + return + + thumb_h = max(1, int((visible * height) / total)) + if thumb_h > height: + thumb_h = height + + max_top = max(1, total - visible) + max_pos = max(0, height - thumb_h) + thumb_y = int((top * max_pos) / max_top) + + for r in range(thumb_h): + _safe_addch(stdscr, y + thumb_y + r, x, "#", thumb_attr) + + +def _run_ncurses_section(stdscr, theme: Dict[str, int], title: str, options: List[OptionItem], values: Dict[str, bool]) -> None: selected = 0 top = 0 @@ -245,19 +405,48 @@ def _run_ncurses_section(stdscr, title: str, options: List[OptionItem], values: stdscr.erase() h, w = stdscr.getmaxyx() - if h < 10 or w < 40: - _safe_addnstr(stdscr, 0, 0, "Terminal too small for menuconfig (need >= 40x10).", curses.A_BOLD) + if h < 14 or w < 70: + _safe_addnstr(stdscr, 0, 0, "Terminal too small for rich UI (need >= 70x14).", theme["status_warn"]) _safe_addnstr(stdscr, 2, 0, "Resize terminal then press any key, or ESC to go back.") key = stdscr.getch() if key in (27,): return continue - list_top = 2 - desc_area = 4 - help_area = 2 - list_bottom = h - (desc_area + help_area) - 1 - visible = max(1, list_bottom - list_top + 1) + left_w = max(38, int(w * 0.58)) + right_w = w - left_w + if right_w < 24: + left_w = w - 24 + right_w = 24 + + list_box_y = 2 + list_box_x = 0 + list_box_h = h - 4 + list_box_w = left_w + + detail_box_y = 2 + detail_box_x = left_w + detail_box_h = h - 4 + detail_box_w = w - left_w + + _safe_addnstr(stdscr, 0, 0, f" CLeonOS menuconfig / {title} ", theme["header"]) + enabled_count = sum(1 for item in options if _option_enabled(values, item)) + _safe_addnstr( + stdscr, + 1, + 0, + f" {enabled_count}/{len(options)} enabled | Arrow/jk move Space toggle a/n all PgUp/PgDn Enter/ESC back ", + theme["subtitle"], + ) + + _draw_box(stdscr, list_box_y, list_box_x, list_box_h, list_box_w, "Options", theme["panel_border"], theme["panel_title"]) + _draw_box(stdscr, detail_box_y, detail_box_x, detail_box_h, detail_box_w, "Details", theme["panel_border"], theme["panel_title"]) + + list_inner_y = list_box_y + 1 + list_inner_x = list_box_x + 1 + list_inner_h = list_box_h - 2 + list_inner_w = list_box_w - 2 + visible = max(1, list_inner_h) if selected < 0: selected = 0 @@ -271,36 +460,70 @@ def _run_ncurses_section(stdscr, title: str, options: List[OptionItem], values: if top < 0: top = 0 - _safe_addnstr(stdscr, 0, 0, f"CLeonOS menuconfig / {title}", curses.A_REVERSE) - _safe_addnstr(stdscr, 1, 0, f"Items: {len(options)}") - for row in range(visible): idx = top + row if idx >= len(options): break item = options[idx] - mark = "x" if _option_enabled(values, item) else " " - line = f"{idx + 1:3d}. [{mark}] {item.title}" - attr = curses.A_REVERSE if idx == selected else 0 - _safe_addnstr(stdscr, list_top + row, 0, line, attr) + enabled = _option_enabled(values, item) + mark = "x" if enabled else " " + prefix = ">" if idx == selected else " " + line = f"{prefix} {idx + 1:03d} [{mark}] {item.title}" + base_attr = theme["enabled"] if enabled else theme["disabled"] + attr = theme["selected"] if idx == selected else base_attr + _safe_addnstr(stdscr, list_inner_y + row, list_inner_x, line, attr) + + _draw_scrollbar( + stdscr, + list_inner_y, + list_box_x + list_box_w - 2, + list_inner_h, + len(options), + top, + visible, + theme["scroll_track"], + theme["scroll_thumb"], + ) if options: cur = options[selected] - key_line = f"{cur.key}" - desc_line = cur.description - wrapped = textwrap.wrap(desc_line, max(10, w - 2)) - _safe_addnstr(stdscr, list_bottom + 1, 0, key_line, curses.A_DIM) - for i, part in enumerate(wrapped[: max(1, desc_area - 1)]): - _safe_addnstr(stdscr, list_bottom + 2 + i, 0, part) + detail_inner_y = detail_box_y + 1 + detail_inner_x = detail_box_x + 2 + detail_inner_w = detail_box_w - 4 + detail_inner_h = detail_box_h - 2 - _safe_addnstr( - stdscr, - h - 2, - 0, - "Arrows/jk move Space toggle a all-on n all-off PgUp/PgDn Home/End", - curses.A_DIM, - ) - _safe_addnstr(stdscr, h - 1, 0, "Enter/ESC/q back", curses.A_DIM) + state_text = "ENABLED" if _option_enabled(values, cur) else "DISABLED" + state_attr = theme["status_ok"] if _option_enabled(values, cur) else theme["status_warn"] + + _safe_addnstr(stdscr, detail_inner_y + 0, detail_inner_x, cur.title, theme["value_label"]) + _safe_addnstr(stdscr, detail_inner_y + 1, detail_inner_x, cur.key, theme["value_key"]) + _safe_addnstr(stdscr, detail_inner_y + 2, detail_inner_x, f"State: {state_text}", state_attr) + _safe_addnstr( + stdscr, + detail_inner_y + 3, + detail_inner_x, + f"Item: {selected + 1}/{len(options)}", + theme["value_label"], + ) + _draw_progress_bar( + stdscr, + detail_inner_y + 4, + detail_inner_x, + max(12, detail_inner_w), + enabled_count, + max(1, len(options)), + theme["progress_on"], + theme["progress_off"], + ) + + desc_title_y = detail_inner_y + 6 + _safe_addnstr(stdscr, desc_title_y, detail_inner_x, "Description:", theme["value_label"]) + wrapped = textwrap.wrap(cur.description, max(12, detail_inner_w)) + max_desc_lines = max(1, detail_inner_h - 8) + for i, part in enumerate(wrapped[:max_desc_lines]): + _safe_addnstr(stdscr, desc_title_y + 1 + i, detail_inner_x, part, 0) + + _safe_addnstr(stdscr, h - 1, 0, " Space:toggle a:all-on n:all-off Enter/ESC:back ", theme["help"]) stdscr.refresh() key = stdscr.getch() @@ -339,6 +562,7 @@ def _run_ncurses_section(stdscr, title: str, options: List[OptionItem], values: def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, bool]) -> bool: + theme = _curses_theme() try: curses.curs_set(0) except Exception: @@ -348,10 +572,12 @@ def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List while True: stdscr.erase() - h, _w = stdscr.getmaxyx() + h, w = stdscr.getmaxyx() clks_on = sum(1 for item in clks_options if _option_enabled(values, item)) user_on = sum(1 for item in user_options if _option_enabled(values, item)) + total_items = len(clks_options) + len(user_options) + total_on = clks_on + user_on items = [ f"CLKS features ({clks_on}/{len(clks_options)} enabled)", @@ -360,15 +586,38 @@ def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List "Quit without Saving", ] - _safe_addnstr(stdscr, 0, 0, "CLeonOS menuconfig (ncurses)", curses.A_REVERSE) - _safe_addnstr(stdscr, 1, 0, "Enter open/select, Arrow/jk move, s save, q quit", curses.A_DIM) + if h < 12 or w < 58: + _safe_addnstr(stdscr, 0, 0, "Terminal too small for menuconfig (need >= 58x12).", theme["status_warn"]) + _safe_addnstr(stdscr, 2, 0, "Resize terminal then press any key.") + stdscr.getch() + continue - base = 3 + _safe_addnstr(stdscr, 0, 0, " CLeonOS menuconfig ", theme["header"]) + _safe_addnstr(stdscr, 1, 0, " Stylish ncurses UI | Enter: open/select s: save q: quit ", theme["subtitle"]) + + _draw_box(stdscr, 2, 0, h - 5, w, "Main", theme["panel_border"], theme["panel_title"]) + + base = 4 for i, text in enumerate(items): - attr = curses.A_REVERSE if i == selected else 0 - _safe_addnstr(stdscr, base + i, 0, text, attr) + prefix = ">" if i == selected else " " + row_text = f"{prefix} {text}" + attr = theme["selected"] if i == selected else theme["value_label"] + _safe_addnstr(stdscr, base + i, 2, row_text, attr) - _safe_addnstr(stdscr, h - 1, 0, "Tip: Space toggles options inside sections.", curses.A_DIM) + _safe_addnstr(stdscr, base + 6, 2, "Global Progress:", theme["value_label"]) + _draw_progress_bar( + stdscr, + base + 7, + 2, + max(18, w - 6), + total_on, + max(1, total_items), + theme["progress_on"], + theme["progress_off"], + ) + + _safe_addnstr(stdscr, h - 2, 0, " Arrows/jk move Enter select s save q quit ", theme["help"]) + _safe_addnstr(stdscr, h - 1, 0, " Tip: open CLKS/USER section then use Space to toggle options. ", theme["help"]) stdscr.refresh() key = stdscr.getch() @@ -385,9 +634,9 @@ def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List continue if key in (curses.KEY_ENTER, 10, 13): if selected == 0: - _run_ncurses_section(stdscr, "CLKS", clks_options, values) + _run_ncurses_section(stdscr, theme, "CLKS", clks_options, values) elif selected == 1: - _run_ncurses_section(stdscr, "USER", user_options, values) + _run_ncurses_section(stdscr, theme, "USER", user_options, values) elif selected == 2: return True else: