mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
FEATURE: add Bsh + userspace terminal, remove legacy cmd/cli utils
This commit is contained in:
parent
a8866da3cb
commit
bdd43f43cd
34 changed files with 3099 additions and 2912 deletions
4
Makefile
4
Makefile
|
|
@ -139,6 +139,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
|
|||
mkdir -p $(BUILD_DIR)/initrd/Library/images/gif
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/Fonts/Emoji
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/DOOM
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/bsh
|
||||
mkdir -p $(BUILD_DIR)/initrd/docs
|
||||
|
||||
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
|
||||
|
|
@ -156,6 +157,9 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
|
|||
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
|
||||
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \
|
||||
done
|
||||
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
|
||||
@for f in $$(find docs -name '*.md' 2>/dev/null); do \
|
||||
if [ -f "$$f" ]; then \
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ The BoredOS Terminal provides a powerful command-line interface (CLI) for advanc
|
|||
|
||||
## The Shell
|
||||
|
||||
The default shell in BoredOS is a custom-built, lightweight command processor integrated into the Window Manager. It features:
|
||||
The default shell in BoredOS is **BoredShell (Bsh)**, a userspace shell with a dedicated terminal app. It features:
|
||||
- **ANSI Color Support**: Rich text output with colors and styles.
|
||||
- **Command History**: Use the **Up** and **Down** arrow keys to navigate through your previous commands (up to 64 history entries).
|
||||
- **Output Redirection**:
|
||||
|
|
@ -13,6 +13,33 @@ The default shell in BoredOS is a custom-built, lightweight command processor in
|
|||
- **Piping**:
|
||||
- `command1 | command2`: Pass the output of the first command as input to the second.
|
||||
|
||||
### Bsh Configuration
|
||||
|
||||
Bsh loads its configuration from:
|
||||
|
||||
`/Library/bsh/bshrc`
|
||||
|
||||
This file is similar to `.zshrc` and can define:
|
||||
- `PATH` for command lookup
|
||||
- `STARTUP` for interactive shell startup scripts
|
||||
- `BOOT_SCRIPT` for a once-per-boot script
|
||||
- prompt templates (`PROMPT_LEFT`, `PROMPT_RIGHT`)
|
||||
|
||||
Prompt tokens:
|
||||
- `%n` username
|
||||
- `%h` hostname
|
||||
- `%~` cwd ("~" for `/root`)
|
||||
- `%T` time (HH:MM)
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
PATH=/bin:/root/Apps
|
||||
PROMPT_LEFT=%n@%h:%~$
|
||||
STARTUP=/Library/bsh/startup.bsh
|
||||
BOOT_SCRIPT=/Library/bsh/boot.bsh
|
||||
```
|
||||
|
||||
## Common Commands
|
||||
|
||||
Below are some of the most used commands available in `/bin`:
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ void kmain(void) {
|
|||
log_ok("Graphics and Console ready");
|
||||
|
||||
if (memmap_request.response != NULL) {
|
||||
// The memory manager will now scan the memory map and manage all usable regions.
|
||||
memory_manager_init_from_memmap(memmap_request.response);
|
||||
log_ok("Memory manager ready");
|
||||
smp_init_bsp();
|
||||
|
|
@ -278,17 +277,15 @@ void kmain(void) {
|
|||
fat32_mkdir("/Library/images/gif");
|
||||
fat32_mkdir("/Library/Fonts");
|
||||
fat32_mkdir("/Library/DOOM");
|
||||
fat32_mkdir("/Library/bsh");
|
||||
fat32_mkdir("/docs");
|
||||
|
||||
// Initialize Virtual Filesystems
|
||||
sysfs_init_subsystems();
|
||||
vfs_mount("/sys", "sysfs", "sysfs", sysfs_get_ops(), NULL);
|
||||
vfs_mount("/proc", "procfs", "procfs", procfs_get_ops(), NULL);
|
||||
|
||||
// Initialize bootfs with default values
|
||||
bootfs_init();
|
||||
|
||||
// Populate bootfs with real Limine bootloader information BEFORE mounting
|
||||
if (bootloader_info_request.response != NULL) {
|
||||
if (bootloader_info_request.response->name) {
|
||||
k_strcpy(g_bootfs_state.bootloader_name, bootloader_info_request.response->name);
|
||||
|
|
@ -298,7 +295,6 @@ void kmain(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get kernel size from kernel file request
|
||||
if (kernel_file_request.response != NULL && kernel_file_request.response->kernel_file != NULL) {
|
||||
g_bootfs_state.kernel_size = kernel_file_request.response->kernel_file->size;
|
||||
serial_write("[INIT] Kernel size from bootloader: ");
|
||||
|
|
@ -306,16 +302,13 @@ void kmain(void) {
|
|||
serial_write(" bytes\n");
|
||||
}
|
||||
|
||||
// Set boot time to current ticks
|
||||
extern uint32_t wm_get_ticks(void);
|
||||
g_bootfs_state.boot_time_ms = wm_get_ticks();
|
||||
|
||||
// BEFORE mounting bootfs, capture initrd from Limine modules
|
||||
if (module_request.response != NULL) {
|
||||
g_bootfs_state.num_modules = module_request.response->module_count;
|
||||
|
||||
serial_write("[INIT] Scanning modules for bootfs state...\n");
|
||||
// Scan modules to find initrd
|
||||
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
||||
struct limine_file *mod = module_request.response->modules[i];
|
||||
const char *path = mod->path;
|
||||
|
|
@ -379,12 +372,10 @@ void kmain(void) {
|
|||
fat32_close(fh);
|
||||
}
|
||||
}
|
||||
// Register all discovered modules in our module manager for /sys/module
|
||||
module_manager_register(clean_path, (uint64_t)mod->address, mod->size);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize fonts now that FAT32 and modules are loaded
|
||||
uint64_t current_rsp;
|
||||
asm volatile("mov %%rsp, %0" : "=r"(current_rsp));
|
||||
serial_write("[INIT] Stack Alignment: 0x");
|
||||
|
|
@ -397,10 +388,8 @@ void kmain(void) {
|
|||
ps2_init();
|
||||
asm("sti");
|
||||
|
||||
// Initialize LAPIC for IPI support
|
||||
lapic_init();
|
||||
|
||||
// Initialize SMP
|
||||
if (smp_request.response != NULL) {
|
||||
uint32_t online = smp_init(smp_request.response);
|
||||
log_ok("SMP initialized");
|
||||
|
|
|
|||
|
|
@ -92,17 +92,7 @@ uint64_t keyboard_handler(registers_t *regs) {
|
|||
extended_scancode = false;
|
||||
}
|
||||
|
||||
if (ps2_ctrl_pressed && scancode == 0x2E) {
|
||||
extern process_t* process_get_current(void);
|
||||
process_t* proc = process_get_current();
|
||||
if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) {
|
||||
if (((Window*)proc->ui_window)->focused) {
|
||||
extern uint64_t process_terminate_current(void);
|
||||
outb(0x20, 0x20);
|
||||
return process_terminate_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
||||
shift_pressed = true;
|
||||
|
|
|
|||
|
|
@ -297,6 +297,9 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
|
|||
if (k_strcmp(h->type, "name") == 0 || k_strcmp(h->type, "cmdline") == 0) {
|
||||
k_strcpy(out, proc->name);
|
||||
k_strcpy(out + k_strlen(out), "\n");
|
||||
} else if (k_strcmp(h->type, "cwd") == 0) {
|
||||
k_strcpy(out, proc->cwd);
|
||||
k_strcpy(out + k_strlen(out), "\n");
|
||||
} else if (k_strcmp(h->type, "status") == 0) {
|
||||
k_strcpy(out, "Name: ");
|
||||
k_strcpy(out + k_strlen(out), proc->name);
|
||||
|
|
@ -387,6 +390,7 @@ int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, in
|
|||
k_strcpy(entries[out++].name, "name");
|
||||
k_strcpy(entries[out++].name, "status");
|
||||
k_strcpy(entries[out++].name, "cmdline");
|
||||
k_strcpy(entries[out++].name, "cwd");
|
||||
k_strcpy(entries[out++].name, "signal");
|
||||
for(int i=0; i<out; i++) entries[i].is_directory = 0;
|
||||
return out;
|
||||
|
|
|
|||
4
src/library/bsh/boot.bsh
Normal file
4
src/library/bsh/boot.bsh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# BoredShell boot script
|
||||
# Runs once on boot for the first shell.
|
||||
# Example:
|
||||
# echo "Boot script executed"
|
||||
35
src/library/bsh/bshrc
Normal file
35
src/library/bsh/bshrc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# BoredShell rc file
|
||||
# Location: /Library/bsh/bshrc
|
||||
#
|
||||
# Format (initial): key=value
|
||||
# Lines starting with # are comments.
|
||||
#
|
||||
# PATH controls command lookup order (colon-separated).
|
||||
PATH=/bin:/root/Apps
|
||||
|
||||
# Scripts to run automatically.
|
||||
# STARTUP runs for interactive shells.
|
||||
# BOOT_SCRIPT runs once on boot (if enabled by the shell).
|
||||
STARTUP=/Library/bsh/startup.bsh
|
||||
BOOT_SCRIPT=/Library/bsh/boot.bsh
|
||||
|
||||
# Prompt templates.
|
||||
# Tokens:
|
||||
# %n = username
|
||||
# %h = hostname
|
||||
# %~ = cwd ("~" for /root)
|
||||
# %T = time (HH:MM)
|
||||
PROMPT_LEFT=%n@%h:%~$
|
||||
PROMPT_RIGHT=%T
|
||||
|
||||
# History settings.
|
||||
HISTORY_FILE=/Library/bsh/history
|
||||
HISTORY_SIZE=200
|
||||
|
||||
# Feature toggles.
|
||||
GLOB=true
|
||||
COMPLETE=true
|
||||
SUGGEST=true
|
||||
|
||||
# Example aliases (future support).
|
||||
# alias ll=ls -la
|
||||
4
src/library/bsh/startup.bsh
Normal file
4
src/library/bsh/startup.bsh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# BoredShell startup script
|
||||
# Runs for interactive shells.
|
||||
# Example:
|
||||
# echo "Welcome to BoredShell"
|
||||
|
|
@ -4,15 +4,15 @@
|
|||
#ifndef CMD_H
|
||||
#define CMD_H
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
extern Window win_cmd;
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void cmd_init(void);
|
||||
void cmd_reset(void);
|
||||
|
||||
// IO Functions
|
||||
void cmd_write(const char *str);
|
||||
void cmd_write_len(const char *str, size_t len);
|
||||
void cmd_putchar(char c);
|
||||
void cmd_write_int(int n);
|
||||
void cmd_write_hex(uint64_t n);
|
||||
|
|
@ -22,10 +22,9 @@ void cmd_screen_clear(void);
|
|||
void cmd_increment_msg_count(void);
|
||||
void cmd_reset_msg_count(void);
|
||||
|
||||
void cmd_handle_resize(Window *win, int w, int h);
|
||||
void cmd_handle_click(Window *win, int x, int y);
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key);
|
||||
void cmd_set_current_color(uint32_t color);
|
||||
void cmd_set_raw_mode(bool enabled);
|
||||
void cmd_process_finished(void);
|
||||
|
||||
#endif
|
||||
76
src/sys/cmd_stub.c
Normal file
76
src/sys/cmd_stub.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "cmd.h"
|
||||
#include "core/kutils.h"
|
||||
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
static void serial_write_char(char c) {
|
||||
char buf[2] = { c, 0 };
|
||||
serial_write(buf);
|
||||
}
|
||||
|
||||
void cmd_init(void) {
|
||||
}
|
||||
|
||||
void cmd_reset(void) {
|
||||
}
|
||||
|
||||
void cmd_write(const char *str) {
|
||||
if (!str) return;
|
||||
serial_write(str);
|
||||
}
|
||||
|
||||
void cmd_write_len(const char *str, size_t len) {
|
||||
if (!str || len == 0) return;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
serial_write_char(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_putchar(char c) {
|
||||
serial_write_char(c);
|
||||
}
|
||||
|
||||
void cmd_write_int(int n) {
|
||||
char buf[32];
|
||||
k_itoa(n, buf);
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
void cmd_write_hex(uint64_t n) {
|
||||
char buf[17];
|
||||
k_itoa_hex(n, buf);
|
||||
cmd_write("0x");
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
int cmd_get_cursor_col(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_screen_clear(void) {
|
||||
}
|
||||
|
||||
void cmd_increment_msg_count(void) {
|
||||
}
|
||||
|
||||
void cmd_reset_msg_count(void) {
|
||||
}
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key) {
|
||||
(void)key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_set_current_color(uint32_t color) {
|
||||
(void)color;
|
||||
}
|
||||
|
||||
void cmd_set_raw_mode(bool enabled) {
|
||||
(void)enabled;
|
||||
}
|
||||
|
||||
void cmd_process_finished(void) {
|
||||
}
|
||||
|
|
@ -27,23 +27,24 @@ static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
|
|||
static spinlock_t runqueue_lock = SPINLOCK_INIT;
|
||||
static uint32_t next_cpu_assign = 1;
|
||||
|
||||
static void process_cleanup_inner(process_t *proc);
|
||||
|
||||
void process_init(void) {
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
processes[i].pid = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// Current kernel execution is PID 0
|
||||
process_t *kernel_proc = &processes[0];
|
||||
kernel_proc->pid = next_pid++;
|
||||
kernel_proc->is_user = false;
|
||||
kernel_proc->is_idle = true;
|
||||
kernel_proc->tty_id = -1;
|
||||
kernel_proc->kill_pending = false;
|
||||
|
||||
|
||||
// We don't have its RSP or PML4 yet, but it's already running.
|
||||
// The timer interrupt will naturally capture its context on the first tick!
|
||||
kernel_proc->pml4_phys = paging_get_pml4_phys();
|
||||
kernel_proc->kernel_stack = 0;
|
||||
|
||||
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
|
||||
kernel_proc->fpu_initialized = true;
|
||||
|
||||
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
|
||||
|
|
@ -78,11 +79,14 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
|
|||
|
||||
new_proc->pid = next_pid++;
|
||||
new_proc->is_user = is_user;
|
||||
new_proc->tty_id = -1;
|
||||
new_proc->kill_pending = false;
|
||||
|
||||
process_t *parent = process_get_current();
|
||||
if (parent) {
|
||||
extern void mem_memcpy(void *dest, const void *src, size_t len);
|
||||
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
|
||||
new_proc->tty_id = parent->tty_id;
|
||||
} else {
|
||||
mem_memset(new_proc->cwd, 0, 1024);
|
||||
new_proc->cwd[0] = '/';
|
||||
|
|
@ -207,6 +211,18 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
|
|||
new_proc->heap_start = 0x20000000; // 512MB mark
|
||||
new_proc->heap_end = 0x20000000;
|
||||
new_proc->is_terminal_proc = false;
|
||||
new_proc->tty_id = -1;
|
||||
new_proc->kill_pending = false;
|
||||
|
||||
process_t *parent = process_get_current();
|
||||
if (parent) {
|
||||
extern void mem_memcpy(void *dest, const void *src, size_t len);
|
||||
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
|
||||
} else {
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
mem_memset(new_proc->cwd, 0, 1024);
|
||||
new_proc->cwd[0] = '/';
|
||||
}
|
||||
|
||||
// 2. Load ELF executable
|
||||
size_t elf_load_size = 0;
|
||||
|
|
@ -426,6 +442,69 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
|||
// Save context
|
||||
cur->rsp = current_rsp;
|
||||
|
||||
if (cur->kill_pending && cur->pid != 0xFFFFFFFF && cur->pid != 0) {
|
||||
process_cleanup_inner(cur);
|
||||
|
||||
process_t *prev = cur;
|
||||
while (prev->next != cur) {
|
||||
prev = prev->next;
|
||||
}
|
||||
|
||||
if (prev != cur) {
|
||||
prev->next = cur->next;
|
||||
|
||||
process_t *next_proc = cur->next;
|
||||
while (next_proc != cur) {
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) break;
|
||||
next_proc = next_proc->next;
|
||||
}
|
||||
|
||||
if (next_proc == cur || next_proc->cpu_affinity != my_cpu) {
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
|
||||
next_proc = &processes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_process[my_cpu] = next_proc;
|
||||
|
||||
cur->pid = 0xFFFFFFFF;
|
||||
cur->cpu_affinity = 0xFFFFFFFF;
|
||||
cur->ui_window = NULL;
|
||||
cur->is_terminal_proc = false;
|
||||
cur->kill_pending = false;
|
||||
|
||||
free_kernel_stack_later[my_cpu] = cur->kernel_stack_alloc;
|
||||
cur->kernel_stack_alloc = NULL;
|
||||
free_pml4_later[my_cpu] = cur->pml4_phys;
|
||||
cur->pml4_phys = 0;
|
||||
|
||||
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
|
||||
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
|
||||
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
|
||||
if (cpu_state) {
|
||||
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
|
||||
}
|
||||
}
|
||||
|
||||
paging_switch_directory(current_process[my_cpu]->pml4_phys);
|
||||
|
||||
current_process[my_cpu]->ticks++;
|
||||
uint64_t next_rsp = current_process[my_cpu]->rsp;
|
||||
|
||||
spinlock_release_irqrestore(&runqueue_lock, rflags);
|
||||
if (cleanup_stack) kfree(cleanup_stack);
|
||||
if (cleanup_pml4) {
|
||||
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
|
||||
paging_destroy_user_pml4_phys(cleanup_pml4);
|
||||
}
|
||||
|
||||
return next_rsp;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch to next ready process assigned to this CPU
|
||||
extern uint32_t wm_get_ticks(void);
|
||||
uint32_t now = wm_get_ticks();
|
||||
|
|
@ -435,7 +514,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
|||
|
||||
while (next_proc != start) {
|
||||
// Only consider processes assigned to our CPU and not terminated
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF) {
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) {
|
||||
if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -497,12 +576,20 @@ process_t* process_get_by_pid(uint32_t pid) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void process_kill_by_tty(int tty_id) {
|
||||
if (tty_id < 0) return;
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) {
|
||||
process_terminate(&processes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_cleanup_inner(process_t *proc) {
|
||||
if (!proc || proc->pid == 0xFFFFFFFF) return;
|
||||
|
||||
// 1. Cleanup side effects
|
||||
extern Window win_cmd;
|
||||
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
|
||||
if (proc->ui_window) {
|
||||
wm_remove_window((Window *)proc->ui_window);
|
||||
proc->ui_window = NULL;
|
||||
}
|
||||
|
|
@ -529,6 +616,14 @@ static void process_cleanup_inner(process_t *proc) {
|
|||
void process_terminate(process_t *to_delete) {
|
||||
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
|
||||
|
||||
uint32_t cpu_count = smp_cpu_count();
|
||||
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
|
||||
if (current_process[c] == to_delete) {
|
||||
to_delete->kill_pending = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
|
||||
|
||||
process_cleanup_inner(to_delete);
|
||||
|
|
@ -549,7 +644,6 @@ void process_terminate(process_t *to_delete) {
|
|||
prev->next = to_delete->next;
|
||||
|
||||
// Update per-CPU current_process if this was the current on any CPU
|
||||
uint32_t cpu_count = smp_cpu_count();
|
||||
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
|
||||
if (current_process[c] == to_delete) {
|
||||
process_t *np = to_delete->next;
|
||||
|
|
@ -571,6 +665,7 @@ void process_terminate(process_t *to_delete) {
|
|||
// Mark slot as free
|
||||
to_delete->pid = 0xFFFFFFFF;
|
||||
to_delete->cpu_affinity = 0xFFFFFFFF;
|
||||
to_delete->kill_pending = false;
|
||||
|
||||
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
|
||||
// Defer kernel stack until we switch away from it
|
||||
|
|
@ -639,6 +734,7 @@ uint64_t process_terminate_current(void) {
|
|||
to_delete->cpu_affinity = 0xFFFFFFFF;
|
||||
to_delete->ui_window = NULL;
|
||||
to_delete->is_terminal_proc = false;
|
||||
to_delete->kill_pending = false;
|
||||
|
||||
// 4. Load context for the NEXT process
|
||||
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ typedef struct process {
|
|||
void *user_stack_alloc;
|
||||
|
||||
bool is_terminal_proc;
|
||||
int tty_id;
|
||||
bool kill_pending;
|
||||
|
||||
struct process *next;
|
||||
|
||||
|
|
@ -75,6 +77,7 @@ uint64_t process_schedule(uint64_t current_rsp);
|
|||
uint64_t process_terminate_current(void);
|
||||
void process_terminate(process_t *proc);
|
||||
process_t* process_get_by_pid(uint32_t pid);
|
||||
void process_kill_by_tty(int tty_id);
|
||||
|
||||
// SMP: IPI handler for AP scheduling
|
||||
uint64_t sched_ipi_handler(registers_t *regs);
|
||||
|
|
|
|||
|
|
@ -19,11 +19,16 @@
|
|||
#include "network.h"
|
||||
#include "icmp.h"
|
||||
#include "cmd.h"
|
||||
#include "tty.h"
|
||||
#include "font_manager.h"
|
||||
#include "graphics.h"
|
||||
|
||||
extern bool ps2_ctrl_pressed;
|
||||
|
||||
#define SPAWN_FLAG_TERMINAL 0x1
|
||||
#define SPAWN_FLAG_INHERIT_TTY 0x2
|
||||
#define SPAWN_FLAG_TTY_ID 0x4
|
||||
|
||||
// Read MSR
|
||||
static inline uint64_t rdmsr(uint32_t msr) {
|
||||
uint32_t low, high;
|
||||
|
|
@ -180,6 +185,12 @@ static void user_window_resize(Window *win, int w, int h) {
|
|||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
mem_memset(win->pixels, 0, w * h * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
process_t *proc = process_get_by_ui_window(win);
|
||||
if (proc) {
|
||||
gui_event_t ev = { .type = GUI_EVENT_RESIZE, .arg1 = w, .arg2 = h };
|
||||
process_push_gui_event(proc, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -197,9 +208,21 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
|||
if (syscall_num == 1) { // SYS_WRITE
|
||||
extern void cmd_write_len(const char *str, size_t len);
|
||||
process_t *proc = process_get_current();
|
||||
if (!proc || !proc->is_user || proc->is_terminal_proc) {
|
||||
cmd_write_len((const char*)arg2, (size_t)arg3);
|
||||
const char *buf = (const char*)arg2;
|
||||
size_t len = (size_t)arg3;
|
||||
if (!proc || !proc->is_user) {
|
||||
cmd_write_len(buf, len);
|
||||
return len;
|
||||
}
|
||||
if (proc->is_terminal_proc) {
|
||||
if (proc->tty_id >= 0) {
|
||||
tty_write_output(proc->tty_id, buf, len);
|
||||
return len;
|
||||
}
|
||||
cmd_write_len(buf, len);
|
||||
return len;
|
||||
}
|
||||
return len;
|
||||
} else if (syscall_num == 3) { // SYS_GUI
|
||||
int cmd = (int)arg1;
|
||||
process_t *proc = process_get_current();
|
||||
|
|
@ -1254,6 +1277,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
|||
return cmd_get_config_value(key);
|
||||
} else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR
|
||||
uint32_t color = (uint32_t)arg2;
|
||||
if (proc->is_terminal_proc && proc->tty_id >= 0) {
|
||||
char seq[32];
|
||||
int pos = 0;
|
||||
int r = (color >> 16) & 0xFF;
|
||||
int g = (color >> 8) & 0xFF;
|
||||
int b = color & 0xFF;
|
||||
|
||||
seq[pos++] = 0x1B;
|
||||
seq[pos++] = '[';
|
||||
seq[pos++] = '3';
|
||||
seq[pos++] = '8';
|
||||
seq[pos++] = ';';
|
||||
seq[pos++] = '2';
|
||||
seq[pos++] = ';';
|
||||
|
||||
char num[8];
|
||||
k_itoa(r, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = ';';
|
||||
k_itoa(g, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = ';';
|
||||
k_itoa(b, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = 'm';
|
||||
|
||||
tty_write_output(proc->tty_id, seq, (size_t)pos);
|
||||
return 0;
|
||||
}
|
||||
cmd_set_current_color(color);
|
||||
return 0;
|
||||
} else if (cmd == 31) { // SYSTEM_CMD_SET_WALLPAPER_PATH
|
||||
|
|
@ -1374,6 +1426,85 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
|||
}
|
||||
return -1;
|
||||
return -1;
|
||||
} else if (cmd == 60) { // SYSTEM_CMD_TTY_CREATE
|
||||
return tty_create();
|
||||
} else if (cmd == 61) { // SYSTEM_CMD_TTY_READ_OUT
|
||||
int tty_id = (int)arg2;
|
||||
char *buf = (char *)arg3;
|
||||
size_t len = (size_t)arg4;
|
||||
if (!buf || len == 0) return 0;
|
||||
return tty_read_output(tty_id, buf, len);
|
||||
} else if (cmd == 62) { // SYSTEM_CMD_TTY_WRITE_IN
|
||||
int tty_id = (int)arg2;
|
||||
const char *buf = (const char *)arg3;
|
||||
size_t len = (size_t)arg4;
|
||||
if (!buf || len == 0) return 0;
|
||||
return tty_write_input(tty_id, buf, len);
|
||||
} else if (cmd == 63) { // SYSTEM_CMD_TTY_READ_IN
|
||||
char *buf = (char *)arg2;
|
||||
size_t len = (size_t)arg3;
|
||||
if (!buf || len == 0) return 0;
|
||||
if (proc->tty_id < 0) return 0;
|
||||
return tty_read_input(proc->tty_id, buf, len);
|
||||
} else if (cmd == 65) { // SYSTEM_CMD_TTY_SET_FG
|
||||
int tty_id = (int)arg2;
|
||||
int pid = (int)arg3;
|
||||
return tty_set_foreground(tty_id, pid);
|
||||
} else if (cmd == 66) { // SYSTEM_CMD_TTY_GET_FG
|
||||
int tty_id = (int)arg2;
|
||||
return tty_get_foreground(tty_id);
|
||||
} else if (cmd == 67) { // SYSTEM_CMD_TTY_KILL_FG
|
||||
int tty_id = (int)arg2;
|
||||
int pid = tty_get_foreground(tty_id);
|
||||
if (pid <= 0) return 0;
|
||||
process_t *target = process_get_by_pid((uint32_t)pid);
|
||||
if (target) process_terminate(target);
|
||||
tty_set_foreground(tty_id, 0);
|
||||
return 0;
|
||||
} else if (cmd == 68) {
|
||||
int tty_id = (int)arg2;
|
||||
process_kill_by_tty(tty_id);
|
||||
tty_set_foreground(tty_id, 0);
|
||||
return 0;
|
||||
} else if (cmd == 69) {
|
||||
int tty_id = (int)arg2;
|
||||
return tty_destroy(tty_id);
|
||||
} else if (cmd == 64) {
|
||||
const char *user_path = (const char *)arg2;
|
||||
const char *user_args = (const char *)arg3;
|
||||
uint64_t flags = arg4;
|
||||
int tty_id = (int)arg5;
|
||||
|
||||
if (!user_path) return -1;
|
||||
|
||||
char path_buf[256];
|
||||
int pi = 0;
|
||||
while (pi < 255 && user_path[pi]) {
|
||||
path_buf[pi] = user_path[pi];
|
||||
pi++;
|
||||
}
|
||||
path_buf[pi] = 0;
|
||||
|
||||
char args_buf[512];
|
||||
const char *args_ptr = NULL;
|
||||
if (user_args) {
|
||||
int ai = 0;
|
||||
while (ai < 511 && user_args[ai]) {
|
||||
args_buf[ai] = user_args[ai];
|
||||
ai++;
|
||||
}
|
||||
args_buf[ai] = 0;
|
||||
args_ptr = args_buf;
|
||||
}
|
||||
|
||||
process_t *child = process_create_elf(path_buf, args_ptr);
|
||||
if (!child) return -1;
|
||||
|
||||
if (flags & SPAWN_FLAG_TERMINAL) child->is_terminal_proc = true;
|
||||
if (flags & SPAWN_FLAG_TTY_ID) child->tty_id = tty_id;
|
||||
else if (flags & SPAWN_FLAG_INHERIT_TTY) child->tty_id = proc->tty_id;
|
||||
|
||||
return (uint64_t)child->pid;
|
||||
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
|
||||
void (*user_fn)(void*) = (void (*)(void*))arg2;
|
||||
void **args = (void **)arg3;
|
||||
|
|
|
|||
158
src/sys/tty.c
Normal file
158
src/sys/tty.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "tty.h"
|
||||
#include "spinlock.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TTY_MAX 8
|
||||
#define TTY_OUT_SIZE 16384
|
||||
#define TTY_IN_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
bool used;
|
||||
int id;
|
||||
char out_buf[TTY_OUT_SIZE];
|
||||
uint32_t out_head;
|
||||
uint32_t out_tail;
|
||||
char in_buf[TTY_IN_SIZE];
|
||||
uint32_t in_head;
|
||||
uint32_t in_tail;
|
||||
int fg_pid;
|
||||
spinlock_t lock;
|
||||
} tty_t;
|
||||
|
||||
static tty_t ttys[TTY_MAX] = {0};
|
||||
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
|
||||
static tty_t *tty_get(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return NULL;
|
||||
if (!ttys[tty_id].used) return NULL;
|
||||
return &ttys[tty_id];
|
||||
}
|
||||
|
||||
int tty_create(void) {
|
||||
for (int i = 0; i < TTY_MAX; i++) {
|
||||
if (!ttys[i].used) {
|
||||
ttys[i].used = true;
|
||||
ttys[i].id = i;
|
||||
ttys[i].out_head = 0;
|
||||
ttys[i].out_tail = 0;
|
||||
ttys[i].in_head = 0;
|
||||
ttys[i].in_tail = 0;
|
||||
ttys[i].fg_pid = -1;
|
||||
ttys[i].lock = SPINLOCK_INIT;
|
||||
mem_memset(ttys[i].out_buf, 0, sizeof(ttys[i].out_buf));
|
||||
mem_memset(ttys[i].in_buf, 0, sizeof(ttys[i].in_buf));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tty_destroy(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return -1;
|
||||
tty_t *tty = &ttys[tty_id];
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
if (!tty->used) {
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tty->used = false;
|
||||
tty->id = -1;
|
||||
tty->out_head = 0;
|
||||
tty->out_tail = 0;
|
||||
tty->in_head = 0;
|
||||
tty->in_tail = 0;
|
||||
tty->fg_pid = -1;
|
||||
mem_memset(tty->out_buf, 0, sizeof(tty->out_buf));
|
||||
mem_memset(tty->in_buf, 0, sizeof(tty->in_buf));
|
||||
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tty_write_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, const char *data, size_t len) {
|
||||
int written = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint32_t next = (*head + 1) % size;
|
||||
if (next == *tail) break;
|
||||
buf[*head] = data[i];
|
||||
*head = next;
|
||||
written++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static int tty_read_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, char *out, size_t max_len) {
|
||||
int read = 0;
|
||||
while (*tail != *head && (size_t)read < max_len) {
|
||||
out[read++] = buf[*tail];
|
||||
*tail = (*tail + 1) % size;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_output(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_input(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_set_foreground(int tty_id, int pid) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
tty->fg_pid = pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty_get_foreground(int tty_id) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int pid = tty->fg_pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return pid;
|
||||
}
|
||||
18
src/sys/tty.h
Normal file
18
src/sys/tty.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef TTY_H
|
||||
#define TTY_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int tty_create(void);
|
||||
int tty_destroy(int tty_id);
|
||||
int tty_write_output(int tty_id, const char *data, size_t len);
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len);
|
||||
int tty_write_input(int tty_id, const char *data, size_t len);
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len);
|
||||
int tty_set_foreground(int tty_id, int pid);
|
||||
int tty_get_foreground(int tty_id);
|
||||
|
||||
#endif
|
||||
1449
src/userland/cli/bsh.c
Normal file
1449
src/userland/cli/bsh.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: cat <filename>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = sys_open(argv[1], "r");
|
||||
if (fd < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot open %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write(1, buffer, bytes);
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
void combine_path(char *dest, const char *path1, const char *path2) {
|
||||
int i = 0;
|
||||
while (path1[i]) {
|
||||
dest[i] = path1[i];
|
||||
i++;
|
||||
}
|
||||
if (i > 0 && dest[i-1] != '/') {
|
||||
dest[i++] = '/';
|
||||
}
|
||||
int j = 0;
|
||||
while (path2[j]) {
|
||||
dest[i++] = path2[j++];
|
||||
}
|
||||
dest[i] = 0;
|
||||
}
|
||||
|
||||
const char* get_basename(const char *path) {
|
||||
const char *last_slash = NULL;
|
||||
int len = 0;
|
||||
while (path[len]) {
|
||||
if (path[len] == '/') last_slash = path + len;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!last_slash) return path;
|
||||
|
||||
// If it ends with a slash, skip it and find the previous one
|
||||
if (last_slash[1] == '\0') {
|
||||
if (len <= 1) return path; // root "/"
|
||||
int i = len - 2;
|
||||
while (i >= 0 && path[i] != '/') i--;
|
||||
if (i < 0) return path;
|
||||
return path + i + 1;
|
||||
}
|
||||
|
||||
return last_slash + 1;
|
||||
}
|
||||
|
||||
void copy_recursive(const char *src, const char *dst) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(src, &info) < 0) {
|
||||
printf("Error: Cannot get info for %s\n", src);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.is_directory) {
|
||||
if (sys_mkdir(dst) < 0) {
|
||||
// Might already exist
|
||||
}
|
||||
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(src, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
|
||||
char sub_src[512], sub_dst[512];
|
||||
combine_path(sub_src, src, entries[i].name);
|
||||
combine_path(sub_dst, dst, entries[i].name);
|
||||
copy_recursive(sub_src, sub_dst);
|
||||
}
|
||||
} else {
|
||||
int fd_in = sys_open(src, "r");
|
||||
if (fd_in < 0) {
|
||||
printf("Error: Cannot open source %s\n", src);
|
||||
return;
|
||||
}
|
||||
int fd_out = sys_open(dst, "w");
|
||||
if (fd_out < 0) {
|
||||
printf("Error: Cannot create destination %s\n", dst);
|
||||
sys_close(fd_in);
|
||||
return;
|
||||
}
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool recursive = false;
|
||||
char *src_path = NULL;
|
||||
char *dst_path = NULL;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-r") == 0) {
|
||||
recursive = true;
|
||||
} else if (!src_path) {
|
||||
src_path = argv[i];
|
||||
} else if (!dst_path) {
|
||||
dst_path = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!src_path || !dst_path) {
|
||||
printf("Usage: cp [-r] <source> <dest>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FAT32_FileInfo info_src;
|
||||
if (sys_get_file_info(src_path, &info_src) < 0) {
|
||||
printf("Error: Source %s does not exist\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info_src.is_directory && !recursive) {
|
||||
printf("Error: %s is a directory (use -r to copy recursively)\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char actual_dst[512];
|
||||
FAT32_FileInfo info_dst;
|
||||
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
|
||||
// If destination is a directory, copy INTO it
|
||||
const char *base = get_basename(src_path);
|
||||
// Clean up trailing slash from basename if any (get_basename handles it mostly)
|
||||
char clean_base[256];
|
||||
int k = 0;
|
||||
while (base[k] && base[k] != '/') {
|
||||
clean_base[k] = base[k];
|
||||
k++;
|
||||
}
|
||||
clean_base[k] = 0;
|
||||
combine_path(actual_dst, dst_path, clean_base);
|
||||
} else {
|
||||
strcpy(actual_dst, dst_path);
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
copy_recursive(src_path, actual_dst);
|
||||
} else {
|
||||
int fd_in = sys_open(src_path, "r");
|
||||
if (fd_in < 0) {
|
||||
printf("Error: Cannot open source %s\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
int fd_out = sys_open(actual_dst, "w");
|
||||
if (fd_out < 0) {
|
||||
printf("Error: Cannot create destination %s\n", actual_dst);
|
||||
sys_close(fd_in);
|
||||
return 1;
|
||||
}
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64_t dir_color = sys_get_shell_config("dir_color");
|
||||
uint64_t file_color = sys_get_shell_config("file_color");
|
||||
uint64_t size_color = sys_get_shell_config("size_color");
|
||||
uint64_t error_color = sys_get_shell_config("error_color");
|
||||
uint64_t default_color = sys_get_shell_config("default_text_color");
|
||||
|
||||
char path[256];
|
||||
if (argc > 1) {
|
||||
strcpy(path, argv[1]);
|
||||
} else {
|
||||
if (!sys_getcwd(path, sizeof(path))) {
|
||||
strcpy(path, "/");
|
||||
}
|
||||
}
|
||||
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(path, &info) < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Path '%s' does not exist\n", path);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!info.is_directory) {
|
||||
sys_set_text_color(file_color);
|
||||
printf("[FILE] %s", info.name);
|
||||
sys_set_text_color(size_color);
|
||||
printf(" (%d bytes)\n", info.size);
|
||||
sys_set_text_color(default_color);
|
||||
printf("\nTotal: 1 items\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FAT32_FileInfo entries[128];
|
||||
int count = sys_list(path, entries, 128);
|
||||
|
||||
if (count < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot list directory %s\n", path);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (entries[i].is_directory) {
|
||||
sys_set_text_color(dir_color);
|
||||
printf("[DIR] %s\n", entries[i].name);
|
||||
} else {
|
||||
sys_set_text_color(file_color);
|
||||
printf("[FILE] %s", entries[i].name);
|
||||
sys_set_text_color(size_color);
|
||||
printf(" (%d bytes)\n", entries[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
sys_set_text_color(default_color);
|
||||
printf("\nTotal: %d items\n", count);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: mkdir <dirname>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sys_mkdir(argv[1]) == 0) {
|
||||
sys_set_text_color(success_color);
|
||||
printf("Created directory: %s\n", argv[1]);
|
||||
} else {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot create directory %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
sys_set_text_color(default_color);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
void combine_path(char *dest, const char *path1, const char *path2) {
|
||||
int i = 0;
|
||||
while (path1[i]) {
|
||||
dest[i] = path1[i];
|
||||
i++;
|
||||
}
|
||||
if (i > 0 && dest[i-1] != '/') {
|
||||
dest[i++] = '/';
|
||||
}
|
||||
int j = 0;
|
||||
while (path2[j]) {
|
||||
dest[i++] = path2[j++];
|
||||
}
|
||||
dest[i] = 0;
|
||||
}
|
||||
|
||||
const char* get_basename(const char *path) {
|
||||
const char *last_slash = NULL;
|
||||
int len = 0;
|
||||
while (path[len]) {
|
||||
if (path[len] == '/') last_slash = path + len;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!last_slash) return path;
|
||||
|
||||
if (last_slash[1] == '\0') {
|
||||
if (len <= 1) return path;
|
||||
int i = len - 2;
|
||||
while (i >= 0 && path[i] != '/') i--;
|
||||
if (i < 0) return path;
|
||||
return path + i + 1;
|
||||
}
|
||||
|
||||
return last_slash + 1;
|
||||
}
|
||||
|
||||
void copy_recursive(const char *src, const char *dst) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(src, &info) < 0) return;
|
||||
|
||||
if (info.is_directory) {
|
||||
sys_mkdir(dst);
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(src, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
char sub_src[512], sub_dst[512];
|
||||
combine_path(sub_src, src, entries[i].name);
|
||||
combine_path(sub_dst, dst, entries[i].name);
|
||||
copy_recursive(sub_src, sub_dst);
|
||||
}
|
||||
} else {
|
||||
int fd_in = sys_open(src, "r");
|
||||
if (fd_in < 0) return;
|
||||
int fd_out = sys_open(dst, "w");
|
||||
if (fd_out < 0) { sys_close(fd_in); return; }
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
}
|
||||
|
||||
void delete_recursive(const char *path) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(path, &info) < 0) return;
|
||||
|
||||
if (info.is_directory) {
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(path, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
char sub_path[512];
|
||||
combine_path(sub_path, path, entries[i].name);
|
||||
delete_recursive(sub_path);
|
||||
}
|
||||
sys_delete(path);
|
||||
} else {
|
||||
sys_delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
printf("Usage: mv <source> <dest>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *src_path = argv[1];
|
||||
char *dst_path = argv[2];
|
||||
|
||||
FAT32_FileInfo info_src;
|
||||
if (sys_get_file_info(src_path, &info_src) < 0) {
|
||||
printf("Error: Cannot open source %s\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char actual_dst[512];
|
||||
FAT32_FileInfo info_dst;
|
||||
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
|
||||
const char *base = get_basename(src_path);
|
||||
char clean_base[256];
|
||||
int k = 0;
|
||||
while (base[k] && base[k] != '/') {
|
||||
clean_base[k] = base[k];
|
||||
k++;
|
||||
}
|
||||
clean_base[k] = 0;
|
||||
combine_path(actual_dst, dst_path, clean_base);
|
||||
} else {
|
||||
strcpy(actual_dst, dst_path);
|
||||
}
|
||||
|
||||
copy_recursive(src_path, actual_dst);
|
||||
delete_recursive(src_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: rm <path>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Simple rm (no recursive support yet for simplicity, but can be added)
|
||||
if (sys_delete(argv[1]) == 0) {
|
||||
sys_set_text_color(success_color);
|
||||
printf("Deleted: %s\n", argv[1]);
|
||||
} else {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot delete %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
sys_set_text_color(default_color);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: touch <filename>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if file already exists
|
||||
if (sys_exists(argv[1])) {
|
||||
// Just return success if it exists (simplification)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd = sys_open(argv[1], "w");
|
||||
if (fd < 0) {
|
||||
printf("Error: Cannot create %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
}
|
||||
893
src/userland/gui/terminal.c
Normal file
893
src/userland/gui/terminal.c
Normal file
|
|
@ -0,0 +1,893 @@
|
|||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "libc/libui.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_COLS 116
|
||||
#define DEFAULT_ROWS 41
|
||||
#define SCROLLBACK_LINES 800
|
||||
#define SCROLLBACK_COLS 256
|
||||
#define CHAR_W 8
|
||||
#define DEFAULT_LINE_H 10
|
||||
#define TAB_BAR_H 20
|
||||
#define CONTENT_PAD_BOTTOM 20
|
||||
#define TAB_CLOSE_W 12
|
||||
#define TAB_CLOSE_PAD 6
|
||||
#define MAX_TABS 4
|
||||
#define TTY_READ_CHUNK 512
|
||||
|
||||
typedef struct {
|
||||
char c;
|
||||
uint32_t color;
|
||||
} CharCell;
|
||||
|
||||
typedef struct {
|
||||
int tty_id;
|
||||
int bsh_pid;
|
||||
CharCell *cells;
|
||||
CharCell *scrollback;
|
||||
int *scroll_cols;
|
||||
int scroll_head;
|
||||
int scroll_count;
|
||||
int scroll_offset;
|
||||
int cursor_row;
|
||||
int cursor_col;
|
||||
uint32_t fg_color;
|
||||
uint32_t bg_color;
|
||||
|
||||
int ansi_state;
|
||||
int ansi_params[8];
|
||||
int ansi_param_count;
|
||||
int saved_row;
|
||||
int saved_col;
|
||||
} TerminalSession;
|
||||
|
||||
static ui_window_t g_win;
|
||||
static TerminalSession g_tabs[MAX_TABS];
|
||||
static int g_tab_count = 0;
|
||||
static int g_active_tab = 0;
|
||||
static int g_cols = DEFAULT_COLS;
|
||||
static int g_rows = DEFAULT_ROWS;
|
||||
static int g_line_h = DEFAULT_LINE_H;
|
||||
static int g_win_w = DEFAULT_COLS * CHAR_W;
|
||||
static int g_win_h = TAB_BAR_H + (DEFAULT_ROWS * DEFAULT_LINE_H);
|
||||
|
||||
static void str_copy(char *dst, const char *src, int max_len) {
|
||||
int i = 0;
|
||||
if (max_len <= 0) return;
|
||||
while (i < max_len - 1 && src && src[i]) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
static void str_append(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = (int)strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void trim_end(char *s) {
|
||||
if (!s) return;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || s[len - 1] == ' ' || s[len - 1] == '\t')) {
|
||||
s[len - 1] = 0;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void scrollback_init(TerminalSession *s) {
|
||||
s->scroll_head = 0;
|
||||
s->scroll_count = 0;
|
||||
s->scroll_offset = 0;
|
||||
if (s->scrollback) {
|
||||
for (int i = 0; i < SCROLLBACK_LINES * SCROLLBACK_COLS; i++) {
|
||||
s->scrollback[i].c = ' ';
|
||||
s->scrollback[i].color = s->fg_color;
|
||||
}
|
||||
}
|
||||
if (s->scroll_cols) {
|
||||
for (int i = 0; i < SCROLLBACK_LINES; i++) s->scroll_cols[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void scrollback_push_row(TerminalSession *s, const CharCell *row, int cols) {
|
||||
if (!s->scrollback || !s->scroll_cols) return;
|
||||
int idx = s->scroll_head;
|
||||
CharCell *dest = s->scrollback + (idx * SCROLLBACK_COLS);
|
||||
int copy_cols = cols;
|
||||
if (copy_cols > SCROLLBACK_COLS) copy_cols = SCROLLBACK_COLS;
|
||||
|
||||
for (int i = 0; i < SCROLLBACK_COLS; i++) {
|
||||
dest[i].c = ' ';
|
||||
dest[i].color = s->fg_color;
|
||||
}
|
||||
for (int i = 0; i < copy_cols; i++) {
|
||||
dest[i] = row[i];
|
||||
}
|
||||
|
||||
s->scroll_cols[idx] = copy_cols;
|
||||
s->scroll_head = (idx + 1) % SCROLLBACK_LINES;
|
||||
if (s->scroll_count < SCROLLBACK_LINES) {
|
||||
s->scroll_count++;
|
||||
} else if (s->scroll_offset > 0) {
|
||||
s->scroll_offset--;
|
||||
}
|
||||
|
||||
if (s->scroll_offset > 0) {
|
||||
s->scroll_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
static int scrollback_max_offset(TerminalSession *s) {
|
||||
int total = s->scroll_count + g_rows;
|
||||
if (total <= g_rows) return 0;
|
||||
return total - g_rows;
|
||||
}
|
||||
|
||||
static void session_adjust_scroll(TerminalSession *s, int delta) {
|
||||
if (!s) return;
|
||||
int max_offset = scrollback_max_offset(s);
|
||||
s->scroll_offset += delta;
|
||||
if (s->scroll_offset < 0) s->scroll_offset = 0;
|
||||
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
|
||||
}
|
||||
|
||||
static CharCell *scrollback_get_line(TerminalSession *s, int line_index, int *out_cols) {
|
||||
if (!s || line_index < 0 || line_index >= s->scroll_count) return NULL;
|
||||
int start = s->scroll_head - s->scroll_count;
|
||||
if (start < 0) start += SCROLLBACK_LINES;
|
||||
int idx = (start + line_index) % SCROLLBACK_LINES;
|
||||
if (out_cols) *out_cols = s->scroll_cols[idx];
|
||||
return s->scrollback + (idx * SCROLLBACK_COLS);
|
||||
}
|
||||
|
||||
static uint32_t ansi_color_16(int idx) {
|
||||
static uint32_t base[16] = {
|
||||
0xFF000000, 0xFFAA0000, 0xFF00AA00, 0xFFAA5500,
|
||||
0xFF0000AA, 0xFFAA00AA, 0xFF00AAAA, 0xFFAAAAAA,
|
||||
0xFF555555, 0xFFFF5555, 0xFF55FF55, 0xFFFFFF55,
|
||||
0xFF5555FF, 0xFFFF55FF, 0xFF55FFFF, 0xFFFFFFFF
|
||||
};
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx > 15) idx = 15;
|
||||
return base[idx];
|
||||
}
|
||||
|
||||
static void session_reset_colors(TerminalSession *s) {
|
||||
s->fg_color = 0xFFFFFFFF;
|
||||
s->bg_color = 0xFF1E1E1E;
|
||||
}
|
||||
|
||||
static void session_clear(TerminalSession *s) {
|
||||
for (int i = 0; i < g_cols * g_rows; i++) {
|
||||
s->cells[i].c = ' ';
|
||||
s->cells[i].color = s->fg_color;
|
||||
}
|
||||
s->cursor_row = 0;
|
||||
s->cursor_col = 0;
|
||||
s->scroll_offset = 0;
|
||||
}
|
||||
|
||||
static void session_scroll(TerminalSession *s) {
|
||||
int row_bytes = g_cols * (int)sizeof(CharCell);
|
||||
if (g_rows > 0) {
|
||||
scrollback_push_row(s, s->cells, g_cols);
|
||||
}
|
||||
memmove(s->cells, s->cells + g_cols, row_bytes * (g_rows - 1));
|
||||
for (int i = 0; i < g_cols; i++) {
|
||||
int idx = (g_rows - 1) * g_cols + i;
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
s->cursor_row = g_rows - 1;
|
||||
s->cursor_col = 0;
|
||||
}
|
||||
|
||||
static void session_put_char(TerminalSession *s, char c) {
|
||||
if (c == '\n') {
|
||||
s->cursor_row++;
|
||||
s->cursor_col = 0;
|
||||
if (s->cursor_row >= g_rows) session_scroll(s);
|
||||
return;
|
||||
}
|
||||
if (c == '\r') {
|
||||
s->cursor_col = 0;
|
||||
return;
|
||||
}
|
||||
if (c == '\b') {
|
||||
if (s->cursor_col > 0) s->cursor_col--;
|
||||
int idx = s->cursor_row * g_cols + s->cursor_col;
|
||||
if (idx >= 0 && idx < g_cols * g_rows) {
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (c == '\t') {
|
||||
int next = ((s->cursor_col / 4) + 1) * 4;
|
||||
while (s->cursor_col < next) {
|
||||
session_put_char(s, ' ');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (c < 32) return;
|
||||
|
||||
int idx = s->cursor_row * g_cols + s->cursor_col;
|
||||
if (idx >= 0 && idx < g_cols * g_rows) {
|
||||
s->cells[idx].c = c;
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
s->cursor_col++;
|
||||
if (s->cursor_col >= g_cols) {
|
||||
s->cursor_col = 0;
|
||||
s->cursor_row++;
|
||||
if (s->cursor_row >= g_rows) session_scroll(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void ansi_handle_sgr(TerminalSession *s) {
|
||||
if (s->ansi_param_count == 0) {
|
||||
session_reset_colors(s);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < s->ansi_param_count; i++) {
|
||||
int p = s->ansi_params[i];
|
||||
if (p == 0) session_reset_colors(s);
|
||||
else if (p >= 30 && p <= 37) s->fg_color = ansi_color_16(p - 30);
|
||||
else if (p >= 90 && p <= 97) s->fg_color = ansi_color_16(8 + (p - 90));
|
||||
else if (p >= 40 && p <= 47) s->bg_color = ansi_color_16(p - 40);
|
||||
else if (p >= 100 && p <= 107) s->bg_color = ansi_color_16(8 + (p - 100));
|
||||
else if (p == 38 || p == 48) {
|
||||
if (i + 4 < s->ansi_param_count && s->ansi_params[i + 1] == 2) {
|
||||
int r = s->ansi_params[i + 2] & 0xFF;
|
||||
int g = s->ansi_params[i + 3] & 0xFF;
|
||||
int b = s->ansi_params[i + 4] & 0xFF;
|
||||
uint32_t color = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
|
||||
if (p == 38) s->fg_color = color;
|
||||
else s->bg_color = color;
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ansi_finalize(TerminalSession *s, char cmd) {
|
||||
if (cmd == 'm') {
|
||||
ansi_handle_sgr(s);
|
||||
} else if (cmd == 'J') {
|
||||
int mode = s->ansi_param_count > 0 ? s->ansi_params[0] : 0;
|
||||
if (mode == 2 || mode == 0) session_clear(s);
|
||||
} else if (cmd == 'K') {
|
||||
int row = s->cursor_row;
|
||||
for (int col = s->cursor_col; col < g_cols; col++) {
|
||||
int idx = row * g_cols + col;
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
} else if (cmd == 'H' || cmd == 'f') {
|
||||
int row = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
int col = (s->ansi_param_count > 1) ? s->ansi_params[1] : 1;
|
||||
if (row < 1) row = 1;
|
||||
if (col < 1) col = 1;
|
||||
s->cursor_row = row - 1;
|
||||
s->cursor_col = col - 1;
|
||||
} else if (cmd == 'A') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_row -= n;
|
||||
if (s->cursor_row < 0) s->cursor_row = 0;
|
||||
} else if (cmd == 'B') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_row += n;
|
||||
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
|
||||
} else if (cmd == 'C') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_col += n;
|
||||
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
|
||||
} else if (cmd == 'D') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_col -= n;
|
||||
if (s->cursor_col < 0) s->cursor_col = 0;
|
||||
} else if (cmd == 's') {
|
||||
s->saved_row = s->cursor_row;
|
||||
s->saved_col = s->cursor_col;
|
||||
} else if (cmd == 'u') {
|
||||
s->cursor_row = s->saved_row;
|
||||
s->cursor_col = s->saved_col;
|
||||
}
|
||||
|
||||
s->ansi_state = 0;
|
||||
s->ansi_param_count = 0;
|
||||
}
|
||||
|
||||
static void session_process_char(TerminalSession *s, char c) {
|
||||
if (s->ansi_state == 0) {
|
||||
if (c == 27) {
|
||||
s->ansi_state = 1;
|
||||
s->ansi_param_count = 0;
|
||||
s->ansi_params[0] = 0;
|
||||
return;
|
||||
}
|
||||
session_put_char(s, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->ansi_state == 1) {
|
||||
if (c == '[') {
|
||||
s->ansi_state = 2;
|
||||
s->ansi_param_count = 0;
|
||||
s->ansi_params[0] = 0;
|
||||
return;
|
||||
}
|
||||
s->ansi_state = 0;
|
||||
session_put_char(s, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->ansi_state == 2) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
int idx = s->ansi_param_count;
|
||||
s->ansi_params[idx] = s->ansi_params[idx] * 10 + (c - '0');
|
||||
return;
|
||||
}
|
||||
if (c == ';') {
|
||||
if (s->ansi_param_count < 7) {
|
||||
s->ansi_param_count++;
|
||||
s->ansi_params[s->ansi_param_count] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
s->ansi_param_count++;
|
||||
ansi_finalize(s, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void session_process_output(TerminalSession *s, const char *buf, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
session_process_char(s, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_tab_width(void) {
|
||||
if (g_tab_count <= 0) return g_win_w;
|
||||
int tab_w = g_win_w / g_tab_count;
|
||||
if (tab_w < 60) tab_w = 60;
|
||||
return tab_w;
|
||||
}
|
||||
|
||||
static int read_proc_field(int pid, const char *field, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return -1;
|
||||
char path[64];
|
||||
path[0] = 0;
|
||||
str_append(path, "/proc/", sizeof(path));
|
||||
char pid_buf[16];
|
||||
itoa(pid, pid_buf);
|
||||
str_append(path, pid_buf, sizeof(path));
|
||||
str_append(path, "/", sizeof(path));
|
||||
str_append(path, field, sizeof(path));
|
||||
|
||||
int fd = sys_open(path, "r");
|
||||
if (fd < 0) return -1;
|
||||
int bytes = sys_read(fd, out, max_len - 1);
|
||||
sys_close(fd);
|
||||
if (bytes <= 0) return -1;
|
||||
out[bytes] = 0;
|
||||
trim_end(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void truncate_label(const char *src, char *out, int max_chars) {
|
||||
if (!out || max_chars <= 0) return;
|
||||
int len = (int)strlen(src);
|
||||
if (len <= max_chars) {
|
||||
str_copy(out, src, max_chars + 1);
|
||||
return;
|
||||
}
|
||||
if (max_chars <= 3) {
|
||||
for (int i = 0; i < max_chars; i++) out[i] = src[i];
|
||||
out[max_chars] = 0;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < max_chars - 3; i++) out[i] = src[i];
|
||||
out[max_chars - 3] = '.';
|
||||
out[max_chars - 2] = '.';
|
||||
out[max_chars - 1] = '.';
|
||||
out[max_chars] = 0;
|
||||
}
|
||||
|
||||
static void get_tab_title(TerminalSession *s, char *out, int max_len) {
|
||||
if (!s || !out) return;
|
||||
int fg = sys_tty_get_fg(s->tty_id);
|
||||
if (fg > 0) {
|
||||
if (read_proc_field(fg, "name", out, max_len) == 0) return;
|
||||
}
|
||||
if (s->bsh_pid > 0) {
|
||||
if (read_proc_field(s->bsh_pid, "cwd", out, max_len) == 0) return;
|
||||
}
|
||||
str_copy(out, "Bsh", max_len);
|
||||
}
|
||||
|
||||
static int read_config_value(const char *key, char *out, int max_len) {
|
||||
if (!key || !out || max_len <= 0) return -1;
|
||||
int fd = sys_open("/Library/bsh/bshrc", "r");
|
||||
if (fd < 0) return -1;
|
||||
|
||||
char buf[4096];
|
||||
int bytes = sys_read(fd, buf, sizeof(buf) - 1);
|
||||
sys_close(fd);
|
||||
if (bytes <= 0) return -1;
|
||||
buf[bytes] = 0;
|
||||
|
||||
char *line = buf;
|
||||
while (*line) {
|
||||
char *end = line;
|
||||
while (*end && *end != '\n' && *end != '\r') end++;
|
||||
char saved = *end;
|
||||
*end = 0;
|
||||
|
||||
trim_end(line);
|
||||
if (line[0] != '#' && line[0] != 0) {
|
||||
char *sep = line;
|
||||
while (*sep && *sep != '=') sep++;
|
||||
if (*sep == '=') {
|
||||
*sep = 0;
|
||||
if (strcmp(line, key) == 0) {
|
||||
str_copy(out, sep + 1, max_len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line = end + (saved ? 1 : 0);
|
||||
if (saved == '\r' && *line == '\n') line++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void resolve_bsh_path(char *out, int max_len) {
|
||||
char path_line[256];
|
||||
if (read_config_value("PATH", path_line, sizeof(path_line)) != 0) {
|
||||
str_copy(path_line, "/bin", sizeof(path_line));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int start = 0;
|
||||
while (1) {
|
||||
if (path_line[i] == ':' || path_line[i] == 0) {
|
||||
int len = i - start;
|
||||
if (len > 0) {
|
||||
char base[128];
|
||||
if (len >= (int)sizeof(base)) len = (int)sizeof(base) - 1;
|
||||
for (int j = 0; j < len; j++) base[j] = path_line[start + j];
|
||||
base[len] = 0;
|
||||
|
||||
char candidate[160];
|
||||
candidate[0] = 0;
|
||||
str_append(candidate, base, sizeof(candidate));
|
||||
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
|
||||
str_append(candidate, "bsh.elf", sizeof(candidate));
|
||||
if (sys_exists(candidate)) {
|
||||
str_copy(out, candidate, max_len);
|
||||
return;
|
||||
}
|
||||
|
||||
candidate[0] = 0;
|
||||
str_append(candidate, base, sizeof(candidate));
|
||||
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
|
||||
str_append(candidate, "bsh", sizeof(candidate));
|
||||
if (sys_exists(candidate)) {
|
||||
str_copy(out, candidate, max_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
start = i + 1;
|
||||
}
|
||||
if (path_line[i] == 0) break;
|
||||
i++;
|
||||
}
|
||||
|
||||
str_copy(out, "/bin/bsh.elf", max_len);
|
||||
}
|
||||
|
||||
static bool has_space(const char *s) {
|
||||
if (!s) return false;
|
||||
while (*s) {
|
||||
if (*s == ' ') return true;
|
||||
s++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void terminal_resize(int w, int h) {
|
||||
int min_w = CHAR_W * 40;
|
||||
int min_h = TAB_BAR_H + (g_line_h * 10);
|
||||
if (w < min_w) w = min_w;
|
||||
if (h < min_h) h = min_h;
|
||||
|
||||
g_win_w = w;
|
||||
g_win_h = h;
|
||||
|
||||
int new_cols = w / CHAR_W;
|
||||
int content_h = h - TAB_BAR_H - CONTENT_PAD_BOTTOM;
|
||||
if (g_line_h <= 0) g_line_h = DEFAULT_LINE_H;
|
||||
if (content_h < g_line_h) content_h = g_line_h;
|
||||
int new_rows = content_h / g_line_h;
|
||||
if (new_cols < 10) new_cols = 10;
|
||||
if (new_rows < 5) new_rows = 5;
|
||||
|
||||
if (new_cols == g_cols && new_rows == g_rows) return;
|
||||
|
||||
int old_cols = g_cols;
|
||||
int old_rows = g_rows;
|
||||
g_cols = new_cols;
|
||||
g_rows = new_rows;
|
||||
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
TerminalSession *s = &g_tabs[i];
|
||||
int old_scroll_count = s->scroll_count;
|
||||
int old_scroll_offset = s->scroll_offset;
|
||||
int old_total_lines = old_scroll_count + old_rows;
|
||||
int old_bottom_line = old_total_lines - 1 - old_scroll_offset;
|
||||
int old_top_line = old_bottom_line - (old_rows - 1);
|
||||
CharCell *old_cells = s->cells;
|
||||
int old_cursor_row = s->cursor_row;
|
||||
int old_cursor_col = s->cursor_col;
|
||||
|
||||
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
|
||||
for (int j = 0; j < g_cols * g_rows; j++) {
|
||||
s->cells[j].c = ' ';
|
||||
s->cells[j].color = s->fg_color;
|
||||
}
|
||||
|
||||
int row_start = 0;
|
||||
if (old_rows > g_rows) {
|
||||
if (old_cursor_row >= g_rows) {
|
||||
row_start = old_cursor_row - (g_rows - 1);
|
||||
} else {
|
||||
row_start = 0;
|
||||
}
|
||||
int max_start = old_rows - g_rows;
|
||||
if (row_start > max_start) row_start = max_start;
|
||||
if (row_start < 0) row_start = 0;
|
||||
}
|
||||
int dropped = 0;
|
||||
if (row_start > 0) {
|
||||
int projected = old_scroll_count + row_start;
|
||||
if (projected > SCROLLBACK_LINES) dropped = projected - SCROLLBACK_LINES;
|
||||
}
|
||||
int desired_top_line = old_top_line - dropped;
|
||||
if (desired_top_line < 0) desired_top_line = 0;
|
||||
for (int r = 0; r < row_start; r++) {
|
||||
scrollback_push_row(s, old_cells + (r * old_cols), old_cols);
|
||||
}
|
||||
|
||||
int copy_rows = old_rows;
|
||||
if (copy_rows > g_rows) copy_rows = g_rows;
|
||||
int copy_cols = old_cols < g_cols ? old_cols : g_cols;
|
||||
for (int r = 0; r < copy_rows; r++) {
|
||||
CharCell *src = old_cells + ((row_start + r) * old_cols);
|
||||
CharCell *dst = s->cells + (r * g_cols);
|
||||
for (int c = 0; c < copy_cols; c++) {
|
||||
dst[c] = src[c];
|
||||
}
|
||||
}
|
||||
|
||||
s->cursor_row = old_cursor_row - row_start;
|
||||
if (old_rows <= g_rows) s->cursor_row = old_cursor_row;
|
||||
if (s->cursor_row < 0) s->cursor_row = 0;
|
||||
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
|
||||
|
||||
s->cursor_col = old_cursor_col;
|
||||
if (s->cursor_col < 0) s->cursor_col = 0;
|
||||
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
|
||||
|
||||
if (old_scroll_offset == 0) {
|
||||
s->scroll_offset = 0;
|
||||
} else {
|
||||
int new_total_lines = s->scroll_count + g_rows;
|
||||
int desired_bottom = desired_top_line + (g_rows - 1);
|
||||
s->scroll_offset = (new_total_lines - 1) - desired_bottom;
|
||||
}
|
||||
session_adjust_scroll(s, 0);
|
||||
|
||||
if (old_cells) free(old_cells);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_tabs(void) {
|
||||
if (g_tab_count <= 0) return;
|
||||
ui_draw_rect(g_win, 0, 0, g_win_w, TAB_BAR_H, 0xFF1A1A1A);
|
||||
int tab_w = get_tab_width();
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
int x = i * tab_w;
|
||||
uint32_t bg = (i == g_active_tab) ? 0xFF333333 : 0xFF222222;
|
||||
ui_draw_rect(g_win, x, 0, tab_w - 2, TAB_BAR_H, bg);
|
||||
int close_size = TAB_CLOSE_W;
|
||||
int close_x = x + tab_w - TAB_CLOSE_PAD - close_size;
|
||||
int close_y = (TAB_BAR_H - close_size) / 2;
|
||||
uint32_t close_bg = (i == g_active_tab) ? 0xFF444444 : 0xFF333333;
|
||||
ui_draw_rect(g_win, close_x, close_y, close_size, close_size, close_bg);
|
||||
ui_draw_string(g_win, close_x + 2, close_y + 1, "x", 0xFFFFFFFF);
|
||||
char title[64];
|
||||
char label[64];
|
||||
get_tab_title(&g_tabs[i], title, sizeof(title));
|
||||
int text_w = tab_w - 10 - (TAB_CLOSE_PAD + TAB_CLOSE_W);
|
||||
int max_chars = text_w / CHAR_W;
|
||||
if (max_chars < 4) max_chars = 4;
|
||||
truncate_label(title, label, max_chars);
|
||||
ui_draw_string(g_win, x + 6, 4, label, 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_session(TerminalSession *s) {
|
||||
int base_y = TAB_BAR_H;
|
||||
ui_draw_rect(g_win, 0, base_y, g_win_w, g_win_h - base_y, s->bg_color);
|
||||
|
||||
int max_offset = scrollback_max_offset(s);
|
||||
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
|
||||
int total_lines = s->scroll_count + g_rows;
|
||||
int bottom_line = total_lines - 1 - s->scroll_offset;
|
||||
int top_line = bottom_line - (g_rows - 1);
|
||||
|
||||
for (int row = 0; row < g_rows; row++) {
|
||||
int line_index = top_line + row;
|
||||
CharCell *line = NULL;
|
||||
int line_cols = 0;
|
||||
if (line_index >= 0 && line_index < s->scroll_count) {
|
||||
line = scrollback_get_line(s, line_index, &line_cols);
|
||||
} else if (line_index >= s->scroll_count && line_index < total_lines) {
|
||||
int live_row = line_index - s->scroll_count;
|
||||
if (live_row >= 0 && live_row < g_rows) {
|
||||
line = s->cells + (live_row * g_cols);
|
||||
line_cols = g_cols;
|
||||
}
|
||||
}
|
||||
for (int col = 0; col < g_cols; col++) {
|
||||
char ch = ' ';
|
||||
uint32_t color = s->fg_color;
|
||||
if (line && col < line_cols) {
|
||||
ch = line[col].c;
|
||||
if (ch == 0) ch = ' ';
|
||||
color = line[col].color;
|
||||
}
|
||||
char str[2] = { ch, 0 };
|
||||
int x = col * CHAR_W;
|
||||
int y = base_y + row * g_line_h;
|
||||
ui_draw_string_bitmap(g_win, x, y, str, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->scroll_offset == 0) {
|
||||
int cx = s->cursor_col * CHAR_W;
|
||||
int cy = base_y + s->cursor_row * g_line_h;
|
||||
ui_draw_rect(g_win, cx, cy + g_line_h - 2, CHAR_W, 2, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
ui_mark_dirty(g_win, 0, 0, g_win_w, g_win_h);
|
||||
}
|
||||
|
||||
static void tab_init(TerminalSession *s, int tty_id, int bsh_pid) {
|
||||
s->tty_id = tty_id;
|
||||
s->bsh_pid = bsh_pid;
|
||||
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
|
||||
s->scrollback = (CharCell *)malloc(sizeof(CharCell) * SCROLLBACK_LINES * SCROLLBACK_COLS);
|
||||
s->scroll_cols = (int *)malloc(sizeof(int) * SCROLLBACK_LINES);
|
||||
s->cursor_row = 0;
|
||||
s->cursor_col = 0;
|
||||
s->ansi_state = 0;
|
||||
s->ansi_param_count = 0;
|
||||
s->saved_row = 0;
|
||||
s->saved_col = 0;
|
||||
session_reset_colors(s);
|
||||
scrollback_init(s);
|
||||
session_clear(s);
|
||||
}
|
||||
|
||||
static void tab_free(TerminalSession *s) {
|
||||
if (!s) return;
|
||||
if (s->cells) {
|
||||
free(s->cells);
|
||||
s->cells = NULL;
|
||||
}
|
||||
if (s->scrollback) {
|
||||
free(s->scrollback);
|
||||
s->scrollback = NULL;
|
||||
}
|
||||
if (s->scroll_cols) {
|
||||
free(s->scroll_cols);
|
||||
s->scroll_cols = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_tab(int idx) {
|
||||
if (idx < 0 || idx >= g_tab_count) return;
|
||||
TerminalSession *s = &g_tabs[idx];
|
||||
|
||||
sys_tty_kill_all(s->tty_id);
|
||||
sys_tty_destroy(s->tty_id);
|
||||
|
||||
tab_free(s);
|
||||
|
||||
for (int i = idx; i < g_tab_count - 1; i++) {
|
||||
g_tabs[i] = g_tabs[i + 1];
|
||||
}
|
||||
g_tab_count--;
|
||||
if (g_tab_count <= 0) {
|
||||
sys_exit(0);
|
||||
}
|
||||
if (g_active_tab > idx) {
|
||||
g_active_tab--;
|
||||
} else if (g_active_tab == idx) {
|
||||
if (g_active_tab >= g_tab_count) g_active_tab = g_tab_count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int create_tab(void) {
|
||||
if (g_tab_count >= MAX_TABS) return -1;
|
||||
|
||||
int tty_id = sys_tty_create();
|
||||
if (tty_id < 0) return -1;
|
||||
|
||||
char start_dir[256];
|
||||
start_dir[0] = 0;
|
||||
if (g_tab_count > 0) {
|
||||
TerminalSession *active = &g_tabs[g_active_tab];
|
||||
if (active->bsh_pid > 0) {
|
||||
read_proc_field(active->bsh_pid, "cwd", start_dir, sizeof(start_dir));
|
||||
}
|
||||
}
|
||||
|
||||
char args[32];
|
||||
args[0] = 0;
|
||||
str_append(args, "-t ", sizeof(args));
|
||||
char id_buf[8];
|
||||
itoa(tty_id, id_buf);
|
||||
str_append(args, id_buf, sizeof(args));
|
||||
|
||||
if (start_dir[0]) {
|
||||
str_append(args, " -d ", sizeof(args));
|
||||
if (has_space(start_dir)) {
|
||||
str_append(args, "\"", sizeof(args));
|
||||
str_append(args, start_dir, sizeof(args));
|
||||
str_append(args, "\"", sizeof(args));
|
||||
} else {
|
||||
str_append(args, start_dir, sizeof(args));
|
||||
}
|
||||
}
|
||||
|
||||
char bsh_path[128];
|
||||
resolve_bsh_path(bsh_path, sizeof(bsh_path));
|
||||
int pid = sys_spawn(bsh_path, args, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_TTY_ID, tty_id);
|
||||
if (pid < 0) return -1;
|
||||
|
||||
tab_init(&g_tabs[g_tab_count], tty_id, pid);
|
||||
g_tab_count++;
|
||||
return g_tab_count - 1;
|
||||
}
|
||||
|
||||
static void handle_key(gui_event_t *ev) {
|
||||
TerminalSession *s = &g_tabs[g_active_tab];
|
||||
char c = (char)ev->arg1;
|
||||
bool ctrl = ev->arg3 != 0;
|
||||
|
||||
if (ctrl && c == 't') {
|
||||
int idx = create_tab();
|
||||
if (idx >= 0) g_active_tab = idx;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && c == 20) {
|
||||
if (g_tab_count > 0) g_active_tab = (g_active_tab + 1) % g_tab_count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && c == 19) {
|
||||
if (g_tab_count > 0) g_active_tab = (g_active_tab + g_tab_count - 1) % g_tab_count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && (c == 'c' || c == 'C')) {
|
||||
int fg = sys_tty_get_fg(s->tty_id);
|
||||
if (fg > 0) {
|
||||
sys_tty_kill_fg(s->tty_id);
|
||||
sys_tty_set_fg(s->tty_id, 0);
|
||||
return;
|
||||
}
|
||||
char ch = 3;
|
||||
sys_tty_write_in(s->tty_id, &ch, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
sys_tty_write_in(s->tty_id, &c, 1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
g_win = ui_window_create("Terminal", 60, 60, g_win_w, g_win_h);
|
||||
ui_window_set_resizable(g_win, true);
|
||||
|
||||
int fh = (int)ui_get_font_height();
|
||||
if (fh > 0) g_line_h = fh;
|
||||
terminal_resize(g_win_w, g_win_h);
|
||||
|
||||
int idx = create_tab();
|
||||
if (idx < 0) return 1;
|
||||
g_active_tab = idx;
|
||||
|
||||
gui_event_t ev;
|
||||
char out_buf[TTY_READ_CHUNK];
|
||||
|
||||
while (1) {
|
||||
bool dirty = false;
|
||||
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
TerminalSession *s = &g_tabs[i];
|
||||
int read = 0;
|
||||
while ((read = sys_tty_read_out(s->tty_id, out_buf, sizeof(out_buf))) > 0) {
|
||||
session_process_output(s, out_buf, read);
|
||||
if (i == g_active_tab) dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
while (ui_get_event(g_win, &ev)) {
|
||||
if (ev.type == GUI_EVENT_CLOSE) {
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
if (g_tabs[i].bsh_pid > 0) sys_kill(g_tabs[i].bsh_pid);
|
||||
}
|
||||
sys_exit(0);
|
||||
} else if (ev.type == GUI_EVENT_KEY) {
|
||||
handle_key(&ev);
|
||||
dirty = true;
|
||||
} else if (ev.type == GUI_EVENT_CLICK) {
|
||||
if (ev.arg2 < TAB_BAR_H) {
|
||||
int tab_w = get_tab_width();
|
||||
int tab = ev.arg1 / tab_w;
|
||||
if (tab >= 0 && tab < g_tab_count) {
|
||||
int close_size = TAB_CLOSE_W;
|
||||
int close_x = tab * tab_w + tab_w - TAB_CLOSE_PAD - close_size;
|
||||
int close_y = (TAB_BAR_H - close_size) / 2;
|
||||
if (ev.arg1 >= close_x && ev.arg1 < close_x + close_size &&
|
||||
ev.arg2 >= close_y && ev.arg2 < close_y + close_size) {
|
||||
close_tab(tab);
|
||||
} else {
|
||||
g_active_tab = tab;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
} else if (ev.type == GUI_EVENT_MOUSE_WHEEL) {
|
||||
int lines = ev.arg1 * 3;
|
||||
if (lines != 0) {
|
||||
session_adjust_scroll(&g_tabs[g_active_tab], lines);
|
||||
dirty = true;
|
||||
}
|
||||
} else if (ev.type == GUI_EVENT_RESIZE) {
|
||||
terminal_resize(ev.arg1, ev.arg2);
|
||||
dirty = true;
|
||||
} else if (ev.type == GUI_EVENT_PAINT) {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
draw_tabs();
|
||||
draw_session(&g_tabs[g_active_tab]);
|
||||
} else {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -40,6 +40,46 @@ static void editor_strcpy(char *dest, const char *src) {
|
|||
*dest = 0;
|
||||
}
|
||||
|
||||
static int editor_strlen(const char *s) {
|
||||
int len = 0;
|
||||
while (s && s[len]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void editor_strncat(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = editor_strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void editor_resolve_path(const char *input, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return;
|
||||
if (!input || input[0] == 0) {
|
||||
out[0] = 0;
|
||||
return;
|
||||
}
|
||||
if (input[0] == '/') {
|
||||
editor_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
char cwd[256];
|
||||
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
|
||||
editor_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
editor_strcpy(out, cwd);
|
||||
int len = editor_strlen(out);
|
||||
if (len > 0 && out[len - 1] != '/') editor_strncat(out, "/", max_len);
|
||||
editor_strncat(out, input, max_len);
|
||||
}
|
||||
|
||||
static void editor_clear_all(void) {
|
||||
for (int i = 0; i < EDITOR_MAX_LINES; i++) {
|
||||
for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) {
|
||||
|
|
@ -71,9 +111,11 @@ static void editor_ensure_cursor_visible(void) {
|
|||
|
||||
void editor_open_file(const char *filename) {
|
||||
editor_clear_all();
|
||||
editor_strcpy(open_filename, filename);
|
||||
char resolved[256];
|
||||
editor_resolve_path(filename, resolved, sizeof(resolved));
|
||||
editor_strcpy(open_filename, resolved);
|
||||
|
||||
int fd = sys_open(filename, "r");
|
||||
int fd = sys_open(resolved, "r");
|
||||
if (fd < 0) {
|
||||
file_modified = 0;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,40 @@ static int viewer_strlen(const char *s) {
|
|||
return len;
|
||||
}
|
||||
|
||||
static void viewer_strncat(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = viewer_strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void viewer_resolve_path(const char *input, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return;
|
||||
if (!input || input[0] == 0) {
|
||||
out[0] = 0;
|
||||
return;
|
||||
}
|
||||
if (input[0] == '/') {
|
||||
viewer_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
char cwd[256];
|
||||
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
|
||||
viewer_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
viewer_strcpy(out, cwd);
|
||||
int len = viewer_strlen(out);
|
||||
if (len > 0 && out[len - 1] != '/') viewer_strncat(out, "/", max_len);
|
||||
viewer_strncat(out, input, max_len);
|
||||
}
|
||||
|
||||
static void viewer_scale_rgba_to_argb(const unsigned char *rgba, int src_w, int src_h,
|
||||
uint32_t *dst, int dst_w, int dst_h) {
|
||||
if (src_w == dst_w && src_h == dst_h) {
|
||||
|
|
@ -141,7 +175,9 @@ static void viewer_paint(ui_window_t win) {
|
|||
}
|
||||
|
||||
void viewer_open_file(const char *path) {
|
||||
int fd = sys_open(path, "r");
|
||||
char resolved[256];
|
||||
viewer_resolve_path(path, resolved, sizeof(resolved));
|
||||
int fd = sys_open(resolved, "r");
|
||||
if (fd < 0) return;
|
||||
|
||||
uint32_t file_size = sys_size(fd);
|
||||
|
|
@ -250,13 +286,13 @@ void viewer_open_file(const char *path) {
|
|||
stbi_image_free(rgba);
|
||||
free(buf);
|
||||
|
||||
viewer_strcpy(viewer_file_path, path);
|
||||
viewer_strcpy(viewer_file_path, resolved);
|
||||
|
||||
const char *fname = path;
|
||||
int plen = viewer_strlen(path);
|
||||
const char *fname = resolved;
|
||||
int plen = viewer_strlen(resolved);
|
||||
for (int i = plen - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
fname = &path[i + 1];
|
||||
if (resolved[i] == '/') {
|
||||
fname = &resolved[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ int chdir(const char *path) {
|
|||
}
|
||||
|
||||
char* getcwd(char *buf, int size) {
|
||||
if (sys_getcwd(buf, size) == 0) return buf;
|
||||
if (sys_getcwd(buf, size) >= 0) return buf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,46 @@ int sys_chdir(const char *path) {
|
|||
return (int)syscall2(SYS_FS, FS_CMD_CHDIR, (uint64_t)path);
|
||||
}
|
||||
|
||||
int sys_tty_create(void) {
|
||||
return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0);
|
||||
}
|
||||
|
||||
int sys_tty_read_out(int tty_id, char *buf, int len) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_OUT, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_tty_write_in(int tty_id, const char *buf, int len) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_WRITE_IN, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_tty_read_in(char *buf, int len) {
|
||||
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_IN, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_SPAWN, (uint64_t)path, (uint64_t)args, flags, (uint64_t)tty_id);
|
||||
}
|
||||
|
||||
int sys_tty_set_fg(int tty_id, int pid) {
|
||||
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_SET_FG, (uint64_t)tty_id, (uint64_t)pid, 0);
|
||||
}
|
||||
|
||||
int sys_tty_get_fg(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_GET_FG, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_kill_fg(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_FG, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_kill_all(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_ALL, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_destroy(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_DESTROY, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
void sys_kill(int pid) {
|
||||
syscall1(SYS_KILL, (uint64_t)pid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,20 @@
|
|||
#define SYSTEM_CMD_TCP_RECV_NB 42
|
||||
#define SYSTEM_CMD_YIELD 43
|
||||
#define SYSTEM_CMD_PARALLEL_RUN 50
|
||||
#define SYSTEM_CMD_TTY_CREATE 60
|
||||
#define SYSTEM_CMD_TTY_READ_OUT 61
|
||||
#define SYSTEM_CMD_TTY_WRITE_IN 62
|
||||
#define SYSTEM_CMD_TTY_READ_IN 63
|
||||
#define SYSTEM_CMD_SPAWN 64
|
||||
#define SYSTEM_CMD_TTY_SET_FG 65
|
||||
#define SYSTEM_CMD_TTY_GET_FG 66
|
||||
#define SYSTEM_CMD_TTY_KILL_FG 67
|
||||
#define SYSTEM_CMD_TTY_KILL_ALL 68
|
||||
#define SYSTEM_CMD_TTY_DESTROY 69
|
||||
|
||||
#define SPAWN_FLAG_TERMINAL 0x1
|
||||
#define SPAWN_FLAG_INHERIT_TTY 0x2
|
||||
#define SPAWN_FLAG_TTY_ID 0x4
|
||||
|
||||
// Internal assembly entry into Ring 0
|
||||
extern uint64_t syscall0(uint64_t sys_num);
|
||||
|
|
@ -115,6 +129,17 @@ int sys_exists(const char *path);
|
|||
int sys_getcwd(char *buf, int size);
|
||||
int sys_chdir(const char *path);
|
||||
|
||||
int sys_tty_create(void);
|
||||
int sys_tty_read_out(int tty_id, char *buf, int len);
|
||||
int sys_tty_write_in(int tty_id, const char *buf, int len);
|
||||
int sys_tty_read_in(char *buf, int len);
|
||||
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id);
|
||||
int sys_tty_set_fg(int tty_id, int pid);
|
||||
int sys_tty_get_fg(int tty_id);
|
||||
int sys_tty_kill_fg(int tty_id);
|
||||
int sys_tty_kill_all(int tty_id);
|
||||
int sys_tty_destroy(int tty_id);
|
||||
|
||||
typedef struct {
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "libc/libui.h"
|
||||
|
||||
|
||||
static int term_cols = 116;
|
||||
static int term_rows = 41;
|
||||
|
|
@ -80,13 +78,10 @@ static void telnet_handle_option(uint8_t cmd, uint8_t opt) {
|
|||
|
||||
case WILL:
|
||||
if (opt == OPT_SUPPRESS_GA) {
|
||||
// Good — accept suppressed GA (most BBS systems do this)
|
||||
telnet_send_3(IAC, DO, OPT_SUPPRESS_GA);
|
||||
} else if (opt == OPT_ECHO) {
|
||||
// Server will echo chars (remote echo) — accept it
|
||||
telnet_send_3(IAC, DO, OPT_ECHO);
|
||||
} else {
|
||||
// Refuse other server offers
|
||||
telnet_send_3(IAC, DONT, opt);
|
||||
}
|
||||
break;
|
||||
|
|
@ -115,7 +110,6 @@ static void telnet_handle_sb_terminal_type(const uint8_t *sb_data, int sb_len) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
TS_DATA = 0,
|
||||
TS_IAC,
|
||||
|
|
@ -159,7 +153,6 @@ static int telnet_process(const uint8_t *data, int len) {
|
|||
if (b == IAC) {
|
||||
ts_state = TS_IAC;
|
||||
} else {
|
||||
// Pass directly to display
|
||||
out_char((char)b);
|
||||
}
|
||||
break;
|
||||
|
|
@ -167,7 +160,6 @@ static int telnet_process(const uint8_t *data, int len) {
|
|||
case TS_IAC:
|
||||
switch (b) {
|
||||
case IAC:
|
||||
// Escaped IAC — literal 0xFF
|
||||
out_char((char)0xFF);
|
||||
ts_state = TS_DATA;
|
||||
break;
|
||||
|
|
@ -233,7 +225,6 @@ static int telnet_process(const uint8_t *data, int len) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int map_key(char c, uint8_t *key_out) {
|
||||
if (c == 29) {
|
||||
// Ctrl+]
|
||||
|
|
@ -276,7 +267,7 @@ static int map_key(char c, uint8_t *key_out) {
|
|||
|
||||
static int my_atoi(const char *s) {
|
||||
int v = 0;
|
||||
while (*s >= '0' && *s <= '9') { v = v*10 + (*s - '0'); s++; }
|
||||
while (*s >= '0' && *s <= '9') { v = v * 10 + (*s - '0'); s++; }
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +275,7 @@ static int parse_ip(const char *s, net_ipv4_address_t *ip) {
|
|||
int part = 0, val = 0;
|
||||
while (*s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
val = val*10 + (*s - '0');
|
||||
val = val * 10 + (*s - '0');
|
||||
if (val > 255) return -1;
|
||||
} else if (*s == '.') {
|
||||
if (part > 3) return -1;
|
||||
|
|
@ -341,27 +332,23 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
printf("Connected. Press Ctrl+] to disconnect.\n\n");
|
||||
|
||||
sys_system(41, 1, 0, 0, 0); // SYSTEM_CMD_SET_RAW_MODE = 1
|
||||
|
||||
uint8_t recv_buf[4096];
|
||||
int total = 0;
|
||||
int idle_count = 0;
|
||||
int connected = 1;
|
||||
|
||||
while (connected) {
|
||||
gui_event_t ev;
|
||||
while (ui_get_event(0, &ev)) { // win=0 for console proc
|
||||
if (ev.type == GUI_EVENT_KEY) {
|
||||
char ch = 0;
|
||||
int got = sys_tty_read_in(&ch, 1);
|
||||
if (got > 0) {
|
||||
uint8_t key_data[16];
|
||||
int key_len = map_key((char)ev.arg1, key_data);
|
||||
int key_len = map_key(ch, key_data);
|
||||
if (key_len < 0) {
|
||||
connected = 0;
|
||||
break;
|
||||
}
|
||||
telnet_send(key_data, key_len);
|
||||
}
|
||||
}
|
||||
if (!connected) break;
|
||||
|
||||
int len = sys_tcp_recv_nb(recv_buf, sizeof(recv_buf) - 1);
|
||||
if (len < 0) {
|
||||
|
|
@ -371,7 +358,6 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
if (len == 0) {
|
||||
idle_count++;
|
||||
// Don't timeout too fast if we are just waiting for user input
|
||||
if (idle_count > 10000000) {
|
||||
printf("\r\n[Connection timed out]\r\n");
|
||||
connected = 0;
|
||||
|
|
@ -395,11 +381,7 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
// Disable raw mode before exiting
|
||||
sys_system(41, 0, 0, 0, 0);
|
||||
|
||||
sys_tcp_close();
|
||||
printf("\r\n[Telnet session ended]\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
2332
src/wm/cmd.c
2332
src/wm/cmd.c
File diff suppressed because it is too large
Load diff
|
|
@ -9,7 +9,6 @@
|
|||
#include "disk.h"
|
||||
#include "wm.h"
|
||||
#include "memory_manager.h"
|
||||
#include "cmd.h"
|
||||
#include "process.h"
|
||||
#define EXPLORER_ITEM_HEIGHT 80
|
||||
#define EXPLORER_ITEM_WIDTH 120
|
||||
|
|
@ -848,7 +847,7 @@ static void explorer_open_item(Window *win, int index) {
|
|||
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
|
||||
process_create_elf("/bin/calculator.elf", NULL); return;
|
||||
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
|
||||
target = &win_cmd; cmd_reset();
|
||||
process_create_elf("/bin/terminal.elf", NULL); return;
|
||||
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
|
||||
process_create_elf("/bin/minesweeper.elf", NULL); return;
|
||||
} else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0 || explorer_strcmp(state->items[index].name, "Settings.shortcut") == 0) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
// External windows references (for opening other apps)
|
||||
extern Window win_explorer;
|
||||
extern Window win_editor;
|
||||
extern Window win_cmd;
|
||||
extern Window win_notepad;
|
||||
extern Window win_markdown;
|
||||
|
||||
|
|
|
|||
16
src/wm/wm.c
16
src/wm/wm.c
|
|
@ -4,7 +4,6 @@
|
|||
#include "wm.h"
|
||||
#include "graphics.h"
|
||||
#include "io.h"
|
||||
#include "cmd.h"
|
||||
#include "process.h"
|
||||
#include "syscall.h"
|
||||
#include "kutils.h"
|
||||
|
|
@ -2485,7 +2484,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
|
|||
if (existing) wm_bring_to_front_locked(existing);
|
||||
else process_create_elf("/bin/boredword.elf", NULL);
|
||||
} else if (str_starts_with(start_menu_pending_app, "Terminal")) {
|
||||
cmd_reset(); wm_bring_to_front_locked(&win_cmd);
|
||||
process_create_elf("/bin/terminal.elf", NULL);
|
||||
} else if (str_starts_with(start_menu_pending_app, "Grapher")) {
|
||||
Window *existing = wm_find_window_by_title_locked("Grapher");
|
||||
if (existing) wm_bring_to_front_locked(existing);
|
||||
|
|
@ -2550,7 +2549,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
|
|||
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
|
||||
process_create_elf("/bin/settings.elf", NULL); handled = true;
|
||||
} else if (str_ends_with(icon->name, "Terminal.shortcut")) {
|
||||
wm_bring_to_front_locked(&win_cmd); handled = true;
|
||||
process_create_elf("/bin/terminal.elf", NULL); handled = true;
|
||||
} else if (str_ends_with(icon->name, "About.shortcut")) {
|
||||
process_create_elf("/bin/about.elf", NULL); handled = true;
|
||||
} else if (str_ends_with(icon->name, "Files.shortcut")) {
|
||||
|
|
@ -3104,9 +3103,6 @@ void wm_init(void) {
|
|||
disk_manager_scan();
|
||||
log_ok("Disk scanning complete");
|
||||
|
||||
cmd_init();
|
||||
log_ok("Command CLI ready");
|
||||
|
||||
explorer_init();
|
||||
log_ok("Explorer ready");
|
||||
|
||||
|
|
@ -3128,19 +3124,15 @@ void wm_init(void) {
|
|||
log_ok("Desktop icons refreshed");
|
||||
|
||||
// Initialize z-indices
|
||||
win_cmd.z_index = 0;
|
||||
win_explorer.z_index = 1;
|
||||
|
||||
all_windows[0] = &win_cmd;
|
||||
all_windows[1] = &win_explorer;
|
||||
window_count = 2;
|
||||
all_windows[0] = &win_explorer;
|
||||
window_count = 1;
|
||||
|
||||
win_explorer.visible = false;
|
||||
win_explorer.focused = false;
|
||||
win_explorer.z_index = 10;
|
||||
|
||||
win_cmd.visible = false;
|
||||
|
||||
force_redraw = true;
|
||||
|
||||
serial_write("[WM] Initialization complete, transitioning to GUI\n");
|
||||
|
|
|
|||
Loading…
Reference in a new issue