Notepad port to userspace and bug fixes

This commit is contained in:
boreddevnl 2026-02-26 16:21:20 +01:00
parent 23ec181f29
commit c2ead0d6a7
67 changed files with 828 additions and 399 deletions

View file

@ -32,7 +32,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \
NASMFLAGS = -f elf64
# Limine Version
LIMINE_VERSION = 7.0.0
LIMINE_VERSION = 10.8.2
LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERSION)
.PHONY: all clean run limine-setup
@ -86,21 +86,21 @@ $(KERNEL_ELF): $(OBJ_FILES)
$(MAKE) -C $(SRC_DIR)/userland
# Create ISO
$(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
$(ISO_IMAGE): $(KERNEL_ELF) limine.conf limine-setup
rm -rf $(ISO_DIR)
mkdir -p $(ISO_DIR)
mkdir -p $(ISO_DIR)/EFI/BOOT
# Copy Kernel and Config
cp $(KERNEL_ELF) $(ISO_DIR)/
# Build ISO limine.cfg natively with modules
cp limine.cfg $(ISO_DIR)/
# Build ISO limine.conf natively with modules
cp limine.conf $(ISO_DIR)/
mkdir -p $(ISO_DIR)/bin
@for f in $(SRC_DIR)/userland/*.elf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/bin/; \
echo " MODULE_PATH=boot:///bin/$$basename" >> $(ISO_DIR)/limine.cfg; \
echo " module_path: boot():/bin/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
disk.img

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
iso_root/bin/notepad.elf Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,11 +0,0 @@
TIMEOUT=3
:BoredOS
PROTOCOL=limine
KERNEL_PATH=boot:///boredos.elf
#FRAMEBUFFER_WIDTH=1280
#FRAMEBUFFER_HEIGHT=720
MODULE_PATH=boot:///bin/calculator.elf
MODULE_PATH=boot:///bin/crash.elf
MODULE_PATH=boot:///bin/hello.elf

11
iso_root/limine.conf Normal file
View file

@ -0,0 +1,11 @@
timeout: 3
/BoredOS
protocol: limine
kernel_path: boot():/boredos.elf
#resolution: 1280x720
module_path: boot():/bin/calculator.elf
module_path: boot():/bin/crash.elf
module_path: boot():/bin/hello.elf
module_path: boot():/bin/notepad.elf

2
limine

@ -1 +1 @@
Subproject commit efd130dbb649983cd3a7667b6cfa4f6e5d8a3da6
Subproject commit 38ff2c855aabb92e4cfa2cc7ef0c8af665ecba94

View file

@ -1,8 +0,0 @@
TIMEOUT=3
:BoredOS
PROTOCOL=limine
KERNEL_PATH=boot:///boredos.elf
#FRAMEBUFFER_WIDTH=1280
#FRAMEBUFFER_HEIGHT=720

7
limine.conf Normal file
View file

@ -0,0 +1,7 @@
timeout: 3
/BoredOS
protocol: limine
kernel_path: boot():/boredos.elf
#resolution: 1280x720

View file

@ -8,7 +8,6 @@ extern void editor_init(void);
extern Window win_editor;
extern Window win_explorer;
extern Window win_cmd;
extern Window win_notepad;
void cli_cmd_txtedit(char *args) {
// Parse the file path argument
@ -50,7 +49,6 @@ void cli_cmd_txtedit(char *args) {
int max_z = 0;
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
win_editor.z_index = max_z + 1;
cli_write("Opening: ");

View file

@ -3,7 +3,7 @@
#include "wm.h"
#include "io.h"
#include "rtc.h"
#include "notepad.h"
#include "fat32.h"
#include "disk.h"
#include "cli_apps/cli_apps.h"
@ -554,7 +554,6 @@ static void internal_cmd_txtedit(char *args) {
// Make editor window visible and focused, bring to front
extern Window win_explorer;
extern Window win_cmd;
extern Window win_notepad;
win_editor.visible = true;
win_editor.focused = true;
@ -563,7 +562,6 @@ static void internal_cmd_txtedit(char *args) {
int max_z = 0;
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
win_editor.z_index = max_z + 1;
cmd_write("Opening: ");
@ -646,7 +644,7 @@ void cmd_exec_elf(char *args) {
}
cmd_is_waiting_for_process = true;
process_create_elf(full_exec_path);
process_create_elf(full_exec_path, args);
}
// Public API for syscall exit
@ -1176,7 +1174,7 @@ static void cmd_exec_single(char *cmd) {
if (fh) {
fat32_close(fh);
cmd_is_waiting_for_process = true;
process_create_elf(search_path);
process_create_elf(search_path, args);
return;
}
}
@ -1197,7 +1195,7 @@ static void cmd_exec_single(char *cmd) {
if (fh) {
fat32_close(fh);
cmd_is_waiting_for_process = true;
process_create_elf(search_path);
process_create_elf(search_path, args);
return;
}
}

View file

@ -59,10 +59,6 @@ uint64_t elf_load(const char *path, uint64_t user_pml4) {
return 0;
}
serial_write("[ELF] Number of program headers: ");
print_hex(ehdr.e_phnum);
serial_write("\n");
// Iterate Over Program Headers
for (int i = 0; i < ehdr.e_phnum; i++) {
fat32_seek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), 0);
@ -72,10 +68,6 @@ uint64_t elf_load(const char *path, uint64_t user_pml4) {
continue;
}
serial_write("[ELF] Header type parsed: ");
print_hex(phdr.p_type);
serial_write("\n");
// Only load segments with type PT_LOAD
if (phdr.p_type == PT_LOAD) {
uint64_t p_vaddr = phdr.p_vaddr;
@ -83,10 +75,6 @@ uint64_t elf_load(const char *path, uint64_t user_pml4) {
uint64_t p_filesz = phdr.p_filesz;
uint64_t p_offset = phdr.p_offset;
serial_write("[ELF] Loaded PT_LOAD segment vaddr=");
print_hex(p_vaddr);
serial_write("\n");
if (p_memsz == 0) continue;
// Calculate page-aligned boundaries
@ -103,36 +91,32 @@ uint64_t elf_load(const char *path, uint64_t user_pml4) {
fat32_close(file);
return 0;
}
// Determine Flags
uint64_t flags = 0x07; // Present, RW, User
paging_map_page(user_pml4, vaddr, v2p((uint64_t)phys), flags);
// Map page to user space (Present, RW, User)
paging_map_page(user_pml4, vaddr, v2p((uint64_t)phys), 0x07);
// Initialize page memory
// Zero out the entire page (handles BSS and padding)
uint8_t* dest = (uint8_t*)phys;
for (int j=0; j<4096; j++) dest[j] = 0; // zero memory (handles BSS)
for (int j=0; j<4096; j++) dest[j] = 0;
// If loading from file
if (p_filesz > 0) {
uint64_t copy_start_offset = 0;
uint64_t file_seek_pos = p_offset;
uint64_t bytes_to_copy = 4096;
// Copy data from file if available for this page
uint64_t page_vaddr_start = vaddr;
uint64_t page_vaddr_end = vaddr + 4096;
if (p == 0) {
copy_start_offset = align_offset;
bytes_to_copy = 4096 - align_offset;
} else {
file_seek_pos += (p * 4096) - align_offset;
}
// What part of the segment (p_vaddr to p_vaddr + p_filesz) overlaps this page?
uint64_t overlap_vaddr_start = p_vaddr;
if (page_vaddr_start > overlap_vaddr_start) overlap_vaddr_start = page_vaddr_start;
if (bytes_to_copy > p_filesz) {
bytes_to_copy = p_filesz;
}
if (bytes_to_copy > 0) {
fat32_seek(file, file_seek_pos, 0);
fat32_read(file, dest + copy_start_offset, bytes_to_copy);
p_filesz -= bytes_to_copy;
}
uint64_t overlap_vaddr_end = p_vaddr + p_filesz;
if (page_vaddr_end < overlap_vaddr_end) overlap_vaddr_end = page_vaddr_end;
if (overlap_vaddr_start < overlap_vaddr_end) {
uint64_t copy_size = overlap_vaddr_end - overlap_vaddr_start;
uint64_t dest_offset = overlap_vaddr_start - page_vaddr_start;
uint64_t file_offset = p_offset + (overlap_vaddr_start - p_vaddr);
fat32_seek(file, file_offset, 0);
fat32_read(file, dest + dest_offset, (uint32_t)copy_size);
}
}
}

View file

@ -7,7 +7,7 @@
#include "editor.h"
#include "markdown.h"
#include "cmd.h"
#include "notepad.h"
#include "process.h"
#include "minesweeper.h"
#include "viewer.h"
@ -817,7 +817,7 @@ static void explorer_open_target(const char *path) {
explorer_open_directory(path);
} else {
if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path);
process_create_elf(path, NULL);
} else if (explorer_is_markdown_file(path)) {
wm_bring_to_front(&win_markdown);
markdown_open_file(path);
@ -853,9 +853,9 @@ static void explorer_open_item(Window *win, int index) {
if (explorer_str_ends_with(state->items[index].name, ".shortcut")) {
Window *target = NULL;
if (explorer_strcmp(state->items[index].name, "Notepad.shortcut") == 0) {
target = &win_notepad;
process_create_elf("/bin/notepad.elf", NULL); return;
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
process_create_elf("/bin/calculator.elf"); return;
process_create_elf("/bin/calculator.elf", NULL); return;
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
target = &win_cmd; cmd_reset();
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
@ -1785,7 +1785,6 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
int max_z = 0;
for (int i = 0; i < explorer_win_count; i++) if (explorer_wins[i]->z_index > max_z) max_z = explorer_wins[i]->z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
if (win_editor.z_index > max_z) max_z = win_editor.z_index;
if (win_markdown.z_index > max_z) max_z = win_markdown.z_index;
if (win_control_panel.z_index > max_z) max_z = win_control_panel.z_index;

View file

@ -6,7 +6,7 @@ section .text
gdt_flush:
lgdt [rdi] ; Load GDT from the pointer passed in RDI
mov ax, 0x10 ; 0x10 is our offset in the GDT to our data segment
mov ax, 0x10 ; 0x10 is the offset in the GDT to data segment
mov ds, ax
mov es, ax
mov fs, ax
@ -14,7 +14,7 @@ gdt_flush:
mov ss, ax
; Far jump to update CS
push 0x08 ; 0x08 is our offset to the code segment
push 0x08 ; 0x08 is the offset to the code segment
lea rax, [rel .flush]
push rax
retfq
@ -23,6 +23,6 @@ gdt_flush:
ret
tss_flush:
mov ax, 0x28 ; 0x28 is our offset in the GDT to the TSS
mov ax, 0x28 ; 0x28 is the offset in the GDT to the TSS
ltr ax
ret

View file

@ -13,6 +13,7 @@
#define GUI_EVENT_CLICK 2
#define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4
#define GUI_EVENT_KEY 5
typedef struct {
int type;

View file

@ -1,230 +0,0 @@
#include "notepad.h"
#include "graphics.h"
#include <stddef.h>
Window win_notepad;
static int notepad_scroll_line = 0;
static void notepad_ensure_cursor_visible(Window *win) {
int visible_lines = (win->h - 40) / 10 + 3;
if (visible_lines < 1) visible_lines = 1;
int cursor_line = 0;
for (int i = 0; i < win->cursor_pos && i < win->buf_len; i++) {
if (win->buffer[i] == '\n') cursor_line++;
}
if (cursor_line < notepad_scroll_line) {
notepad_scroll_line = cursor_line;
}
if (cursor_line >= notepad_scroll_line + visible_lines) {
notepad_scroll_line = cursor_line - visible_lines + 1;
}
}
static void notepad_paint(Window *win) {
// Dark mode background for text
draw_rect(win->x + 4, win->y + 30, win->w - 8, win->h - 34, COLOR_NOTEPAD_BG);
int visual_line = 0;
int current_x = win->x + 8;
int current_y = win->y + 36;
int window_right = win->x + win->w - 16;
for (int i = 0; i < win->buf_len; i++) {
if (visual_line < notepad_scroll_line) {
if (win->buffer[i] == '\n') {
visual_line++;
current_x = win->x + 8;
current_y = win->y + 36;
} else {
if (current_x >= window_right) {
visual_line++;
current_x = win->x + 8;
current_y += 10;
}
current_x += 8;
}
continue;
}
if (visual_line >= notepad_scroll_line + (win->h - 40) / 10) {
break;
}
if (win->buffer[i] == '\n') {
current_x = win->x + 8;
current_y += 10;
visual_line++;
} else {
if (current_x >= window_right) {
current_x = win->x + 8;
current_y += 10;
visual_line++;
if (visual_line >= notepad_scroll_line + (win->h - 40) / 10) {
break;
}
}
char ch[2] = {win->buffer[i], 0};
draw_string(current_x, current_y, ch, COLOR_BLACK);
current_x += 8;
}
}
// Cursor
if (win->focused) {
int cx = win->x + 8;
int cy = win->y + 36;
int visual_line = 0;
int window_right = win->x + win->w - 16; // Right boundary with padding
for (int i = 0; i < win->cursor_pos; i++) {
if (win->buffer[i] == '\n') {
cx = win->x + 8;
cy += 10;
visual_line++;
} else {
if (cx >= window_right) {
cx = win->x + 8;
cy += 10;
visual_line++;
}
cx += 8;
}
}
if (visual_line >= notepad_scroll_line &&
visual_line < notepad_scroll_line + (win->h - 40) / 10) {
draw_rect(cx, cy, 2, 8, COLOR_BLACK);
}
}
}
static void notepad_key(Window *target, char c) {
if (c == 17) { // UP
if (target->cursor_pos > 0) {
int curr = target->cursor_pos;
int line_start = curr;
while (line_start > 0 && target->buffer[line_start - 1] != '\n') {
line_start--;
}
int col = curr - line_start;
if (line_start > 0) {
int prev_line_end = line_start - 1;
int prev_line_start = prev_line_end;
while (prev_line_start > 0 && target->buffer[prev_line_start - 1] != '\n') {
prev_line_start--;
}
int prev_line_len = prev_line_end - prev_line_start;
if (col > prev_line_len) col = prev_line_len;
target->cursor_pos = prev_line_start + col;
}
}
notepad_ensure_cursor_visible(target);
} else if (c == 18) { // DOWN
int len = target->buf_len;
if (target->cursor_pos < len) {
int curr = target->cursor_pos;
int line_start = curr;
while (line_start > 0 && target->buffer[line_start - 1] != '\n') {
line_start--;
}
int col = curr - line_start;
int next_line_start = curr;
while (next_line_start < len && target->buffer[next_line_start] != '\n') {
next_line_start++;
}
if (next_line_start < len) {
next_line_start++; // Skip newline
int next_line_end = next_line_start;
while (next_line_end < len && target->buffer[next_line_end] != '\n') {
next_line_end++;
}
int next_line_len = next_line_end - next_line_start;
if (col > next_line_len) col = next_line_len;
target->cursor_pos = next_line_start + col;
} else {
target->cursor_pos = len;
}
}
notepad_ensure_cursor_visible(target);
} else if (c == 19) { // LEFT
if (target->cursor_pos > 0) target->cursor_pos--;
notepad_ensure_cursor_visible(target);
} else if (c == 20) { // RIGHT
if (target->cursor_pos < target->buf_len) target->cursor_pos++;
notepad_ensure_cursor_visible(target);
} else if (c == '\b') { // Backspace
if (target->cursor_pos > 0) {
// Shift left
for (int i = target->cursor_pos; i < target->buf_len; i++) {
target->buffer[i - 1] = target->buffer[i];
}
target->buf_len--;
target->cursor_pos--;
target->buffer[target->buf_len] = 0;
notepad_ensure_cursor_visible(target);
}
} else if (c == '\n') { // Enter
if (target->buf_len < 1023) {
// Shift right
for (int i = target->buf_len; i > target->cursor_pos; i--) {
target->buffer[i] = target->buffer[i - 1];
}
target->buffer[target->cursor_pos] = c;
target->buf_len++;
target->cursor_pos++;
target->buffer[target->buf_len] = 0;
notepad_ensure_cursor_visible(target);
}
} else {
// Printable char
if (target->buf_len < 1023) {
for (int i = target->buf_len; i > target->cursor_pos; i--) {
target->buffer[i] = target->buffer[i - 1];
}
target->buffer[target->cursor_pos] = c;
target->buf_len++;
target->cursor_pos++;
target->buffer[target->buf_len] = 0;
notepad_ensure_cursor_visible(target);
}
}
}
void notepad_init(void) {
win_notepad.title = "Notepad";
win_notepad.x = 100;
win_notepad.y = 100;
win_notepad.w = 400;
win_notepad.h = 300;
win_notepad.visible = false;
win_notepad.buf_len = 0;
win_notepad.cursor_pos = 0;
win_notepad.focused = false;
win_notepad.z_index = 0;
win_notepad.paint = notepad_paint;
win_notepad.handle_key = notepad_key;
win_notepad.handle_click = NULL;
win_notepad.handle_right_click = NULL;
notepad_scroll_line = 0;
for(int i=0; i<1024; i++) win_notepad.buffer[i] = 0;
}
void notepad_reset(void) {
// Clear notepad buffer and reset cursor on close/reopen
win_notepad.buf_len = 0;
win_notepad.cursor_pos = 0;
win_notepad.focused = false;
notepad_scroll_line = 0;
for(int i=0; i<1024; i++) win_notepad.buffer[i] = 0;
}

View file

@ -1,11 +0,0 @@
#ifndef NOTEPAD_H
#define NOTEPAD_H
#include "wm.h"
extern Window win_notepad;
void notepad_init(void);
void notepad_reset(void);
#endif

View file

@ -28,6 +28,8 @@ void process_init(void) {
kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0;
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
kernel_proc->next = kernel_proc; // Circular linked list
current_process = kernel_proc;
}
@ -102,7 +104,7 @@ void process_create(void* entry_point, bool is_user) {
current_process->next = new_proc;
}
void process_create_elf(const char* filepath) {
void process_create_elf(const char* filepath, const char* args_str) {
if (process_count >= MAX_PROCESSES) return;
process_t *new_proc = &processes[process_count];
@ -113,6 +115,11 @@ void process_create_elf(const char* filepath) {
new_proc->pml4_phys = paging_create_user_pml4_phys();
if (!new_proc->pml4_phys) return;
for (int i = 0; i < MAX_PROCESS_FDS; i++) new_proc->fds[i] = NULL;
new_proc->gui_event_head = 0;
new_proc->gui_event_tail = 0;
new_proc->ui_window = NULL;
// 2. Load ELF executable
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys);
if (entry_point == 0) {
@ -127,22 +134,107 @@ void process_create_elf(const char* filepath) {
void* stack = kmalloc_aligned(4096, 4096);
void* kernel_stack = kmalloc_aligned(16384, 16384);
// Map User stack to 0x800000 -> Note: ELFs might overwrite this if they load there!
// But our ELF loader defaults 0x400000 for standard code.
// Map User stack to 0x800000
paging_map_page(new_proc->pml4_phys, 0x800000, v2p((uint64_t)stack), PT_PRESENT | PT_RW | PT_USER);
// 4. Build Stack Frame
// Parse arguments and push them to the user stack
// We'll place the strings at the very high end of the user stack
int argc = 1;
char *args_buf = (char *)stack + 4096;
uint64_t user_args_buf = 0x800000 + 4096;
// Copy filepath as argv[0]
int path_len = 0;
while (filepath[path_len]) path_len++;
args_buf -= (path_len + 1);
user_args_buf -= (path_len + 1);
for (int i = 0; i <= path_len; i++) args_buf[i] = filepath[i];
uint64_t argv_ptrs[32];
argv_ptrs[0] = user_args_buf;
if (args_str) {
int i = 0;
while (args_str[i] && argc < 31) {
// Skip spaces
while (args_str[i] == ' ') i++;
if (!args_str[i]) break;
int arg_start = i;
bool in_quotes = false;
if (args_str[i] == '"') {
in_quotes = true;
i++;
arg_start = i;
while (args_str[i] && args_str[i] != '"') i++;
} else {
while (args_str[i] && args_str[i] != ' ') i++;
}
int arg_len = i - arg_start;
args_buf -= (arg_len + 1);
user_args_buf -= (arg_len + 1);
for (int k = 0; k < arg_len; k++) {
args_buf[k] = args_str[arg_start + k];
}
args_buf[arg_len] = '\0';
argv_ptrs[argc++] = user_args_buf;
if (in_quotes && args_str[i] == '"') i++; // Skip closing quote
}
}
argv_ptrs[argc] = 0; // Null terminator for argv
// Align stack to 8 bytes before pushing argv array
uint64_t current_user_sp = user_args_buf;
current_user_sp &= ~7ULL;
args_buf = (char *)((uint64_t)stack + (current_user_sp - 0x800000));
// Push argv array
int argv_size = (argc + 1) * sizeof(uint64_t);
args_buf -= argv_size;
current_user_sp -= argv_size;
uint64_t actual_argv_ptr = current_user_sp; // Store the true pointer to argv array
uint64_t *user_argv_array = (uint64_t *)args_buf;
for (int i = 0; i <= argc; i++) {
user_argv_array[i] = argv_ptrs[i];
}
// Align stack to 16 bytes. crt0.asm does `and rsp, -16`, but it's good practice
current_user_sp &= ~15ULL;
// 4. Build Stack Frame for context switch via IRETQ
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 16384);
*(--stack_ptr) = 0x1B; // SS (User Mode Data)
*(--stack_ptr) = 0x800000 + 4096; // RSP
*(--stack_ptr) = current_user_sp; // RSP (Updated user stack pointer)
*(--stack_ptr) = 0x202; // RFLAGS (Interrupts Enabled)
*(--stack_ptr) = 0x23; // CS (User Mode Code)
*(--stack_ptr) = entry_point; // RIP
*(--stack_ptr) = entry_point; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
// 15 General purpose registers
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
*(--stack_ptr) = 0; // RAX
*(--stack_ptr) = 0; // RBX
*(--stack_ptr) = 0; // RCX
*(--stack_ptr) = 0; // RDX
*(--stack_ptr) = 0; // RBP
*(--stack_ptr) = argc; // RDI = argc
*(--stack_ptr) = actual_argv_ptr; // RSI = actual argv array
*(--stack_ptr) = 0; // R8
*(--stack_ptr) = 0; // R9
*(--stack_ptr) = 0; // R10
*(--stack_ptr) = 0; // R11
*(--stack_ptr) = 0; // R12
*(--stack_ptr) = 0; // R13
*(--stack_ptr) = 0; // R14
*(--stack_ptr) = 0; // R15
new_proc->kernel_stack = (uint64_t)kernel_stack + 16384;
new_proc->rsp = (uint64_t)stack_ptr;
@ -193,10 +285,20 @@ uint64_t process_terminate_current(void) {
// 1. Cleanup side effects
if (current_process->ui_window) {
extern void serial_write(const char *str);
serial_write("PROC: Terminating proc with window\n");
wm_remove_window((Window *)current_process->ui_window);
current_process->ui_window = NULL;
}
extern void fat32_close(struct FAT32_FileHandle *fh);
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (current_process->fds[i]) {
fat32_close(current_process->fds[i]);
current_process->fds[i] = NULL;
}
}
extern void cmd_process_finished(void);
cmd_process_finished();
@ -234,9 +336,23 @@ uint64_t process_terminate_current(void) {
void process_push_gui_event(process_t *proc, gui_event_t *ev) {
if (!proc) return;
// Coalesce PAINT events: if a PAINT event is already in the queue, don't add another
if (ev->type == 1) { // GUI_EVENT_PAINT
int curr = proc->gui_event_head;
while (curr != proc->gui_event_tail) {
if (proc->gui_events[curr].type == 1) {
return; // Already has a paint event pending
}
curr = (curr + 1) % MAX_GUI_EVENTS;
}
}
int next_tail = (proc->gui_event_tail + 1) % MAX_GUI_EVENTS;
// Drop event if queue is full
if (next_tail == proc->gui_event_head) {
extern void serial_write(const char *str);
serial_write("PROC: GUI event queue full, dropping event!\n");
return;
}
proc->gui_events[proc->gui_event_tail] = *ev;

View file

@ -6,6 +6,9 @@
#include "gui_ipc.h"
#define MAX_GUI_EVENTS 32
#define MAX_PROCESS_FDS 16
struct FAT32_FileHandle;
// Registers saved on the stack by interrupts/exceptions
typedef struct {
@ -27,12 +30,14 @@ typedef struct process {
int gui_event_tail;
void *ui_window; // Pointer to the active Window
void *fds[MAX_PROCESS_FDS];
struct process *next;
} process_t;
void process_init(void);
void process_create(void* entry_point, bool is_user);
void process_create_elf(const char* filepath);
void process_create_elf(const char* filepath, const char* args_str);
process_t* process_get_current(void);
uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void);

View file

@ -34,3 +34,28 @@ process_jump_usermode:
; Jump to Ring 3!
iretq
; void context_switch_to(uint64_t rsp)
; Restores context from isr frame and jumps
global context_switch_to
context_switch_to:
mov rsp, rdi
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
add rsp, 16 ; drop int_no and err_code
iretq

View file

@ -4,6 +4,8 @@
#include "gui_ipc.h"
#include "process.h"
#include "wm.h"
#include "fat32.h"
#include "fat32.h"
// Read MSR
static inline uint64_t rdmsr(uint32_t msr) {
@ -67,6 +69,14 @@ static void user_window_click(Window *win, int x, int y) {
process_push_gui_event(proc, &ev);
}
static void user_window_key(Window *win, char c) {
process_t *proc = (process_t *)win->data;
if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_KEY, .arg1 = (int)c };
process_push_gui_event(proc, &ev);
}
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
extern void cmd_write(const char *str);
extern void serial_write(const char *str);
@ -76,37 +86,60 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
// arg2 is the buffer based on our user_test logic
cmd_write((const char*)arg2);
serial_write((const char*)arg2);
} else if (syscall_num == 0) { // SYS_EXIT
process_t *proc = process_get_current();
if (proc && proc->ui_window) {
wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL;
}
cmd_process_finished();
// The actual process termination and scheduling will be handled later
// For now this just releases the CMD prompt lock.
// We will eventually need to mark the process_t as DEAD here.
} else if (syscall_num == 0 || syscall_num == 60) { // SYS_EXIT
serial_write("Kernel: SYS_EXIT called\n");
uint64_t next_rsp = process_terminate_current();
extern void context_switch_to(uint64_t rsp);
context_switch_to(next_rsp);
// This point is never reached
while(1);
} else if (syscall_num == 3) { // SYS_GUI
int cmd = (int)arg1;
process_t *proc = process_get_current();
if (cmd == GUI_CMD_WINDOW_CREATE) {
extern void serial_write(const char *str);
serial_write("Kernel: GUI_CMD_WINDOW_CREATE\n");
const char *title = (const char *)arg2;
uint64_t *params = (uint64_t *)arg3;
if (!params) return 0;
uint64_t *u_params = (uint64_t *)arg3;
if (!u_params) {
serial_write("Kernel: Error - params is NULL\n");
return 0;
}
// Copy params from user space to kernel space for safety
uint64_t params[4];
for (int i = 0; i < 4; i++) params[i] = u_params[i];
serial_write("Kernel: Window params copied.\n");
Window *win = kmalloc(sizeof(Window));
if (!win) return 0;
if (!win) {
serial_write("Kernel: Error - kmalloc failed for Window\n");
return 0;
}
serial_write("Kernel: Window allocated.\n");
// Copy title from user space to kernel space so wm.c can access it safely
int title_len = 0;
while (title[title_len] && title_len < 255) title_len++;
if (title) {
while (title[title_len] && title_len < 255) title_len++;
}
char *kernel_title = kmalloc(title_len + 1);
if (kernel_title) {
for (int i = 0; i < title_len; i++) {
kernel_title[i] = title[i];
}
kernel_title[title_len] = '\0';
serial_write("Kernel: Title copied: ");
serial_write(kernel_title);
serial_write("\n");
} else {
serial_write("Kernel: Warning - kernel_title kmalloc failed\n");
}
// Basic initialization
@ -116,6 +149,8 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
win->w = (int)params[2];
win->h = (int)params[3];
serial_write("Kernel: Init win dims.\n");
// Sanity checks for dimensions
if (win->w <= 0 || win->w > 4096) win->w = 400;
if (win->h <= 0 || win->h > 4096) win->h = 400;
@ -127,10 +162,15 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
win->buffer[0] = 0;
win->data = proc;
serial_write("Kernel: Dims initialized.\n");
// Safe allocation
size_t pixel_size = (size_t)win->w * win->h * 4;
win->pixels = kmalloc(pixel_size);
win->comp_pixels = kmalloc(pixel_size);
serial_write("Kernel: Buffers allocated.\n");
if (win->pixels) {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->pixels, 0, pixel_size);
@ -140,10 +180,13 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
mem_memset(win->comp_pixels, 0, pixel_size);
}
serial_write("Kernel: Buffers cleared.\n");
// Set callbacks
win->paint = user_window_paint;
win->handle_click = user_window_click;
win->handle_close = user_window_close;
win->handle_key = NULL;
win->handle_key = user_window_key;
win->handle_right_click = NULL;
proc->ui_window = win;
@ -152,9 +195,12 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
return (uint64_t)win;
} else if (cmd == GUI_CMD_DRAW_RECT) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
uint64_t *u_params = (uint64_t *)arg3;
uint32_t color = (uint32_t)arg4;
if (win && p) {
if (win && u_params) {
uint64_t params[4];
for (int i = 0; i < 4; i++) params[i] = u_params[i];
extern void draw_rect(int x, int y, int w, int h, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
@ -163,8 +209,8 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
if (win->pixels) {
// Strict user-to-window relative clamping
int rx = (int)p[0]; int ry = (int)p[1];
int rw = (int)p[2]; int rh = (int)p[3];
int rx = (int)params[0]; int ry = (int)params[1];
int rw = (int)params[2]; int rh = (int)params[3];
if (rx < 0) { rw += rx; rx = 0; }
if (ry < 0) { rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx;
@ -176,16 +222,19 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_rect(win->x + p[0], win->y + p[1], p[2], p[3], color);
draw_rect(win->x + params[0], win->y + params[1], params[2], params[3], color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == GUI_CMD_DRAW_ROUNDED_RECT_FILLED) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
uint64_t *u_params = (uint64_t *)arg3;
uint32_t color = (uint32_t)arg4;
if (win && p) {
if (win && u_params) {
uint64_t params[5];
for (int i = 0; i < 5; i++) params[i] = u_params[i];
extern void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
@ -193,9 +242,9 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (win->pixels) {
int rx = (int)p[0]; int ry = (int)p[1];
int rw = (int)p[2]; int rh = (int)p[3];
int rr = (int)p[4];
int rx = (int)params[0]; int ry = (int)params[1];
int rw = (int)params[2]; int rh = (int)params[3];
int rr = (int)params[4];
if (rx < 0) { rw += rx; rx = 0; }
if (ry < 0) { rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx;
@ -206,8 +255,6 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
draw_rounded_rect_filled(rx, ry, rw, rh, rr, color);
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_rounded_rect_filled(win->x + p[0], win->y + p[1], p[2], p[3], p[4], color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
@ -251,14 +298,17 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
}
} else if (cmd == GUI_CMD_MARK_DIRTY) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
if (win && p) {
uint64_t *u_params = (uint64_t *)arg3;
if (win && u_params) {
uint64_t params[4];
for (int i = 0; i < 4; i++) params[i] = u_params[i];
// Dual-buffer commit: copy pixels to comp_pixels
if (win->pixels && win->comp_pixels) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(win->comp_pixels, win->pixels, (size_t)win->w * win->h * 4);
}
wm_mark_dirty(win->x + p[0], win->y + p[1], p[2], p[3]);
wm_mark_dirty(win->x + (int)params[0], win->y + (int)params[1], (int)params[2], (int)params[3]);
}
} else if (cmd == GUI_CMD_GET_EVENT) {
Window *win = (Window *)arg2;
@ -271,6 +321,83 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
}
return 0;
}
} else if (syscall_num == SYS_FS) {
int cmd = (int)arg1;
process_t *proc = process_get_current();
if (cmd == FS_CMD_OPEN) {
const char *path = (const char *)arg2;
const char *mode = (const char *)arg3;
if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode);
if (!fh) return -1;
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) {
proc->fds[i] = fh;
return (uint64_t)i;
}
}
fat32_close(fh);
return -1;
} else if (cmd == FS_CMD_READ) {
int fd = (int)arg2;
void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_read((FAT32_FileHandle*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_WRITE) {
int fd = (int)arg2;
const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_write((FAT32_FileHandle*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_CLOSE) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
fat32_close((FAT32_FileHandle*)proc->fds[fd]);
proc->fds[fd] = NULL;
return 0;
} else if (cmd == FS_CMD_SEEK) {
int fd = (int)arg2;
int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_seek((FAT32_FileHandle*)proc->fds[fd], offset, whence);
} else if (cmd == FS_CMD_TELL) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->position;
} else if (cmd == FS_CMD_SIZE) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size;
}
else if (cmd == FS_CMD_LIST) {
const char *path = (const char *)arg2;
FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3;
int max_entries = (int)arg4;
if (!path || !entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
} else if (cmd == FS_CMD_DELETE) {
const char *path = (const char *)arg2;
if (!path) return -1;
return fat32_delete(path) ? 0 : -1;
} else if (cmd == FS_CMD_MKDIR) {
const char *path = (const char *)arg2;
if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2;
if (!path) return 0;
return fat32_exists(path) ? 1 : 0;
}
return 0;
} else if (syscall_num == 8) { // DEBUG_SERIAL_WRITE
extern void serial_write(const char *str);
serial_write((const char *)arg2);
return 0;
}
return 0;

View file

@ -12,8 +12,23 @@
// Syscall Numbers
#define SYS_WRITE 1
#define SYS_GUI 3
#define SYS_FS 4
#define SYS_EXIT 60
// FS Commands
#define FS_CMD_OPEN 1
#define FS_CMD_READ 2
#define FS_CMD_WRITE 3
#define FS_CMD_CLOSE 4
#define FS_CMD_SEEK 5
#define FS_CMD_TELL 6
#define FS_CMD_LIST 7
#define FS_CMD_DELETE 8
#define FS_CMD_SIZE 9
#define FS_CMD_MKDIR 10
#define FS_CMD_EXISTS 11
void syscall_init(void);
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);

View file

@ -257,6 +257,9 @@ int main(void) {
} else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0);
}
} else {
// Avoid high CPU usage
for(volatile int i=0; i<10000; i++);
}
}

Binary file not shown.

Binary file not shown.

View file

@ -12,9 +12,6 @@ _start:
and rsp, -16
; Call main(argc, argv)
; We don't have argc or argv yet, pass 0
xor rdi, rdi
xor rsi, rsi
call main
; If main returns, call exit(status)

Binary file not shown.

View file

@ -1,7 +1,42 @@
#include "syscall.h"
int main() {
int strlen(const char* str) {
int len = 0;
while(str[len]) len++;
return len;
}
void print_int(int n) {
char buf[16];
if (n == 0) {
sys_write(1, "0", 1);
return;
}
int i = 0;
while(n > 0) {
buf[i++] = (n % 10) + '0';
n /= 10;
}
for(int j = i - 1; j >= 0; j--) {
sys_write(1, &buf[j], 1);
}
}
int main(int argc, char** argv) {
const char* msg = "Hello from Userland ELF!\n";
sys_write(1, msg, 25);
sys_write(1, "argc: ", 6);
print_int(argc);
sys_write(1, "\n", 1);
for (int i = 0; i < argc; i++) {
sys_write(1, "argv[", 5);
print_int(i);
sys_write(1, "]: ", 3);
sys_write(1, argv[i], strlen(argv[i]));
sys_write(1, "\n", 1);
}
return 0;
}

Binary file not shown.

Binary file not shown.

View file

@ -1,5 +1,6 @@
#include "libui.h"
#include "syscall.h"
#include "syscall_user.h"
#include <stddef.h>
extern uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);

View file

@ -18,6 +18,7 @@
#define GUI_EVENT_CLICK 2
#define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4
#define GUI_EVENT_KEY 5
typedef struct {
int type;
@ -26,7 +27,7 @@ typedef struct {
} gui_event_t;
// Window Handle
typedef int ui_window_t;
typedef uint64_t ui_window_t;
// libui API
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h);

View file

@ -69,3 +69,48 @@ void sys_exit(int status) {
int sys_write(int fd, const char *buf, int len) {
return (int)syscall3(SYS_WRITE, (uint64_t)fd, (uint64_t)buf, (uint64_t)len);
}
int sys_open(const char *path, const char *mode) {
return (int)syscall3(SYS_FS, FS_CMD_OPEN, (uint64_t)path, (uint64_t)mode);
}
int sys_read(int fd, void *buf, uint32_t len) {
return (int)syscall4(SYS_FS, FS_CMD_READ, (uint64_t)fd, (uint64_t)buf, (uint64_t)len);
}
int sys_write_fs(int fd, const void *buf, uint32_t len) {
return (int)syscall4(SYS_FS, FS_CMD_WRITE, (uint64_t)fd, (uint64_t)buf, (uint64_t)len);
}
void sys_close(int fd) {
syscall2(SYS_FS, FS_CMD_CLOSE, (uint64_t)fd);
}
int sys_seek(int fd, int offset, int whence) {
return (int)syscall4(SYS_FS, FS_CMD_SEEK, (uint64_t)fd, (uint64_t)offset, (uint64_t)whence);
}
uint32_t sys_tell(int fd) {
return (uint32_t)syscall2(SYS_FS, FS_CMD_TELL, (uint64_t)fd);
}
uint32_t sys_size(int fd) {
return (uint32_t)syscall2(SYS_FS, FS_CMD_SIZE, (uint64_t)fd);
}
int sys_list(const char *path, struct FAT32_FileInfo *entries, int max_entries) {
return (int)syscall4(SYS_FS, FS_CMD_LIST, (uint64_t)path, (uint64_t)entries, (uint64_t)max_entries);
}
int sys_delete(const char *path) {
return (int)syscall2(SYS_FS, FS_CMD_DELETE, (uint64_t)path);
}
int sys_mkdir(const char *path) {
return (int)syscall2(SYS_FS, FS_CMD_MKDIR, (uint64_t)path);
}
int sys_exists(const char *path) {
return (int)syscall2(SYS_FS, FS_CMD_EXISTS, (uint64_t)path);
}

View file

@ -6,6 +6,21 @@
// Standard syscalls available from Kernel mode
#define SYS_EXIT 0
#define SYS_WRITE 1
#define SYS_GUI 3
#define SYS_FS 4
// FS Commands
#define FS_CMD_OPEN 1
#define FS_CMD_READ 2
#define FS_CMD_WRITE 3
#define FS_CMD_CLOSE 4
#define FS_CMD_SEEK 5
#define FS_CMD_TELL 6
#define FS_CMD_LIST 7
#define FS_CMD_DELETE 8
#define FS_CMD_SIZE 9
#define FS_CMD_MKDIR 10
#define FS_CMD_EXISTS 11
// Internal assembly entry into Ring 0
extern uint64_t syscall0(uint64_t sys_num);
@ -19,4 +34,19 @@ extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_
void sys_exit(int status);
int sys_write(int fd, const char *buf, int len);
// FS API
int sys_open(const char *path, const char *mode);
int sys_read(int fd, void *buf, uint32_t len);
int sys_write_fs(int fd, const void *buf, uint32_t len);
void sys_close(int fd);
int sys_seek(int fd, int offset, int whence);
uint32_t sys_tell(int fd);
uint32_t sys_size(int fd);
int sys_delete(const char *path);
int sys_mkdir(const char *path);
int sys_exists(const char *path);
struct FAT32_FileInfo;
int sys_list(const char *path, struct FAT32_FileInfo *entries, int max_entries);
#endif

Binary file not shown.

View file

@ -0,0 +1,11 @@
#ifndef SYSCALL_USER_H
#define SYSCALL_USER_H
#include "syscall.h"
#include <stddef.h>
static inline void sys_serial_write(const char *str) {
syscall2(8, 0, (uint64_t)str);
}
#endif

View file

@ -0,0 +1,251 @@
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/syscall_user.h"
#include <stddef.h>
#define COLOR_NOTEPAD_BG 0xFFFFFFFF
#define COLOR_BLACK 0xFF000000
#define NOTEPAD_BUF_SIZE (64 * 1024)
static char buffer[NOTEPAD_BUF_SIZE];
static int buf_len = 0;
static int cursor_pos = 0;
static int notepad_scroll_line = 0;
static void notepad_ensure_cursor_visible(int h) {
int visible_lines = (h - 40) / 10 + 3;
if (visible_lines < 1) visible_lines = 1;
int cursor_line = 0;
for (int i = 0; i < cursor_pos && i < buf_len; i++) {
if (buffer[i] == '\n') cursor_line++;
}
if (cursor_line < notepad_scroll_line) {
notepad_scroll_line = cursor_line;
}
if (cursor_line >= notepad_scroll_line + visible_lines) {
notepad_scroll_line = cursor_line - visible_lines + 1;
}
}
static void notepad_load_state() {
int fd = sys_open("A:/tmp/notepad_state.txt", "r");
if (fd >= 0) {
sys_serial_write("Notepad: Loading state...\n");
buf_len = sys_read(fd, buffer, NOTEPAD_BUF_SIZE - 1);
if (buf_len < 0) buf_len = 0;
buffer[buf_len] = 0;
sys_close(fd);
}
cursor_pos = buf_len;
}
static void notepad_save_state() {
// Ensure dir exists
sys_mkdir("A:/tmp");
int fd = sys_open("A:/tmp/notepad_state.txt", "w");
if (fd >= 0) {
sys_write_fs(fd, buffer, buf_len);
sys_close(fd);
}
}
static void notepad_paint(ui_window_t win, int w, int h) {
ui_draw_rect(win, 4, 30, w - 8, h - 34, COLOR_NOTEPAD_BG);
int visual_line = 0;
int current_x = 8;
int current_y = 36;
int window_right = w - 16;
for (int i = 0; i < buf_len; i++) {
if (visual_line < notepad_scroll_line) {
if (buffer[i] == '\n') {
visual_line++;
current_x = 8;
current_y = 36;
} else {
if (current_x >= window_right) {
visual_line++;
current_x = 8;
current_y += 10;
}
current_x += 8;
}
continue;
}
if (visual_line >= notepad_scroll_line + (h - 40) / 10) {
break;
}
if (buffer[i] == '\n') {
current_x = 8;
current_y += 10;
visual_line++;
} else {
if (current_x >= window_right) {
current_x = 8;
current_y += 10;
visual_line++;
if (visual_line >= notepad_scroll_line + (h - 40) / 10) {
break;
}
}
char ch[2] = {buffer[i], 0};
ui_draw_string(win, current_x, current_y, ch, COLOR_BLACK);
current_x += 8;
}
}
// Cursor
int cx = 8;
int cy = 36;
int c_visual_line = 0;
for (int i = 0; i < cursor_pos; i++) {
if (buffer[i] == '\n') {
cx = 8;
cy += 10;
c_visual_line++;
} else {
if (cx >= window_right) {
cx = 8;
cy += 10;
c_visual_line++;
}
cx += 8;
}
}
if (c_visual_line >= notepad_scroll_line &&
c_visual_line < notepad_scroll_line + (h - 40) / 10) {
ui_draw_rect(win, cx, cy, 2, 8, COLOR_BLACK);
}
ui_mark_dirty(win, 0, 0, w, h);
}
static void notepad_key(ui_window_t win, int h, char c) {
if (c == 17) { // UP
if (cursor_pos > 0) {
int curr = cursor_pos;
int line_start = curr;
while (line_start > 0 && buffer[line_start - 1] != '\n') {
line_start--;
}
int col = curr - line_start;
if (line_start > 0) {
int prev_line_end = line_start - 1;
int prev_line_start = prev_line_end;
while (prev_line_start > 0 && buffer[prev_line_start - 1] != '\n') {
prev_line_start--;
}
int prev_line_len = prev_line_end - prev_line_start;
if (col > prev_line_len) col = prev_line_len;
cursor_pos = prev_line_start + col;
}
}
} else if (c == 18) { // DOWN
if (cursor_pos < buf_len) {
int curr = cursor_pos;
int line_start = curr;
while (line_start > 0 && buffer[line_start - 1] != '\n') {
line_start--;
}
int col = curr - line_start;
int next_line_start = curr;
while (next_line_start < buf_len && buffer[next_line_start] != '\n') {
next_line_start++;
}
if (next_line_start < buf_len) {
next_line_start++; // Skip newline
int next_line_end = next_line_start;
while (next_line_end < buf_len && buffer[next_line_end] != '\n') {
next_line_end++;
}
int next_line_len = next_line_end - next_line_start;
if (col > next_line_len) col = next_line_len;
cursor_pos = next_line_start + col;
} else {
cursor_pos = buf_len;
}
}
} else if (c == 19) { // LEFT
if (cursor_pos > 0) cursor_pos--;
} else if (c == 20) { // RIGHT
if (cursor_pos < buf_len) cursor_pos++;
} else if (c == '\b') { // Backspace
if (cursor_pos > 0) {
for (int i = cursor_pos; i < buf_len; i++) {
buffer[i - 1] = buffer[i];
}
buf_len--;
cursor_pos--;
buffer[buf_len] = 0;
}
} else if (c == '\n') { // Enter
if (buf_len < 1023) {
for (int i = buf_len; i > cursor_pos; i--) {
buffer[i] = buffer[i - 1];
}
buffer[cursor_pos] = c;
buf_len++;
cursor_pos++;
buffer[buf_len] = 0;
}
} else {
if (buf_len < NOTEPAD_BUF_SIZE - 1) {
for (int i = buf_len; i > cursor_pos; i--) {
buffer[i] = buffer[i - 1];
}
buffer[cursor_pos] = c;
buf_len++;
cursor_pos++;
buffer[buf_len] = 0;
}
}
notepad_ensure_cursor_visible(h);
}
int main(int argc, char **argv) {
sys_serial_write("Notepad: Starting userspace main...\n");
ui_window_t win = ui_window_create("Notepad", 100, 100, 400, 300);
if (win == 0) {
sys_serial_write("Notepad: Failed to create window!\n");
return 1;
}
sys_serial_write("Notepad: Window created successfully.\n");
notepad_load_state();
gui_event_t ev;
sys_serial_write("Notepad: Entering event loop...\n");
while (1) {
if (ui_get_event(win, &ev)) {
if (ev.type == GUI_EVENT_PAINT) {
notepad_paint(win, 400, 300);
} else if (ev.type == GUI_EVENT_KEY) {
notepad_key(win, 300, (char)ev.arg1);
notepad_paint(win, 400, 300);
} else if (ev.type == GUI_EVENT_CLOSE) {
sys_serial_write("Notepad: CLOSE\n");
notepad_save_state();
sys_exit(0);
}
} else {
// Optional: sys_yield() or similar to avoid high CPU
// For now, just keep looping but it's better than nothing
for(volatile int i=0; i<10000; i++);
}
}
return 0;
}

BIN
src/kernel/userland/notepad.elf Executable file

Binary file not shown.

View file

@ -9,7 +9,7 @@
#include "markdown.h"
#include <stdbool.h>
#include <stddef.h>
#include "notepad.h"
#include "viewer.h"
#include "wallpaper.h"
#include "control_panel.h"
@ -21,6 +21,17 @@
#include "paint.h"
#include "disk.h"
extern void serial_write(const char *str);
static bool str_eq(const char *s1, const char *s2) {
if (!s1 || !s2) return false;
while (*s1 && *s2) {
if (*s1 != *s2) return false;
s1++; s2++;
}
return (*s1 == *s2);
}
// --- State ---
static int mx = 400, my = 300; // Mouse Pos
static int prev_mx = 400, prev_my = 300; // Previous mouse position
@ -120,10 +131,6 @@ static bool str_starts_with(const char *str, const char *prefix) {
return true;
}
static int str_eq(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) { s1++; s2++; }
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
static void refresh_desktop_icons(void) {
// Update limit in FS
@ -1344,9 +1351,24 @@ void wm_add_window(Window *win) {
}
}
Window* wm_find_window_by_title(const char *title) {
if (!title) return NULL;
for (int i = 0; i < window_count; i++) {
if (all_windows[i] && all_windows[i]->title && str_eq(all_windows[i]->title, title)) {
return all_windows[i];
}
}
return NULL;
}
void wm_remove_window(Window *win) {
if (!win) return;
serial_write("WM: Removing window '");
if (win->title) serial_write(win->title);
else serial_write("unknown");
serial_write("'\n");
int index = -1;
for (int i = 0; i < window_count; i++) {
if (all_windows[i] == win) {
@ -1375,6 +1397,8 @@ void wm_remove_window(Window *win) {
kfree(win);
force_redraw = true;
} else {
serial_write("WM: Window not found in all_windows list!\n");
}
}
@ -1824,13 +1848,23 @@ void wm_handle_right_click(int x, int y) {
if (str_starts_with(start_menu_pending_app, "Files")) {
explorer_open_directory("/");
} else if (str_starts_with(start_menu_pending_app, "Notepad")) {
wm_bring_to_front(&win_notepad);
Window *existing = wm_find_window_by_title("Notepad");
if (existing) {
wm_bring_to_front(existing);
} else {
process_create_elf("/bin/notepad.elf", NULL);
}
} else if (str_starts_with(start_menu_pending_app, "Editor")) {
wm_bring_to_front(&win_editor);
} else if (str_starts_with(start_menu_pending_app, "Terminal")) {
cmd_reset(); wm_bring_to_front(&win_cmd);
} else if (str_starts_with(start_menu_pending_app, "Calculator")) {
process_create_elf("/bin/calculator.elf");
Window *existing = wm_find_window_by_title("Calculator");
if (existing) {
wm_bring_to_front(existing);
} else {
process_create_elf("/bin/calculator.elf", NULL);
}
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
wm_bring_to_front(&win_minesweeper);
} else if (str_starts_with(start_menu_pending_app, "Settings")) {
@ -1858,9 +1892,9 @@ void wm_handle_right_click(int x, int y) {
if (icon->type == 2) { // App Shortcut
// Check name to launch app
if (str_ends_with(icon->name, "Notepad.shortcut")) {
wm_bring_to_front(&win_notepad); handled = true;
process_create_elf("/bin/notepad.elf", NULL); handled = true;
} else if (str_ends_with(icon->name, "Calculator.shortcut")) {
process_create_elf("/bin/calculator.elf"); handled = true;
process_create_elf("/bin/calculator.elf", NULL); handled = true;
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
wm_bring_to_front(&win_minesweeper); handled = true;
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
@ -1912,7 +1946,7 @@ void wm_handle_right_click(int x, int y) {
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".elf")) {
process_create_elf(path);
process_create_elf(path, NULL);
} else if (str_ends_with(icon->name, ".pnt")) {
paint_load(path);
wm_bring_to_front(&win_paint);
@ -2254,7 +2288,6 @@ void wm_init(void) {
disk_manager_scan();
// Drives are now dynamically managed - only real drives are registered
notepad_init();
cmd_init();
explorer_init();
editor_init();
@ -2269,37 +2302,32 @@ void wm_init(void) {
refresh_desktop_icons();
// Initialize z-indices
win_notepad.z_index = 0;
win_cmd.z_index = 1;
win_explorer.z_index = 2;
win_editor.z_index = 3;
win_markdown.z_index = 4;
win_control_panel.z_index = 5;
win_about.z_index = 6;
win_minesweeper.z_index = 7;
win_paint.z_index = 8;
win_cmd.z_index = 0;
win_explorer.z_index = 1;
win_editor.z_index = 2;
win_markdown.z_index = 3;
win_control_panel.z_index = 4;
win_about.z_index = 5;
win_minesweeper.z_index = 6;
win_paint.z_index = 7;
win_viewer.z_index = 8;
all_windows[0] = &win_notepad;
all_windows[1] = &win_cmd;
all_windows[2] = &win_explorer;
all_windows[3] = &win_editor;
all_windows[4] = &win_markdown;
all_windows[5] = &win_control_panel;
all_windows[6] = &win_about;
all_windows[7] = &win_minesweeper;
all_windows[8] = &win_paint;
all_windows[9] = &win_viewer;
window_count = 10;
all_windows[0] = &win_cmd;
all_windows[1] = &win_explorer;
all_windows[2] = &win_editor;
all_windows[3] = &win_markdown;
all_windows[4] = &win_control_panel;
all_windows[5] = &win_about;
all_windows[6] = &win_minesweeper;
all_windows[7] = &win_paint;
all_windows[8] = &win_viewer;
window_count = 9;
// Only show Explorer and Notepad on desktop (Explorer on top)
// Only show Explorer on desktop (initially hidden)
win_explorer.visible = false;
win_explorer.focused = false;
win_explorer.z_index = 10;
win_notepad.visible = false;
win_notepad.focused = false;
win_notepad.z_index = 9;
// Rest are hidden initially
win_cmd.visible = false;
win_editor.visible = false;

View file

@ -68,6 +68,7 @@ void wm_process_input(void);
void wm_add_window(Window *win);
void wm_remove_window(Window *win);
void wm_bring_to_front(Window *win);
Window* wm_find_window_by_title(const char *title);
// Redraw system
void wm_mark_dirty(int x, int y, int w, int h);