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

@@ -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.