diff --git a/clks/drivers/video/framebuffer.c b/clks/drivers/video/framebuffer.c index 25f792d..2a60b4d 100644 --- a/clks/drivers/video/framebuffer.c +++ b/clks/drivers/video/framebuffer.c @@ -1,4 +1,5 @@ #include +#include #include #include "psf_font.h" @@ -103,6 +104,45 @@ void clks_fb_clear(u32 rgb) { } } +void clks_fb_scroll_up(u32 pixel_rows, u32 fill_rgb) { + usize row_bytes; + usize move_bytes; + u32 y; + u32 x; + + if (clks_fb.ready == CLKS_FALSE) { + return; + } + + if (clks_fb.info.bpp != 32) { + return; + } + + if (pixel_rows == 0U) { + return; + } + + if (pixel_rows >= clks_fb.info.height) { + clks_fb_clear(fill_rgb); + return; + } + + row_bytes = (usize)clks_fb.info.pitch; + move_bytes = (usize)(clks_fb.info.height - pixel_rows) * row_bytes; + + clks_memmove( + (void *)clks_fb.address, + (const void *)(clks_fb.address + ((usize)pixel_rows * row_bytes)), + move_bytes + ); + + for (y = clks_fb.info.height - pixel_rows; y < clks_fb.info.height; y++) { + for (x = 0U; x < clks_fb.info.width; x++) { + clks_fb_put_pixel(x, y, fill_rgb); + } + } +} + void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb) { const u8 *glyph; u32 row; diff --git a/clks/include/clks/framebuffer.h b/clks/include/clks/framebuffer.h index 18d4c73..b7bea95 100644 --- a/clks/include/clks/framebuffer.h +++ b/clks/include/clks/framebuffer.h @@ -15,6 +15,7 @@ void clks_fb_init(const struct limine_framebuffer *fb); clks_bool clks_fb_ready(void); struct clks_framebuffer_info clks_fb_info(void); void clks_fb_clear(u32 rgb); +void clks_fb_scroll_up(u32 pixel_rows, u32 fill_rgb); void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb); clks_bool clks_fb_load_psf_font(const void *blob, u64 blob_size); u32 clks_fb_cell_width(void); diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 69f9381..73b78ae 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -95,7 +95,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE16 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE18 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -218,10 +218,12 @@ void clks_kernel_main(void) { clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks); clks_shell_init(); + clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE"); clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count()); clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active()); clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); + clks_log(CLKS_LOG_INFO, "TTY", "CURSOR ENABLED"); clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); clks_cpu_halt_forever(); diff --git a/clks/kernel/tty.c b/clks/kernel/tty.c index f5c97e8..7459e2d 100644 --- a/clks/kernel/tty.c +++ b/clks/kernel/tty.c @@ -20,6 +20,7 @@ static u32 clks_tty_active_index = 0; static u32 clks_tty_cell_width = 8U; static u32 clks_tty_cell_height = 8U; static clks_bool clks_tty_is_ready = CLKS_FALSE; +static clks_bool clks_tty_cursor_visible = CLKS_FALSE; static void clks_tty_fill_row(u32 tty_index, u32 row, char ch) { u32 col; @@ -29,8 +30,57 @@ static void clks_tty_fill_row(u32 tty_index, u32 row, char ch) { } } +static void clks_tty_draw_cell_with_colors(u32 row, u32 col, char ch, u32 fg, u32 bg) { + clks_fb_draw_char(col * clks_tty_cell_width, row * clks_tty_cell_height, ch, fg, bg); +} + static void clks_tty_draw_cell(u32 row, u32 col, char ch) { - clks_fb_draw_char(col * clks_tty_cell_width, row * clks_tty_cell_height, ch, CLKS_TTY_FG, CLKS_TTY_BG); + clks_tty_draw_cell_with_colors(row, col, ch, CLKS_TTY_FG, CLKS_TTY_BG); +} + +static void clks_tty_hide_cursor(void) { + u32 row; + u32 col; + + if (clks_tty_is_ready == CLKS_FALSE || clks_tty_cursor_visible == CLKS_FALSE) { + return; + } + + row = clks_tty_cursor_row[clks_tty_active_index]; + col = clks_tty_cursor_col[clks_tty_active_index]; + + if (row < clks_tty_rows && col < clks_tty_cols) { + clks_tty_draw_cell(row, col, clks_tty_cells[clks_tty_active_index][row][col]); + } + + clks_tty_cursor_visible = CLKS_FALSE; +} + +static void clks_tty_draw_cursor(void) { + u32 row; + u32 col; + + if (clks_tty_is_ready == CLKS_FALSE) { + return; + } + + row = clks_tty_cursor_row[clks_tty_active_index]; + col = clks_tty_cursor_col[clks_tty_active_index]; + + if (row >= clks_tty_rows || col >= clks_tty_cols) { + clks_tty_cursor_visible = CLKS_FALSE; + return; + } + + clks_tty_draw_cell_with_colors( + row, + col, + clks_tty_cells[clks_tty_active_index][row][col], + CLKS_TTY_BG, + CLKS_TTY_FG + ); + + clks_tty_cursor_visible = CLKS_TRUE; } static void clks_tty_redraw_active(void) { @@ -38,12 +88,15 @@ static void clks_tty_redraw_active(void) { u32 col; clks_fb_clear(CLKS_TTY_BG); + clks_tty_cursor_visible = CLKS_FALSE; for (row = 0; row < clks_tty_rows; row++) { for (col = 0; col < clks_tty_cols; col++) { clks_tty_draw_cell(row, col, clks_tty_cells[clks_tty_active_index][row][col]); } } + + clks_tty_draw_cursor(); } static void clks_tty_scroll_up(u32 tty_index) { @@ -60,7 +113,13 @@ static void clks_tty_scroll_up(u32 tty_index) { clks_tty_fill_row(tty_index, clks_tty_rows - 1, ' '); if (tty_index == clks_tty_active_index) { - clks_tty_redraw_active(); + u32 col; + + clks_fb_scroll_up(clks_tty_cell_height, CLKS_TTY_BG); + + for (col = 0U; col < clks_tty_cols; col++) { + clks_tty_draw_cell(clks_tty_rows - 1U, col, clks_tty_cells[tty_index][clks_tty_rows - 1U][col]); + } } } @@ -121,6 +180,7 @@ void clks_tty_init(void) { clks_tty_active_index = 0; clks_tty_is_ready = CLKS_TRUE; + clks_tty_cursor_visible = CLKS_FALSE; clks_tty_redraw_active(); } @@ -133,12 +193,15 @@ void clks_tty_write_char(char ch) { return; } + clks_tty_hide_cursor(); + tty_index = clks_tty_active_index; row = clks_tty_cursor_row[tty_index]; col = clks_tty_cursor_col[tty_index]; if (ch == '\r') { clks_tty_cursor_col[tty_index] = 0; + clks_tty_draw_cursor(); return; } @@ -151,11 +214,13 @@ void clks_tty_write_char(char ch) { clks_tty_cursor_row[tty_index] = clks_tty_rows - 1; } + clks_tty_draw_cursor(); return; } if (ch == '\b') { if (col == 0U && row == 0U) { + clks_tty_draw_cursor(); return; } @@ -169,6 +234,7 @@ void clks_tty_write_char(char ch) { clks_tty_put_visible(tty_index, row, col, ' '); clks_tty_cursor_row[tty_index] = row; clks_tty_cursor_col[tty_index] = col; + clks_tty_draw_cursor(); return; } @@ -192,6 +258,8 @@ void clks_tty_write_char(char ch) { clks_tty_cursor_row[tty_index] = clks_tty_rows - 1; } } + + clks_tty_draw_cursor(); } void clks_tty_write(const char *text) { @@ -216,13 +284,16 @@ void clks_tty_switch(u32 tty_index) { return; } + clks_tty_hide_cursor(); clks_tty_active_index = tty_index; + clks_tty_cursor_visible = CLKS_FALSE; clks_tty_redraw_active(); } u32 clks_tty_active(void) { return clks_tty_active_index; } + u32 clks_tty_count(void) { return CLKS_TTY_COUNT; } diff --git a/clks/kernel/userland.c b/clks/kernel/userland.c index 6f2a9fb..6697079 100644 --- a/clks/kernel/userland.c +++ b/clks/kernel/userland.c @@ -9,6 +9,7 @@ static clks_bool clks_user_shell_ready = CLKS_FALSE; static clks_bool clks_user_shell_exec_requested_flag = CLKS_FALSE; +static clks_bool clks_user_shell_exec_enabled = CLKS_FALSE; static u64 clks_user_launch_attempt_count = 0ULL; static u64 clks_user_launch_success_count = 0ULL; static u64 clks_user_launch_fail_count = 0ULL; @@ -82,6 +83,7 @@ clks_bool clks_userland_init(void) { clks_user_shell_ready = CLKS_FALSE; clks_user_shell_exec_requested_flag = CLKS_FALSE; + clks_user_shell_exec_enabled = CLKS_FALSE; clks_user_launch_attempt_count = 0ULL; clks_user_launch_success_count = 0ULL; clks_user_launch_fail_count = 0ULL; @@ -103,12 +105,14 @@ clks_bool clks_userland_init(void) { return CLKS_FALSE; } - (void)clks_userland_request_shell_exec(); + clks_log(CLKS_LOG_INFO, "USER", "USER SHELL EXEC DISABLED (KERNEL SHELL MODE)"); return CLKS_TRUE; } void clks_userland_tick(u64 tick) { - if (clks_user_shell_ready == CLKS_FALSE || clks_user_shell_exec_requested_flag == CLKS_TRUE) { + if (clks_user_shell_exec_enabled == CLKS_FALSE || + clks_user_shell_ready == CLKS_FALSE || + clks_user_shell_exec_requested_flag == CLKS_TRUE) { return; } @@ -138,4 +142,4 @@ u64 clks_userland_launch_success(void) { u64 clks_userland_launch_failures(void) { return clks_user_launch_fail_count; -} +} \ No newline at end of file diff --git a/ramdisk/system/tty.psf b/ramdisk/system/tty.psf index 0a78e48..f59610e 100644 Binary files a/ramdisk/system/tty.psf and b/ramdisk/system/tty.psf differ diff --git a/scripts/gen-tty-psf.ps1 b/scripts/gen-tty-psf.ps1 new file mode 100644 index 0000000..e796502 --- /dev/null +++ b/scripts/gen-tty-psf.ps1 @@ -0,0 +1,84 @@ +param( + [string]$OutputPath = "ramdisk/system/tty.psf" +) + +Add-Type -AssemblyName System.Drawing + +$width = 8 +$height = 16 +$glyphCount = 256 +$headerSize = 4 +$bytesPerGlyph = $height +$totalSize = $headerSize + ($glyphCount * $bytesPerGlyph) + +$fontCandidates = @("Consolas", "Lucida Console", "Courier New") +$font = $null +foreach ($name in $fontCandidates) { + try { + $font = New-Object System.Drawing.Font($name, 14, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Pixel) + if ($font.Name -eq $name) { + break + } + } catch { + $font = $null + } +} + +if ($null -eq $font) { + throw "No usable monospace font found for PSF generation" +} + +$bytes = New-Object byte[] $totalSize +$bytes[0] = 0x36 +$bytes[1] = 0x04 +$bytes[2] = 0x00 +$bytes[3] = [byte]$bytesPerGlyph + +# default all glyphs to blank, avoids noisy '?' for undefined chars. +for ($i = $headerSize; $i -lt $totalSize; $i++) { + $bytes[$i] = 0x00 +} + +$white = [System.Drawing.Brushes]::White +$black = [System.Drawing.Color]::Black + +for ($code = 32; $code -le 126; $code++) { + $ch = [char]$code + + $bmp = New-Object System.Drawing.Bitmap $width, $height + $g = [System.Drawing.Graphics]::FromImage($bmp) + $g.Clear($black) + $g.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::SingleBitPerPixelGridFit + + # Small negative offset keeps glyph centered in 8x16 cell for common monospace fonts. + $g.DrawString([string]$ch, $font, $white, -1, -2) + + $glyphOffset = $headerSize + ($code * $bytesPerGlyph) + + for ($y = 0; $y -lt $height; $y++) { + [byte]$rowBits = 0 + + for ($x = 0; $x -lt $width; $x++) { + $p = $bmp.GetPixel($x, $y) + if (($p.R + $p.G + $p.B) -ge 384) { + $rowBits = [byte]($rowBits -bor (1 -shl (7 - $x))) + } + } + + $bytes[$glyphOffset + $y] = $rowBits + } + + $g.Dispose() + $bmp.Dispose() +} + +$font.Dispose() + +$dir = Split-Path -Parent $OutputPath +if (-not [string]::IsNullOrWhiteSpace($dir)) { + New-Item -ItemType Directory -Force -Path $dir | Out-Null +} + +$fullOutput = Join-Path (Resolve-Path '.') $OutputPath +[System.IO.File]::WriteAllBytes($fullOutput, $bytes) +Write-Output "Generated $OutputPath ($($bytes.Length) bytes)" \ No newline at end of file