mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 10:26:59 +00:00
Compare commits
30 Commits
26.4-stabl
...
26.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e123b6429 | ||
|
|
4177484366 | ||
|
|
8dc5ee5867 | ||
|
|
884c2f8980 | ||
|
|
36d61e3b7b | ||
|
|
013f0b513f | ||
|
|
28108adde3 | ||
|
|
62ac2ab849 | ||
|
|
7f510c6aa5 | ||
|
|
7116de4152 | ||
|
|
049d67e821 | ||
|
|
0f3971bb1c | ||
|
|
66f55242a7 | ||
|
|
8a8fb7de27 | ||
|
|
914c60e1f1 | ||
|
|
5141eaea60 | ||
|
|
6e90c3e197 | ||
|
|
bdd43f43cd | ||
|
|
a8866da3cb | ||
|
|
14decdd705 | ||
|
|
ed73b88ec1 | ||
|
|
f9bc6c7c38 | ||
|
|
bb187faf79 | ||
|
|
fd7fa4f16e | ||
|
|
5bd9e537c5 | ||
|
|
e4603792b6 | ||
|
|
a27b2c6423 | ||
|
|
bb176f2193 | ||
|
|
8dd756f25b | ||
|
|
d13fca2d4a |
23
Makefile
23
Makefile
@@ -139,6 +139,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/images/gif
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/Fonts/Emoji
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/DOOM
|
||||
mkdir -p $(BUILD_DIR)/initrd/Library/bsh
|
||||
mkdir -p $(BUILD_DIR)/initrd/docs
|
||||
|
||||
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
|
||||
@@ -156,6 +157,9 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
|
||||
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
|
||||
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \
|
||||
done
|
||||
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
|
||||
@for f in $$(find docs -name '*.md' 2>/dev/null); do \
|
||||
if [ -f "$$f" ]; then \
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
286
build.log
@@ -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);
|
||||
| ^~~~
|
||||
@@ -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
33
docs/usage/booting.md
Normal 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
35
docs/usage/desktop.md
Normal 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)
|
||||
31
docs/usage/launching_apps.md
Normal file
31
docs/usage/launching_apps.md
Normal 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
29
docs/usage/lumos.md
Normal 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
60
docs/usage/terminal.md
Normal 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)
|
||||
@@ -13,3 +13,4 @@ backdrop: 000000
|
||||
/BoredOS
|
||||
protocol: limine
|
||||
path: boot():/boredos.elf
|
||||
cmdline: -v
|
||||
|
||||
95
src/core/kconsole.c
Normal file
95
src/core/kconsole.c
Normal 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
13
src/core/kconsole.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
209
src/core/main.c
209
src/core/main.c
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
541
src/fs/bootfs.c
Normal 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
13
src/fs/bootfs.h
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
201
src/fs/procfs.c
201
src/fs/procfs.c
@@ -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;
|
||||
|
||||
@@ -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
4
src/library/bsh/boot.bsh
Normal file
@@ -0,0 +1,4 @@
|
||||
# BoredShell boot script
|
||||
# Runs once on boot for the first shell.
|
||||
# Example:
|
||||
# echo "Boot script executed"
|
||||
35
src/library/bsh/bshrc
Normal file
35
src/library/bsh/bshrc
Normal file
@@ -0,0 +1,35 @@
|
||||
# BoredShell rc file
|
||||
# Location: /Library/bsh/bshrc
|
||||
#
|
||||
# Format (initial): key=value
|
||||
# Lines starting with # are comments.
|
||||
#
|
||||
# PATH controls command lookup order (colon-separated).
|
||||
PATH=/bin:/root/Apps
|
||||
|
||||
# Scripts to run automatically.
|
||||
# STARTUP runs for interactive shells.
|
||||
# BOOT_SCRIPT runs once on boot (if enabled by the shell).
|
||||
STARTUP=/Library/bsh/startup.bsh
|
||||
BOOT_SCRIPT=/Library/bsh/boot.bsh
|
||||
|
||||
# Prompt templates.
|
||||
# Tokens:
|
||||
# %n = username
|
||||
# %h = hostname
|
||||
# %~ = cwd ("~" for /root)
|
||||
# %T = time (HH:MM)
|
||||
PROMPT_LEFT=%n@%h:%~$
|
||||
PROMPT_RIGHT=%T
|
||||
|
||||
# History settings.
|
||||
HISTORY_FILE=/Library/bsh/history
|
||||
HISTORY_SIZE=200
|
||||
|
||||
# Feature toggles.
|
||||
GLOB=true
|
||||
COMPLETE=true
|
||||
SUGGEST=true
|
||||
|
||||
# Example aliases (future support).
|
||||
# alias ll=ls -la
|
||||
5
src/library/bsh/startup.bsh
Normal file
5
src/library/bsh/startup.bsh
Normal file
@@ -0,0 +1,5 @@
|
||||
# BoredShell startup script
|
||||
# Runs for interactive shells.
|
||||
# Example:
|
||||
# echo "Welcome to BoredShell"
|
||||
sysfetch
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
24
src/sys/bootfs_state.h
Normal 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
|
||||
@@ -4,15 +4,15 @@
|
||||
#ifndef CMD_H
|
||||
#define CMD_H
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
extern Window win_cmd;
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void cmd_init(void);
|
||||
void cmd_reset(void);
|
||||
|
||||
// IO Functions
|
||||
void cmd_write(const char *str);
|
||||
void cmd_write_len(const char *str, size_t len);
|
||||
void cmd_putchar(char c);
|
||||
void cmd_write_int(int n);
|
||||
void cmd_write_hex(uint64_t n);
|
||||
@@ -22,10 +22,9 @@ void cmd_screen_clear(void);
|
||||
void cmd_increment_msg_count(void);
|
||||
void cmd_reset_msg_count(void);
|
||||
|
||||
void cmd_handle_resize(Window *win, int w, int h);
|
||||
void cmd_handle_click(Window *win, int x, int y);
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key);
|
||||
void cmd_set_current_color(uint32_t color);
|
||||
void cmd_set_raw_mode(bool enabled);
|
||||
void cmd_process_finished(void);
|
||||
|
||||
#endif
|
||||
76
src/sys/cmd_stub.c
Normal file
76
src/sys/cmd_stub.c
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "cmd.h"
|
||||
#include "core/kutils.h"
|
||||
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
static void serial_write_char(char c) {
|
||||
char buf[2] = { c, 0 };
|
||||
serial_write(buf);
|
||||
}
|
||||
|
||||
void cmd_init(void) {
|
||||
}
|
||||
|
||||
void cmd_reset(void) {
|
||||
}
|
||||
|
||||
void cmd_write(const char *str) {
|
||||
if (!str) return;
|
||||
serial_write(str);
|
||||
}
|
||||
|
||||
void cmd_write_len(const char *str, size_t len) {
|
||||
if (!str || len == 0) return;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
serial_write_char(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_putchar(char c) {
|
||||
serial_write_char(c);
|
||||
}
|
||||
|
||||
void cmd_write_int(int n) {
|
||||
char buf[32];
|
||||
k_itoa(n, buf);
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
void cmd_write_hex(uint64_t n) {
|
||||
char buf[17];
|
||||
k_itoa_hex(n, buf);
|
||||
cmd_write("0x");
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
int cmd_get_cursor_col(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_screen_clear(void) {
|
||||
}
|
||||
|
||||
void cmd_increment_msg_count(void) {
|
||||
}
|
||||
|
||||
void cmd_reset_msg_count(void) {
|
||||
}
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key) {
|
||||
(void)key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_set_current_color(uint32_t color) {
|
||||
(void)color;
|
||||
}
|
||||
|
||||
void cmd_set_raw_mode(bool enabled) {
|
||||
(void)enabled;
|
||||
}
|
||||
|
||||
void cmd_process_finished(void) {
|
||||
}
|
||||
477
src/sys/file_index.c
Normal file
477
src/sys/file_index.c
Normal 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
50
src/sys/file_index.h
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
158
src/sys/tty.c
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "tty.h"
|
||||
#include "spinlock.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TTY_MAX 8
|
||||
#define TTY_OUT_SIZE 16384
|
||||
#define TTY_IN_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
bool used;
|
||||
int id;
|
||||
char out_buf[TTY_OUT_SIZE];
|
||||
uint32_t out_head;
|
||||
uint32_t out_tail;
|
||||
char in_buf[TTY_IN_SIZE];
|
||||
uint32_t in_head;
|
||||
uint32_t in_tail;
|
||||
int fg_pid;
|
||||
spinlock_t lock;
|
||||
} tty_t;
|
||||
|
||||
static tty_t ttys[TTY_MAX] = {0};
|
||||
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
|
||||
static tty_t *tty_get(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return NULL;
|
||||
if (!ttys[tty_id].used) return NULL;
|
||||
return &ttys[tty_id];
|
||||
}
|
||||
|
||||
int tty_create(void) {
|
||||
for (int i = 0; i < TTY_MAX; i++) {
|
||||
if (!ttys[i].used) {
|
||||
ttys[i].used = true;
|
||||
ttys[i].id = i;
|
||||
ttys[i].out_head = 0;
|
||||
ttys[i].out_tail = 0;
|
||||
ttys[i].in_head = 0;
|
||||
ttys[i].in_tail = 0;
|
||||
ttys[i].fg_pid = -1;
|
||||
ttys[i].lock = SPINLOCK_INIT;
|
||||
mem_memset(ttys[i].out_buf, 0, sizeof(ttys[i].out_buf));
|
||||
mem_memset(ttys[i].in_buf, 0, sizeof(ttys[i].in_buf));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tty_destroy(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return -1;
|
||||
tty_t *tty = &ttys[tty_id];
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
if (!tty->used) {
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tty->used = false;
|
||||
tty->id = -1;
|
||||
tty->out_head = 0;
|
||||
tty->out_tail = 0;
|
||||
tty->in_head = 0;
|
||||
tty->in_tail = 0;
|
||||
tty->fg_pid = -1;
|
||||
mem_memset(tty->out_buf, 0, sizeof(tty->out_buf));
|
||||
mem_memset(tty->in_buf, 0, sizeof(tty->in_buf));
|
||||
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tty_write_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, const char *data, size_t len) {
|
||||
int written = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint32_t next = (*head + 1) % size;
|
||||
if (next == *tail) break;
|
||||
buf[*head] = data[i];
|
||||
*head = next;
|
||||
written++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static int tty_read_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, char *out, size_t max_len) {
|
||||
int read = 0;
|
||||
while (*tail != *head && (size_t)read < max_len) {
|
||||
out[read++] = buf[*tail];
|
||||
*tail = (*tail + 1) % size;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_output(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_input(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_set_foreground(int tty_id, int pid) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
tty->fg_pid = pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty_get_foreground(int tty_id) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int pid = tty->fg_pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return pid;
|
||||
}
|
||||
18
src/sys/tty.h
Normal file
18
src/sys/tty.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef TTY_H
|
||||
#define TTY_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int tty_create(void);
|
||||
int tty_destroy(int tty_id);
|
||||
int tty_write_output(int tty_id, const char *data, size_t len);
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len);
|
||||
int tty_write_input(int tty_id, const char *data, size_t len);
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len);
|
||||
int tty_set_foreground(int tty_id, int pid);
|
||||
int tty_get_foreground(int tty_id);
|
||||
|
||||
#endif
|
||||
1454
src/userland/cli/bsh.c
Normal file
1454
src/userland/cli/bsh.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: cat <filename>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = sys_open(argv[1], "r");
|
||||
if (fd < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot open %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write(1, buffer, bytes);
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
void combine_path(char *dest, const char *path1, const char *path2) {
|
||||
int i = 0;
|
||||
while (path1[i]) {
|
||||
dest[i] = path1[i];
|
||||
i++;
|
||||
}
|
||||
if (i > 0 && dest[i-1] != '/') {
|
||||
dest[i++] = '/';
|
||||
}
|
||||
int j = 0;
|
||||
while (path2[j]) {
|
||||
dest[i++] = path2[j++];
|
||||
}
|
||||
dest[i] = 0;
|
||||
}
|
||||
|
||||
const char* get_basename(const char *path) {
|
||||
const char *last_slash = NULL;
|
||||
int len = 0;
|
||||
while (path[len]) {
|
||||
if (path[len] == '/') last_slash = path + len;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!last_slash) return path;
|
||||
|
||||
// If it ends with a slash, skip it and find the previous one
|
||||
if (last_slash[1] == '\0') {
|
||||
if (len <= 1) return path; // root "/"
|
||||
int i = len - 2;
|
||||
while (i >= 0 && path[i] != '/') i--;
|
||||
if (i < 0) return path;
|
||||
return path + i + 1;
|
||||
}
|
||||
|
||||
return last_slash + 1;
|
||||
}
|
||||
|
||||
void copy_recursive(const char *src, const char *dst) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(src, &info) < 0) {
|
||||
printf("Error: Cannot get info for %s\n", src);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.is_directory) {
|
||||
if (sys_mkdir(dst) < 0) {
|
||||
// Might already exist
|
||||
}
|
||||
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(src, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
|
||||
char sub_src[512], sub_dst[512];
|
||||
combine_path(sub_src, src, entries[i].name);
|
||||
combine_path(sub_dst, dst, entries[i].name);
|
||||
copy_recursive(sub_src, sub_dst);
|
||||
}
|
||||
} else {
|
||||
int fd_in = sys_open(src, "r");
|
||||
if (fd_in < 0) {
|
||||
printf("Error: Cannot open source %s\n", src);
|
||||
return;
|
||||
}
|
||||
int fd_out = sys_open(dst, "w");
|
||||
if (fd_out < 0) {
|
||||
printf("Error: Cannot create destination %s\n", dst);
|
||||
sys_close(fd_in);
|
||||
return;
|
||||
}
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool recursive = false;
|
||||
char *src_path = NULL;
|
||||
char *dst_path = NULL;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-r") == 0) {
|
||||
recursive = true;
|
||||
} else if (!src_path) {
|
||||
src_path = argv[i];
|
||||
} else if (!dst_path) {
|
||||
dst_path = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!src_path || !dst_path) {
|
||||
printf("Usage: cp [-r] <source> <dest>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FAT32_FileInfo info_src;
|
||||
if (sys_get_file_info(src_path, &info_src) < 0) {
|
||||
printf("Error: Source %s does not exist\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info_src.is_directory && !recursive) {
|
||||
printf("Error: %s is a directory (use -r to copy recursively)\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char actual_dst[512];
|
||||
FAT32_FileInfo info_dst;
|
||||
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
|
||||
// If destination is a directory, copy INTO it
|
||||
const char *base = get_basename(src_path);
|
||||
// Clean up trailing slash from basename if any (get_basename handles it mostly)
|
||||
char clean_base[256];
|
||||
int k = 0;
|
||||
while (base[k] && base[k] != '/') {
|
||||
clean_base[k] = base[k];
|
||||
k++;
|
||||
}
|
||||
clean_base[k] = 0;
|
||||
combine_path(actual_dst, dst_path, clean_base);
|
||||
} else {
|
||||
strcpy(actual_dst, dst_path);
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
copy_recursive(src_path, actual_dst);
|
||||
} else {
|
||||
int fd_in = sys_open(src_path, "r");
|
||||
if (fd_in < 0) {
|
||||
printf("Error: Cannot open source %s\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
int fd_out = sys_open(actual_dst, "w");
|
||||
if (fd_out < 0) {
|
||||
printf("Error: Cannot create destination %s\n", actual_dst);
|
||||
sys_close(fd_in);
|
||||
return 1;
|
||||
}
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64_t dir_color = sys_get_shell_config("dir_color");
|
||||
uint64_t file_color = sys_get_shell_config("file_color");
|
||||
uint64_t size_color = sys_get_shell_config("size_color");
|
||||
uint64_t error_color = sys_get_shell_config("error_color");
|
||||
uint64_t default_color = sys_get_shell_config("default_text_color");
|
||||
|
||||
char path[256];
|
||||
if (argc > 1) {
|
||||
strcpy(path, argv[1]);
|
||||
} else {
|
||||
if (!sys_getcwd(path, sizeof(path))) {
|
||||
strcpy(path, "/");
|
||||
}
|
||||
}
|
||||
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(path, &info) < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Path '%s' does not exist\n", path);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!info.is_directory) {
|
||||
sys_set_text_color(file_color);
|
||||
printf("[FILE] %s", info.name);
|
||||
sys_set_text_color(size_color);
|
||||
printf(" (%d bytes)\n", info.size);
|
||||
sys_set_text_color(default_color);
|
||||
printf("\nTotal: 1 items\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FAT32_FileInfo entries[128];
|
||||
int count = sys_list(path, entries, 128);
|
||||
|
||||
if (count < 0) {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot list directory %s\n", path);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (entries[i].is_directory) {
|
||||
sys_set_text_color(dir_color);
|
||||
printf("[DIR] %s\n", entries[i].name);
|
||||
} else {
|
||||
sys_set_text_color(file_color);
|
||||
printf("[FILE] %s", entries[i].name);
|
||||
sys_set_text_color(size_color);
|
||||
printf(" (%d bytes)\n", entries[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
sys_set_text_color(default_color);
|
||||
printf("\nTotal: %d items\n", count);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: mkdir <dirname>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sys_mkdir(argv[1]) == 0) {
|
||||
sys_set_text_color(success_color);
|
||||
printf("Created directory: %s\n", argv[1]);
|
||||
} else {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot create directory %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
sys_set_text_color(default_color);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
void combine_path(char *dest, const char *path1, const char *path2) {
|
||||
int i = 0;
|
||||
while (path1[i]) {
|
||||
dest[i] = path1[i];
|
||||
i++;
|
||||
}
|
||||
if (i > 0 && dest[i-1] != '/') {
|
||||
dest[i++] = '/';
|
||||
}
|
||||
int j = 0;
|
||||
while (path2[j]) {
|
||||
dest[i++] = path2[j++];
|
||||
}
|
||||
dest[i] = 0;
|
||||
}
|
||||
|
||||
const char* get_basename(const char *path) {
|
||||
const char *last_slash = NULL;
|
||||
int len = 0;
|
||||
while (path[len]) {
|
||||
if (path[len] == '/') last_slash = path + len;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!last_slash) return path;
|
||||
|
||||
if (last_slash[1] == '\0') {
|
||||
if (len <= 1) return path;
|
||||
int i = len - 2;
|
||||
while (i >= 0 && path[i] != '/') i--;
|
||||
if (i < 0) return path;
|
||||
return path + i + 1;
|
||||
}
|
||||
|
||||
return last_slash + 1;
|
||||
}
|
||||
|
||||
void copy_recursive(const char *src, const char *dst) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(src, &info) < 0) return;
|
||||
|
||||
if (info.is_directory) {
|
||||
sys_mkdir(dst);
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(src, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
char sub_src[512], sub_dst[512];
|
||||
combine_path(sub_src, src, entries[i].name);
|
||||
combine_path(sub_dst, dst, entries[i].name);
|
||||
copy_recursive(sub_src, sub_dst);
|
||||
}
|
||||
} else {
|
||||
int fd_in = sys_open(src, "r");
|
||||
if (fd_in < 0) return;
|
||||
int fd_out = sys_open(dst, "w");
|
||||
if (fd_out < 0) { sys_close(fd_in); return; }
|
||||
char buffer[4096];
|
||||
int bytes;
|
||||
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
|
||||
sys_write_fs(fd_out, buffer, bytes);
|
||||
}
|
||||
sys_close(fd_in);
|
||||
sys_close(fd_out);
|
||||
}
|
||||
}
|
||||
|
||||
void delete_recursive(const char *path) {
|
||||
FAT32_FileInfo info;
|
||||
if (sys_get_file_info(path, &info) < 0) return;
|
||||
|
||||
if (info.is_directory) {
|
||||
FAT32_FileInfo entries[64];
|
||||
int count = sys_list(path, entries, 64);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
|
||||
char sub_path[512];
|
||||
combine_path(sub_path, path, entries[i].name);
|
||||
delete_recursive(sub_path);
|
||||
}
|
||||
sys_delete(path);
|
||||
} else {
|
||||
sys_delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
printf("Usage: mv <source> <dest>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *src_path = argv[1];
|
||||
char *dst_path = argv[2];
|
||||
|
||||
FAT32_FileInfo info_src;
|
||||
if (sys_get_file_info(src_path, &info_src) < 0) {
|
||||
printf("Error: Cannot open source %s\n", src_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char actual_dst[512];
|
||||
FAT32_FileInfo info_dst;
|
||||
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
|
||||
const char *base = get_basename(src_path);
|
||||
char clean_base[256];
|
||||
int k = 0;
|
||||
while (base[k] && base[k] != '/') {
|
||||
clean_base[k] = base[k];
|
||||
k++;
|
||||
}
|
||||
clean_base[k] = 0;
|
||||
combine_path(actual_dst, dst_path, clean_base);
|
||||
} else {
|
||||
strcpy(actual_dst, dst_path);
|
||||
}
|
||||
|
||||
copy_recursive(src_path, actual_dst);
|
||||
delete_recursive(src_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
|
||||
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
|
||||
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: rm <path>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Simple rm (no recursive support yet for simplicity, but can be added)
|
||||
if (sys_delete(argv[1]) == 0) {
|
||||
sys_set_text_color(success_color);
|
||||
printf("Deleted: %s\n", argv[1]);
|
||||
} else {
|
||||
sys_set_text_color(error_color);
|
||||
printf("Error: Cannot delete %s\n", argv[1]);
|
||||
sys_set_text_color(default_color);
|
||||
return 1;
|
||||
}
|
||||
sys_set_text_color(default_color);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: touch <filename>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if file already exists
|
||||
if (sys_exists(argv[1])) {
|
||||
// Just return success if it exists (simplification)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd = sys_open(argv[1], "w");
|
||||
if (fd < 0) {
|
||||
printf("Error: Cannot create %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
}
|
||||
236
src/userland/cli/uname.c
Normal file
236
src/userland/cli/uname.c
Normal 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
519
src/userland/games/2048.c
Normal 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
893
src/userland/gui/terminal.c
Normal file
@@ -0,0 +1,893 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "libc/libui.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_COLS 116
|
||||
#define DEFAULT_ROWS 41
|
||||
#define SCROLLBACK_LINES 800
|
||||
#define SCROLLBACK_COLS 256
|
||||
#define CHAR_W 8
|
||||
#define DEFAULT_LINE_H 10
|
||||
#define TAB_BAR_H 20
|
||||
#define CONTENT_PAD_BOTTOM 20
|
||||
#define TAB_CLOSE_W 12
|
||||
#define TAB_CLOSE_PAD 6
|
||||
#define MAX_TABS 4
|
||||
#define TTY_READ_CHUNK 512
|
||||
|
||||
typedef struct {
|
||||
char c;
|
||||
uint32_t color;
|
||||
} CharCell;
|
||||
|
||||
typedef struct {
|
||||
int tty_id;
|
||||
int bsh_pid;
|
||||
CharCell *cells;
|
||||
CharCell *scrollback;
|
||||
int *scroll_cols;
|
||||
int scroll_head;
|
||||
int scroll_count;
|
||||
int scroll_offset;
|
||||
int cursor_row;
|
||||
int cursor_col;
|
||||
uint32_t fg_color;
|
||||
uint32_t bg_color;
|
||||
|
||||
int ansi_state;
|
||||
int ansi_params[8];
|
||||
int ansi_param_count;
|
||||
int saved_row;
|
||||
int saved_col;
|
||||
} TerminalSession;
|
||||
|
||||
static ui_window_t g_win;
|
||||
static TerminalSession g_tabs[MAX_TABS];
|
||||
static int g_tab_count = 0;
|
||||
static int g_active_tab = 0;
|
||||
static int g_cols = DEFAULT_COLS;
|
||||
static int g_rows = DEFAULT_ROWS;
|
||||
static int g_line_h = DEFAULT_LINE_H;
|
||||
static int g_win_w = DEFAULT_COLS * CHAR_W;
|
||||
static int g_win_h = TAB_BAR_H + (DEFAULT_ROWS * DEFAULT_LINE_H);
|
||||
|
||||
static void str_copy(char *dst, const char *src, int max_len) {
|
||||
int i = 0;
|
||||
if (max_len <= 0) return;
|
||||
while (i < max_len - 1 && src && src[i]) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
static void str_append(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = (int)strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void trim_end(char *s) {
|
||||
if (!s) return;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || s[len - 1] == ' ' || s[len - 1] == '\t')) {
|
||||
s[len - 1] = 0;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void scrollback_init(TerminalSession *s) {
|
||||
s->scroll_head = 0;
|
||||
s->scroll_count = 0;
|
||||
s->scroll_offset = 0;
|
||||
if (s->scrollback) {
|
||||
for (int i = 0; i < SCROLLBACK_LINES * SCROLLBACK_COLS; i++) {
|
||||
s->scrollback[i].c = ' ';
|
||||
s->scrollback[i].color = s->fg_color;
|
||||
}
|
||||
}
|
||||
if (s->scroll_cols) {
|
||||
for (int i = 0; i < SCROLLBACK_LINES; i++) s->scroll_cols[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void scrollback_push_row(TerminalSession *s, const CharCell *row, int cols) {
|
||||
if (!s->scrollback || !s->scroll_cols) return;
|
||||
int idx = s->scroll_head;
|
||||
CharCell *dest = s->scrollback + (idx * SCROLLBACK_COLS);
|
||||
int copy_cols = cols;
|
||||
if (copy_cols > SCROLLBACK_COLS) copy_cols = SCROLLBACK_COLS;
|
||||
|
||||
for (int i = 0; i < SCROLLBACK_COLS; i++) {
|
||||
dest[i].c = ' ';
|
||||
dest[i].color = s->fg_color;
|
||||
}
|
||||
for (int i = 0; i < copy_cols; i++) {
|
||||
dest[i] = row[i];
|
||||
}
|
||||
|
||||
s->scroll_cols[idx] = copy_cols;
|
||||
s->scroll_head = (idx + 1) % SCROLLBACK_LINES;
|
||||
if (s->scroll_count < SCROLLBACK_LINES) {
|
||||
s->scroll_count++;
|
||||
} else if (s->scroll_offset > 0) {
|
||||
s->scroll_offset--;
|
||||
}
|
||||
|
||||
if (s->scroll_offset > 0) {
|
||||
s->scroll_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
static int scrollback_max_offset(TerminalSession *s) {
|
||||
int total = s->scroll_count + g_rows;
|
||||
if (total <= g_rows) return 0;
|
||||
return total - g_rows;
|
||||
}
|
||||
|
||||
static void session_adjust_scroll(TerminalSession *s, int delta) {
|
||||
if (!s) return;
|
||||
int max_offset = scrollback_max_offset(s);
|
||||
s->scroll_offset += delta;
|
||||
if (s->scroll_offset < 0) s->scroll_offset = 0;
|
||||
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
|
||||
}
|
||||
|
||||
static CharCell *scrollback_get_line(TerminalSession *s, int line_index, int *out_cols) {
|
||||
if (!s || line_index < 0 || line_index >= s->scroll_count) return NULL;
|
||||
int start = s->scroll_head - s->scroll_count;
|
||||
if (start < 0) start += SCROLLBACK_LINES;
|
||||
int idx = (start + line_index) % SCROLLBACK_LINES;
|
||||
if (out_cols) *out_cols = s->scroll_cols[idx];
|
||||
return s->scrollback + (idx * SCROLLBACK_COLS);
|
||||
}
|
||||
|
||||
static uint32_t ansi_color_16(int idx) {
|
||||
static uint32_t base[16] = {
|
||||
0xFF000000, 0xFFAA0000, 0xFF00AA00, 0xFFAA5500,
|
||||
0xFF0000AA, 0xFFAA00AA, 0xFF00AAAA, 0xFFAAAAAA,
|
||||
0xFF555555, 0xFFFF5555, 0xFF55FF55, 0xFFFFFF55,
|
||||
0xFF5555FF, 0xFFFF55FF, 0xFF55FFFF, 0xFFFFFFFF
|
||||
};
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx > 15) idx = 15;
|
||||
return base[idx];
|
||||
}
|
||||
|
||||
static void session_reset_colors(TerminalSession *s) {
|
||||
s->fg_color = 0xFFFFFFFF;
|
||||
s->bg_color = 0xFF1E1E1E;
|
||||
}
|
||||
|
||||
static void session_clear(TerminalSession *s) {
|
||||
for (int i = 0; i < g_cols * g_rows; i++) {
|
||||
s->cells[i].c = ' ';
|
||||
s->cells[i].color = s->fg_color;
|
||||
}
|
||||
s->cursor_row = 0;
|
||||
s->cursor_col = 0;
|
||||
s->scroll_offset = 0;
|
||||
}
|
||||
|
||||
static void session_scroll(TerminalSession *s) {
|
||||
int row_bytes = g_cols * (int)sizeof(CharCell);
|
||||
if (g_rows > 0) {
|
||||
scrollback_push_row(s, s->cells, g_cols);
|
||||
}
|
||||
memmove(s->cells, s->cells + g_cols, row_bytes * (g_rows - 1));
|
||||
for (int i = 0; i < g_cols; i++) {
|
||||
int idx = (g_rows - 1) * g_cols + i;
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
s->cursor_row = g_rows - 1;
|
||||
s->cursor_col = 0;
|
||||
}
|
||||
|
||||
static void session_put_char(TerminalSession *s, char c) {
|
||||
if (c == '\n') {
|
||||
s->cursor_row++;
|
||||
s->cursor_col = 0;
|
||||
if (s->cursor_row >= g_rows) session_scroll(s);
|
||||
return;
|
||||
}
|
||||
if (c == '\r') {
|
||||
s->cursor_col = 0;
|
||||
return;
|
||||
}
|
||||
if (c == '\b') {
|
||||
if (s->cursor_col > 0) s->cursor_col--;
|
||||
int idx = s->cursor_row * g_cols + s->cursor_col;
|
||||
if (idx >= 0 && idx < g_cols * g_rows) {
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (c == '\t') {
|
||||
int next = ((s->cursor_col / 4) + 1) * 4;
|
||||
while (s->cursor_col < next) {
|
||||
session_put_char(s, ' ');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (c < 32) return;
|
||||
|
||||
int idx = s->cursor_row * g_cols + s->cursor_col;
|
||||
if (idx >= 0 && idx < g_cols * g_rows) {
|
||||
s->cells[idx].c = c;
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
s->cursor_col++;
|
||||
if (s->cursor_col >= g_cols) {
|
||||
s->cursor_col = 0;
|
||||
s->cursor_row++;
|
||||
if (s->cursor_row >= g_rows) session_scroll(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void ansi_handle_sgr(TerminalSession *s) {
|
||||
if (s->ansi_param_count == 0) {
|
||||
session_reset_colors(s);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < s->ansi_param_count; i++) {
|
||||
int p = s->ansi_params[i];
|
||||
if (p == 0) session_reset_colors(s);
|
||||
else if (p >= 30 && p <= 37) s->fg_color = ansi_color_16(p - 30);
|
||||
else if (p >= 90 && p <= 97) s->fg_color = ansi_color_16(8 + (p - 90));
|
||||
else if (p >= 40 && p <= 47) s->bg_color = ansi_color_16(p - 40);
|
||||
else if (p >= 100 && p <= 107) s->bg_color = ansi_color_16(8 + (p - 100));
|
||||
else if (p == 38 || p == 48) {
|
||||
if (i + 4 < s->ansi_param_count && s->ansi_params[i + 1] == 2) {
|
||||
int r = s->ansi_params[i + 2] & 0xFF;
|
||||
int g = s->ansi_params[i + 3] & 0xFF;
|
||||
int b = s->ansi_params[i + 4] & 0xFF;
|
||||
uint32_t color = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
|
||||
if (p == 38) s->fg_color = color;
|
||||
else s->bg_color = color;
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ansi_finalize(TerminalSession *s, char cmd) {
|
||||
if (cmd == 'm') {
|
||||
ansi_handle_sgr(s);
|
||||
} else if (cmd == 'J') {
|
||||
int mode = s->ansi_param_count > 0 ? s->ansi_params[0] : 0;
|
||||
if (mode == 2 || mode == 0) session_clear(s);
|
||||
} else if (cmd == 'K') {
|
||||
int row = s->cursor_row;
|
||||
for (int col = s->cursor_col; col < g_cols; col++) {
|
||||
int idx = row * g_cols + col;
|
||||
s->cells[idx].c = ' ';
|
||||
s->cells[idx].color = s->fg_color;
|
||||
}
|
||||
} else if (cmd == 'H' || cmd == 'f') {
|
||||
int row = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
int col = (s->ansi_param_count > 1) ? s->ansi_params[1] : 1;
|
||||
if (row < 1) row = 1;
|
||||
if (col < 1) col = 1;
|
||||
s->cursor_row = row - 1;
|
||||
s->cursor_col = col - 1;
|
||||
} else if (cmd == 'A') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_row -= n;
|
||||
if (s->cursor_row < 0) s->cursor_row = 0;
|
||||
} else if (cmd == 'B') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_row += n;
|
||||
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
|
||||
} else if (cmd == 'C') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_col += n;
|
||||
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
|
||||
} else if (cmd == 'D') {
|
||||
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
|
||||
s->cursor_col -= n;
|
||||
if (s->cursor_col < 0) s->cursor_col = 0;
|
||||
} else if (cmd == 's') {
|
||||
s->saved_row = s->cursor_row;
|
||||
s->saved_col = s->cursor_col;
|
||||
} else if (cmd == 'u') {
|
||||
s->cursor_row = s->saved_row;
|
||||
s->cursor_col = s->saved_col;
|
||||
}
|
||||
|
||||
s->ansi_state = 0;
|
||||
s->ansi_param_count = 0;
|
||||
}
|
||||
|
||||
static void session_process_char(TerminalSession *s, char c) {
|
||||
if (s->ansi_state == 0) {
|
||||
if (c == 27) {
|
||||
s->ansi_state = 1;
|
||||
s->ansi_param_count = 0;
|
||||
s->ansi_params[0] = 0;
|
||||
return;
|
||||
}
|
||||
session_put_char(s, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->ansi_state == 1) {
|
||||
if (c == '[') {
|
||||
s->ansi_state = 2;
|
||||
s->ansi_param_count = 0;
|
||||
s->ansi_params[0] = 0;
|
||||
return;
|
||||
}
|
||||
s->ansi_state = 0;
|
||||
session_put_char(s, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->ansi_state == 2) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
int idx = s->ansi_param_count;
|
||||
s->ansi_params[idx] = s->ansi_params[idx] * 10 + (c - '0');
|
||||
return;
|
||||
}
|
||||
if (c == ';') {
|
||||
if (s->ansi_param_count < 7) {
|
||||
s->ansi_param_count++;
|
||||
s->ansi_params[s->ansi_param_count] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
s->ansi_param_count++;
|
||||
ansi_finalize(s, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void session_process_output(TerminalSession *s, const char *buf, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
session_process_char(s, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_tab_width(void) {
|
||||
if (g_tab_count <= 0) return g_win_w;
|
||||
int tab_w = g_win_w / g_tab_count;
|
||||
if (tab_w < 60) tab_w = 60;
|
||||
return tab_w;
|
||||
}
|
||||
|
||||
static int read_proc_field(int pid, const char *field, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return -1;
|
||||
char path[64];
|
||||
path[0] = 0;
|
||||
str_append(path, "/proc/", sizeof(path));
|
||||
char pid_buf[16];
|
||||
itoa(pid, pid_buf);
|
||||
str_append(path, pid_buf, sizeof(path));
|
||||
str_append(path, "/", sizeof(path));
|
||||
str_append(path, field, sizeof(path));
|
||||
|
||||
int fd = sys_open(path, "r");
|
||||
if (fd < 0) return -1;
|
||||
int bytes = sys_read(fd, out, max_len - 1);
|
||||
sys_close(fd);
|
||||
if (bytes <= 0) return -1;
|
||||
out[bytes] = 0;
|
||||
trim_end(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void truncate_label(const char *src, char *out, int max_chars) {
|
||||
if (!out || max_chars <= 0) return;
|
||||
int len = (int)strlen(src);
|
||||
if (len <= max_chars) {
|
||||
str_copy(out, src, max_chars + 1);
|
||||
return;
|
||||
}
|
||||
if (max_chars <= 3) {
|
||||
for (int i = 0; i < max_chars; i++) out[i] = src[i];
|
||||
out[max_chars] = 0;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < max_chars - 3; i++) out[i] = src[i];
|
||||
out[max_chars - 3] = '.';
|
||||
out[max_chars - 2] = '.';
|
||||
out[max_chars - 1] = '.';
|
||||
out[max_chars] = 0;
|
||||
}
|
||||
|
||||
static void get_tab_title(TerminalSession *s, char *out, int max_len) {
|
||||
if (!s || !out) return;
|
||||
int fg = sys_tty_get_fg(s->tty_id);
|
||||
if (fg > 0) {
|
||||
if (read_proc_field(fg, "name", out, max_len) == 0) return;
|
||||
}
|
||||
if (s->bsh_pid > 0) {
|
||||
if (read_proc_field(s->bsh_pid, "cwd", out, max_len) == 0) return;
|
||||
}
|
||||
str_copy(out, "Bsh", max_len);
|
||||
}
|
||||
|
||||
static int read_config_value(const char *key, char *out, int max_len) {
|
||||
if (!key || !out || max_len <= 0) return -1;
|
||||
int fd = sys_open("/Library/bsh/bshrc", "r");
|
||||
if (fd < 0) return -1;
|
||||
|
||||
char buf[4096];
|
||||
int bytes = sys_read(fd, buf, sizeof(buf) - 1);
|
||||
sys_close(fd);
|
||||
if (bytes <= 0) return -1;
|
||||
buf[bytes] = 0;
|
||||
|
||||
char *line = buf;
|
||||
while (*line) {
|
||||
char *end = line;
|
||||
while (*end && *end != '\n' && *end != '\r') end++;
|
||||
char saved = *end;
|
||||
*end = 0;
|
||||
|
||||
trim_end(line);
|
||||
if (line[0] != '#' && line[0] != 0) {
|
||||
char *sep = line;
|
||||
while (*sep && *sep != '=') sep++;
|
||||
if (*sep == '=') {
|
||||
*sep = 0;
|
||||
if (strcmp(line, key) == 0) {
|
||||
str_copy(out, sep + 1, max_len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line = end + (saved ? 1 : 0);
|
||||
if (saved == '\r' && *line == '\n') line++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void resolve_bsh_path(char *out, int max_len) {
|
||||
char path_line[256];
|
||||
if (read_config_value("PATH", path_line, sizeof(path_line)) != 0) {
|
||||
str_copy(path_line, "/bin", sizeof(path_line));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int start = 0;
|
||||
while (1) {
|
||||
if (path_line[i] == ':' || path_line[i] == 0) {
|
||||
int len = i - start;
|
||||
if (len > 0) {
|
||||
char base[128];
|
||||
if (len >= (int)sizeof(base)) len = (int)sizeof(base) - 1;
|
||||
for (int j = 0; j < len; j++) base[j] = path_line[start + j];
|
||||
base[len] = 0;
|
||||
|
||||
char candidate[160];
|
||||
candidate[0] = 0;
|
||||
str_append(candidate, base, sizeof(candidate));
|
||||
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
|
||||
str_append(candidate, "bsh.elf", sizeof(candidate));
|
||||
if (sys_exists(candidate)) {
|
||||
str_copy(out, candidate, max_len);
|
||||
return;
|
||||
}
|
||||
|
||||
candidate[0] = 0;
|
||||
str_append(candidate, base, sizeof(candidate));
|
||||
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
|
||||
str_append(candidate, "bsh", sizeof(candidate));
|
||||
if (sys_exists(candidate)) {
|
||||
str_copy(out, candidate, max_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
start = i + 1;
|
||||
}
|
||||
if (path_line[i] == 0) break;
|
||||
i++;
|
||||
}
|
||||
|
||||
str_copy(out, "/bin/bsh.elf", max_len);
|
||||
}
|
||||
|
||||
static bool has_space(const char *s) {
|
||||
if (!s) return false;
|
||||
while (*s) {
|
||||
if (*s == ' ') return true;
|
||||
s++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void terminal_resize(int w, int h) {
|
||||
int min_w = CHAR_W * 40;
|
||||
int min_h = TAB_BAR_H + (g_line_h * 10);
|
||||
if (w < min_w) w = min_w;
|
||||
if (h < min_h) h = min_h;
|
||||
|
||||
g_win_w = w;
|
||||
g_win_h = h;
|
||||
|
||||
int new_cols = w / CHAR_W;
|
||||
int content_h = h - TAB_BAR_H - CONTENT_PAD_BOTTOM;
|
||||
if (g_line_h <= 0) g_line_h = DEFAULT_LINE_H;
|
||||
if (content_h < g_line_h) content_h = g_line_h;
|
||||
int new_rows = content_h / g_line_h;
|
||||
if (new_cols < 10) new_cols = 10;
|
||||
if (new_rows < 5) new_rows = 5;
|
||||
|
||||
if (new_cols == g_cols && new_rows == g_rows) return;
|
||||
|
||||
int old_cols = g_cols;
|
||||
int old_rows = g_rows;
|
||||
g_cols = new_cols;
|
||||
g_rows = new_rows;
|
||||
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
TerminalSession *s = &g_tabs[i];
|
||||
int old_scroll_count = s->scroll_count;
|
||||
int old_scroll_offset = s->scroll_offset;
|
||||
int old_total_lines = old_scroll_count + old_rows;
|
||||
int old_bottom_line = old_total_lines - 1 - old_scroll_offset;
|
||||
int old_top_line = old_bottom_line - (old_rows - 1);
|
||||
CharCell *old_cells = s->cells;
|
||||
int old_cursor_row = s->cursor_row;
|
||||
int old_cursor_col = s->cursor_col;
|
||||
|
||||
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
|
||||
for (int j = 0; j < g_cols * g_rows; j++) {
|
||||
s->cells[j].c = ' ';
|
||||
s->cells[j].color = s->fg_color;
|
||||
}
|
||||
|
||||
int row_start = 0;
|
||||
if (old_rows > g_rows) {
|
||||
if (old_cursor_row >= g_rows) {
|
||||
row_start = old_cursor_row - (g_rows - 1);
|
||||
} else {
|
||||
row_start = 0;
|
||||
}
|
||||
int max_start = old_rows - g_rows;
|
||||
if (row_start > max_start) row_start = max_start;
|
||||
if (row_start < 0) row_start = 0;
|
||||
}
|
||||
int dropped = 0;
|
||||
if (row_start > 0) {
|
||||
int projected = old_scroll_count + row_start;
|
||||
if (projected > SCROLLBACK_LINES) dropped = projected - SCROLLBACK_LINES;
|
||||
}
|
||||
int desired_top_line = old_top_line - dropped;
|
||||
if (desired_top_line < 0) desired_top_line = 0;
|
||||
for (int r = 0; r < row_start; r++) {
|
||||
scrollback_push_row(s, old_cells + (r * old_cols), old_cols);
|
||||
}
|
||||
|
||||
int copy_rows = old_rows;
|
||||
if (copy_rows > g_rows) copy_rows = g_rows;
|
||||
int copy_cols = old_cols < g_cols ? old_cols : g_cols;
|
||||
for (int r = 0; r < copy_rows; r++) {
|
||||
CharCell *src = old_cells + ((row_start + r) * old_cols);
|
||||
CharCell *dst = s->cells + (r * g_cols);
|
||||
for (int c = 0; c < copy_cols; c++) {
|
||||
dst[c] = src[c];
|
||||
}
|
||||
}
|
||||
|
||||
s->cursor_row = old_cursor_row - row_start;
|
||||
if (old_rows <= g_rows) s->cursor_row = old_cursor_row;
|
||||
if (s->cursor_row < 0) s->cursor_row = 0;
|
||||
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
|
||||
|
||||
s->cursor_col = old_cursor_col;
|
||||
if (s->cursor_col < 0) s->cursor_col = 0;
|
||||
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
|
||||
|
||||
if (old_scroll_offset == 0) {
|
||||
s->scroll_offset = 0;
|
||||
} else {
|
||||
int new_total_lines = s->scroll_count + g_rows;
|
||||
int desired_bottom = desired_top_line + (g_rows - 1);
|
||||
s->scroll_offset = (new_total_lines - 1) - desired_bottom;
|
||||
}
|
||||
session_adjust_scroll(s, 0);
|
||||
|
||||
if (old_cells) free(old_cells);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_tabs(void) {
|
||||
if (g_tab_count <= 0) return;
|
||||
ui_draw_rect(g_win, 0, 0, g_win_w, TAB_BAR_H, 0xFF1A1A1A);
|
||||
int tab_w = get_tab_width();
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
int x = i * tab_w;
|
||||
uint32_t bg = (i == g_active_tab) ? 0xFF333333 : 0xFF222222;
|
||||
ui_draw_rect(g_win, x, 0, tab_w - 2, TAB_BAR_H, bg);
|
||||
int close_size = TAB_CLOSE_W;
|
||||
int close_x = x + tab_w - TAB_CLOSE_PAD - close_size;
|
||||
int close_y = (TAB_BAR_H - close_size) / 2;
|
||||
uint32_t close_bg = (i == g_active_tab) ? 0xFF444444 : 0xFF333333;
|
||||
ui_draw_rect(g_win, close_x, close_y, close_size, close_size, close_bg);
|
||||
ui_draw_string(g_win, close_x + 2, close_y + 1, "x", 0xFFFFFFFF);
|
||||
char title[64];
|
||||
char label[64];
|
||||
get_tab_title(&g_tabs[i], title, sizeof(title));
|
||||
int text_w = tab_w - 10 - (TAB_CLOSE_PAD + TAB_CLOSE_W);
|
||||
int max_chars = text_w / CHAR_W;
|
||||
if (max_chars < 4) max_chars = 4;
|
||||
truncate_label(title, label, max_chars);
|
||||
ui_draw_string(g_win, x + 6, 4, label, 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_session(TerminalSession *s) {
|
||||
int base_y = TAB_BAR_H;
|
||||
ui_draw_rect(g_win, 0, base_y, g_win_w, g_win_h - base_y, s->bg_color);
|
||||
|
||||
int max_offset = scrollback_max_offset(s);
|
||||
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
|
||||
int total_lines = s->scroll_count + g_rows;
|
||||
int bottom_line = total_lines - 1 - s->scroll_offset;
|
||||
int top_line = bottom_line - (g_rows - 1);
|
||||
|
||||
for (int row = 0; row < g_rows; row++) {
|
||||
int line_index = top_line + row;
|
||||
CharCell *line = NULL;
|
||||
int line_cols = 0;
|
||||
if (line_index >= 0 && line_index < s->scroll_count) {
|
||||
line = scrollback_get_line(s, line_index, &line_cols);
|
||||
} else if (line_index >= s->scroll_count && line_index < total_lines) {
|
||||
int live_row = line_index - s->scroll_count;
|
||||
if (live_row >= 0 && live_row < g_rows) {
|
||||
line = s->cells + (live_row * g_cols);
|
||||
line_cols = g_cols;
|
||||
}
|
||||
}
|
||||
for (int col = 0; col < g_cols; col++) {
|
||||
char ch = ' ';
|
||||
uint32_t color = s->fg_color;
|
||||
if (line && col < line_cols) {
|
||||
ch = line[col].c;
|
||||
if (ch == 0) ch = ' ';
|
||||
color = line[col].color;
|
||||
}
|
||||
char str[2] = { ch, 0 };
|
||||
int x = col * CHAR_W;
|
||||
int y = base_y + row * g_line_h;
|
||||
ui_draw_string_bitmap(g_win, x, y, str, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->scroll_offset == 0) {
|
||||
int cx = s->cursor_col * CHAR_W;
|
||||
int cy = base_y + s->cursor_row * g_line_h;
|
||||
ui_draw_rect(g_win, cx, cy + g_line_h - 2, CHAR_W, 2, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
ui_mark_dirty(g_win, 0, 0, g_win_w, g_win_h);
|
||||
}
|
||||
|
||||
static void tab_init(TerminalSession *s, int tty_id, int bsh_pid) {
|
||||
s->tty_id = tty_id;
|
||||
s->bsh_pid = bsh_pid;
|
||||
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
|
||||
s->scrollback = (CharCell *)malloc(sizeof(CharCell) * SCROLLBACK_LINES * SCROLLBACK_COLS);
|
||||
s->scroll_cols = (int *)malloc(sizeof(int) * SCROLLBACK_LINES);
|
||||
s->cursor_row = 0;
|
||||
s->cursor_col = 0;
|
||||
s->ansi_state = 0;
|
||||
s->ansi_param_count = 0;
|
||||
s->saved_row = 0;
|
||||
s->saved_col = 0;
|
||||
session_reset_colors(s);
|
||||
scrollback_init(s);
|
||||
session_clear(s);
|
||||
}
|
||||
|
||||
static void tab_free(TerminalSession *s) {
|
||||
if (!s) return;
|
||||
if (s->cells) {
|
||||
free(s->cells);
|
||||
s->cells = NULL;
|
||||
}
|
||||
if (s->scrollback) {
|
||||
free(s->scrollback);
|
||||
s->scrollback = NULL;
|
||||
}
|
||||
if (s->scroll_cols) {
|
||||
free(s->scroll_cols);
|
||||
s->scroll_cols = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_tab(int idx) {
|
||||
if (idx < 0 || idx >= g_tab_count) return;
|
||||
TerminalSession *s = &g_tabs[idx];
|
||||
|
||||
sys_tty_kill_all(s->tty_id);
|
||||
sys_tty_destroy(s->tty_id);
|
||||
|
||||
tab_free(s);
|
||||
|
||||
for (int i = idx; i < g_tab_count - 1; i++) {
|
||||
g_tabs[i] = g_tabs[i + 1];
|
||||
}
|
||||
g_tab_count--;
|
||||
if (g_tab_count <= 0) {
|
||||
sys_exit(0);
|
||||
}
|
||||
if (g_active_tab > idx) {
|
||||
g_active_tab--;
|
||||
} else if (g_active_tab == idx) {
|
||||
if (g_active_tab >= g_tab_count) g_active_tab = g_tab_count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int create_tab(void) {
|
||||
if (g_tab_count >= MAX_TABS) return -1;
|
||||
|
||||
int tty_id = sys_tty_create();
|
||||
if (tty_id < 0) return -1;
|
||||
|
||||
char start_dir[256];
|
||||
start_dir[0] = 0;
|
||||
if (g_tab_count > 0) {
|
||||
TerminalSession *active = &g_tabs[g_active_tab];
|
||||
if (active->bsh_pid > 0) {
|
||||
read_proc_field(active->bsh_pid, "cwd", start_dir, sizeof(start_dir));
|
||||
}
|
||||
}
|
||||
|
||||
char args[32];
|
||||
args[0] = 0;
|
||||
str_append(args, "-t ", sizeof(args));
|
||||
char id_buf[8];
|
||||
itoa(tty_id, id_buf);
|
||||
str_append(args, id_buf, sizeof(args));
|
||||
|
||||
if (start_dir[0]) {
|
||||
str_append(args, " -d ", sizeof(args));
|
||||
if (has_space(start_dir)) {
|
||||
str_append(args, "\"", sizeof(args));
|
||||
str_append(args, start_dir, sizeof(args));
|
||||
str_append(args, "\"", sizeof(args));
|
||||
} else {
|
||||
str_append(args, start_dir, sizeof(args));
|
||||
}
|
||||
}
|
||||
|
||||
char bsh_path[128];
|
||||
resolve_bsh_path(bsh_path, sizeof(bsh_path));
|
||||
int pid = sys_spawn(bsh_path, args, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_TTY_ID, tty_id);
|
||||
if (pid < 0) return -1;
|
||||
|
||||
tab_init(&g_tabs[g_tab_count], tty_id, pid);
|
||||
g_tab_count++;
|
||||
return g_tab_count - 1;
|
||||
}
|
||||
|
||||
static void handle_key(gui_event_t *ev) {
|
||||
TerminalSession *s = &g_tabs[g_active_tab];
|
||||
char c = (char)ev->arg1;
|
||||
bool ctrl = ev->arg3 != 0;
|
||||
|
||||
if (ctrl && c == 't') {
|
||||
int idx = create_tab();
|
||||
if (idx >= 0) g_active_tab = idx;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && c == 20) {
|
||||
if (g_tab_count > 0) g_active_tab = (g_active_tab + 1) % g_tab_count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && c == 19) {
|
||||
if (g_tab_count > 0) g_active_tab = (g_active_tab + g_tab_count - 1) % g_tab_count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl && (c == 'c' || c == 'C')) {
|
||||
int fg = sys_tty_get_fg(s->tty_id);
|
||||
if (fg > 0) {
|
||||
sys_tty_kill_fg(s->tty_id);
|
||||
sys_tty_set_fg(s->tty_id, 0);
|
||||
return;
|
||||
}
|
||||
char ch = 3;
|
||||
sys_tty_write_in(s->tty_id, &ch, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
sys_tty_write_in(s->tty_id, &c, 1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
g_win = ui_window_create("Terminal", 60, 60, g_win_w, g_win_h);
|
||||
ui_window_set_resizable(g_win, true);
|
||||
|
||||
int fh = (int)ui_get_font_height();
|
||||
if (fh > 0) g_line_h = fh;
|
||||
terminal_resize(g_win_w, g_win_h);
|
||||
|
||||
int idx = create_tab();
|
||||
if (idx < 0) return 1;
|
||||
g_active_tab = idx;
|
||||
|
||||
gui_event_t ev;
|
||||
char out_buf[TTY_READ_CHUNK];
|
||||
|
||||
while (1) {
|
||||
bool dirty = false;
|
||||
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
TerminalSession *s = &g_tabs[i];
|
||||
int read = 0;
|
||||
while ((read = sys_tty_read_out(s->tty_id, out_buf, sizeof(out_buf))) > 0) {
|
||||
session_process_output(s, out_buf, read);
|
||||
if (i == g_active_tab) dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
while (ui_get_event(g_win, &ev)) {
|
||||
if (ev.type == GUI_EVENT_CLOSE) {
|
||||
for (int i = 0; i < g_tab_count; i++) {
|
||||
if (g_tabs[i].bsh_pid > 0) sys_kill(g_tabs[i].bsh_pid);
|
||||
}
|
||||
sys_exit(0);
|
||||
} else if (ev.type == GUI_EVENT_KEY) {
|
||||
handle_key(&ev);
|
||||
dirty = true;
|
||||
} else if (ev.type == GUI_EVENT_CLICK) {
|
||||
if (ev.arg2 < TAB_BAR_H) {
|
||||
int tab_w = get_tab_width();
|
||||
int tab = ev.arg1 / tab_w;
|
||||
if (tab >= 0 && tab < g_tab_count) {
|
||||
int close_size = TAB_CLOSE_W;
|
||||
int close_x = tab * tab_w + tab_w - TAB_CLOSE_PAD - close_size;
|
||||
int close_y = (TAB_BAR_H - close_size) / 2;
|
||||
if (ev.arg1 >= close_x && ev.arg1 < close_x + close_size &&
|
||||
ev.arg2 >= close_y && ev.arg2 < close_y + close_size) {
|
||||
close_tab(tab);
|
||||
} else {
|
||||
g_active_tab = tab;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
} else if (ev.type == GUI_EVENT_MOUSE_WHEEL) {
|
||||
int lines = ev.arg1 * 3;
|
||||
if (lines != 0) {
|
||||
session_adjust_scroll(&g_tabs[g_active_tab], lines);
|
||||
dirty = true;
|
||||
}
|
||||
} else if (ev.type == GUI_EVENT_RESIZE) {
|
||||
terminal_resize(ev.arg1, ev.arg2);
|
||||
dirty = true;
|
||||
} else if (ev.type == GUI_EVENT_PAINT) {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
draw_tabs();
|
||||
draw_session(&g_tabs[g_active_tab]);
|
||||
} else {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -40,6 +40,46 @@ static void editor_strcpy(char *dest, const char *src) {
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
static int editor_strlen(const char *s) {
|
||||
int len = 0;
|
||||
while (s && s[len]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void editor_strncat(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = editor_strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void editor_resolve_path(const char *input, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return;
|
||||
if (!input || input[0] == 0) {
|
||||
out[0] = 0;
|
||||
return;
|
||||
}
|
||||
if (input[0] == '/') {
|
||||
editor_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
char cwd[256];
|
||||
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
|
||||
editor_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
editor_strcpy(out, cwd);
|
||||
int len = editor_strlen(out);
|
||||
if (len > 0 && out[len - 1] != '/') editor_strncat(out, "/", max_len);
|
||||
editor_strncat(out, input, max_len);
|
||||
}
|
||||
|
||||
static void editor_clear_all(void) {
|
||||
for (int i = 0; i < EDITOR_MAX_LINES; i++) {
|
||||
for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) {
|
||||
@@ -71,9 +111,11 @@ static void editor_ensure_cursor_visible(void) {
|
||||
|
||||
void editor_open_file(const char *filename) {
|
||||
editor_clear_all();
|
||||
editor_strcpy(open_filename, filename);
|
||||
char resolved[256];
|
||||
editor_resolve_path(filename, resolved, sizeof(resolved));
|
||||
editor_strcpy(open_filename, resolved);
|
||||
|
||||
int fd = sys_open(filename, "r");
|
||||
int fd = sys_open(resolved, "r");
|
||||
if (fd < 0) {
|
||||
file_modified = 0;
|
||||
return;
|
||||
|
||||
@@ -41,6 +41,40 @@ static int viewer_strlen(const char *s) {
|
||||
return len;
|
||||
}
|
||||
|
||||
static void viewer_strncat(char *dst, const char *src, int max_len) {
|
||||
if (!dst || !src || max_len <= 0) return;
|
||||
int dlen = viewer_strlen(dst);
|
||||
int i = 0;
|
||||
while (dlen + i < max_len - 1 && src[i]) {
|
||||
dst[dlen + i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[dlen + i] = 0;
|
||||
}
|
||||
|
||||
static void viewer_resolve_path(const char *input, char *out, int max_len) {
|
||||
if (!out || max_len <= 0) return;
|
||||
if (!input || input[0] == 0) {
|
||||
out[0] = 0;
|
||||
return;
|
||||
}
|
||||
if (input[0] == '/') {
|
||||
viewer_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
char cwd[256];
|
||||
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
|
||||
viewer_strcpy(out, input);
|
||||
return;
|
||||
}
|
||||
|
||||
viewer_strcpy(out, cwd);
|
||||
int len = viewer_strlen(out);
|
||||
if (len > 0 && out[len - 1] != '/') viewer_strncat(out, "/", max_len);
|
||||
viewer_strncat(out, input, max_len);
|
||||
}
|
||||
|
||||
static void viewer_scale_rgba_to_argb(const unsigned char *rgba, int src_w, int src_h,
|
||||
uint32_t *dst, int dst_w, int dst_h) {
|
||||
if (src_w == dst_w && src_h == dst_h) {
|
||||
@@ -141,7 +175,9 @@ static void viewer_paint(ui_window_t win) {
|
||||
}
|
||||
|
||||
void viewer_open_file(const char *path) {
|
||||
int fd = sys_open(path, "r");
|
||||
char resolved[256];
|
||||
viewer_resolve_path(path, resolved, sizeof(resolved));
|
||||
int fd = sys_open(resolved, "r");
|
||||
if (fd < 0) return;
|
||||
|
||||
uint32_t file_size = sys_size(fd);
|
||||
@@ -250,13 +286,13 @@ void viewer_open_file(const char *path) {
|
||||
stbi_image_free(rgba);
|
||||
free(buf);
|
||||
|
||||
viewer_strcpy(viewer_file_path, path);
|
||||
viewer_strcpy(viewer_file_path, resolved);
|
||||
|
||||
const char *fname = path;
|
||||
int plen = viewer_strlen(path);
|
||||
const char *fname = resolved;
|
||||
int plen = viewer_strlen(resolved);
|
||||
for (int i = plen - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
fname = &path[i + 1];
|
||||
if (resolved[i] == '/') {
|
||||
fname = &resolved[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ int chdir(const char *path) {
|
||||
}
|
||||
|
||||
char* getcwd(char *buf, int size) {
|
||||
if (sys_getcwd(buf, size) == 0) return buf;
|
||||
if (sys_getcwd(buf, size) >= 0) return buf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,46 @@ int sys_chdir(const char *path) {
|
||||
return (int)syscall2(SYS_FS, FS_CMD_CHDIR, (uint64_t)path);
|
||||
}
|
||||
|
||||
int sys_tty_create(void) {
|
||||
return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0);
|
||||
}
|
||||
|
||||
int sys_tty_read_out(int tty_id, char *buf, int len) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_OUT, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_tty_write_in(int tty_id, const char *buf, int len) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_WRITE_IN, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_tty_read_in(char *buf, int len) {
|
||||
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_IN, (uint64_t)buf, (uint64_t)len, 0);
|
||||
}
|
||||
|
||||
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id) {
|
||||
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_SPAWN, (uint64_t)path, (uint64_t)args, flags, (uint64_t)tty_id);
|
||||
}
|
||||
|
||||
int sys_tty_set_fg(int tty_id, int pid) {
|
||||
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_SET_FG, (uint64_t)tty_id, (uint64_t)pid, 0);
|
||||
}
|
||||
|
||||
int sys_tty_get_fg(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_GET_FG, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_kill_fg(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_FG, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_kill_all(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_ALL, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
int sys_tty_destroy(int tty_id) {
|
||||
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_DESTROY, (uint64_t)tty_id, 0);
|
||||
}
|
||||
|
||||
void sys_kill(int pid) {
|
||||
syscall1(SYS_KILL, (uint64_t)pid);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,20 @@
|
||||
#define SYSTEM_CMD_TCP_RECV_NB 42
|
||||
#define SYSTEM_CMD_YIELD 43
|
||||
#define SYSTEM_CMD_PARALLEL_RUN 50
|
||||
#define SYSTEM_CMD_TTY_CREATE 60
|
||||
#define SYSTEM_CMD_TTY_READ_OUT 61
|
||||
#define SYSTEM_CMD_TTY_WRITE_IN 62
|
||||
#define SYSTEM_CMD_TTY_READ_IN 63
|
||||
#define SYSTEM_CMD_SPAWN 64
|
||||
#define SYSTEM_CMD_TTY_SET_FG 65
|
||||
#define SYSTEM_CMD_TTY_GET_FG 66
|
||||
#define SYSTEM_CMD_TTY_KILL_FG 67
|
||||
#define SYSTEM_CMD_TTY_KILL_ALL 68
|
||||
#define SYSTEM_CMD_TTY_DESTROY 69
|
||||
|
||||
#define SPAWN_FLAG_TERMINAL 0x1
|
||||
#define SPAWN_FLAG_INHERIT_TTY 0x2
|
||||
#define SPAWN_FLAG_TTY_ID 0x4
|
||||
|
||||
// Internal assembly entry into Ring 0
|
||||
extern uint64_t syscall0(uint64_t sys_num);
|
||||
@@ -115,6 +129,17 @@ int sys_exists(const char *path);
|
||||
int sys_getcwd(char *buf, int size);
|
||||
int sys_chdir(const char *path);
|
||||
|
||||
int sys_tty_create(void);
|
||||
int sys_tty_read_out(int tty_id, char *buf, int len);
|
||||
int sys_tty_write_in(int tty_id, const char *buf, int len);
|
||||
int sys_tty_read_in(char *buf, int len);
|
||||
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id);
|
||||
int sys_tty_set_fg(int tty_id, int pid);
|
||||
int sys_tty_get_fg(int tty_id);
|
||||
int sys_tty_kill_fg(int tty_id);
|
||||
int sys_tty_kill_all(int tty_id);
|
||||
int sys_tty_destroy(int tty_id);
|
||||
|
||||
typedef struct {
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
2343
src/wm/cmd.c
2343
src/wm/cmd.c
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@
|
||||
#include "disk.h"
|
||||
#include "wm.h"
|
||||
#include "memory_manager.h"
|
||||
#include "cmd.h"
|
||||
#include "process.h"
|
||||
#define EXPLORER_ITEM_HEIGHT 80
|
||||
#define EXPLORER_ITEM_WIDTH 120
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
442
src/wm/wm.c
442
src/wm/wm.c
@@ -4,7 +4,6 @@
|
||||
#include "wm.h"
|
||||
#include "graphics.h"
|
||||
#include "io.h"
|
||||
#include "cmd.h"
|
||||
#include "process.h"
|
||||
#include "syscall.h"
|
||||
#include "kutils.h"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
20
src/wm/wm.h
20
src/wm/wm.h
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user