30 Commits

Author SHA1 Message Date
boreddevnl
7e123b6429 VER: 4.0.1-stable --> v1.0-stable 2026-04-16 23:54:29 +02:00
boreddevnl
4177484366 VER: 26.4 --> 26.4.2 2026-04-16 23:53:50 +02:00
boreddevnl
8dc5ee5867 CREDIT: re-added original credit in 2048. Fixes #2
Co-authored-by: Lluciocc <Lluciocc@users.noreply.github.com>
2026-04-16 22:41:00 +02:00
boreddevnl
884c2f8980 FIX: update explorer spawns for new process_create_elf signature 2026-04-16 22:34:36 +02:00
boreddevnl
36d61e3b7b FEAT: Seperate run parameters for windows, mac and linux 2026-04-16 22:28:31 +02:00
boreddevnl
013f0b513f Merge branch 'main' of https://github.com/boreddevnl/BoredOS 2026-04-16 22:14:22 +02:00
boreddevnl
28108adde3 FIX: Retry to stop false application launch failures 2026-04-16 22:14:18 +02:00
boreddevnl
62ac2ab849 FIX: Redraw menubar upon application start 2026-04-16 22:13:21 +02:00
boreddevnl
7f510c6aa5 FIX: Race condition causing applications to print to serial out instead of the CLI 2026-04-16 22:12:20 +02:00
boreddevnl
7116de4152 TWEAK: rename TOPBAR --> MENUBAR 2026-04-16 22:11:44 +02:00
boreddevnl
049d67e821 FIX: Redraw menubar upon application launch 2026-04-16 22:10:58 +02:00
Chris
0f3971bb1c MERGE: Add 2048 game - LLuciocc
Add 2048 game
2026-04-16 22:03:06 +02:00
Lluciocc
66f55242a7 Update man_entries.h 2026-04-16 18:39:21 +02:00
Lluciocc
8a8fb7de27 Remove credit 2026-04-16 18:06:47 +02:00
Lluciocc
914c60e1f1 Adding 2048.c 2026-04-16 17:58:08 +02:00
boreddevnl
5141eaea60 FEAT: uname 2026-04-15 23:36:42 +02:00
boreddevnl
6e90c3e197 TWEAK: sysfetch added in startup.bsh 2026-04-15 23:36:11 +02:00
boreddevnl
bdd43f43cd FEATURE: add Bsh + userspace terminal, remove legacy cmd/cli utils 2026-04-15 22:47:24 +02:00
boreddevnl
a8866da3cb FEAT: mute terminal output from applications not launched via cli 2026-04-15 20:10:53 +02:00
boreddevnl
14decdd705 DOC: Update README.md 2026-04-15 11:00:06 +02:00
boreddevnl
ed73b88ec1 enable verbose by default 2026-04-15 10:59:44 +02:00
boreddevnl
f9bc6c7c38 FIX: FAT32 cluster management, allocation performance, and AHCI safety 2026-04-14 14:29:19 +02:00
boreddevnl
bb187faf79 DOC: small user manual 2026-04-14 10:59:52 +02:00
boreddevnl
fd7fa4f16e FIX: man entries 2026-04-14 10:59:28 +02:00
boreddevnl
5bd9e537c5 FEAT: bootfs 2026-04-13 16:04:47 +02:00
boreddevnl
e4603792b6 FEAT: Verbose boot 2026-04-13 12:17:39 +02:00
boreddevnl
a27b2c6423 RN: Renamed spotlight to lumos for legal reasons :kek: 2026-04-12 21:51:24 +02:00
boreddevnl
bb176f2193 FEAT: Lumos file searcher 2026-04-12 21:46:28 +02:00
boreddevnl
8dd756f25b FIX: cpuinfo stack overflow and add GUI tab character support 2026-04-12 19:07:08 +02:00
boreddevnl
d13fca2d4a CHECKP: vfs 2026-04-12 18:23:38 +02:00
75 changed files with 6559 additions and 3446 deletions

View File

@@ -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 \
@@ -166,6 +170,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
done
@if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi
@if [ -f limine.conf ]; then cp limine.conf $(BUILD_DIR)/initrd/; fi
cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar *
@@ -201,12 +206,26 @@ clean:
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
$(MAKE) -C $(SRC_DIR)/userland clean
run: $(ISO_IMAGE)
run-windows: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.img,format=raw,file.locking=off
run-mac: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12346-:12345 -device virtio-net-pci,netdev=net0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display cocoa,show-cursor=off \
-drive file=disk.img,format=raw,file.locking=off \
-cpu max
run-linux: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev pa,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display gtk,show-cursor=off \
-drive file=disk.img,format=raw,file.locking=off \
-cpu max

View File

@@ -29,7 +29,7 @@ BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE)
* **Multiboot2 Compliant:** Bootable on real hardware and modern emulators.
* **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface.
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage.
* **Networking:** Includes the lwIP networking stack.
* **Networking:** Includes the lwIP networking stack and a basic web browser.
### Graphical User Interface
* **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction.

286
build.log
View File

@@ -1,286 +0,0 @@
mkdir -p build
mkdir -p build
nasm -f elf64 src/arch/boot.asm -o build/boot.o
nasm -f elf64 src/arch/gdt_asm.asm -o build/gdt_asm.o
nasm -f elf64 src/arch/interrupts.asm -o build/interrupts.o
nasm -f elf64 src/arch/process_asm.asm -o build/process_asm.o
nasm -f elf64 src/arch/syscalls.asm -o build/syscalls.o
nasm -f elf64 src/arch/test_syscall.asm -o build/test_syscall.o
nasm -f elf64 src/arch/user_test.asm -o build/user_test.o
Building Limine host utility...
make[1]: Nothing to be done for `all'.
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/kutils.c -o build/kutils.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/main.c -o build/main.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/version.c -o build/version.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/elf.c -o build/elf.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/platform.c -o build/platform.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/panic.c -o build/panic.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/idt.c -o build/idt.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/gdt.c -o build/gdt.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/kernel_subsystem.c -o build/kernel_subsystem.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/lapic.c -o build/lapic.o
src/sys/idt.c: In function 'pic_remap':
src/sys/idt.c:120:17: warning: variable 'a2' set but not used [-Wunused-but-set-variable]
120 | uint8_t a1, a2;
| ^~
src/sys/idt.c:120:13: warning: variable 'a1' set but not used [-Wunused-but-set-variable]
120 | uint8_t a1, a2;
| ^~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/module_manager.c -o build/module_manager.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/process.c -o build/process.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/smp.c -o build/smp.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/syscall.c -o build/syscall.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/sysfs_init.c -o build/sysfs_init.o
src/sys/smp.c: In function 'smp_init':
src/sys/smp.c:171:14: warning: variable 'bsp_index' set but not used [-Wunused-but-set-variable]
171 | uint32_t bsp_index = 0;
| ^~~~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/work_queue.c -o build/work_queue.o
mkdir -p build/
src/sys/sysfs_init.c:11:13: warning: 'sys_itoa' defined but not used [-Wunused-function]
11 | static void sys_itoa(int n, char *s) {
| ^~~~~~~~
mkdir -p build/
src/sys/syscall.c: In function 'syscall_handler_inner':
src/sys/syscall.c:493:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
493 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:561:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
561 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:569:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
569 | float slope = *(float*)&slope_bits;
| ^~~~~~~~~~~~~~~~~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/memory_manager.c -o build/memory_manager.o
src/sys/syscall.c:695:21: warning: unused variable 'win' [-Wunused-variable]
695 | Window *win = (Window *)arg2;
| ^~~
src/sys/syscall.c:725:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
725 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:762:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
762 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/paging.c -o build/paging.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/vm.c -o build/vm.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/disk_manager.c -o build/disk_manager.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/ahci.c -o build/ahci.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/pci.c -o build/pci.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/ps2.c -o build/ps2.o
src/dev/pci.c: In function 'pci_enumerate_devices':
src/dev/pci.c:52:31: warning: comparison is always true due to limited range of data type [-Wtype-limits]
52 | for (uint8_t bus = 0; bus < 256 && count < max_devices; bus++) {
| ^
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/rtc.c -o build/rtc.o
src/dev/rtc.c: In function 'rtc_get_datetime':
src/dev/rtc.c:28:13: warning: unused variable 'last_century' [-Wunused-variable]
28 | uint8_t last_century;
| ^~~~~~~~~~~~
src/dev/rtc.c:21:13: warning: unused variable 'century' [-Wunused-variable]
21 | uint8_t century;
| ^~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/lwip_port.c -o build/lwip_port.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/network.c -o build/network.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/e1000.c -o build/e1000.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/nic.c -o build/nic.o
src/net/network.c: In function 'network_dhcp_acquire':
src/net/network.c:186:9: warning: unused variable 'loops' [-Wunused-variable]
186 | int loops = 0;
| ^~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/nic_netif.c -o build/nic_netif.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/rtl8111.c -o build/rtl8111.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/rtl8139.c -o build/rtl8139.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/virtio_net.c -o build/virtio_net.o
mkdir -p build/
src/net/network.c: In function 'network_init':
src/net/network.c:93:9: warning: 'ip.bytes[0]' may be used uninitialized [-Wmaybe-uninitialized]
93 | k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[0]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:94:9: warning: 'ip.bytes[1]' may be used uninitialized [-Wmaybe-uninitialized]
94 | k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[1]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:95:9: warning: 'ip.bytes[2]' may be used uninitialized [-Wmaybe-uninitialized]
95 | k_itoa(ip.bytes[2], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[2]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:96:9: warning: 'ip.bytes[3]' may be used uninitialized [-Wmaybe-uninitialized]
96 | k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[3]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/nic/rtl8111.c: In function 'rtl8111_init':
src/net/nic/rtl8111.c:67:14: warning: unused variable 'bar2' [-Wunused-variable]
67 | uint32_t bar2 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x18);
| ^~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/fat32.c -o build/fat32.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/procfs.c -o build/procfs.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/sysfs.c -o build/sysfs.o
src/fs/fat32.c: In function 'realfs_delete_from_vol':
src/fs/fat32.c:1110:14: warning: variable 'entry_offset' set but not used [-Wunused-but-set-variable]
1110 | uint32_t entry_offset = 0;
| ^~~~~~~~~~~~
src/fs/fat32.c:1109:14: warning: variable 'entry_sector' set but not used [-Wunused-but-set-variable]
1109 | uint32_t entry_sector = 0;
| ^~~~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_open':
src/fs/sysfs.c:12:31: warning: unused parameter 'fs_private' [-Wunused-parameter]
12 | static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c:12:73: warning: unused parameter 'mode' [-Wunused-parameter]
12 | static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~~~~~~~^~~~
src/fs/sysfs.c: In function 'sysfs_close':
src/fs/sysfs.c:43:31: warning: unused parameter 'fs_private' [-Wunused-parameter]
43 | static void sysfs_close(void *fs_private, void *handle) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_read':
src/fs/sysfs.c:47:29: warning: unused parameter 'fs_private' [-Wunused-parameter]
47 | static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_write':
src/fs/sysfs.c:56:30: warning: unused parameter 'fs_private' [-Wunused-parameter]
56 | static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_readdir':
src/fs/sysfs.c:86:49: warning: comparison of integer expressions of different signedness: 'size_t' {aka 'long unsigned int'} and 'int' [-Wsign-compare]
86 | if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
| ^
src/fs/sysfs.c:65:32: warning: unused parameter 'fs_private' [-Wunused-parameter]
65 | static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_exists':
src/fs/sysfs.c:142:31: warning: comparison of integer expressions of different signedness: 'size_t' {aka 'long unsigned int'} and 'int' [-Wsign-compare]
142 | if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
| ^
src/fs/sysfs.c:116:32: warning: unused parameter 'fs_private' [-Wunused-parameter]
116 | static bool sysfs_exists(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_open':
src/fs/procfs.c:15:25: warning: unused parameter 'fs_private' [-Wunused-parameter]
15 | void* procfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c:15:67: warning: unused parameter 'mode' [-Wunused-parameter]
15 | void* procfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~~~~~~~^~~~
src/fs/procfs.c: In function 'procfs_close':
src/fs/procfs.c:50:25: warning: unused parameter 'fs_private' [-Wunused-parameter]
50 | void procfs_close(void *fs_private, void *handle) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_read':
src/fs/procfs.c:54:23: warning: unused parameter 'fs_private' [-Wunused-parameter]
54 | int procfs_read(void *fs_private, void *handle, void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_write':
src/fs/procfs.c:178:24: warning: unused parameter 'fs_private' [-Wunused-parameter]
178 | int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_readdir':
src/fs/procfs.c:200:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
200 | int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_exists':
src/fs/procfs.c:241:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
241 | bool procfs_exists(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_is_dir':
src/fs/procfs.c:264:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
264 | bool procfs_is_dir(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/tar.c -o build/tar.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/vfs.c -o build/vfs.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/cmd.c -o build/cmd.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/explorer.c -o build/explorer.o
src/wm/cmd.c: In function 'internal_cmd_cd':
src/wm/cmd.c:1014:13: error: too few arguments to function 'vfs_normalize_path'; expected 3, have 2
1014 | vfs_normalize_path(full_path, normalized_path);
| ^~~~~~~~~~~~~~~~~~
In file included from src/wm/cmd.c:11:
src/fs/vfs.h:111:6: note: declared here
111 | void vfs_normalize_path(const char *cwd, const char *path, char *normalized);
| ^~~~~~~~~~~~~~~~~~
mkdir -p build/
make: *** [build/cmd.o] Error 1
make: *** Waiting for unfinished jobs....
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/font_manager.c -o build/font_manager.o
src/wm/explorer.c: In function 'explorer_draw_file_icon':
src/wm/explorer.c:885:73: warning: unused parameter 'color' [-Wunused-parameter]
885 | static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, const char *filename, const char *current_path) {
| ~~~~~~~~~^~~~~
src/wm/explorer.c: In function 'explorer_paint':
src/wm/explorer.c:922:15: warning: unused variable 'dirty' [-Wunused-variable]
922 | DirtyRect dirty = graphics_get_dirty_rect();
| ^~~~~
In file included from src/wm/font_manager.c:4:
src/wm/stb_truetype.h: In function 'stbtt_FreeShape':
src/wm/stb_truetype.h:2672:54: warning: unused parameter 'info' [-Wunused-parameter]
2672 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
| ~~~~~~~~~~~~~~~~~~~~~~^~~~
src/wm/stb_truetype.h: In function 'stbtt__hheap_alloc':
src/wm/stb_truetype.h:2770:70: warning: unused parameter 'userdata' [-Wunused-parameter]
2770 | static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt__hheap_cleanup':
src/wm/stb_truetype.h:2797:58: warning: unused parameter 'userdata' [-Wunused-parameter]
2797 | static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FlattenCurves':
src/wm/stb_truetype.h:3618:154: warning: unused parameter 'userdata' [-Wunused-parameter]
3618 | static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FreeBitmap':
src/wm/stb_truetype.h:3708:62: warning: unused parameter 'userdata' [-Wunused-parameter]
3708 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FreeSDF':
src/wm/stb_truetype.h:4767:59: warning: unused parameter 'userdata' [-Wunused-parameter]
4767 | STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/font_manager.c: In function 'font_manager_load':
src/wm/font_manager.c:112:9: warning: unused variable 'read' [-Wunused-variable]
112 | int read = fat32_read(fh, buffer, fsize);
| ^~~~

BIN
disk.img

Binary file not shown.

View File

@@ -5,7 +5,7 @@
---
Welcome to the internal documentation for BoredOS! This directory contains detailed guides on how the OS functions, how to build it, and how to develop applications for it.
Welcome to the documentation for BoredOS! This directory contains detailed guides on how the OS functions, how to build it, and how to develop applications for it.
## 📚 Table of Contents
@@ -32,4 +32,12 @@ The SDK and toolchain guides for creating your own `.elf` userland binaries.
- [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking.
- [`Grapher`](appdev/grapher.md): Full reference for the built-in mathematical graphing application — equation syntax, keyboard controls, architecture, and configuration.
### 4. [Usage](usage/)
General guides on how to interact with the OS.
- [`Booting`](usage/booting.md): How to use the Limine bootloader and toggle kernel boot flags like `-v`.
- [`Desktop`](usage/desktop.md): Window management, shortcuts, and desktop interaction.
- [`Lumos`](usage/lumos.md): Using the system-wide search (`Shift + Ctrl + Space`).
- [`Terminal`](usage/terminal.md): Command line interface, redirection, and common commands.
- [`Launching Apps`](usage/launching_apps.md): Ways to launch files and applications, plus a software overview.
---

33
docs/usage/booting.md Normal file
View File

@@ -0,0 +1,33 @@
# Booting BoredOS
BoredOS uses the Limine bootloader, which provides a flexible way to configure the boot process and pass parameters to the kernel.
## Boot Parameters
You can modify system behavior at startup by passing specific boot flags.
### Verbose Boot (`-v`)
The `-v` flag enables the kernel console (`kconsole`) during the boot process. When enabled, the kernel will display detailed initialization logs on the screen. By default, this is often disabled in the included configuration for a cleaner "splash-only" boot experience.
#### Toggling Verbose Boot at Runtime
You can enable or disable the verbose boot log directly from the Limine boot menu without modifying the source files:
1. **Select Entry**: When the Limine boot menu appears, highlight the **BoredOS** entry.
2. **Edit**: Press `E` to enter the entry editor.
3. **Modify Flag**: Find the line containing `cmdline: -v`.
- To **Enable**: Remove the `#` character if the line is commented out (change `# cmdline: -v` to `cmdline: -v`).
- To **Disable**: Add a `# ` at the start of the line.
4. **Boot**: Press `F10` to boot using the modified parameters.
#### Persistent Configuration
To change the default behavior permanently, modify the `limine.conf` file in the repository root before building the ISO:
```conf
/BoredOS
protocol: limine
path: boot():/boredos.elf
cmdline: -v
```

35
docs/usage/desktop.md Normal file
View File

@@ -0,0 +1,35 @@
# Using the Desktop
The BoredOS desktop environment is designed to be intuitive while providing powerful window management and icons for quick access to your files and applications.
## Window Management
BoredOS uses a stacking window manager (BoredWM) that allows you to overlap and organize multiple windows.
### Basic Actions
- **Focus**: Click anywhere on a window to bring it to the front and make it the active window.
- **Move**: Click and drag the **title bar** (the top bar of the window) to reposition it on the screen.
- **Close**: Click the red traffic light close button in the top-left corner of the window.
### System-wide Shortcuts
BoredOS includes several global shortcuts to help you manage your workflow:
- **`Ctrl + P`**: Take a screenshot. The image will be saved to `/root/Desktop` as `screenshot.jpg`.
- **`Shift + Ctrl + Space`**: Toggle **Lumos** search (see the [Lumos guide](lumos.md)).
## Desktop Icons
Your desktop represents the contents of the `/root/Desktop` directory.
- **Launching**: Double-click an icon to open the file or launch the application.
- **Snapping**: Icons automatically snap to a grid for a clean look. You can toggle "Snap to Grid" and "Auto Align" in the [Settings app](../launching_apps.md).
- **Context Menu**: Right-click on the desktop background to create new files, folders, or refresh the layout.
## The Bottom Dock
The dock at the bottom of the screen provides quick shortcuts to your most-used applications, with for example:
- **Files**: Browse the entire filesystem.
- **Terminal**: Access the command-line interface.
- **Calculator / Notepad / Grapher**: Essential productivity tools.
---
[Return to Documentation Index](../README.md)

View File

@@ -0,0 +1,31 @@
# Launching Applications
BoredOS provides several ways to launch applications and files, depending on your preferred workflow.
## 1. Using the File Explorer
The File Explorer is the primary way to navigate the filesystem and launch any `.elf` binary or associated document.
1. Open the **Explorer** from the dock or desktop.
2. Navigate to `/bin` for system applications or your own user folders.
3. **Double-click** any executable (`.elf`) to run it.
4. Standard files (like `.jpg` or `.txt`) will automatically open in their default viewer.
## 2. Desktop Shortcuts and Icons
Commonly used applications are placed directly on the desktop.
- Simply **Double-click** any icon on the desktop to launch it.
- You can also create desktop shortcuts by right-clicking on a file and selecting **"Create Shortcut"**.
## 3. Using Lumos (Global Search)
For the fastest access, use **Lumos** to search and launch by name:
1. Press **`Shift + Ctrl + Space`**.
2. Type the name of the app (e.g., "DOOM.elf").
3. Press **Enter** to launch.
---
[Return to Documentation Index](../README.md)

29
docs/usage/lumos.md Normal file
View File

@@ -0,0 +1,29 @@
# Lumos: System Search
**Lumos** is the powerful, system-wide search and launch assistant for BoredOS. It allows you to find applications, documents, and system files instantly without navigating through folders.
## Opening Lumos
To activate Lumos at any time, use the global keyboard shortcut:
**`Shift + Ctrl + Space`**
The Lumos search modal will appear in the center of your screen, ready for input.
## Features
- **Fuzzy Searching**: You don't need to type the exact name. Lumos uses fuzzy matching to find the most relevant results as you type.
- **Deep Indexing**: Lumos indexes files across the entire system.
- **Quick Launch**: Once you find what you're looking for, launching it is as simple as pressing `Enter`.
## Navigation
When the Lumos window is open:
- **Type**: Just start typing to filter results.
- **Arrow Keys (Up/Down)**: Move the selection highlight through the list of results.
- **Enter**: Launch the selected file or application.
- **Backspace**: Delete characters in your search query.
- **Escape**: Close Lumos and return to the desktop.
---
[Return to Documentation Index](../README.md)

60
docs/usage/terminal.md Normal file
View File

@@ -0,0 +1,60 @@
# Terminal & Command Line
The BoredOS Terminal provides a powerful command-line interface (CLI) for advanced users and developers. It supports standard Unix-like features and provides direct access to the kernel's system calls.
## The Shell
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**:
- `command > file`: Write output to a new file (or overwrite existing).
- `command >> file`: Append output to an existing file.
- **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`:
| Command | Description |
| :--- | :--- |
| `ls` | List files and directories in the current path. |
| `cd` | Change the current working directory. |
| `cat` | Display the contents of a file. |
| `ls` | List directory contents. |
| `rm` | Remove a file. |
| `mkdir` | Create a new directory. |
| `man` | View the manual for a specific command (e.g., `man ls`). |
| `sysfetch` | Display system and hardware information. |
---
[Return to Documentation Index](../README.md)

View File

@@ -13,3 +13,4 @@ backdrop: 000000
/BoredOS
protocol: limine
path: boot():/boredos.elf
cmdline: -v

95
src/core/kconsole.c Normal file
View File

@@ -0,0 +1,95 @@
#include "kconsole.h"
#include "graphics.h"
#include "sys/spinlock.h"
#include <stddef.h>
static spinlock_t console_lock = SPINLOCK_INIT;
static int cursor_x = 0;
static int cursor_y = 0;
static bool kconsole_active = false;
static uint32_t text_color = 0xFFFFFFFF; // White
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 10
void kconsole_init(void) {
cursor_x = 10;
cursor_y = 10;
kconsole_active = false;
// Initial clear screen during boot
graphics_clear_back_buffer(0x00000000);
graphics_mark_screen_dirty();
graphics_flip_buffer();
}
void kconsole_set_active(bool active) {
kconsole_active = active;
}
void kconsole_set_color(uint32_t color) {
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
text_color = color;
spinlock_release_irqrestore(&console_lock, flags);
}
static void kconsole_scroll(void) {
if (cursor_y + CHAR_HEIGHT >= get_screen_height() - 10) {
graphics_scroll_back_buffer(CHAR_HEIGHT);
cursor_y -= CHAR_HEIGHT;
graphics_mark_screen_dirty();
graphics_flip_buffer();
}
}
static void kconsole_putc_nolock(char c) {
if (!kconsole_active) return;
if (c == '\n') {
cursor_x = 10;
cursor_y += CHAR_HEIGHT;
kconsole_scroll();
graphics_flip_buffer();
return;
}
if (c == '\r') {
cursor_x = 10;
return;
}
if (c == '\t') {
cursor_x += CHAR_WIDTH * 4;
return;
}
// Draw character
draw_char_bitmap(cursor_x, cursor_y, c, text_color);
graphics_mark_screen_dirty();
cursor_x += CHAR_WIDTH;
if (cursor_x + CHAR_WIDTH >= get_screen_width() - 10) {
cursor_x = 10;
cursor_y += CHAR_HEIGHT;
kconsole_scroll();
}
}
void kconsole_putc(char c) {
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
kconsole_putc_nolock(c);
spinlock_release_irqrestore(&console_lock, flags);
}
void kconsole_write(const char *s) {
if (!s) return;
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
while (*s) {
kconsole_putc_nolock(*s++);
}
// Flip buffer after a write batch during boot
graphics_flip_buffer();
spinlock_release_irqrestore(&console_lock, flags);
}

13
src/core/kconsole.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef KCONSOLE_H
#define KCONSOLE_H
#include <stdint.h>
#include <stdbool.h>
void kconsole_init(void);
void kconsole_set_color(uint32_t color);
void kconsole_putc(char c);
void kconsole_write(const char *s);
void kconsole_set_active(bool active);
#endif // KCONSOLE_H

View File

@@ -155,3 +155,17 @@ void k_beep_process(void) {
}
}
char *k_strstr(const char *haystack, const char *needle) {
if (!*needle) return (char *)haystack;
for (; *haystack; haystack++) {
const char *h = haystack;
const char *n = needle;
while (*h && *n && *h == *n) {
h++;
n++;
}
if (!*n) return (char *)haystack;
}
return NULL;
}

View File

@@ -26,5 +26,6 @@ void k_reboot(void);
void k_shutdown(void);
void k_beep(int freq, int ms);
void k_beep_process(void);
char *k_strstr(const char *haystack, const char *needle);
#endif

View File

@@ -18,6 +18,8 @@
#include "fat32.h"
#include "tar.h"
#include "vfs.h"
#include "core/kconsole.h"
#include "core/kutils.h"
#include "memory_manager.h"
#include "platform.h"
#include "wallpaper.h"
@@ -26,8 +28,10 @@
#include "lapic.h"
#include "fs/sysfs.h"
#include "fs/procfs.h"
#include "fs/bootfs.h"
#include "sys/kernel_subsystem.h"
#include "sys/module_manager.h"
#include "sys/bootfs_state.h"
extern void sysfs_init_subsystems(void);
@@ -60,12 +64,26 @@ static volatile struct limine_smp_request smp_request = {
.flags = 0
};
__attribute__((used, section(".requests")))
static volatile struct limine_bootloader_info_request bootloader_info_request = {
.id = LIMINE_BOOTLOADER_INFO_REQUEST,
.revision = 0
};
__attribute__((used, section(".requests")))
static volatile struct limine_kernel_file_request kernel_file_request = {
.id = LIMINE_KERNEL_FILE_REQUEST,
.revision = 0
};
__attribute__((used, section(".requests_start")))
static volatile struct limine_request *const requests_start_marker[] = {
(struct limine_request *)&framebuffer_request,
(struct limine_request *)&memmap_request,
(struct limine_request *)&module_request,
(struct limine_request *)&smp_request,
(struct limine_request *)&bootloader_info_request,
(struct limine_request *)&kernel_file_request,
NULL
};
@@ -91,26 +109,70 @@ static void init_serial() {
outb(0x3F8 + 4, 0x0B);
}
static spinlock_t serial_lock = SPINLOCK_INIT;
void serial_write(const char *str) {
while (*str) {
uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
const char *p = str;
while (*p) {
char c = *p++;
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, *str++);
outb(0x3F8, c);
}
kconsole_write(str);
spinlock_release_irqrestore(&serial_lock, flags);
}
static void serial_write_num_locked(uint32_t n) {
if (n >= 10) serial_write_num_locked(n / 10);
char c = '0' + (n % 10);
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, c);
kconsole_putc(c);
}
void serial_write_num(uint32_t n) {
if (n >= 10) serial_write_num(n / 10);
uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
serial_write_num_locked(n);
spinlock_release_irqrestore(&serial_lock, flags);
}
static void serial_write_hex_locked(uint64_t n) {
char *hex = "0123456789ABCDEF";
if (n >= 16) serial_write_hex_locked(n / 16);
char c = hex[n % 16];
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, '0' + (n % 10));
outb(0x3F8, c);
kconsole_putc(c);
}
void serial_write_hex(uint64_t n) {
char *hex = "0123456789ABCDEF";
if (n >= 16) serial_write_hex(n / 16);
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, hex[n % 16]);
uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
serial_write_hex_locked(n);
spinlock_release_irqrestore(&serial_lock, flags);
}
void log_ok(const char *msg) {
serial_write("[ ");
kconsole_set_color(0xFF00FF00);
serial_write("OK");
kconsole_set_color(0xFFFFFFFF);
serial_write(" ] ");
serial_write(msg);
serial_write("\n");
}
void log_fail(const char *msg) {
serial_write("[ ");
kconsole_set_color(0xFFFF0000);
serial_write("FAIL");
kconsole_set_color(0xFFFFFFFF);
serial_write(" ] ");
serial_write(msg);
serial_write("\n");
}
// Kernel Entry Point
static void fat32_mkdir_recursive(const char *path) {
@@ -141,64 +203,73 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) {
init_serial();
vfs_init();
serial_write("\n[DEBUG] Entering kmain...\n");
serial_write("\n");
platform_init();
serial_write("[DEBUG] platform_init OK\n");
log_ok("Platform initialized");
extern uint64_t hhdm_offset;
extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base;
serial_write("[DEBUG] HHDM Offset: 0x");
serial_write("[INIT] HHDM Offset: 0x");
serial_write_hex(hhdm_offset);
serial_write("\n");
serial_write("[DEBUG] Kernel Phys: 0x");
serial_write("[INIT] Kernel Phys: 0x");
serial_write_hex(kernel_phys_base);
serial_write("\n");
serial_write("[DEBUG] Kernel Virt: 0x");
serial_write("[INIT] Kernel Virt: 0x");
serial_write_hex(kernel_virt_base);
serial_write("\n");
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);
serial_write("[DEBUG] memory_manager_init OK\n");
smp_init_bsp();
serial_write("[DEBUG] smp_init_bsp OK\n");
} else {
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
hcf();
}
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
serial_write("[DEBUG] No framebuffer! Halting.\n");
serial_write("[INIT] No framebuffer! Halting.\n");
hcf();
}
struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0];
graphics_init(fb);
serial_write("[DEBUG] graphics_init OK\n");
kconsole_init();
// Check for verbose boot flag
if (kernel_file_request.response != NULL && kernel_file_request.response->kernel_file != NULL) {
const char *cmdline = kernel_file_request.response->kernel_file->cmdline;
if (cmdline != NULL && k_strstr(cmdline, "-v") != NULL) {
kconsole_set_active(true);
}
}
log_ok("Graphics and Console ready");
if (memmap_request.response != NULL) {
memory_manager_init_from_memmap(memmap_request.response);
log_ok("Memory manager ready");
smp_init_bsp();
log_ok("SMP BSP initialized");
} else {
log_fail("No usable memory for heap! Check Limine memmap.");
hcf();
}
gdt_init();
serial_write("[DEBUG] gdt_init OK\n");
log_ok("GDT initialized");
paging_init();
serial_write("[DEBUG] paging_init OK\n");
log_ok("Paging ready");
syscall_init();
serial_write("[DEBUG] syscall_init OK\n");
log_ok("Syscalls ready");
idt_init();
idt_register_interrupts();
idt_load();
serial_write("[DEBUG] idt_init OK\n");
log_ok("IDT ready");
process_init();
fat32_init();
serial_write("[DEBUG] fat32_init OK\n");
log_ok("FAT32 ready");
fat32_mkdir("/bin");
fat32_mkdir("/Library");
fat32_mkdir("/Library/images");
@@ -206,19 +277,68 @@ 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);
bootfs_init();
if (bootloader_info_request.response != NULL) {
if (bootloader_info_request.response->name) {
k_strcpy(g_bootfs_state.bootloader_name, bootloader_info_request.response->name);
}
if (bootloader_info_request.response->version) {
k_strcpy(g_bootfs_state.bootloader_version, bootloader_info_request.response->version);
}
}
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: ");
serial_write_hex(g_bootfs_state.kernel_size);
serial_write(" bytes\n");
}
extern uint32_t wm_get_ticks(void);
g_bootfs_state.boot_time_ms = wm_get_ticks();
if (module_request.response != NULL) {
g_bootfs_state.num_modules = module_request.response->module_count;
serial_write("[INIT] Scanning modules for bootfs state...\n");
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;
if (fs_starts_with(path, "boot():")) path += 7;
else if (fs_starts_with(path, "boot:///")) path += 8;
int path_len = 0;
while (path[path_len]) path_len++;
serial_write("[INIT] Module: ");
serial_write(path);
serial_write(" (");
serial_write_hex(mod->size);
serial_write(" bytes)\n");
if (path_len >= 5 && path[path_len-4] == '.' && path[path_len-3] == 't' &&
path[path_len-2] == 'a' && path[path_len-1] == 'r') {
g_bootfs_state.initrd_size = mod->size;
serial_write("[INIT] -> Initrd detected\n");
}
}
}
vfs_mount("/boot", "bootfs", "bootfs", bootfs_get_ops(), NULL);
if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
log_fail("Limine module response NULL");
} else {
serial_write("[DEBUG] Limine Module Response found. Count: ");
serial_write_num(module_request.response->module_count);
serial_write("\n");
log_ok("Limine modules loaded");
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i];
@@ -230,7 +350,7 @@ void kmain(void) {
while(clean_path[len]) len++;
if (len >= 4 && clean_path[len-4] == '.' && clean_path[len-3] == 't' && clean_path[len-2] == 'a' && clean_path[len-1] == 'r') {
serial_write("[DEBUG] Parsing TAR initrd: ");
serial_write("[INIT] Parsing TAR initrd: ");
serial_write(clean_path);
serial_write("\n");
tar_parse(mod->address, mod->size);
@@ -252,15 +372,13 @@ 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("[DEBUG] Stack Alignment: 0x");
serial_write("[INIT] Stack Alignment: 0x");
serial_write_hex(current_rsp);
serial_write("\n");
@@ -270,17 +388,13 @@ 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);
serial_write("[DEBUG] SMP init complete, CPUs online: ");
serial_write_num(online);
serial_write("\n");
log_ok("SMP initialized");
} else {
serial_write("[DEBUG] No SMP response from bootloader\n");
serial_write("[INIT] No SMP response from bootloader\n");
smp_init(NULL);
}
@@ -288,6 +402,9 @@ void kmain(void) {
asm volatile("sti");
extern void bootfs_refresh_from_disk(void);
bootfs_refresh_from_disk();
while (1) {
wm_process_input();
wm_process_deferred_thumbs();

View File

@@ -15,7 +15,7 @@ static size_t man_strlen(const char *str) {
static void write_man_file(const char *name, const char *content) {
char path[128] = "/Library/man/";
int i = 15;
int i = 13;
while (*name) path[i++] = *name++;
path[i++] = '.';
path[i++] = 't';
@@ -56,6 +56,7 @@ void create_man_entries(void) {
write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into executables. (execute these with ./>file<)");
write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers.");
write_man_file("sysfetch", "SYSFETCH - Show OS information\n\nUsage: sysfetch\n\nDisplays system information in a neofetch-like layout. Configurable via /Library/conf/sysfetch.cfg.");
write_man_file("uname", "UNAME - Print system information\n\nUsage: uname [-amnoprsv]\n\nOptions:\n -a Print all information\n -s Kernel name\n -n Node name\n -r Kernel release\n -v Kernel build date and time\n -m Machine hardware name\n -p Processor type\n -o Operating system name");
write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics.");
write_man_file("pci_list", "PCI_LIST - Scan PCI bus\n\nUsage: pci_list\n\nScans the PCI bus and lists all detected hardware devices.");
write_man_file("reboot", "REBOOT - Restart system\n\nUsage: reboot\n\nRestarts the computer immediately.");
@@ -69,6 +70,7 @@ void create_man_entries(void) {
write_man_file("math", "MATH - Expression evaluator\n\nUsage: math <expression>\n\nEvaluates simple arithmetic expressions from the command line.");
write_man_file("viewer", "VIEWER - Image viewer\n\nUsage: viewer <file.ppm>\n\nA graphical application for viewing image files.");
write_man_file("settings", "SETTINGS - System settings\n\nUsage: settings\n\nOpens the graphical system configuration tool.");
write_man_file("2048", "2048 - Classic game\n\nUsage: 2048\n\nPlays the classic 2048 game.");
}
#endif

View File

@@ -5,6 +5,7 @@
#include "limine.h"
#include <stddef.h>
#include "platform.h"
#include "kutils.h"
static volatile struct limine_hhdm_request hhdm_request __attribute__((used, section(".requests"))) = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
@@ -80,3 +81,73 @@ void platform_get_cpu_vendor(char *vendor) {
*((uint32_t *)&p[8]) = ecx;
p[12] = '\0';
}
void platform_get_cpu_info(cpu_info_t *info) {
uint32_t eax, ebx, ecx, edx;
// CPUID leaf 1: basic feature information
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
info->stepping = eax & 0xF;
info->model = (eax >> 4) & 0xF;
info->family = (eax >> 8) & 0xF;
info->microcode = (ebx >> 8) & 0xFF;
info->flags = ((uint64_t)ecx << 32) | edx; // ECX and EDX contain feature flags
info->cache_size = (ebx >> 16) & 0xFF; // Cache line size in bytes
}
void platform_get_cpu_flags(char *flags_str) {
uint32_t eax, ebx, ecx, edx;
flags_str[0] = '\0';
// CPUID leaf 1
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
// ECX flags
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "sse3 ");
if (ecx & (1 << 1)) k_strcpy(flags_str + k_strlen(flags_str), "pclmulqdq ");
if (ecx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "monitor ");
if (ecx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "ssse3 ");
if (ecx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "sdbg ");
if (ecx & (1 << 12)) k_strcpy(flags_str + k_strlen(flags_str), "fma ");
if (ecx & (1 << 13)) k_strcpy(flags_str + k_strlen(flags_str), "cx16 ");
if (ecx & (1 << 19)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_1 ");
if (ecx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_2 ");
if (ecx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "popcnt ");
if (ecx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "aes ");
if (ecx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "xsave ");
if (ecx & (1 << 28)) k_strcpy(flags_str + k_strlen(flags_str), "avx ");
// EDX flags
if (edx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "fpu ");
if (edx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "pse ");
if (edx & (1 << 4)) k_strcpy(flags_str + k_strlen(flags_str), "tsc ");
if (edx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "pae ");
if (edx & (1 << 8)) k_strcpy(flags_str + k_strlen(flags_str), "cx8 ");
if (edx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "apic ");
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "sep ");
if (edx & (1 << 15)) k_strcpy(flags_str + k_strlen(flags_str), "cmov ");
if (edx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "mmx ");
if (edx & (1 << 24)) k_strcpy(flags_str + k_strlen(flags_str), "fxsr ");
if (edx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "sse ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "sse2 ");
// Extended leaf 0x80000001 for advanced flags
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0x80000001));
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "syscall ");
if (edx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "nx ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "pdpe1gb ");
if (edx & (1 << 27)) k_strcpy(flags_str + k_strlen(flags_str), "rdtscp ");
if (edx & (1 << 29)) k_strcpy(flags_str + k_strlen(flags_str), "lm ");
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "lahf_lm ");
if (ecx & (1 << 5)) k_strcpy(flags_str + k_strlen(flags_str), "abm ");
// Remove trailing space
int len = k_strlen(flags_str);
if (len > 0 && flags_str[len-1] == ' ') {
flags_str[len-1] = '\0';
}
}

View File

@@ -6,10 +6,21 @@
#include <stdint.h>
typedef struct {
uint32_t family;
uint32_t model;
uint32_t stepping;
uint32_t microcode;
uint64_t flags;
uint32_t cache_size;
} cpu_info_t;
void platform_init(void);
uint64_t p2v(uint64_t phys);
uint64_t v2p(uint64_t virt);
void platform_get_cpu_model(char *model);
void platform_get_cpu_vendor(char *vendor);
void platform_get_cpu_info(cpu_info_t *info);
void platform_get_cpu_flags(char *flags_str);
#endif

View File

@@ -13,10 +13,10 @@ void get_os_info(os_info_t *info) {
for (size_t i = 0; i < sizeof(os_info_t); i++) p[i] = 0;
const char *os_name = "BoredOS";
const char *os_version = "26.4";
const char *os_version = "26.4.2";
const char *os_codename = "Voyager";
const char *kernel_name = "Boredkernel";
const char *kernel_version = "4.0.0-stable";
const char *kernel_version = "4.1.0-stable";
const char *build_date = __DATE__;
const char *build_time = __TIME__;
const char *build_arch = "x86_64";

View File

@@ -8,6 +8,7 @@
#include "paging.h"
#include "io.h"
#include <stddef.h>
#include "../sys/spinlock.h"
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
@@ -30,6 +31,7 @@ typedef struct {
HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned
void *fis_base; // 256B, 256B aligned
HBA_CMD_TBL *cmd_tbl; // Command table for slot 0
spinlock_t lock; // Port-level lock for thread-safety
} ahci_port_state_t;
static ahci_port_state_t ports[MAX_AHCI_PORTS];
@@ -150,6 +152,7 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
HBA_PORT *port = ps->port;
// Clear any pending interrupts/errors
@@ -198,9 +201,8 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
while (timeout-- > 0) {
if (!(port->ci & (1 << slot))) break;
if (port->is & (1 << 30)) { // Task File Error
serial_write("[AHCI] Read error on port ");
serial_write_num(port_num);
serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
}
@@ -209,9 +211,11 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
serial_write("[AHCI] Read timeout on port ");
serial_write_num(port_num);
serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
spinlock_release_irqrestore(&ps->lock, rflags);
return 0;
}
@@ -220,6 +224,7 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
HBA_PORT *port = ps->port;
port->is = 0xFFFFFFFF;
@@ -266,6 +271,7 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
serial_write("[AHCI] Write error on port ");
serial_write_num(port_num);
serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
}
@@ -274,9 +280,11 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
serial_write("[AHCI] Write timeout on port ");
serial_write_num(port_num);
serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
spinlock_release_irqrestore(&ps->lock, rflags);
return 0;
}
@@ -387,9 +395,8 @@ void ahci_init(void) {
for (int i = 0; i < 32; i++) {
ports[i].active = false;
if (!(pi & (1 << i))) continue;
HBA_PORT *port = &abar->ports[i];
ports[i].lock = SPINLOCK_INIT;
int type = ahci_check_port_type(port);
if (type == 0) { // SATA drive

View File

@@ -21,6 +21,8 @@ static int next_sd_index = 0; // For sda, sdb, sdc...
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
extern void log_ok(const char *msg);
extern void log_fail(const char *msg);
// === String Helpers ===
@@ -306,14 +308,16 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
dm_strcpy(mount_path + 5, part->devname);
if (vfs_mount(mount_path, part->devname, "fat32", fat32_get_realfs_ops(), vol)) {
serial_write("[VFS] Mounted ");
serial_write(mount_path);
serial_write(" to VFS\n");
char ok_msg[64];
dm_strcpy(ok_msg, "Mounted ");
dm_strcpy(ok_msg + 8, mount_path);
log_ok(ok_msg);
wm_notify_fs_change();
} else {
serial_write("[VFS] Failed to mount ");
serial_write(mount_path);
serial_write("\n");
char fail_msg[64];
dm_strcpy(fail_msg, "Failed to mount ");
dm_strcpy(fail_msg + 16, mount_path);
log_fail(fail_msg);
}
}
}
@@ -499,7 +503,7 @@ void disk_manager_init(void) {
next_sd_index = 0;
next_drive_letter_idx = 0;
serial_write("[DISK] Disk manager initialized (VFS mode)\n");
log_ok("Disk manager ready");
// NOTE: Ramdisk (A:) is no longer registered here.
// RAMFS is managed directly by fat32.c and mounted at "/" via VFS.
}
@@ -509,12 +513,13 @@ void disk_manager_scan(void) {
ahci_init();
if (ahci_get_port_count() == 0) {
serial_write("[DISK] No AHCI ports found, falling back to legacy IDE PIO...\n");
serial_write("[DISK] No AHCI ports found, falling back to legacy IDE...\n");
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE Primary Master");
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE Primary Slave");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE Secondary Master");
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE Secondary Slave");
log_ok("IDE probing complete");
} else {
serial_write("[DISK] AHCI initialized successfully, skipping legacy IDE.\n");
log_ok("AHCI ports initialized, skipping IDE");
}
}

View File

@@ -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;
@@ -269,3 +259,7 @@ uint64_t mouse_handler(registers_t *regs) {
void ps2_init(void) {
mouse_init();
}
bool ps2_shift_pressed(void) {
return shift_pressed;
}

View File

@@ -13,4 +13,6 @@ uint64_t timer_handler(registers_t *regs);
uint64_t keyboard_handler(registers_t *regs);
uint64_t mouse_handler(registers_t *regs);
bool ps2_shift_pressed(void);
#endif

541
src/fs/bootfs.c Normal file
View File

@@ -0,0 +1,541 @@
// 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 "bootfs.h"
#include "../sys/bootfs_state.h"
#include "vfs.h"
#include "core/kutils.h"
#include "core/platform.h"
#include "core/kconsole.h"
#include "memory_manager.h"
extern void serial_write(const char *str);
extern void serial_write_hex(uint64_t value);
typedef struct {
char path[512];
int offset;
bool is_root;
bool is_metadata_dir;
} bootfs_handle_t;
static void* bootfs_open(void *fs_private, const char *path, const char *mode);
static void bootfs_close(void *fs_private, void *handle);
static int bootfs_read(void *fs_private, void *handle, void *buf, int size);
static int bootfs_write(void *fs_private, void *handle, const void *buf, int size);
static int bootfs_seek(void *fs_private, void *handle, int offset, int whence);
static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max);
static bool bootfs_mkdir(void *fs_private, const char *rel_path);
static bool bootfs_rmdir(void *fs_private, const char *rel_path);
static bool bootfs_unlink(void *fs_private, const char *rel_path);
static bool bootfs_rename(void *fs_private, const char *old_path, const char *new_path);
static bool bootfs_exists(void *fs_private, const char *rel_path);
static bool bootfs_is_dir(void *fs_private, const char *rel_path);
static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info);
static uint32_t bootfs_get_position(void *file_handle);
static uint32_t bootfs_get_size(void *file_handle);
static vfs_fs_ops_t bootfs_ops = {
.open = bootfs_open,
.close = bootfs_close,
.read = bootfs_read,
.write = bootfs_write,
.seek = bootfs_seek,
.readdir = bootfs_readdir,
.mkdir = bootfs_mkdir,
.rmdir = bootfs_rmdir,
.unlink = bootfs_unlink,
.rename = bootfs_rename,
.exists = bootfs_exists,
.is_dir = bootfs_is_dir,
.get_info = bootfs_get_info,
.get_position = bootfs_get_position,
.get_size = bootfs_get_size,
};
bootfs_state_t g_bootfs_state = {0};
static bool is_metadata_path(const char *path) {
if (!path) return false;
return k_strncmp(path, "metadata", 8) == 0;
}
static bool is_metadata_file(const char *path) {
if (k_strcmp(path, "metadata/boot_time") == 0) return true;
if (k_strcmp(path, "metadata/boot_flags") == 0) return true;
if (k_strcmp(path, "metadata/version") == 0) return true;
return false;
}
static void* bootfs_open(void *fs_private, const char *path, const char *mode) {
if (!path) path = "";
if (path[0] == '/') path++;
bootfs_handle_t *h = (bootfs_handle_t*)kmalloc(sizeof(bootfs_handle_t));
if (!h) return NULL;
k_memset(h, 0, sizeof(bootfs_handle_t));
k_strcpy(h->path, path);
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
} else if (is_metadata_path(path) && path[8] == '\0') {
h->is_metadata_dir = true;
}
return h;
}
static void bootfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
static int generate_metadata_content(const char *file, char *buffer, int max_size) {
if (!buffer || max_size <= 0) return 0;
buffer[0] = '\0';
int len = 0;
if (k_strcmp(file, "metadata/boot_time") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_strcpy(buffer, "Boot time: ");
char time_buf[32];
k_itoa(g_bootfs_state.boot_time_ms, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), " ms\nTicks: ");
k_itoa(ticks, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/version") == 0) {
k_strcpy(buffer, "Bootloader: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_name);
k_strcpy(buffer + k_strlen(buffer), "\nVersion: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_version);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/boot_flags") == 0) {
k_strcpy(buffer, "Boot flags: 0x");
char flags_buf[8];
uint8_t flags = g_bootfs_state.boot_flags;
int hex_digit = (flags >> 4) & 0xF;
flags_buf[0] = hex_digit < 10 ? '0' + hex_digit : 'a' + (hex_digit - 10);
hex_digit = flags & 0xF;
flags_buf[1] = hex_digit < 10 ? '0' + hex_digit : 'a' + (hex_digit - 10);
flags_buf[2] = '\n';
flags_buf[3] = '\0';
k_strcpy(buffer + k_strlen(buffer), flags_buf);
len = k_strlen(buffer);
}
return len;
}
static int bootfs_read(void *fs_private, void *handle, void *buf, int size) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h || !buf || size <= 0) return -1;
char *content_buffer = (char*)kmalloc(4096);
if (!content_buffer) return -1;
int content_len = 0;
if (k_strcmp(h->path, "limine.conf") == 0) {
k_memcpy(content_buffer, g_bootfs_state.limine_conf,
g_bootfs_state.limine_conf_len);
content_len = g_bootfs_state.limine_conf_len;
} else if (k_strcmp(h->path, "kernel") == 0) {
k_strcpy(content_buffer, "Kernel reference\nSize: ");
char size_buf[32];
k_itoa(g_bootfs_state.kernel_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (k_strcmp(h->path, "initrd") == 0) {
k_strcpy(content_buffer, "Initial ramdisk reference\nSize: ");
char size_buf[32];
k_itoa(g_bootfs_state.initrd_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (is_metadata_file(h->path)) {
content_len = generate_metadata_content(h->path, content_buffer, 4096);
} else {
kfree(content_buffer);
return -1;
}
// Handle offset and reading
if (h->offset >= content_len) {
kfree(content_buffer);
return 0;
}
int available = content_len - h->offset;
int read_size = (available < size) ? available : size;
k_memcpy(buf, content_buffer + h->offset, read_size);
h->offset += read_size;
kfree(content_buffer);
return read_size;
}
static int bootfs_write(void *fs_private, void *handle, const void *buf, int size) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h || !buf || size <= 0) return -1;
if (k_strcmp(h->path, "limine.conf") != 0) {
return -1;
}
int max_write = 2048 - h->offset;
if (max_write <= 0) return -1;
int write_size = (size < max_write) ? size : max_write;
k_memcpy(g_bootfs_state.limine_conf + h->offset, buf, write_size);
h->offset += write_size;
if (h->offset > g_bootfs_state.limine_conf_len) {
g_bootfs_state.limine_conf_len = h->offset;
}
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_write(vfs_file_t *file, const void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *fat_conf = vfs_open("/limine.conf", "w");
if (fat_conf) {
vfs_write(fat_conf, g_bootfs_state.limine_conf, g_bootfs_state.limine_conf_len);
vfs_close(fat_conf);
}
return write_size;
}
static int bootfs_seek(void *fs_private, void *handle, int offset, int whence) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h) return -1;
switch (whence) {
case 0: // SEEK_SET
h->offset = offset;
break;
case 1: // SEEK_CUR
h->offset += offset;
break;
case 2: // SEEK_END
return -1;
default:
return -1;
}
return h->offset;
}
static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max) {
if (!entries || max <= 0) return 0;
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
int count = 0;
if (rel_path[0] == '\0') {
if (count < max) {
k_strcpy(entries[count].name, "limine.conf");
entries[count].size = g_bootfs_state.limine_conf_len;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "kernel");
entries[count].size = g_bootfs_state.kernel_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "initrd");
entries[count].size = g_bootfs_state.initrd_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "metadata");
entries[count].size = 0;
entries[count].is_directory = 1;
count++;
}
}
else if (k_strcmp(rel_path, "metadata") == 0) {
const char *meta_files[] = {
"boot_time",
"boot_flags",
"version"
};
for (int i = 0; i < 3 && count < max; i++) {
k_strcpy(entries[count].name, meta_files[i]);
entries[count].size = 0;
entries[count].is_directory = 0;
count++;
}
}
return count;
}
static bool bootfs_mkdir(void *fs_private, const char *rel_path) {
return false;
}
static bool bootfs_rmdir(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (k_strcmp(rel_path, "metadata") == 0) {
return false; /* metadata directory is protected */
}
return false; /* no other directories to remove */
}
static bool bootfs_unlink(void *fs_private, const char *rel_path) {
if (!rel_path) return false;
if (rel_path[0] == '/') rel_path++;
/* Only limine.conf can be deleted */
if (k_strcmp(rel_path, "limine.conf") != 0) {
return false;
}
/* Clear the bootfs state */
g_bootfs_state.limine_conf[0] = '\0';
g_bootfs_state.limine_conf_len = 0;
/* Delete from partition */
extern bool vfs_delete(const char *path);
bool result = vfs_delete("/limine.conf");
if (result) {
serial_write("[BOOTFS] Deleted limine.conf from partition\n");
} else {
serial_write("[BOOTFS] Warning: Could not delete limine.conf from partition\n");
}
return result;
}
static bool bootfs_rename(void *fs_private, const char *old_path, const char *new_path) {
if (!old_path || !new_path) return false;
const char *old_rel = old_path;
const char *new_rel = new_path;
if (old_rel[0] == '/') old_rel++;
if (new_rel[0] == '/') new_rel++;
/* Only limine.conf can be renamed */
if (k_strcmp(old_rel, "limine.conf") != 0) {
return false;
}
/* kernel and initrd are protected */
if (k_strcmp(new_rel, "kernel") == 0 || k_strcmp(new_rel, "initrd") == 0) {
return false;
}
/* metadata directory is protected */
if (k_strncmp(new_rel, "metadata", 8) == 0) {
return false;
}
extern bool vfs_rename(const char *old_path, const char *new_path);
char new_partition_path[256];
k_strcpy(new_partition_path, "/");
/* Manually append new_rel to new_partition_path */
int path_len = 0;
while (new_partition_path[path_len]) path_len++;
int rel_len = 0;
while (new_rel[rel_len]) rel_len++;
if (path_len + rel_len >= 256) {
serial_write("[BOOTFS] Error: new path too long\n");
return false;
}
k_memcpy(new_partition_path + path_len, new_rel, rel_len + 1);
/* Rename on partition filesystem */
bool result = vfs_rename("/limine.conf", new_partition_path);
if (result) {
serial_write("[BOOTFS] Renamed limine.conf to ");
serial_write(new_rel);
serial_write("\n");
} else {
serial_write("[BOOTFS] Warning: Could not rename limine.conf to ");
serial_write(new_rel);
serial_write("\n");
}
return result;
}
static bool bootfs_exists(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (rel_path[0] == '\0') return true;
if (k_strcmp(rel_path, "limine.conf") == 0) return true;
if (k_strcmp(rel_path, "kernel") == 0) return true;
if (k_strcmp(rel_path, "initrd") == 0) return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
if (is_metadata_file(rel_path)) return true;
return false;
}
static bool bootfs_is_dir(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (rel_path[0] == '\0') return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
return false;
}
static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info) {
if (!info) return -1;
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
k_memset(info, 0, sizeof(vfs_dirent_t));
if (rel_path[0] == '\0') {
k_strcpy(info->name, "/");
info->is_directory = 1;
return 0;
}
if (k_strcmp(rel_path, "limine.conf") == 0) {
k_strcpy(info->name, "limine.conf");
info->size = g_bootfs_state.limine_conf_len;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "kernel") == 0) {
k_strcpy(info->name, "kernel");
info->size = g_bootfs_state.kernel_size;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "initrd") == 0) {
k_strcpy(info->name, "initrd");
info->size = g_bootfs_state.initrd_size;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "metadata") == 0) {
k_strcpy(info->name, "metadata");
info->is_directory = 1;
return 0;
}
if (is_metadata_file(rel_path)) {
char temp_buf[4096];
int len = generate_metadata_content(rel_path, temp_buf, 4096);
k_strcpy(info->name, rel_path + 9);
info->size = len;
info->is_directory = 0;
return 0;
}
return -1;
}
static uint32_t bootfs_get_position(void *file_handle) {
bootfs_handle_t *h = (bootfs_handle_t*)file_handle;
if (!h) return 0;
return h->offset;
}
static uint32_t bootfs_get_size(void *file_handle) {
bootfs_handle_t *h = (bootfs_handle_t*)file_handle;
if (!h) return 0;
if (k_strcmp(h->path, "limine.conf") == 0) {
return g_bootfs_state.limine_conf_len;
} else if (k_strcmp(h->path, "kernel") == 0) {
return g_bootfs_state.kernel_size;
} else if (k_strcmp(h->path, "initrd") == 0) {
return g_bootfs_state.initrd_size;
} else if (is_metadata_file(h->path)) {
char temp_buf[4096];
return generate_metadata_content(h->path, temp_buf, 4096);
}
return 0;
}
vfs_fs_ops_t* bootfs_get_ops(void) {
return &bootfs_ops;
}
void bootfs_state_init(void) {
k_memset(&g_bootfs_state, 0, sizeof(bootfs_state_t));
k_strcpy(g_bootfs_state.bootloader_name, "Limine");
k_strcpy(g_bootfs_state.bootloader_version, "6.0.0");
g_bootfs_state.limine_conf[0] = '\0';
g_bootfs_state.limine_conf_len = 0;
g_bootfs_state.kernel_size = 0;
g_bootfs_state.initrd_size = 0;
g_bootfs_state.boot_time_ms = 0;
}
void bootfs_init(void) {
bootfs_state_init();
}
void bootfs_refresh_from_disk(void) {
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_read(vfs_file_t *file, void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *boot_conf = vfs_open("/limine.conf", "r");
if (boot_conf) {
int bytes_read = vfs_read(boot_conf, g_bootfs_state.limine_conf, 2047);
if (bytes_read > 0) {
g_bootfs_state.limine_conf[bytes_read] = '\0';
g_bootfs_state.limine_conf_len = bytes_read;
serial_write("[BOOTFS] Loaded limine.conf from partition: ");
extern void serial_write_hex(uint64_t value);
serial_write_hex(bytes_read);
serial_write(" bytes\n");
}
vfs_close(boot_conf);
} else {
serial_write("[BOOTFS] Warning: /limine.conf not found on partition\n");
}
}

13
src/fs/bootfs.h Normal file
View File

@@ -0,0 +1,13 @@
// 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 BOOTFS_H
#define BOOTFS_H
#include "vfs.h"
void bootfs_init(void);
void bootfs_refresh_from_disk(void);
vfs_fs_ops_t* bootfs_get_ops(void);
#endif

View File

@@ -55,6 +55,7 @@ typedef struct {
bool mounted;
uint32_t cached_fat_sector;
uint8_t cached_fat_buf[512];
uint32_t last_allocated_cluster; // Hint for faster allocation
spinlock_t lock; // Per-volume lock for physical disk operations
} FAT32_Volume;
@@ -455,6 +456,7 @@ static bool realfs_mount_volume(FAT32_Volume *vol, Disk *disk) {
vol->total_sectors = bpb->total_sectors_32;
vol->mounted = true;
vol->cached_fat_sector = 0xFFFFFFFF;
vol->last_allocated_cluster = 2;
fs_serial_str("[FAT32] Mounted volume: /dev/");
fs_serial_str(disk->devname);
@@ -971,8 +973,10 @@ static bool realfs_find_contiguous_free(FAT32_Volume *vol, uint32_t dir_start_cl
}
static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
uint32_t current = 2;
uint32_t current = vol->last_allocated_cluster;
if (current < 2) current = 2;
uint32_t fat_entries = (vol->fat_size * 512) / 4;
uint32_t first_search = current;
// Skip cluster 2 as it's reserved for the root directory in FAT32
if (current == vol->root_cluster) current++;
@@ -998,10 +1002,13 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
if (vol->cached_fat_sector == sector) {
vol->cached_fat_sector = 0xFFFFFFFF;
}
vol->last_allocated_cluster = current;
kfree(fat_buf);
return current;
}
current++;
if (current >= fat_entries) current = 2;
if (current == first_search) break;
}
kfree(fat_buf);
return 0; // Full
@@ -1043,11 +1050,31 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
while (bytes_written < size) {
uint32_t offset = handle->position % cluster_size;
if (offset == 0 && handle->position > 0) {
uint32_t next = realfs_next_cluster(vol, handle->cluster);
if (next >= 0x0FFFFFF8) {
uint32_t new_cluster = realfs_allocate_cluster(vol);
if (new_cluster == 0) break;
uint32_t fs = vol->fat_begin_lba + (handle->cluster * 4) / 512;
uint32_t fo = (handle->cluster * 4) % 512;
uint8_t fbuf[512];
if (vol->disk->read_sector(vol->disk, fs, fbuf) == 0) {
uint32_t old = *(uint32_t*)&fbuf[fo];
*(uint32_t*)&fbuf[fo] = (old & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fs, fbuf);
if (vol->cached_fat_sector == fs) vol->cached_fat_sector = 0xFFFFFFFF;
}
next = new_cluster;
}
handle->cluster = next;
}
// Always zero the buffer first to ensure clean state
for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0;
// If we're in the middle of a cluster, read the existing data first
if (offset > 0) {
if (offset > 0 || (handle->position < handle->size)) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
}
@@ -1065,27 +1092,6 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
bytes_written += to_copy;
handle->position += to_copy;
if (handle->position > handle->size) handle->size = handle->position;
if (handle->position % cluster_size == 0 && bytes_written < size) {
uint32_t next = realfs_next_cluster(vol, handle->cluster);
if (next >= 0x0FFFFFF8) {
uint32_t new_cluster = realfs_allocate_cluster(vol);
if (new_cluster == 0) break;
// Link current to new
uint32_t fs = vol->fat_begin_lba + (handle->cluster * 4) / 512;
uint32_t fo = (handle->cluster * 4) % 512;
uint8_t fbuf[512];
if (vol->disk->read_sector(vol->disk, fs, fbuf) == 0) {
uint32_t old = *(uint32_t*)&fbuf[fo];
*(uint32_t*)&fbuf[fo] = (old & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fs, fbuf);
if (vol->cached_fat_sector == fs) vol->cached_fat_sector = 0xFFFFFFFF;
}
next = new_cluster;
}
handle->cluster = next;
}
}
if (bytes_written > 0) realfs_update_dir_entry_size(vol, handle);
@@ -1646,12 +1652,22 @@ static int vfs_realfs_read(void *fs_private, void *file_handle, void *buf, int s
uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size);
if (!cluster_buf) return -1;
int total_read = 0;
while (total_read < size) {
int to_read = size - total_read;
if (to_read > (int)cluster_size) to_read = (int)cluster_size;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
int ret = realfs_read_file(handle, buf, size, cluster_buf);
int ret = realfs_read_file(handle, (uint8_t*)buf + total_read, to_read, cluster_buf);
spinlock_release_irqrestore(&vol->lock, rflags);
if (ret <= 0) break;
total_read += ret;
if (ret < to_read) break;
}
kfree(cluster_buf);
return ret;
return total_read;
}
static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf, int size) {
@@ -1664,12 +1680,22 @@ static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf
uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size);
if (!cluster_buf) return -1;
int total_written = 0;
while (total_written < size) {
int to_write = size - total_written;
if (to_write > (int)cluster_size) to_write = (int)cluster_size;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
int ret = realfs_write_file(handle, buf, size, cluster_buf);
int ret = realfs_write_file(handle, (const uint8_t*)buf + total_written, to_write, cluster_buf);
spinlock_release_irqrestore(&vol->lock, rflags);
if (ret <= 0) break;
total_written += ret;
if (ret < to_write) break;
}
kfree(cluster_buf);
return ret;
return total_written;
}
static int vfs_realfs_seek(void *fs_private, void *file_handle, int offset, int whence) {

View File

@@ -4,6 +4,7 @@
#include "../dev/disk.h"
#include "memory_manager.h"
#include "core/kutils.h"
#include "core/platform.h"
typedef struct {
uint32_t pid;
@@ -55,7 +56,8 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h) return -1;
char out[1024];
char *out = (char*)kmalloc(16384);
if (!out) return -1;
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
@@ -88,34 +90,173 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *model);
char model[64];
platform_get_cpu_model(model);
extern void platform_get_cpu_vendor(char *vendor);
extern void platform_get_cpu_info(cpu_info_t *info);
extern void platform_get_cpu_flags(char *flags_str);
k_strcpy(out, "Processor: ");
char model[64];
char vendor[16];
char flags[1024];
cpu_info_t info;
platform_get_cpu_model(model);
platform_get_cpu_vendor(vendor);
platform_get_cpu_info(&info);
platform_get_cpu_flags(flags);
uint32_t cpu_count = smp_cpu_count();
out[0] = '\0';
// Output info for each processor
for (uint32_t i = 0; i < cpu_count; i++) {
char buf[32];
k_strcpy(out + k_strlen(out), "processor\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "vendor_id\t: ");
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu family\t: ");
k_itoa(info.family, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model\t\t: ");
k_itoa(info.model, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model name\t: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\nCores: ");
char c_s[16]; k_itoa(smp_cpu_count(), c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\nArchitecture: x86_64\n");
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "stepping\t: ");
k_itoa(info.stepping, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "microcode\t: 0x");
char hex[16];
int temp = info.microcode;
int hex_pos = 0;
for (int j = 7; j >= 0; j--) {
int digit = (temp >> (j * 4)) & 0xF;
hex[hex_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10);
}
hex[hex_pos] = '\0';
k_strcpy(out + k_strlen(out), hex);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cache size\t: ");
k_itoa(info.cache_size, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), " KB\n");
k_strcpy(out + k_strlen(out), "physical id\t: 0\n");
k_strcpy(out + k_strlen(out), "siblings\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "core id\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu cores\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "apicid\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "initial apicid\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "fpu\t\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu_exception\t: yes\n");
k_strcpy(out + k_strlen(out), "cpuid level\t: 13\n");
k_strcpy(out + k_strlen(out), "wp\t\t: yes\n");
k_strcpy(out + k_strlen(out), "flags\t\t: ");
k_strcpy(out + k_strlen(out), flags);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "bugs\t\t: \n");
k_strcpy(out + k_strlen(out), "bogomips\t: 4800.00\n");
if (i < cpu_count - 1) {
k_strcpy(out + k_strlen(out), "\n");
}
}
} else if (k_strcmp(h->type, "meminfo") == 0) {
extern MemStats memory_get_stats(void);
MemStats stats = memory_get_stats();
k_strcpy(out, "MemTotal: ");
char m_s[32]; k_itoa(stats.total_memory / 1024, m_s);
char m_s[32];
k_strcpy(out, "MemTotal:\t");
k_itoa(stats.total_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemFree: ");
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemFree:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemUsed: ");
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemAvailable:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "Buffers:\t0 kB\n");
k_strcpy(out + k_strlen(out), "Cached:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "MemUsed:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nPeak: ");
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemPeak:\t");
k_itoa(stats.peak_memory_used / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nBlocks: ");
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "SwapTotal:\t0 kB\n");
k_strcpy(out + k_strlen(out), "SwapFree:\t0 kB\n");
k_strcpy(out + k_strlen(out), "Dirty:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Writeback:\t0 kB\n");
k_strcpy(out + k_strlen(out), "AnonPages:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "Mapped:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Shmem:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Blocks:\t\t");
k_itoa(stats.allocated_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "FreeBlocks:\t");
k_itoa(stats.free_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "Fragmentation:\t");
k_itoa(stats.fragmentation_percent, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "%\n");
@@ -123,24 +264,42 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
k_strcpy(out, "Block Devices:\n");
out[0] = '\0';
k_strcpy(out, "Character devices:\n");
k_strcpy(out + k_strlen(out), " 1 mem\n");
k_strcpy(out + k_strlen(out), " 4 tty\n");
k_strcpy(out + k_strlen(out), " 5 cua\n");
k_strcpy(out + k_strlen(out), " 7 vcs\n");
k_strcpy(out + k_strlen(out), " 8 stdin\n");
k_strcpy(out + k_strlen(out), " 13 input\n");
k_strcpy(out + k_strlen(out), " 14 sound\n");
k_strcpy(out + k_strlen(out), " 29 fb\n");
k_strcpy(out + k_strlen(out), "189 usb\n\n");
k_strcpy(out + k_strlen(out), "Block devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
k_strcpy(out + k_strlen(out), " - ");
if (d && !d->is_partition) {
k_strcpy(out + k_strlen(out), " 8 ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), "\n");
}
}
k_strcpy(out + k_strlen(out), " 11 sr\n");
k_strcpy(out + k_strlen(out), "253 virtblk\n");
}
}
else {
process_t *proc = process_get_by_pid(h->pid);
if (!proc) return -1;
if (!proc) { kfree(out); return -1; }
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);
@@ -165,13 +324,14 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
}
int len = k_strlen(out);
if (h->offset >= len) return 0;
if (h->offset >= len) { kfree(out); return 0; }
int to_copy = len - h->offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + h->offset, to_copy);
h->offset += to_copy;
kfree(out);
return to_copy;
}
@@ -230,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;

View File

@@ -189,7 +189,7 @@ void vfs_init(void) {
}
mount_count = 0;
serial_write("[VFS] Virtual File System initialized\n");
serial_write("[VFS] Ready\n");
}
// ===============

4
src/library/bsh/boot.bsh Normal file
View 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
View 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

View File

@@ -0,0 +1,5 @@
# BoredShell startup script
# Runs for interactive shells.
# Example:
# echo "Welcome to BoredShell"
sysfetch

View File

@@ -479,7 +479,7 @@ void memory_validate(void) {
}
if (errors == 0) {
cmd_write("Memory validation: OK\n");
cmd_write("Memory validation: [OK]\n");
} else {
cmd_write("Memory validation failed with ");
cmd_write_int(errors);

View File

@@ -1133,7 +1133,6 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
(cpcb->remote_port == port) &&
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
/* linux returns EISCONN here, but ERR_USE should be OK for us */
return ERR_USE;
}
}

View File

@@ -88,7 +88,7 @@ int network_init(void) {
ipv4_address_t ip;
network_get_ipv4_address(&ip);
extern void serial_write(const char *str);
serial_write("[NETWORK] IP Assigned: ");
serial_write("[NET] IP Assigned: ");
char buf[32];
k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write(".");
k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write(".");
@@ -96,7 +96,7 @@ int network_init(void) {
k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n");
} else {
extern void serial_write(const char *str);
serial_write("[NETWORK] DHCP Failed during init\n");
serial_write("[NET] DHCP Failed during init\n");
}
// Set default DNS server (1.1.1.1)

24
src/sys/bootfs_state.h Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
#ifndef BOOTFS_STATE_H
#define BOOTFS_STATE_H
#include <stdint.h>
#include <stdbool.h>
typedef struct {
char bootloader_name[64];
char bootloader_version[64];
uint64_t boot_time_ms;
uint8_t boot_flags;
char limine_conf[2048];
int limine_conf_len;
uint32_t num_modules;
uint32_t kernel_size;
uint32_t initrd_size;
} bootfs_state_t;
extern bootfs_state_t g_bootfs_state;
void bootfs_state_init(void);
#endif

View File

@@ -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
View 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) {
}

477
src/sys/file_index.c Normal file
View File

@@ -0,0 +1,477 @@
// 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 "file_index.h"
#include "vfs.h"
#include "memory_manager.h"
#include "spinlock.h"
#include <stddef.h>
static file_index_t g_file_index = {0};
static spinlock_t g_index_lock = SPINLOCK_INIT;
static bool g_index_valid = false;
static int str_len(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
static void str_copy(char *d, const char *s) {
while ((*d++ = *s++));
}
static int str_cmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static void str_cat(char *d, const char *s) {
while (*d) d++;
str_copy(d, s);
}
static bool str_starts_with(const char *str, const char *prefix) {
while (*prefix) {
if (*str++ != *prefix++) return false;
}
return true;
}
static int fuzzy_match_score(const char *query, const char *filename) {
if (!query || !filename) return 0;
int score = 0;
int query_idx = 0;
int consecutive = 0;
for (int i = 0; filename[i] && query_idx < 256; i++) {
char fc = filename[i];
char qc = query[query_idx];
if (fc >= 'A' && fc <= 'Z') fc += 32;
if (qc >= 'A' && qc <= 'Z') qc += 32;
if (fc == qc) {
score += 10;
consecutive++;
if (consecutive > 1) score += 5;
query_idx++;
} else {
consecutive = 0;
}
}
if (query_idx < str_len(query)) {
return 0;
}
if (str_starts_with(filename, query)) {
score += 20;
}
return score;
}
static void index_walk_directory(const char *path, int depth) {
if (depth > 16 || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
return;
}
if (str_starts_with(path, "/proc") ||
str_starts_with(path, "/sys") ||
str_starts_with(path, "/dev")) {
return;
}
vfs_dirent_t *entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * 1024);
if (!entries) {
return;
}
int count = vfs_list_directory(path, entries, 1024);
if (count <= 0 || count > 1024) {
kfree(entries);
return;
}
for (int i = 0; i < count; i++) {
if (g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
break;
}
vfs_dirent_t *entry = &entries[i];
if (!entry) continue;
if (!entry->name || entry->name[0] == 0) {
continue;
}
if (str_cmp(entry->name, ".color") == 0 ||
str_cmp(entry->name, ".origin") == 0 ||
str_cmp(entry->name, ".") == 0 ||
str_cmp(entry->name, "..") == 0) {
continue;
}
char full_path[FILE_INDEX_MAX_PATH];
int path_len = 0;
for (int j = 0; path[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
full_path[path_len++] = path[j];
}
if (path_len > 0 && full_path[path_len - 1] != '/' && path_len < FILE_INDEX_MAX_PATH - 1) {
full_path[path_len++] = '/';
}
for (int j = 0; entry->name[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
full_path[path_len++] = entry->name[j];
}
full_path[path_len] = 0;
if (path_len >= FILE_INDEX_MAX_PATH - 1) {
continue;
}
file_index_entry_t *idx_entry = &g_file_index.entries[g_file_index.count];
str_copy(idx_entry->path, full_path);
idx_entry->size = entry->size;
idx_entry->mod_time_low = entry->write_date;
idx_entry->mod_time_high = entry->write_time;
idx_entry->is_directory = entry->is_directory;
g_file_index.count++;
if (entry->is_directory && !str_starts_with(full_path, "/proc") &&
!str_starts_with(full_path, "/sys") && !str_starts_with(full_path, "/dev")) {
index_walk_directory(full_path, depth + 1);
}
}
kfree(entries);
}
void file_index_init(void) {
g_file_index.count = 0;
g_file_index.capacity = FILE_INDEX_MAX_ENTRIES;
g_index_valid = false;
}
bool file_index_build(void) {
g_file_index.count = 0;
const char *safe_paths[] = {"/root", "/bin", "/Library", "/docs", NULL};
for (int p = 0; safe_paths[p] != NULL && g_file_index.count < FILE_INDEX_MAX_ENTRIES; p++) {
index_walk_directory(safe_paths[p], 0);
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_index_valid = true;
spinlock_release_irqrestore(&g_index_lock, flags);
file_index_save();
return true;
}
bool file_index_load(void) {
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "r");
if (!file) {
return false;
}
uint32_t version = 0;
if (vfs_read(file, &version, sizeof(version)) != sizeof(version)) {
vfs_close(file);
return false;
}
if (version != FILE_INDEX_VERSION) {
vfs_close(file);
return false;
}
int count = 0;
if (vfs_read(file, &count, sizeof(count)) != sizeof(count)) {
vfs_close(file);
return false;
}
if (count < 0 || count > FILE_INDEX_MAX_ENTRIES) {
vfs_close(file);
return false;
}
file_index_entry_t temp_entries[FILE_INDEX_MAX_ENTRIES];
for (int i = 0; i < count; i++) {
file_index_entry_t *entry = &temp_entries[i];
int bytes_read = vfs_read(file, entry->path, FILE_INDEX_MAX_PATH);
if (bytes_read != FILE_INDEX_MAX_PATH) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
vfs_close(file);
return false;
}
}
vfs_close(file);
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_file_index.count = 0;
for (int i = 0; i < count; i++) {
g_file_index.entries[i] = temp_entries[i];
g_file_index.count++;
}
g_index_valid = true;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
bool file_index_save(void) {
if (!vfs_mkdir("/Library")) {
}
if (!vfs_mkdir("/Library/Index")) {
}
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "w");
if (!file) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int count = g_file_index.count;
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
for (int i = 0; i < count; i++) {
entries[i] = g_file_index.entries[i];
}
spinlock_release_irqrestore(&g_index_lock, flags);
uint32_t version = FILE_INDEX_VERSION;
if (vfs_write(file, &version, sizeof(version)) != sizeof(version)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &count, sizeof(count)) != sizeof(count)) {
vfs_close(file);
return false;
}
for (int i = 0; i < count; i++) {
file_index_entry_t *entry = &entries[i];
if (vfs_write(file, entry->path, FILE_INDEX_MAX_PATH) != FILE_INDEX_MAX_PATH) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
vfs_close(file);
return false;
}
}
vfs_close(file);
return true;
}
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results) {
if (!query || !results || max_results <= 0) {
return 0;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int result_count = 0;
for (int i = 0; i < g_file_index.count && result_count < max_results; i++) {
if (i < 0 || i >= FILE_INDEX_MAX_ENTRIES) {
break;
}
const char *path = g_file_index.entries[i].path;
if (!path || path[0] == 0) {
continue;
}
const char *filename = path;
for (int j = 0; path[j]; j++) {
if (path[j] == '/') {
filename = &path[j + 1];
}
}
if (!filename || filename[0] == 0) {
continue;
}
int score = fuzzy_match_score(query, filename);
if (score > 0) {
results[result_count].entry = g_file_index.entries[i];
results[result_count].match_score = score;
result_count++;
}
}
spinlock_release_irqrestore(&g_index_lock, flags);
for (int i = 0; i < result_count; i++) {
for (int j = i + 1; j < result_count; j++) {
if (results[j].match_score > results[i].match_score) {
file_index_result_t tmp = results[i];
results[i] = results[j];
results[j] = tmp;
}
}
}
return result_count;
}
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low,
uint32_t mod_time_high, bool is_dir) {
if (!path || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
for (int i = 0; i < g_file_index.count; i++) {
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
g_file_index.entries[i].size = size;
g_file_index.entries[i].mod_time_low = mod_time_low;
g_file_index.entries[i].mod_time_high = mod_time_high;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
}
file_index_entry_t *entry = &g_file_index.entries[g_file_index.count];
str_copy(entry->path, path);
entry->size = size;
entry->mod_time_low = mod_time_low;
entry->mod_time_high = mod_time_high;
entry->is_directory = is_dir;
g_file_index.count++;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
bool file_index_remove_entry(const char *path) {
if (!path) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
for (int i = 0; i < g_file_index.count; i++) {
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
for (int j = i; j < g_file_index.count - 1; j++) {
g_file_index.entries[j] = g_file_index.entries[j + 1];
}
g_file_index.count--;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
}
spinlock_release_irqrestore(&g_index_lock, flags);
return false;
}
int file_index_get_entry_count(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int count = g_file_index.count;
spinlock_release_irqrestore(&g_index_lock, flags);
return count;
}
void file_index_clear(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_file_index.count = 0;
g_index_valid = false;
spinlock_release_irqrestore(&g_index_lock, flags);
}
void file_index_invalidate_cache(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_index_valid = false;
spinlock_release_irqrestore(&g_index_lock, flags);
}
bool file_index_is_valid(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
bool valid = g_index_valid;
spinlock_release_irqrestore(&g_index_lock, flags);
return valid;
}

50
src/sys/file_index.h Normal file
View File

@@ -0,0 +1,50 @@
// 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 FILE_INDEX_H
#define FILE_INDEX_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define FILE_INDEX_MAX_ENTRIES 50000
#define FILE_INDEX_MAX_PATH 1024
#define FILE_INDEX_CACHE_PATH "/Library/Index/file_index.dat"
#define FILE_INDEX_VERSION 1
typedef struct {
char path[FILE_INDEX_MAX_PATH];
uint32_t size;
uint32_t mod_time_low;
uint32_t mod_time_high;
bool is_directory;
} file_index_entry_t;
typedef struct {
file_index_entry_t entry;
int match_score;
} file_index_result_t;
typedef struct {
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
int count;
int capacity;
} file_index_t;
void file_index_init(void);
bool file_index_build(void);
bool file_index_load(void);
bool file_index_save(void);
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results);
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low, uint32_t mod_time_high, bool is_dir);
bool file_index_remove_entry(const char *path);
int file_index_get_entry_count(void);
void file_index_clear(void);
void file_index_invalidate_cache(void);
bool file_index_is_valid(void);
#endif

View File

@@ -140,12 +140,12 @@ static void pic_remap(void) {
static void pit_setup(void) {
uint16_t divisor = 1193182 / 60; // ~60Hz
// Send command byte
outb(0x43, 0x36); // Channel 0, lobyte/hibyte, mode 3 (square wave), binary
// Mode 2: Rate Generator (more appropriate for periodic interrupts)
outb(0x43, 0x34); io_wait(); // Channel 0, lobyte/hibyte, mode 2, binary
// Send divisor
outb(0x40, divisor & 0xFF);
outb(0x40, (divisor >> 8) & 0xFF);
outb(0x40, divisor & 0xFF); io_wait();
outb(0x40, (divisor >> 8) & 0xFF); io_wait();
}
void idt_init(void) {

View File

@@ -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] = '/';
@@ -175,7 +179,7 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
return new_proc;
}
process_t* process_create_elf(const char* filepath, const char* args_str) {
process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id) {
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
process_t *new_proc = NULL;
@@ -206,16 +210,27 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
new_proc->ui_window = NULL;
new_proc->heap_start = 0x20000000; // 512MB mark
new_proc->heap_end = 0x20000000;
new_proc->is_terminal_proc = false;
new_proc->is_terminal_proc = terminal_proc;
new_proc->tty_id = tty_id;
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;
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys, &elf_load_size);
if (entry_point == 0) {
serial_write("[PROCESS] Failed to load ELF: ");
serial_write("[PROC] Failed to load ELF: ");
serial_write(filepath);
serial_write("\n");
// We technically leak the page table here, but let's ignore cleanup for now
return NULL;
}
@@ -371,7 +386,7 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
current_process[0]->next = new_proc;
spinlock_release_irqrestore(&runqueue_lock, rflags);
serial_write("[PROCESS] Spawned ELF Executable: ");
serial_write("[PROC] Exec: ");
serial_write(filepath);
serial_write("\n");
return new_proc;
@@ -426,6 +441,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 +513,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 +575,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 +615,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 +643,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 +664,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 +733,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) {

View File

@@ -43,6 +43,8 @@ typedef struct process {
void *user_stack_alloc;
bool is_terminal_proc;
int tty_id;
bool kill_pending;
struct process *next;
@@ -67,7 +69,7 @@ typedef struct {
void process_init(void);
process_t* process_create(void (*entry_point)(void), bool is_user);
process_t* process_create_elf(const char* filepath, const char* args_str);
process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id);
process_t* process_get_current(void);
void process_set_current_for_cpu(uint32_t cpu_id, process_t* p);
process_t* process_get_current_for_cpu(uint32_t cpu_id);
@@ -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);

View File

@@ -31,24 +31,24 @@ static inline void wrmsr(uint32_t msr, uint64_t value) {
}
static uint32_t read_lapic_id(void) {
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
return (ebx >> 24) & 0xFF;
extern uint64_t hhdm_offset;
volatile uint32_t *lapic = (volatile uint32_t *)(hhdm_offset + 0xFEE00000ULL);
return (lapic[0x020 / 4] >> 24) & 0xFF;
}
uint32_t smp_this_cpu_id(void) {
if (total_cpus <= 1) return 0;
// Use GS-based self-pointer to get the structure first
cpu_state_t *state;
asm volatile("movq %%gs:0, %0" : "=r"(state) : : "memory");
if (state) return state->cpu_id;
if (!cpu_states || total_cpus == 0) return 0;
uint32_t lapic = read_lapic_id();
if (lapic == bsp_lapic_id) return 0;
cpu_state_t *state = NULL;
asm volatile("movq %%gs:0, %0" : "=r"(state) : : "memory");
if (state && state->lapic_id == lapic) return state->cpu_id;
for (uint32_t i = 0; i < total_cpus; i++) {
if (cpu_states[i].lapic_id == lapic) return i;
if (cpu_states[i].online && cpu_states[i].lapic_id == lapic) return i;
}
return 0; // Fallback to BSP
return 0;
}
uint32_t smp_cpu_count(void) {
@@ -123,7 +123,8 @@ static void ap_entry(struct limine_smp_info *info) {
void smp_init_bsp(void) {
static cpu_state_t bsp_state_static = {0};
bsp_state_static.cpu_id = 0;
bsp_state_static.lapic_id = read_lapic_id();
bsp_lapic_id = read_lapic_id();
bsp_state_static.lapic_id = bsp_lapic_id;
bsp_state_static.self = &bsp_state_static;
bsp_state_static.online = true;

View File

@@ -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);
}
}
@@ -196,19 +207,36 @@ 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);
cmd_write_len((const char*)arg2, (size_t)arg3);
process_t *proc = process_get_current();
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();
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;
serial_write("[WM] CreateWindow: ");
serial_write(title ? title : "Unknown");
serial_write("\n");
uint64_t *u_params = (uint64_t *)arg3;
if (!u_params) {
serial_write("Kernel: Error - params is NULL\n");
serial_write("[WM] Error - params is NULL\n");
return 0;
}
@@ -216,14 +244,14 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
uint64_t params[4];
for (int i = 0; i < 4; i++) params[i] = u_params[i];
serial_write("Kernel: Window params copied.\n");
// params verified
Window *win = kmalloc(sizeof(Window));
if (!win) {
serial_write("Kernel: Error - kmalloc failed for Window\n");
serial_write("[WM] Error - kmalloc failed for Window\n");
return 0;
}
serial_write("Kernel: Window allocated.\n");
// win allocated
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win, 0, sizeof(Window));
@@ -240,11 +268,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
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");
serial_write("[WM] Warning: kernel_title kmalloc failed\n");
}
// Basic initialization
@@ -254,7 +279,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->w = (int)params[2];
win->h = (int)params[3];
serial_write("Kernel: Init win dims.\n");
// dims ready
// Sanity checks for dimensions
if (win->w <= 0 || win->w > 4096) win->w = 400;
@@ -268,7 +293,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->font = NULL;
win->lock = SPINLOCK_INIT;
serial_write("Kernel: Dims initialized.\n");
// ready
size_t pixel_size = 0;
// Safe allocation
@@ -283,7 +308,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->comp_pixels = kmalloc(pixel_size);
}
serial_write("Kernel: Buffers allocated.\n");
// buffs ok
if (win->pixels) {
extern void mem_memset(void *dest, int val, size_t len);
@@ -294,7 +319,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
mem_memset(win->comp_pixels, 0, pixel_size);
}
serial_write("Kernel: Buffers cleared.\n");
serial_write("[WM] Buffers ready\n");
// Set callbacks
win->paint = user_window_paint;
@@ -307,6 +332,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
proc->ui_window = win;
wm_add_window(win);
wm_mark_dirty(0, 0, get_screen_width(), 30);
return (uint64_t)win;
} else if (cmd == GUI_CMD_DRAW_RECT) {
@@ -415,11 +441,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, font->pixel_height);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', font->pixel_height) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
}
}
} else {
draw_string(ux, uy, kernel_str, color);
}
@@ -430,11 +464,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, font->pixel_height);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', font->pixel_height) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
}
}
} else {
draw_string(win->x + ux, win->y + uy, kernel_str, color);
}
@@ -519,11 +561,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
}
} else {
draw_string_scaled(ux, uy, kernel_str, color, scale);
}
@@ -534,11 +584,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
}
} else {
draw_string_scaled(win->x + ux, win->y + uy, kernel_str, color, scale);
}
@@ -595,12 +653,20 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t));
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
}
} else {
draw_string_scaled_sloped(ux, uy, kernel_str, color, scale, slope);
}
@@ -611,12 +677,20 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t));
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\n') {
cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
}
} else {
draw_string_scaled_sloped(win->x + ux, win->y + uy, kernel_str, color, scale, slope);
}
@@ -752,7 +826,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
Window *win = (Window *)arg2;
if (win) {
extern void serial_write(const char *str);
serial_write("Kernel: Setting window resizable to ");
serial_write("[WM] Resizable: ");
serial_write(arg3 ? "true\n" : "false\n");
win->resizable = (arg3 != 0);
}
@@ -1204,6 +1278,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
@@ -1324,6 +1427,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;
}
bool terminal_proc = (flags & SPAWN_FLAG_TERMINAL) != 0;
int effective_tty = -1;
if (flags & SPAWN_FLAG_TTY_ID) effective_tty = tty_id;
else if (flags & SPAWN_FLAG_INHERIT_TTY) effective_tty = proc ? proc->tty_id : -1;
process_t *child = process_create_elf(path_buf, args_ptr, terminal_proc, effective_tty);
if (!child) return -1;
return (uint64_t)child->pid;
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3;

View File

@@ -7,6 +7,7 @@
#include "core/kutils.h"
#include "wm/graphics.h"
#include "core/platform.h"
#include "dev/disk.h"
// --- Helper: itoa ---
static void sys_itoa(int n, char *s) {
@@ -131,15 +132,172 @@ static int read_pci_bus(char *buf, int size, int offset) {
// --- CPU System Implementation ---
static int read_cpu_info(char *buf, int size, int offset) {
char *out = (char*)kmalloc(16384);
if (!out) return 0;
out[0] = 0;
char vendor[16];
char model[64];
char flags[1024];
cpu_info_t info;
platform_get_cpu_vendor(vendor);
platform_get_cpu_model(model);
platform_get_cpu_info(&info);
platform_get_cpu_flags(flags);
uint32_t cpu_count = smp_cpu_count();
for (uint32_t i = 0; i < cpu_count; i++) {
char c_s[32];
k_strcpy(out + k_strlen(out), "processor\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "vendor_id\t: ");
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu family\t: ");
k_itoa(info.family, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model\t\t: ");
k_itoa(info.model, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model name\t: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "stepping\t: ");
k_itoa(info.stepping, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "microcode\t: 0x");
char hex[16];
int temp = info.microcode;
int hex_pos = 0;
for (int j = 7; j >= 0; j--) {
int digit = (temp >> (j * 4)) & 0xF;
hex[hex_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10);
}
hex[hex_pos] = '\0';
k_strcpy(out + k_strlen(out), hex);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cache size\t: ");
k_itoa(info.cache_size, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), " KB\n");
k_strcpy(out + k_strlen(out), "physical id\t: 0\n");
k_strcpy(out + k_strlen(out), "siblings\t: ");
k_itoa(cpu_count, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "core id\t\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu cores\t: ");
k_itoa(cpu_count, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "apicid\t\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "initial apicid\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "fpu\t\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu_exception\t: yes\n");
k_strcpy(out + k_strlen(out), "cpuid level\t: 13\n");
k_strcpy(out + k_strlen(out), "wp\t\t: yes\n");
k_strcpy(out + k_strlen(out), "flags\t\t: ");
k_strcpy(out + k_strlen(out), flags);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "bugs\t\t: \n");
k_strcpy(out + k_strlen(out), "bogomips\t: 4800.00\n");
if (i < cpu_count - 1) {
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = (int)k_strlen(out);
if (offset >= len) { kfree(out); return 0; }
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
kfree(out);
return to_copy;
}
// --- Devices Implementation ---
static int read_sys_devices(char *buf, int size, int offset) {
char out[2048];
k_memset(out, 0, 2048);
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
k_strcpy(out, "Block Devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d && !d->is_partition) {
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), d->label);
k_strcpy(out + k_strlen(out), "\n");
}
}
k_strcpy(out + k_strlen(out), "\nCharacter Devices:\n");
k_strcpy(out + k_strlen(out), " console - System console\n");
k_strcpy(out + k_strlen(out), " tty - Terminal devices\n");
k_strcpy(out + k_strlen(out), " psmouse - Mouse input\n");
k_strcpy(out + k_strlen(out), " keyboard - Keyboard input\n");
k_strcpy(out + k_strlen(out), " framebuffer - Framebuffer device\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Class Implementation ---
static int read_sys_class(char *buf, int size, int offset) {
char out[1024];
k_memset(out, 0, 1024);
char vendor[16];
platform_get_cpu_vendor(vendor);
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\nCores: ");
char c_s[16]; k_itoa(smp_cpu_count(), c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\nSpeed: ~3.00 GHz\nFeatures: sse sse2 sse3 apic smp\n");
k_strcpy(out, "Classes:\n");
k_strcpy(out + k_strlen(out), " block - Block device class\n");
k_strcpy(out + k_strlen(out), " input - Input device class\n");
k_strcpy(out + k_strlen(out), " tty - TTY device class\n");
k_strcpy(out + k_strlen(out), " sound - Sound device class\n");
k_strcpy(out + k_strlen(out), " video - Video device class\n");
k_strcpy(out + k_strlen(out), " net - Network device class\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
@@ -174,6 +332,12 @@ void sysfs_init_subsystems(void) {
subsystem_register("class", &class);
subsystem_register("kernel/debug", &debug);
// Devices info
subsystem_add_file(devices, "list", read_sys_devices, NULL);
// Class info
subsystem_add_file(class, "list", read_sys_class, NULL);
// CPU info
subsystem_add_file(kernel, "cpuinfo", read_cpu_info, NULL);

158
src/sys/tty.c Normal file
View 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
View 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

1454
src/userland/cli/bsh.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

236
src/userland/cli/uname.c Normal file
View File

@@ -0,0 +1,236 @@
// 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 <stdbool.h>
static void str_copy(char *dst, const char *src, int max_len) {
int i = 0;
if (!dst || max_len <= 0) return;
if (!src) { dst[0] = 0; return; }
while (i < max_len - 1 && src[i]) {
dst[i] = src[i];
i++;
}
dst[i] = 0;
}
static bool starts_with(const char *s, const char *prefix) {
if (!s || !prefix) return false;
while (*prefix) {
if (*s != *prefix) return false;
s++; prefix++;
}
return true;
}
static void trim_end(char *s) {
int len;
if (!s) return;
len = (int)strlen(s);
while (len > 0) {
char c = s[len - 1];
if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
s[len - 1] = 0;
len--;
} else {
break;
}
}
}
static const char *trim_start(const char *s) {
if (!s) return "";
while (*s == ' ' || *s == '\t') s++;
return s;
}
static void copy_range(char *dst, const char *start, int len, int max_len) {
int i;
if (!dst || max_len <= 0) return;
if (!start || len <= 0) { dst[0] = 0; return; }
if (len > max_len - 1) len = max_len - 1;
for (i = 0; i < len; i++) dst[i] = start[i];
dst[len] = 0;
trim_end(dst);
}
static void copy_line(const char *start, char *out, int out_len) {
int i = 0;
if (!start || !out || out_len <= 0) return;
while (start[i] && start[i] != '\n' && start[i] != '\r' && i < out_len - 1) {
out[i] = start[i];
i++;
}
out[i] = 0;
trim_end(out);
}
static int read_file_to_buf(const char *path, char *buf, int max_len) {
int fd;
int bytes;
if (!buf || max_len <= 0) return -1;
fd = sys_open(path, "r");
if (fd < 0) return -1;
bytes = sys_read(fd, buf, max_len - 1);
sys_close(fd);
if (bytes < 0) return -1;
buf[bytes] = 0;
return bytes;
}
static void parse_version_info(const char *vbuf,
char *os_name, int os_name_len,
char *kernel_name, int kernel_name_len,
char *kernel_ver, int kernel_ver_len,
char *build_str, int build_str_len) {
char line1[256] = {0};
char line2[256] = {0};
char line3[256] = {0};
const char *l2;
const char *l3;
const char *end;
if (!vbuf || !vbuf[0]) return;
copy_line(vbuf, line1, sizeof(line1));
l2 = strchr(vbuf, '\n');
if (l2) {
l2++;
copy_line(l2, line2, sizeof(line2));
l3 = strchr(l2, '\n');
if (l3) {
l3++;
copy_line(l3, line3, sizeof(line3));
}
}
if (line1[0]) {
end = strstr(line1, " [");
if (!end) end = strstr(line1, " Version");
if (!end) end = line1 + (int)strlen(line1);
copy_range(os_name, line1, (int)(end - line1), os_name_len);
}
if (line2[0]) {
const char *p = line2;
if (starts_with(p, "Kernel:")) {
p += 7;
if (*p == ' ') p++;
}
p = trim_start(p);
if (*p) {
const char *t = p;
while (*t && *t != ' ') t++;
copy_range(kernel_name, p, (int)(t - p), kernel_name_len);
while (*t == ' ') t++;
if (*t) copy_range(kernel_ver, t, (int)strlen(t), kernel_ver_len);
}
}
if (line3[0]) {
const char *p = line3;
if (starts_with(p, "Build:")) {
p += 6;
if (*p == ' ') p++;
}
p = trim_start(p);
if (*p) copy_range(build_str, p, (int)strlen(p), build_str_len);
}
}
static void parse_cpu_model(const char *buf, char *out, int out_len) {
const char *p;
const char *colon;
const char *end;
if (!buf || !out || out_len <= 0) return;
p = strstr(buf, "model name");
if (!p) return;
colon = strchr(p, ':');
if (!colon) return;
colon++;
colon = trim_start(colon);
end = colon;
while (*end && *end != '\n' && *end != '\r') end++;
copy_range(out, colon, (int)(end - colon), out_len);
}
static void print_usage(void) {
printf("Usage: uname [-amnoprsv]\n");
}
static void print_field(const char *value, bool *first) {
if (!value || !value[0]) value = "Unknown";
if (!*first) printf(" ");
printf("%s", value);
*first = false;
}
int main(int argc, char **argv) {
bool want_s = false;
bool want_n = false;
bool want_r = false;
bool want_v = false;
bool want_m = false;
bool want_p = false;
bool want_o = false;
bool any_flags = false;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!arg || arg[0] != '-' || arg[1] == 0) {
print_usage();
return 1;
}
for (int j = 1; arg[j]; j++) {
char c = arg[j];
if (c == 'a') {
want_s = want_n = want_r = want_v = want_m = want_p = want_o = true;
any_flags = true;
} else if (c == 's') { want_s = true; any_flags = true; }
else if (c == 'n') { want_n = true; any_flags = true; }
else if (c == 'r') { want_r = true; any_flags = true; }
else if (c == 'v') { want_v = true; any_flags = true; }
else if (c == 'm') { want_m = true; any_flags = true; }
else if (c == 'p') { want_p = true; any_flags = true; }
else if (c == 'o') { want_o = true; any_flags = true; }
else { print_usage(); return 1; }
}
}
if (!any_flags) want_s = true;
char vbuf[1024] = {0};
char cpubuf[2048] = {0};
char os_name[64] = "Unknown";
char kernel_name[64] = "Unknown";
char kernel_ver[64] = "Unknown";
char build_str[64] = "Unknown";
char processor[128] = {0};
if (read_file_to_buf("/proc/version", vbuf, sizeof(vbuf)) > 0) {
parse_version_info(vbuf, os_name, sizeof(os_name), kernel_name, sizeof(kernel_name),
kernel_ver, sizeof(kernel_ver), build_str, sizeof(build_str));
}
if (read_file_to_buf("/proc/cpuinfo", cpubuf, sizeof(cpubuf)) > 0) {
parse_cpu_model(cpubuf, processor, sizeof(processor));
}
const char *nodename = "boredos";
const char *machine = "x86_64";
if (!processor[0]) str_copy(processor, machine, sizeof(processor));
bool first = true;
if (want_s) print_field(kernel_name, &first);
if (want_n) print_field(nodename, &first);
if (want_r) print_field(kernel_ver, &first);
if (want_v) print_field(build_str, &first);
if (want_m) print_field(machine, &first);
if (want_p) print_field(processor, &first);
if (want_o) print_field(os_name, &first);
printf("\n");
return 0;
}

519
src/userland/games/2048.c Normal file
View File

@@ -0,0 +1,519 @@
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*
@Lluciocc
2048 for BoredOS
Controls:
- WASD keys or arrow keys or numpad keys to move
- R key to restart
*/
#define WINDOW_W 300
#define WINDOW_H 430
#define BOARD_SIZE 4
#define TILE_SIZE 56
#define TILE_GAP 8
#define BOARD_X 18
#define BOARD_Y 96
#define BTN_W 54
#define BTN_H 30
#define COLOR_BG 0xFF121212
#define COLOR_PANEL 0xFF202020
#define COLOR_PANEL_2 0xFF2A2A2A
#define COLOR_BORDER 0xFF3D3D3D
#define COLOR_TEXT 0xFFF2F2F2
#define COLOR_TEXT_DARK 0xFF202020
#define COLOR_MUTED 0xFFBBBBBB
#define COLOR_ACCENT 0xFF6EA8FE
#define COLOR_GREEN 0xFF69DB7C
#define COLOR_RED 0xFFFF6B6B
#define COLOR_EMPTY_TILE 0xFF2D2D2D
static int board[BOARD_SIZE][BOARD_SIZE];
static int score = 0;
static int best_tile = 0;
static bool game_over = false;
static bool game_won = false;
static bool has_moved_last_turn = false;
static uint32_t random_seed = 0xC0FFEE12u;
static uint32_t random_next(void) {
random_seed = random_seed * 1664525u + 1013904223u;
return random_seed;
}
static int max_int(int a, int b) {
return (a > b) ? a : b;
}
static void clear_board(void) {
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
board[y][x] = 0;
}
}
}
static int count_empty_cells(void) {
int count = 0;
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
count++;
}
}
}
return count;
}
static void add_random_tile(void) {
int empty_count = count_empty_cells();
if (empty_count <= 0) {
return;
}
int pick = (int)(random_next() % (uint32_t)empty_count);
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
if (pick == 0) {
/* 90% chance of a 2, 10% chance of a 4 */
board[y][x] = ((random_next() % 10u) == 0u) ? 4 : 2;
best_tile = max_int(best_tile, board[y][x]);
return;
}
pick--;
}
}
}
}
static bool can_make_any_move(void) {
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
return true;
}
if (x + 1 < BOARD_SIZE && board[y][x] == board[y][x + 1]) {
return true;
}
if (y + 1 < BOARD_SIZE && board[y][x] == board[y + 1][x]) {
return true;
}
}
}
return false;
}
static void refresh_end_state(void) {
game_won = false;
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] >= 2048) {
game_won = true;
}
if (board[y][x] > best_tile) {
best_tile = board[y][x];
}
}
}
game_over = !can_make_any_move();
}
static void init_game(void) {
clear_board();
score = 0;
best_tile = 0;
game_over = false;
game_won = false;
has_moved_last_turn = false;
add_random_tile();
add_random_tile();
refresh_end_state();
}
static void copy_line_from_row(int row, int out[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
out[i] = board[row][i];
}
}
static void copy_line_to_row(int row, const int in[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
board[row][i] = in[i];
}
}
static void copy_line_from_col(int col, int out[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
out[i] = board[i][col];
}
}
static void copy_line_to_col(int col, const int in[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
board[i][col] = in[i];
}
}
static void reverse_line(int line[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE / 2; i++) {
int tmp = line[i];
line[i] = line[BOARD_SIZE - 1 - i];
line[BOARD_SIZE - 1 - i] = tmp;
}
}
static bool slide_and_merge_line_left(int line[BOARD_SIZE]) {
int compact[BOARD_SIZE];
int merged[BOARD_SIZE];
int compact_len = 0;
int write_index = 0;
bool changed = false;
for (int i = 0; i < BOARD_SIZE; i++) {
merged[i] = 0;
if (line[i] != 0) {
compact[compact_len++] = line[i];
}
}
for (int i = 0; i < compact_len; i++) {
if (i + 1 < compact_len && compact[i] == compact[i + 1]) {
int value = compact[i] * 2;
merged[write_index++] = value;
score += value;
best_tile = max_int(best_tile, value);
i++; /* Skip the second tile, it has been merged. */
} else {
merged[write_index++] = compact[i];
}
}
while (write_index < BOARD_SIZE) {
merged[write_index++] = 0;
}
for (int i = 0; i < BOARD_SIZE; i++) {
if (line[i] != merged[i]) {
changed = true;
}
line[i] = merged[i];
}
return changed;
}
typedef enum {
MOVE_LEFT,
MOVE_RIGHT,
MOVE_UP,
MOVE_DOWN
} move_dir_t;
static bool apply_move(move_dir_t dir) {
bool changed = false;
if (game_over) {
return false;
}
if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
for (int row = 0; row < BOARD_SIZE; row++) {
int line[BOARD_SIZE];
copy_line_from_row(row, line);
if (dir == MOVE_RIGHT) {
reverse_line(line);
}
if (slide_and_merge_line_left(line)) {
changed = true;
}
if (dir == MOVE_RIGHT) {
reverse_line(line);
}
copy_line_to_row(row, line);
}
} else {
for (int col = 0; col < BOARD_SIZE; col++) {
int line[BOARD_SIZE];
copy_line_from_col(col, line);
if (dir == MOVE_DOWN) {
reverse_line(line);
}
if (slide_and_merge_line_left(line)) {
changed = true;
}
if (dir == MOVE_DOWN) {
reverse_line(line);
}
copy_line_to_col(col, line);
}
}
if (changed) {
add_random_tile();
}
has_moved_last_turn = changed;
refresh_end_state();
return changed;
}
static uint32_t get_tile_color(int value) {
switch (value) {
case 0: return COLOR_EMPTY_TILE;
case 2: return 0xFFEEE4DA;
case 4: return 0xFFEDE0C8;
case 8: return 0xFFF2B179;
case 16: return 0xFFF59563;
case 32: return 0xFFF67C5F;
case 64: return 0xFFF65E3B;
case 128: return 0xFFEDCF72;
case 256: return 0xFFEDCC61;
case 512: return 0xFFEDC850;
case 1024: return 0xFFEDC53F;
case 2048: return 0xFFEDC22E;
default: return 0xFF3C91E6;
}
}
static uint32_t get_tile_text_color(int value) {
(void)value;
return 0xFF000000; // for visibility
}
static void int_to_text(int value, char *out) {
itoa(value, out);
}
static void draw_centered_text(ui_window_t win, int x, int y, int w, int h,
const char *text, uint32_t color, float scale) {
(void)scale;
uint32_t text_w = ui_get_string_width(text);
uint32_t text_h = ui_get_font_height();
int draw_x = x + (w - (int)text_w) / 2;
int draw_y = y + (h - (int)text_h) / 2;
ui_draw_string(win, draw_x, draw_y, text, color);
}
static void draw_button(ui_window_t win, int x, int y, int w, int h,
const char *label, uint32_t color) {
ui_draw_rounded_rect_filled(win, x, y, w, h, 6, color);
draw_centered_text(win, x, y, w, h, label, COLOR_TEXT, 1.0f);
}
static void draw_score_box(ui_window_t win, int x, int y, int w, int h,
const char *title, int value) {
char buf[16];
int_to_text(value, buf);
ui_draw_rounded_rect_filled(win, x, y, w, h, 8, COLOR_PANEL_2);
draw_centered_text(win, x, y + 3, w, 14, title, COLOR_MUTED, 0.8f);
draw_centered_text(win, x, y + 16, w, 18, buf, COLOR_TEXT, 1.0f);
}
static void draw_tile(ui_window_t win, int x, int y, int value) {
char buf[16];
float scale = 1.6f;
ui_draw_rounded_rect_filled(win, x, y, TILE_SIZE, TILE_SIZE, 8, get_tile_color(value));
if (value == 0) {
return;
}
int_to_text(value, buf);
if (value >= 1000) {
scale = 1.2f;
} else if (value >= 100) {
scale = 1.4f;
}
draw_centered_text(
win,
x, y,
TILE_SIZE, TILE_SIZE,
buf,
0xFF000000,
scale
);
}
static void draw_board(ui_window_t win) {
int board_w = BOARD_SIZE * TILE_SIZE + (BOARD_SIZE + 1) * TILE_GAP;
int board_h = board_w;
ui_draw_rounded_rect_filled(win, BOARD_X, BOARD_Y, board_w, board_h, 10, COLOR_PANEL_2);
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
int px = BOARD_X + TILE_GAP + x * (TILE_SIZE + TILE_GAP);
int py = BOARD_Y + TILE_GAP + y * (TILE_SIZE + TILE_GAP);
draw_tile(win, px, py, board[y][x]);
}
}
}
static void draw_status(ui_window_t win) {
if (game_over) {
ui_draw_string(win, 18, 70, "Game over", COLOR_RED);
} else if (game_won) {
ui_draw_string(win, 18, 70, "2048 reached - keep going", COLOR_GREEN);
} else {
ui_draw_string(win, 18, 70, "Combine tiles to reach 2048", COLOR_MUTED);
}
}
static void game_paint(ui_window_t win) {
ui_draw_rect(win, 0, 0, WINDOW_W, WINDOW_H, COLOR_BG);
ui_draw_string_scaled(win, 18, 12, "2048", COLOR_TEXT, 1.6f);
ui_draw_string(win, 18, 42, "Use WASD keys", COLOR_MUTED);
draw_score_box(win, 155, 14, 56, 40, "SCORE", score);
draw_score_box(win, 220, 14, 62, 40, "BEST", best_tile);
draw_status(win);
draw_board(win);
/* Not recommended to use
draw_button(win, 18, 352, 86, 30, "Restart", COLOR_ACCENT);
draw_button(win, 123, 352, BTN_W, BTN_H, "Left", COLOR_BORDER);
draw_button(win, 186, 352, BTN_W, BTN_H, "Right", COLOR_BORDER);
draw_button(win, 92, 390, BTN_W, BTN_H, "Up", COLOR_BORDER);
draw_button(win, 155, 390, BTN_W, BTN_H, "Down", COLOR_BORDER);
*/
}
static bool point_in_rect(int px, int py, int x, int y, int w, int h) {
return px >= x && px < x + w && py >= y && py < y + h;
}
/*
37 = left arrow
38 = up arrow
39 = right arrow
40 = down arrow
72 = numpad 8
75 = numpad 4
77 = numpad 6
80 = numpad 2
key = letter
*/
static bool is_left_key(int key) {
return key == 'a' || key == 'A' || key == 37 || key == 75;
}
static bool is_right_key(int key) {
return key == 'd' || key == 'D' || key == 39 || key == 77;
}
static bool is_up_key(int key) {
return key == 'w' || key == 'W' || key == 38 || key == 72;
}
static bool is_down_key(int key) {
return key == 's' || key == 'S' || key == 40 || key == 80;
}
static void handle_click(int x, int y) {
if (point_in_rect(x, y, 18, 352, 86, 30)) {
init_game();
return;
}
if (point_in_rect(x, y, 123, 352, BTN_W, BTN_H)) {
apply_move(MOVE_LEFT);
return;
}
if (point_in_rect(x, y, 186, 352, BTN_W, BTN_H)) {
apply_move(MOVE_RIGHT);
return;
}
if (point_in_rect(x, y, 92, 390, BTN_W, BTN_H)) {
apply_move(MOVE_UP);
return;
}
if (point_in_rect(x, y, 155, 390, BTN_W, BTN_H)) {
apply_move(MOVE_DOWN);
return;
}
}
static void handle_key(int key) {
if (key == 'r' || key == 'R') {
init_game();
return;
}
if (is_left_key(key)) {
apply_move(MOVE_LEFT);
} else if (is_right_key(key)) {
apply_move(MOVE_RIGHT);
} else if (is_up_key(key)) {
apply_move(MOVE_UP);
} else if (is_down_key(key)) {
apply_move(MOVE_DOWN);
}
}
int main(int argc, char **argv) {
(void)argc;
(void)argv;
ui_window_t win = ui_window_create("2048", 240, 120, WINDOW_W, WINDOW_H);
if (!win) {
return 1;
}
init_game();
gui_event_t ev;
while (1) {
if (ui_get_event(win, &ev)) {
if (ev.type == GUI_EVENT_PAINT) {
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_CLICK) {
handle_click(ev.arg1, ev.arg2);
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_KEY) {
handle_key(ev.arg1);
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0);
} else if (ev.type == GUI_EVENT_RESIZE) {
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
}
} else {
sys_yield();
}
}
return 0;
}

893
src/userland/gui/terminal.c Normal file
View 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;
}

View File

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

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

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

View File

@@ -239,8 +239,8 @@ int main(int argc, char** argv) {
}
if (strcmp(argv[1], "init") == 0) {
if (sys_network_init() == 0) printf("Network OK\n");
else printf("Network Fail\n");
if (sys_network_init() == 0) printf("Network [OK]\n");
else printf("Network [FAIL]\n");
return 0;
}

View File

@@ -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+]
@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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
@@ -805,22 +804,22 @@ void explorer_open_directory(const char *path) {
explorer_create_window(path);
}
static void explorer_open_target(const char *path) {
void explorer_open_target(const char *path) {
if (vfs_is_directory(path)) {
explorer_open_directory(path);
} else {
if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path, NULL);
process_create_elf(path, NULL, false, -1);
} else if (explorer_str_ends_with(path, ".pdf")) {
process_create_elf("/bin/boredword.elf", path);
process_create_elf("/bin/boredword.elf", path, false, -1);
} else if (explorer_is_markdown_file(path)) {
process_create_elf("/bin/markdown.elf", path);
process_create_elf("/bin/markdown.elf", path, false, -1);
} else if (explorer_str_ends_with(path, ".pnt")) {
process_create_elf("/bin/paint.elf", path);
process_create_elf("/bin/paint.elf", path, false, -1);
} else if (explorer_is_image_file(path)) {
process_create_elf("/bin/viewer.elf", path);
process_create_elf("/bin/viewer.elf", path, false, -1);
} else {
process_create_elf("/bin/txtedit.elf", path);
process_create_elf("/bin/txtedit.elf", path, false, -1);
}
}
}
@@ -844,17 +843,17 @@ 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) {
process_create_elf("/bin/notepad.elf", NULL); return;
process_create_elf("/bin/notepad.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
process_create_elf("/bin/calculator.elf", NULL); return;
process_create_elf("/bin/calculator.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
target = &win_cmd; cmd_reset();
process_create_elf("/bin/terminal.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
process_create_elf("/bin/minesweeper.elf", NULL); return;
process_create_elf("/bin/minesweeper.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0 || explorer_strcmp(state->items[index].name, "Settings.shortcut") == 0) {
process_create_elf("/bin/settings.elf", NULL); return;
process_create_elf("/bin/settings.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "About.shortcut") == 0) {
process_create_elf("/bin/about.elf", NULL); return;
process_create_elf("/bin/about.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Explorer.shortcut") == 0) {
explorer_open_directory("/"); return;
} else if (explorer_strcmp(state->items[index].name, "Recycle Bin.shortcut") == 0) {
@@ -1605,7 +1604,7 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
state->dialog_input_cursor = explorer_strlen(state->dialog_input);
explorer_strcpy(state->dialog_target_path, full_path);
} else if (clicked_action == 110) { // Open with Text Editor
process_create_elf("/bin/txtedit.elf", full_path);
process_create_elf("/bin/txtedit.elf", full_path, false, -1);
} else if (clicked_action == ACTION_RESTORE) {
explorer_restore_file(win, state->file_context_menu_item);
} else if (clicked_action == ACTION_CREATE_SHORTCUT) {

View File

@@ -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;
@@ -70,10 +69,10 @@ typedef struct {
void explorer_init(void);
void explorer_reset(void);
void explorer_open_directory(const char *path); // Creates a NEW window
void explorer_open_directory(const char *path);
void explorer_open_target(const char *path);
// Drag and Drop support
// This now needs to find WHICH explorer window is under the mouse
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
void explorer_import_file(Window *win, const char *source_path); // To focused or default
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
@@ -81,18 +80,15 @@ void explorer_refresh(Window *win);
void explorer_refresh_all(void);
void explorer_clear_click_state(Window *win);
// String Helpers
size_t explorer_strlen(const char *str);
void explorer_strcpy(char *dest, const char *src);
void explorer_strcat(char *dest, const char *src);
// Clipboard (System-wide)
void explorer_clipboard_copy(const char *path);
void explorer_clipboard_cut(const char *path);
void explorer_clipboard_paste(Window *win, const char *dest_dir);
bool explorer_clipboard_has_content(void);
// File Operations
bool explorer_delete_permanently(const char *path);
bool explorer_delete_recursive(const char *path);
void explorer_create_shortcut(Window *win, const char *target_path);

View File

@@ -330,6 +330,13 @@ int font_manager_get_string_width_scaled(ttf_font_t *font, const char *s, float
while (*s) {
int advance, lsb;
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\t') {
stbtt_GetCodepointHMetrics(info, ' ', &advance, &lsb);
width += (int)(advance * real_scale + 0.5f) * 4;
continue;
}
stbtt_fontinfo *current_info = info;
float current_scale = real_scale;
@@ -350,6 +357,12 @@ int font_manager_get_codepoint_width_scaled(ttf_font_t *font, uint32_t codepoint
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (codepoint == '\t') {
int advance, lsb;
stbtt_GetCodepointHMetrics(info, ' ', &advance, &lsb);
return (int)(advance * real_scale + 0.5f) * 4;
}
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
info = (stbtt_fontinfo *)fallback_font->info;
real_scale = stbtt_ScaleForPixelHeight(info, scale);

View File

@@ -7,6 +7,7 @@
#include "io.h"
#include "font_manager.h"
#include "../mem/memory_manager.h"
#include "sys/spinlock.h"
static struct limine_framebuffer *g_fb = NULL;
static uint32_t g_bg_color = 0xFF696969;
@@ -25,6 +26,7 @@ static int g_bg_image_h = 0;
static bool g_use_image = false;
static DirtyRect g_dirty = {0, 0, 0, 0, false};
static spinlock_t graphics_lock = SPINLOCK_INIT;
#define MAX_FB_WIDTH 2048
@@ -163,15 +165,19 @@ void graphics_mark_dirty(int x, int y, int w, int h) {
return;
}
uint64_t flags = spinlock_acquire_irqsave(&graphics_lock);
merge_dirty_rect(x, y, w, h);
spinlock_release_irqrestore(&graphics_lock, flags);
}
void graphics_mark_screen_dirty(void) {
uint64_t flags = spinlock_acquire_irqsave(&graphics_lock);
g_dirty.x = 0;
g_dirty.y = 0;
g_dirty.w = get_screen_width();
g_dirty.h = get_screen_height();
g_dirty.active = true;
spinlock_release_irqrestore(&graphics_lock, flags);
}
DirtyRect graphics_get_dirty_rect(void) {
@@ -179,11 +185,9 @@ DirtyRect graphics_get_dirty_rect(void) {
}
void graphics_clear_dirty(void) {
extern uint64_t wm_lock_acquire(void);
extern void wm_lock_release(uint64_t);
uint64_t rflags = wm_lock_acquire();
uint64_t flags = spinlock_acquire_irqsave(&graphics_lock);
g_dirty.active = false;
wm_lock_release(rflags);
spinlock_release_irqrestore(&graphics_lock, flags);
}
void graphics_clear_dirty_no_lock(void) {
@@ -402,8 +406,11 @@ void draw_rounded_rect_blurred(int x, int y, int w, int h, int radius, uint32_t
for (int r = 0; r < h; r++) {
int g_y = y + r;
if (g_y < 0 || g_y >= sh) continue;
for (int c = 0; c < w; c++) {
int g_x = x + c;
if (g_x < 0 || g_x >= sw) continue;
int r_sum = 0, g_sum = 0, b_sum = 0, count = 0;
int start_kx = g_x - blur_radius;
@@ -555,6 +562,8 @@ void draw_string_bitmap(int x, int y, const char *str, uint32_t color) {
if (*s == '\n') {
cur_x = x;
cur_y += 10;
} else if (*s == '\t') {
cur_x += 8 * 4;
} else {
draw_char_bitmap(cur_x, cur_y, *s, color);
cur_x += 8;
@@ -583,8 +592,9 @@ int graphics_get_string_width_scaled(const char *s, float scale) {
}
int len = 0;
while (s && *s) {
utf8_decode(&s);
len++;
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\t') len += 4;
else len++;
}
return len * 8; // Fallback bitmap width
}
@@ -607,6 +617,8 @@ void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale
if (codepoint == '\n') {
cur_x = x;
baseline += line_height;
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, ' ', scale) * 4;
} else {
font_manager_render_char_scaled(g_current_ttf, cur_x, baseline, codepoint, color, scale, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, codepoint, scale);
@@ -621,6 +633,8 @@ void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale
if (codepoint == '\n') {
cur_x = x;
cur_y += 10;
} else if (codepoint == '\t') {
cur_x += 8 * 4;
} else {
draw_char(cur_x, cur_y, (codepoint < 128) ? (char)codepoint : '?', color);
cur_x += 8;
@@ -646,6 +660,8 @@ void draw_string_scaled_sloped(int x, int y, const char *s, uint32_t color, floa
if (codepoint == '\n') {
cur_x = x;
baseline += line_height;
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, ' ', scale) * 4;
} else {
font_manager_render_char_sloped(g_current_ttf, cur_x, baseline, codepoint, color, scale, slope, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, codepoint, scale);
@@ -784,13 +800,23 @@ void graphics_clear_back_buffer(uint32_t color) {
}
void graphics_flip_buffer(void) {
if (!g_fb || !g_dirty.active) return;
if (!g_fb) return;
uint64_t flags = spinlock_acquire_irqsave(&graphics_lock);
if (!g_dirty.active) {
spinlock_release_irqrestore(&graphics_lock, flags);
return;
}
int x = g_dirty.x;
int y = g_dirty.y;
int w = g_dirty.w;
int h = g_dirty.h;
// Clear dirty state
g_dirty.active = false;
spinlock_release_irqrestore(&graphics_lock, flags);
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x + w > (int)g_fb->width) w = g_fb->width - x;
@@ -1009,3 +1035,26 @@ void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
}
}
}
void graphics_scroll_back_buffer(int lines) {
if (!g_fb || lines <= 0 || lines >= (int)g_fb->height) return;
extern uint64_t wm_lock_acquire(void);
extern void wm_lock_release(uint64_t);
uint64_t rflags = wm_lock_acquire();
int sw = (int)g_fb->width;
int sh = (int)g_fb->height;
for (int y = 0; y < sh - lines; y++) {
uint32_t *dst = &g_back_buffer[y * sw];
uint32_t *src = &g_back_buffer[(y + lines) * sw];
for (int x = 0; x < sw; x++) dst[x] = src[x];
}
for (int y = sh - lines; y < sh; y++) {
uint32_t *dst = &g_back_buffer[y * sw];
for (int x = 0; x < sw; x++) dst[x] = 0;
}
wm_lock_release(rflags);
}

View File

@@ -56,6 +56,7 @@ void graphics_clear_dirty_no_lock(void);
// Double buffering
void graphics_flip_buffer(void);
void graphics_clear_back_buffer(uint32_t color);
void graphics_scroll_back_buffer(int lines);
// Clipping
void graphics_set_clipping(int x, int y, int w, int h);

View File

@@ -107,7 +107,7 @@ void wallpaper_process_pending(void) {
const char *path = (const char *)pending_wallpaper_path;
pending_wallpaper_path = NULL;
serial_str("[WP] Processing wallpaper: ");
serial_str("[WM] Processing wallpaper: ");
serial_str(path);
serial_str("\n");

View File

@@ -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"
@@ -13,12 +12,15 @@
#include <stddef.h>
#include "wallpaper.h"
#include "fat32.h"
#include "file_index.h"
#include "../dev/ps2.h"
#define STBI_NO_STDIO
#include "userland/stb_image.h"
#include "memory_manager.h"
#include "disk.h"
#include "../sys/work_queue.h"
#include "../sys/smp.h"
#include "../core/kconsole.h"
// Hello developer,
@@ -42,6 +44,8 @@ void wm_lock_release(uint64_t flags) {
}
extern void serial_write(const char *str);
extern void log_ok(const char *msg);
extern void log_fail(const char *msg);
static bool str_eq(const char *s1, const char *s2) {
if (!s1 || !s2) return false;
@@ -87,6 +91,125 @@ static int notif_x_offset = 420; // Starts offscreen
static bool notif_active = false;
extern bool ps2_ctrl_pressed;
static lumos_state_t lumos_state = {0};
static bool lumos_index_built = false;
static bool force_redraw = true;
static bool menubar_dirty_pending = false;
static void lumos_update_search(void) {
int query_hash = 0;
for (int i = 0; lumos_state.search_query[i] && i < 256; i++) {
query_hash = (query_hash * 31) + lumos_state.search_query[i];
}
if (query_hash == lumos_state.last_query_hash) {
return;
}
lumos_state.last_query_hash = query_hash;
lumos_state.result_count = 0;
lumos_state.selected_index = 0;
if (lumos_state.search_len == 0) {
return;
}
file_index_result_t results[LUMOS_MAX_RESULTS];
int count = file_index_find_fuzzy(lumos_state.search_query, results, LUMOS_MAX_RESULTS);
lumos_state.result_count = count;
for (int i = 0; i < count && i < LUMOS_MAX_RESULTS; i++) {
lumos_state.results[i] = results[i];
}
int sw = get_screen_width();
int sh = get_screen_height();
int modal_height = LUMOS_SEARCH_HEIGHT + (lumos_state.result_count * LUMOS_RESULT_HEIGHT) + 10;
int modal_y = (sh * 2 / 5) - (modal_height / 2);
graphics_mark_dirty(0, 0, sw, sh);
force_redraw = true;
}
static void wm_lumos_handle_key(char c) {
if (c == 27) {
lumos_state.visible = false;
force_redraw = true;
return;
}
if (c == '\n') {
if (lumos_state.result_count > 0 && lumos_state.selected_index < lumos_state.result_count) {
const char *file_path = lumos_state.results[lumos_state.selected_index].entry.path;
explorer_open_target(file_path);
lumos_state.visible = false;
force_redraw = true;
}
return;
}
if (c == 17) {
if (lumos_state.selected_index > 0) {
lumos_state.selected_index--;
force_redraw = true;
}
return;
}
if (c == 18) {
if (lumos_state.selected_index < lumos_state.result_count - 1) {
lumos_state.selected_index++;
force_redraw = true;
}
return;
}
if (c == '\b' || c == 127) {
if (lumos_state.cursor_pos > 0) {
for (int i = lumos_state.cursor_pos - 1; i < lumos_state.search_len; i++) {
lumos_state.search_query[i] = lumos_state.search_query[i + 1];
}
lumos_state.search_len--;
lumos_state.cursor_pos--;
lumos_state.search_query[lumos_state.search_len] = 0;
lumos_update_search();
force_redraw = true;
}
return;
}
if (c == 19) {
if (lumos_state.cursor_pos > 0) {
lumos_state.cursor_pos--;
force_redraw = true;
}
return;
}
if (c == 20) {
if (lumos_state.cursor_pos < lumos_state.search_len) {
lumos_state.cursor_pos++;
force_redraw = true;
}
return;
}
if (c >= 32 && c <= 126 && lumos_state.search_len < 255) {
for (int i = lumos_state.search_len; i >= lumos_state.cursor_pos; i--) {
lumos_state.search_query[i + 1] = lumos_state.search_query[i];
}
lumos_state.search_query[lumos_state.cursor_pos] = c;
lumos_state.search_len++;
lumos_state.cursor_pos++;
lumos_state.search_query[lumos_state.search_len] = 0;
lumos_update_search();
force_redraw = true;
}
}
// Dragging State
static bool is_dragging = false;
static bool is_resizing = false;
@@ -114,15 +237,12 @@ static int drag_icon_orig_x = 0;
static int drag_icon_orig_y = 0;
static Window *drag_src_win = NULL;
// Windows array for z-order management
static Window *all_windows[32];
static int window_count = 0;
// Redraw system
static bool force_redraw = true;
static uint32_t timer_ticks = 0;
// Cursor state
static int last_cursor_x = 400;
static int last_cursor_y = 300;
@@ -311,7 +431,7 @@ void wm_refresh_desktop(void) {
static void create_desktop_shortcut(const char *app_name) {
char path[128] = "/root/Desktop/";
int p = 9;
int p = 14;
int n = 0; while(app_name[n]) path[p++] = app_name[n++];
const char *ext = ".shortcut";
int e = 0; while(ext[e]) path[p++] = ext[e++];
@@ -1309,6 +1429,144 @@ bool rect_contains(int x, int y, int w, int h, int px, int py) {
return px >= x && px < x + w && py >= y && py < y + h;
}
static void wm_render_lumos(int y_start, int y_end, DirtyRect dirty) {
if (!lumos_state.visible) {
return;
}
int sw = get_screen_width();
int sh = get_screen_height();
int modal_width = LUMOS_MODAL_WIDTH;
int modal_height = LUMOS_SEARCH_HEIGHT + (lumos_state.result_count * LUMOS_RESULT_HEIGHT) + 10;
if (lumos_state.result_count == 0 && lumos_state.search_len > 0) {
modal_height = LUMOS_SEARCH_HEIGHT + LUMOS_RESULT_HEIGHT + 20;
}
int modal_x = (sw - modal_width) / 2;
int modal_y = (sh * 2 / 5) - (modal_height / 2);
if (modal_y + modal_height <= y_start || modal_y >= y_end) {
return;
}
// Draw modal background - with subtle blur effect
draw_rounded_rect_blurred(modal_x, modal_y, modal_width, modal_height, 12, COLOR_DARK_PANEL, 1, 220);
int search_x = modal_x + 8;
int search_y = modal_y + 8;
int search_width = modal_width - 16;
int search_height = 32;
draw_rounded_rect_filled(search_x, search_y, search_width, search_height, 6, COLOR_DARK_BG);
if (lumos_state.search_len > 0) {
draw_string(search_x + 8, search_y + 8, lumos_state.search_query, COLOR_DARK_TEXT);
} else {
draw_string(search_x + 8, search_y + 8, "Search files...", COLOR_DKGRAY);
}
if (lumos_state.cursor_pos <= lumos_state.search_len && lumos_state.search_len > 0) {
ttf_font_t *ttf = graphics_get_current_ttf();
char temp_query[256];
for (int i = 0; i < lumos_state.cursor_pos && i < 255; i++) {
temp_query[i] = lumos_state.search_query[i];
}
temp_query[lumos_state.cursor_pos] = 0;
int cursor_x_offset = (ttf) ? font_manager_get_string_width(ttf, temp_query) : (lumos_state.cursor_pos * 6);
draw_rect(search_x + 8 + cursor_x_offset, search_y + 8, 1, 16, COLOR_DARK_TEXT);
}
for (int i = 0; i < lumos_state.result_count && i < LUMOS_MAX_RESULTS; i++) {
if (i < 0 || i >= LUMOS_MAX_RESULTS) {
break;
}
int result_x = modal_x + 4;
int result_y = modal_y + LUMOS_SEARCH_HEIGHT + 4 + (i * LUMOS_RESULT_HEIGHT);
int result_width = modal_width - 8;
if (i == lumos_state.selected_index) {
draw_rounded_rect_filled(result_x, result_y, result_width, LUMOS_RESULT_HEIGHT - 2, 6, COLOR_DARK_BORDER);
}
const char *full_path = lumos_state.results[i].entry.path;
if (!full_path || full_path[0] == 0) {
continue;
}
const char *filename = full_path;
for (int j = 0; full_path[j]; j++) {
if (full_path[j] == '/') {
filename = &full_path[j + 1];
}
}
if (!filename || filename[0] == 0) {
continue;
}
draw_string(result_x + 8, result_y + 8, filename, COLOR_DARK_TEXT);
if (!lumos_state.results[i].entry.is_directory) {
char size_str[32];
uint32_t size = lumos_state.results[i].entry.size;
if (size < 1024) {
// Bytes
size_str[0] = '0' + ((size / 1) % 10);
size_str[1] = 'B';
size_str[2] = 0;
} else if (size < 1024 * 1024) {
// Kilobytes - properly format for values up to 1023 KB
int kb = size / 1024;
if (kb >= 100) {
size_str[0] = '0' + (kb / 100);
size_str[1] = '0' + ((kb / 10) % 10);
size_str[2] = '0' + (kb % 10);
size_str[3] = 'K';
size_str[4] = 'B';
size_str[5] = 0;
} else {
size_str[0] = '0' + (kb / 10);
size_str[1] = '0' + (kb % 10);
size_str[2] = 'K';
size_str[3] = 'B';
size_str[4] = 0;
}
} else {
// Megabytes - properly format for any MB value
int mb = size / (1024 * 1024);
if (mb >= 100) {
size_str[0] = '0' + (mb / 100);
size_str[1] = '0' + ((mb / 10) % 10);
size_str[2] = '0' + (mb % 10);
size_str[3] = 'M';
size_str[4] = 'B';
size_str[5] = 0;
} else {
size_str[0] = '0' + (mb / 10);
size_str[1] = '0' + (mb % 10);
size_str[2] = 'M';
size_str[3] = 'B';
size_str[4] = 0;
}
}
int size_x = result_x + result_width - 8 - 32; // Account for wider size strings
draw_string(size_x, result_y + 8, size_str, COLOR_DKGRAY);
}
}
// Draw "No results" message if needed
if (lumos_state.search_len > 0 && lumos_state.result_count == 0) {
int msg_y = modal_y + LUMOS_SEARCH_HEIGHT + 10;
draw_string(modal_x + 20, msg_y, "No results found", COLOR_DKGRAY);
}
}
static Window *sorted_windows_cache[32];
static int sorted_window_count_cache = 0;
@@ -1376,7 +1634,7 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
}
} else if (pass == 2) {
if (0 < cy + ch && 30 > cy) {
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG);
draw_rect(0, 0, sw, 30, COLOR_MENUBAR_BG);
draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12);
}
@@ -1393,7 +1651,7 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
if (dock_y < cy + ch && dock_y + dock_h > cy) {
int d_item_sz = 48, d_space = 10, d_total_w = 12 * (d_item_sz + d_space);
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 1, 180);
int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
draw_dock_files(dx, dy); dx += d_item_sz+d_space;
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
@@ -1466,6 +1724,9 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
}
}
// Render lumos modal
wm_render_lumos(cy, cy + ch, dirty);
if (wm_custom_paint_hook) wm_custom_paint_hook();
if (is_dragging_file) {
@@ -1493,6 +1754,11 @@ void wm_paint(void) {
wm_mark_dirty(mx, my, 12, 12);
DirtyRect dirty = graphics_get_dirty_rect();
if (menubar_dirty_pending) {
graphics_mark_dirty(0, 0, sw, 30);
dirty = graphics_get_dirty_rect();
menubar_dirty_pending = false;
}
if (dirty.active) {
int d_h = 60, d_y = sh - d_h - 6, d_total_w = 11 * (48 + 10);
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
@@ -1591,6 +1857,8 @@ void wm_add_window_locked(Window *win) {
if (window_count < 32) {
all_windows[window_count++] = win;
wm_bring_to_front_locked(win); // Ensure newly added windows are on top
wm_mark_dirty(0, 0, get_screen_width(), 30);
menubar_dirty_pending = true;
}
}
@@ -1661,7 +1929,7 @@ void wm_remove_window(Window *win) {
force_redraw = true;
} else {
wm_lock_release(rflags);
serial_write("WM: Window not found in all_windows list!\n");
log_fail("Window not found in all_windows list!");
return;
}
@@ -1679,6 +1947,29 @@ void wm_handle_click(int x, int y) {
int sh = get_screen_height();
int sw = get_screen_width();
if (lumos_state.visible) {
int modal_width = LUMOS_MODAL_WIDTH;
int modal_height = LUMOS_SEARCH_HEIGHT + (lumos_state.result_count * LUMOS_RESULT_HEIGHT) + 10;
int modal_x = (sw - modal_width) / 2;
int modal_y = (sh * 2 / 5) - (modal_height / 2);
if (rect_contains(modal_x, modal_y, modal_width, modal_height, x, y)) {
int result_click_y = y - (modal_y + LUMOS_SEARCH_HEIGHT + 4);
if (result_click_y >= 0 && result_click_y < lumos_state.result_count * LUMOS_RESULT_HEIGHT) {
int result_idx = result_click_y / LUMOS_RESULT_HEIGHT;
if (result_idx >= 0 && result_idx < lumos_state.result_count) {
lumos_state.selected_index = result_idx;
const char *file_path = lumos_state.results[result_idx].entry.path;
lumos_state.visible = false;
}
}
} else {
lumos_state.visible = false;
}
force_redraw = true;
return;
}
if (msg_box_visible) {
int mw = 320;
int mh = 100;
@@ -1839,11 +2130,11 @@ void wm_handle_click(int x, int y) {
int item = rel_y / 20;
if (item == 0) { // About
process_create_elf("/bin/about.elf", NULL);
process_create_elf("/bin/about.elf", NULL, false, -1);
} else if (item == 1) { // Settings
Window *existing = wm_find_window_by_title_locked("Settings");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/settings.elf", NULL);
else process_create_elf("/bin/settings.elf", NULL, false, -1);
} else if (item == 2) { // Shutdown
k_shutdown();
} else if (item == 3) { // Restart
@@ -2190,55 +2481,55 @@ 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/notepad.elf", NULL);
process_create_elf("/bin/notepad.elf", NULL, false, -1);
}
} else if (str_starts_with(start_menu_pending_app, "Editor")) {
Window *existing = wm_find_window_by_title_locked("Txtedit");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/txtedit.elf", NULL);
else process_create_elf("/bin/txtedit.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Word Processor")) {
Window *existing = wm_find_window_by_title_locked("Word Processor");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/boredword.elf", NULL);
else process_create_elf("/bin/boredword.elf", NULL, false, -1);
} 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, false, -1);
} 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);
else process_create_elf("/bin/grapher.elf", NULL);
else process_create_elf("/bin/grapher.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Calculator")) {
Window *existing = wm_find_window_by_title_locked("Calculator");
if (existing) {
wm_bring_to_front_locked(existing);
} else {
process_create_elf("/bin/calculator.elf", NULL);
process_create_elf("/bin/calculator.elf", NULL, false, -1);
}
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
Window *existing = wm_find_window_by_title_locked("Minesweeper");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/minesweeper.elf", NULL);
else process_create_elf("/bin/minesweeper.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Settings")) {
Window *existing = wm_find_window_by_title_locked("Settings");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/settings.elf", NULL);
else process_create_elf("/bin/settings.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Paint")) {
Window *existing = wm_find_window_by_title_locked("Paint");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/paint.elf", NULL);
else process_create_elf("/bin/paint.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Clock")) {
Window *existing = wm_find_window_by_title_locked("Clock");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/clock.elf", NULL);
else process_create_elf("/bin/clock.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Browser")) {
Window *existing = wm_find_window_by_title_locked("Web Browser");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/browser.elf", NULL);
else process_create_elf("/bin/browser.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "About")) {
process_create_elf("/bin/about.elf", NULL);
process_create_elf("/bin/about.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Task Manager")) {
Window *existing = wm_find_window_by_title_locked("Task Manager");
if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/taskman.elf", NULL);
else process_create_elf("/bin/taskman.elf", NULL, false, -1);
} else if (str_starts_with(start_menu_pending_app, "Shutdown")) {
k_shutdown();
} else if (str_starts_with(start_menu_pending_app, "Restart")) {
@@ -2258,23 +2549,23 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (icon->type == 2) { // App Shortcut
// Check name to launch app
if (str_ends_with(icon->name, "Notepad.shortcut")) {
process_create_elf("/bin/notepad.elf", NULL); handled = true;
process_create_elf("/bin/notepad.elf", NULL, false, -1); handled = true;
} else if (str_ends_with(icon->name, "Calculator.shortcut")) {
process_create_elf("/bin/calculator.elf", NULL); handled = true;
process_create_elf("/bin/calculator.elf", NULL, false, -1); handled = true;
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
process_create_elf("/bin/minesweeper.elf", NULL); handled = true;
process_create_elf("/bin/minesweeper.elf", NULL, false, -1); handled = true;
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
process_create_elf("/bin/settings.elf", NULL); handled = true;
process_create_elf("/bin/settings.elf", NULL, false, -1); 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, false, -1); handled = true;
} else if (str_ends_with(icon->name, "About.shortcut")) {
process_create_elf("/bin/about.elf", NULL); handled = true;
process_create_elf("/bin/about.elf", NULL, false, -1); handled = true;
} else if (str_ends_with(icon->name, "Files.shortcut")) {
explorer_open_directory("/"); handled = true;
} else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) {
explorer_open_directory("/RecycleBin"); handled = true;
} else if (str_ends_with(icon->name, "Paint.shortcut")) {
process_create_elf("/bin/paint.elf", NULL); handled = true;
process_create_elf("/bin/paint.elf", NULL, false, -1); handled = true;
}
if (!handled) {
@@ -2293,7 +2584,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (fat32_is_directory(buf)) {
explorer_open_directory(buf);
} else {
process_create_elf("/bin/txtedit.elf", buf);
process_create_elf("/bin/txtedit.elf", buf, false, -1);
}
pending_desktop_icon_click = -1;
force_redraw = true;
@@ -2311,17 +2602,17 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
int p=14; 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, NULL);
process_create_elf(path, NULL, false, -1);
} else if (str_ends_with(icon->name, ".pnt")) {
process_create_elf("/bin/paint.elf", path);
process_create_elf("/bin/paint.elf", path, false, -1);
} else if (str_ends_with(icon->name, ".md")) {
process_create_elf("/bin/markdown.elf", path);
process_create_elf("/bin/markdown.elf", path, false, -1);
} else if (str_ends_with(icon->name, ".pdf")) {
process_create_elf("/bin/boredword.elf", path);
process_create_elf("/bin/boredword.elf", path, false, -1);
} else if (is_image_file(icon->name)) {
process_create_elf("/bin/viewer.elf", path);
process_create_elf("/bin/viewer.elf", path, false, -1);
} else {
process_create_elf("/bin/txtedit.elf", path);
process_create_elf("/bin/txtedit.elf", path, false, -1);
}
}
}
@@ -2712,9 +3003,39 @@ void wm_show_notification(const char *msg) {
force_redraw = true;
}
// Wrapper for work queue - builds file index asynchronously
static void build_file_index_async(void *arg) {
(void)arg; // Unused
file_index_build();
}
void wm_handle_key(char c, bool pressed) {
if (pressed && c == 'p' && ps2_ctrl_pressed) {
process_create_elf("/bin/screenshot.elf", NULL);
process_create_elf("/bin/screenshot.elf", NULL, false, -1);
return;
}
if (pressed && c == ' ' && ps2_ctrl_pressed && ps2_shift_pressed()) {
lumos_state.visible = !lumos_state.visible;
if (lumos_state.visible) {
// Check current index status - it may still be building in background
lumos_index_built = file_index_is_valid();
// Clear search state when opening
lumos_state.search_len = 0;
lumos_state.search_query[0] = 0;
lumos_state.cursor_pos = 0;
lumos_state.result_count = 0;
lumos_state.selected_index = 0;
}
int sw = get_screen_width();
int sh = get_screen_height();
graphics_mark_dirty(0, 0, sw, sh);
force_redraw = true;
return;
}
if (lumos_state.visible && pressed) {
wm_lumos_handle_key(c);
return;
}
@@ -2785,30 +3106,46 @@ void wm_process_deferred_thumbs(void) {
void wm_init(void) {
disk_manager_init();
disk_manager_scan();
// Drives are now dynamically managed - only real drives are registered
log_ok("Disk Manager ready");
disk_manager_scan();
log_ok("Disk scanning complete");
cmd_init();
explorer_init();
log_ok("Explorer ready");
wallpaper_init();
log_ok("Wallpaper engine ready");
file_index_init();
log_ok("File Indexer ready");
if (!file_index_load()) {
log_ok("No Index cache, background build started");
work_queue_submit(build_file_index_async, NULL);
} else {
log_ok("Index cache loaded");
lumos_index_built = true;
}
refresh_desktop_icons();
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");
kconsole_set_active(false);
graphics_flip_buffer();
}
uint32_t wm_get_ticks(void) {
@@ -2819,10 +3156,6 @@ uint32_t wm_get_ticks(void) {
void wm_timer_tick(void) {
timer_ticks++;
if (!is_dragging && !is_dragging_file) {
// Periodic refresh removed - now triggered by FS events
}
static uint8_t last_second = 0xFF;
outb(0x70, 0x00);
@@ -2831,7 +3164,7 @@ void wm_timer_tick(void) {
if (current_sec != last_second) {
last_second = current_sec;
int sw = get_screen_width();
wm_mark_dirty(sw - 110, 6, 110, 24);
wm_mark_dirty(sw - 110, 0, 110, 30);
}
if (notif_active) {
@@ -2857,4 +3190,7 @@ void wm_timer_tick(void) {
void wm_notify_fs_change(void) {
periodic_refresh_pending = true;
file_index_invalidate_cache();
lumos_index_built = false;
}

View File

@@ -36,7 +36,7 @@ void wm_lock_release(uint64_t flags);
#define COLOR_DARK_TEXT 0xFFF0F0F0 // Light gray text
#define COLOR_DARK_BORDER 0xFF3A3A3A // Border color
#define COLOR_DOCK_BG 0xFF3A3A3A // Dock background
#define COLOR_TOPBAR_BG 0xFF1A1A1A // Top bar background
#define COLOR_MENUBAR_BG 0xFF1A1A1A // Top bar background
#define COLOR_TRAFFIC_RED 0xFFED6158 // Close button red
#define COLOR_TRAFFIC_YELLOW 0xFFFCC02E // Minimize button (not used for now)
#define COLOR_TRAFFIC_GREEN 0xFF5FC038 // Zoom button (not used for now)
@@ -68,6 +68,24 @@ struct Window {
bool resizable;
};
#define LUMOS_MAX_RESULTS 6
#define LUMOS_MODAL_WIDTH 520
#define LUMOS_RESULT_HEIGHT 40
#define LUMOS_SEARCH_HEIGHT 48
#include "../sys/file_index.h"
typedef struct {
bool visible;
char search_query[256];
int search_len;
int cursor_pos;
file_index_result_t results[LUMOS_MAX_RESULTS];
int result_count;
int selected_index;
int last_query_hash;
} lumos_state_t;
void wm_init(void);
void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz);
void wm_handle_key(char c, bool pressed);