59 Commits

Author SHA1 Message Date
boreddevnl
8dd756f25b FIX: cpuinfo stack overflow and add GUI tab character support 2026-04-12 19:07:08 +02:00
boreddevnl
d13fca2d4a CHECKP: vfs 2026-04-12 18:23:38 +02:00
boreddevnl
a1b6d58b77 Tweak: os_codename = Voyager 2026-04-12 17:59:10 +02:00
boreddevnl
cbc196a4b1 tweak: add -stable after kernel_version 2026-04-12 17:57:54 +02:00
boreddevnl
b4c14af48d TWEAK: kernel_version = 3.2.3 --> 4.0.0 2026-04-12 17:56:35 +02:00
boreddevnl
700839e6be FEAT: VFS overhaul 2026-04-12 17:53:31 +02:00
boreddevnl
921e8a5658 RM: Legacy drive selector in explorer 2026-04-12 00:34:22 +02:00
boreddevnl
437d57312f FIX: remove accidentally copied code 2026-04-12 00:28:03 +02:00
boreddevnl
afc4e16fcf STABILITY: SMP improvements 2026-04-12 00:26:04 +02:00
boreddevnl
38ed0b5ffa CHECKP: semi-stable vfs 2026-04-11 23:08:33 +02:00
boreddevnl
5933483009 CHECKP: shitty VFS 2026-04-11 21:41:11 +02:00
boreddevnl
6b6a22d518 OPT: use ui_draw_image in paint.c 2026-04-11 16:14:39 +02:00
boreddevnl
85427041de FEAT: Caps lock support in ps2 driver 2026-04-11 16:12:37 +02:00
boreddevnl
8b77e8c48e doc: update grapher with tri-axis marching 2026-04-04 19:39:49 +02:00
boreddevnl
1ce08c70b0 FEAT: add tri-axis marching pipeline and atomic depth-color updates to Grapher 2026-04-04 18:05:04 +02:00
boreddevnl
fca67f68a9 NEW: math.h/libmath.c 2026-04-03 23:28:29 +02:00
boreddevnl
c330382436 DOCS: math.h 2026-04-03 23:27:45 +02:00
boreddevnl
f0c2963793 CHECKPOINT: polygon rendering 2026-04-03 23:16:03 +02:00
boreddevnl
3b24bc882c FEAT: Add grapher to dock 2026-04-03 13:32:38 +02:00
boreddevnl
2b44e59e9f TWEAK: Adjust window size 2026-04-03 13:32:23 +02:00
boreddevnl
7a2769e8e3 UI: New main wallpaper (bored.jpg) 2026-04-03 13:23:52 +02:00
boreddevnl
1a6e30b52e Docs: Clean up README.md 2026-04-03 11:53:57 +02:00
boreddevnl
69847adee6 DOCS: Refine and add documentation for libwidget.c 2026-04-03 11:52:06 +02:00
boreddevnl
f402e5e4f0 Merge branch 'main' of https://github.com/boreddevnl/BoredOS 2026-04-03 11:47:05 +02:00
boreddevnl
684ed774ee TWEAK: Balanced resolution and cpu usage with GRID_3D resolution 2026-04-03 11:46:33 +02:00
Chris
9ed8eac3e5 Update Buy Me A Coffee link in README 2026-04-03 00:08:05 +02:00
boreddevnl
c6d512b0f2 FIX: Flickering of colors with 3D graphs 2026-04-02 22:00:04 +02:00
boreddevnl
0b7a134282 TWEAK: Update version.c to 26.4 "Geometry" 2026-04-02 21:59:25 +02:00
boreddevnl
91b67bd8d5 OPT: Multithreaded WM rendering 2026-04-02 21:36:00 +02:00
boreddevnl
e60f232812 OPTIMIZATION: Bytecode engine 2026-04-02 20:21:58 +02:00
boreddevnl
3169ec51cb FEAT: SYSTEM_CMD_PARALLEL_RUN 2026-04-02 20:21:06 +02:00
boreddevnl
beb2c724ff FEAT: ROTATE variable to toggle auto rotate in 3d graphs. 2026-04-02 17:58:33 +02:00
boreddevnl
bf3c2cb578 FEAT: always rotate 3d graph 2026-04-02 17:51:43 +02:00
boreddevnl
823e9c0ce7 FIX: autofit when adjusting graph not shooting into outer space (LOL) 2026-04-02 15:58:56 +02:00
boreddevnl
0ddb1e7610 FEAT: Measurements on graph, fixed overflow and ctrl + r to reset zoom 2026-04-02 15:55:27 +02:00
boreddevnl
32a6bb4d72 FEAT: Pass ctrl pressed through syscall 2026-04-02 15:51:16 +02:00
boreddevnl
d8e680604c FIX: gpf when closing boredword.c 2026-04-01 23:33:25 +02:00
boreddevnl
2e28f860cb FEAT: resizing of window in viewer.c 2026-04-01 23:27:49 +02:00
boreddevnl
9634ebb086 FIX: Fixed framebuffer freeze upon screenshot 2026-04-01 23:05:52 +02:00
boreddevnl
d7d97b5a97 MV: graphing.c --> grapher.c 2026-04-01 22:19:57 +02:00
boreddevnl
4a3752583c FEAT: graphing.c a graphing calculator app. 2026-04-01 22:19:10 +02:00
boreddevnl
9de8ee143c OPT: Reduce render calls when zooming 2026-04-01 22:18:57 +02:00
boreddevnl
8d5fa53d3e FEAT: Cursor nav in text box 2026-04-01 22:18:26 +02:00
boreddevnl
ad8db32305 RM: broken .gif 2026-03-24 19:35:56 +01:00
boreddevnl
92928e55fb fix wm freeze explorer 2026-03-24 19:34:47 +01:00
boreddevnl
31eb7afdc6 fix: better parsing in browser.c 2026-03-23 21:25:46 +01:00
boreddevnl
ad9fac3e28 fix: scrollbar functionality 2026-03-23 20:40:38 +01:00
boreddevnl
70cd296d19 BFIX: Fix gpf's in .elf applications 2026-03-23 17:26:41 +01:00
boreddevnl
b7020152c1 feat: .tar application loading 2026-03-23 09:10:17 +01:00
boreddevnl
63749b8734 FEAT: libwidget.c 2026-03-22 22:07:30 +01:00
boreddevnl
4e8ea5acd2 perf: fix core starvation 2026-03-22 21:04:50 +01:00
boreddevnl
5c199e028a OPTIMIZATION: Network and browser optimizations 2026-03-22 19:26:05 +01:00
boreddevnl
ec2a9d1883 OPTIMIZATION: Browser loading optimization 2026-03-22 18:55:55 +01:00
boreddevnl
4c46650c64 OPTIMIZATION: use mem_mcpy in display buffer 2026-03-22 18:50:29 +01:00
boreddevnl
1ee2fcad9e DOC: Remove unneccessary word readme.md 2026-03-21 17:55:56 +01:00
boreddevnl
5c29ac1473 RM: Deletion unnecessary .vcxproj files 2026-03-20 00:02:01 +01:00
boreddevnl
81743261bf tweak: file cleanup 2026-03-19 12:19:41 +01:00
boreddevnl
4eeb907342 TWEAK: rename DOOM window 2026-03-19 10:43:48 +01:00
boreddevnl
e527f63af7 TWEAK: version.c update for BoredOS Syncwave 2026-03-18 18:18:24 +01:00
95 changed files with 11025 additions and 3982 deletions

2
.gitattributes vendored
View File

@@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ limine
.DS_Store .DS_Store
/build/ /build/
*.o *.o
disk.img

110
Makefile
View File

@@ -132,58 +132,56 @@ $(KERNEL_ELF): $(OBJ_FILES)
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES) $(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
$(MAKE) -C $(SRC_DIR)/userland $(MAKE) -C $(SRC_DIR)/userland
$(ISO_IMAGE): $(KERNEL_ELF) limine.conf limine-setup $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
rm -rf $(BUILD_DIR)/initrd
mkdir -p $(BUILD_DIR)/initrd/bin
mkdir -p $(BUILD_DIR)/initrd/Library/images/Wallpapers
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/docs
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/bin/; fi \
done
@for f in $(SRC_DIR)/images/wallpapers/*; do \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; fi \
done
@for f in $(SRC_DIR)/images/gif/*.gif; do \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; fi \
done
@for f in $(SRC_DIR)/fonts/*.ttf; do \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; fi \
done
@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)/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 \
dir=$$(dirname "$$f"); \
mkdir -p $(BUILD_DIR)/initrd/"$$dir"; \
cp "$$f" $(BUILD_DIR)/initrd/"$$dir"/; \
fi \
done
@if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi
cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar *
$(ISO_IMAGE): $(KERNEL_ELF) $(BUILD_DIR)/initrd.tar limine.conf limine-setup
rm -rf $(ISO_DIR) rm -rf $(ISO_DIR)
mkdir -p $(ISO_DIR) mkdir -p $(ISO_DIR)
mkdir -p $(ISO_DIR)/EFI/BOOT mkdir -p $(ISO_DIR)/EFI/BOOT
cp $(KERNEL_ELF) $(ISO_DIR)/ cp $(KERNEL_ELF) $(ISO_DIR)/
cp limine.conf $(ISO_DIR)/ cp limine.conf $(ISO_DIR)/
mkdir -p $(ISO_DIR)/bin
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/bin/; \
echo " module_path: boot():/bin/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
@if [ -f README.md ]; then cp README.md $(ISO_DIR)/; fi cp $(BUILD_DIR)/initrd.tar $(ISO_DIR)/
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then \ echo " module_path: boot():/initrd.tar" >> $(ISO_DIR)/limine.conf
mkdir -p $(ISO_DIR)/Library/DOOM; \
cp $(SRC_DIR)/userland/games/doom/doom1.wad $(ISO_DIR)/Library/DOOM/; \
echo " module_path: boot():/Library/DOOM/doom1.wad" >> $(ISO_DIR)/limine.conf; \
fi
mkdir -p $(ISO_DIR)/Library/images/Wallpapers
@for f in $(SRC_DIR)/images/wallpapers/*; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/Library/images/Wallpapers/; \
echo " module_path: boot():/Library/images/Wallpapers/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
@if [ -f splash.jpg ]; then cp splash.jpg $(ISO_DIR)/; fi @if [ -f splash.jpg ]; then cp splash.jpg $(ISO_DIR)/; fi
mkdir -p $(ISO_DIR)/Library/images/gif
@for f in $(SRC_DIR)/images/gif/*.gif; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/Library/images/gif/; \
echo " module_path: boot():/Library/images/gif/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
mkdir -p $(ISO_DIR)/docs
@for f in $$(find docs -name '*.md'); do \
if [ -f "$$f" ]; then \
dir=$$(dirname "$$f"); \
mkdir -p $(ISO_DIR)/"$$dir"; \
cp "$$f" $(ISO_DIR)/"$$dir"/; \
echo " module_path: boot():/$$f" >> $(ISO_DIR)/limine.conf; \
fi \
done
cp limine/limine-bios.sys $(ISO_DIR)/ cp limine/limine-bios.sys $(ISO_DIR)/
cp limine/limine-bios-cd.bin $(ISO_DIR)/ cp limine/limine-bios-cd.bin $(ISO_DIR)/
cp limine/limine-uefi-cd.bin $(ISO_DIR)/ cp limine/limine-uefi-cd.bin $(ISO_DIR)/
@@ -191,34 +189,6 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.conf limine-setup
cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/ cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/
cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/ cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/
mkdir -p $(ISO_DIR)/Library/Fonts
@for f in $(SRC_DIR)/fonts/*.ttf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/Library/Fonts/; \
echo " module_path: boot():/Library/Fonts/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
mkdir -p $(ISO_DIR)/Library/Fonts/Emoji
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/Library/Fonts/Emoji/; \
echo " module_path: boot():/Library/Fonts/Emoji/$$basename" >> $(ISO_DIR)/limine.conf; \
fi \
done
@if [ -f README.md ]; then \
cp README.md $(ISO_DIR)/; \
echo " module_path: boot():/README.md" >> $(ISO_DIR)/limine.conf; \
fi
@if [ -f LICENSE ]; then \
cp LICENSE $(ISO_DIR)/; \
echo " module_path: boot():/LICENSE" >> $(ISO_DIR)/limine.conf; \
fi
$(XORRISO) -as mkisofs -R -J -b limine-bios-cd.bin \ $(XORRISO) -as mkisofs -R -J -b limine-bios-cd.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \ -no-emul-boot -boot-load-size 4 -boot-info-table \
--efi-boot limine-uefi-cd.bin \ --efi-boot limine-uefi-cd.bin \

View File

@@ -11,7 +11,7 @@
--- ---
BoredOS is a functional x86_64 operating system featuring a custom Desktop Environment (DE), a dedicated Window Manager (BoredWM), and a FAT32 filesystem. It balances low-level kernel exploration with a surprisingly capable userspace. BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE), a dedicated Window Manager (BoredWM), and a FAT32 filesystem. It balances low-level kernel exploration with a surprisingly capable userspace.
![Screenshot](screenshot.jpg) ![Screenshot](screenshot.jpg)
> [!NOTE] > [!NOTE]
@@ -19,9 +19,9 @@ BoredOS is a functional x86_64 operating system featuring a custom Desktop Envir
--- ---
## 🚀 Features ## Features
### ⚙️ System Architecture ### System Architecture
* **64-bit Long Mode:** Fully utilizing the x86_64 architecture. * **64-bit Long Mode:** Fully utilizing the x86_64 architecture.
* **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP. * **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP.
* **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution. * **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution.
@@ -31,12 +31,12 @@ BoredOS is a functional x86_64 operating system featuring a custom Desktop Envir
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage. * **Filesystem:** Full **FAT32** support for persistent and in-memory storage.
* **Networking:** Includes the lwIP networking stack. * **Networking:** Includes the lwIP networking stack.
### 📺 Graphical User Interface ### Graphical User Interface
* **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction. * **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction.
* **Customization:** Adjustable UI to suit your aesthetic. * **Customization:** Adjustable UI to suit your aesthetic.
* **Media Support:** Built-in image decoding. * **Media Support:** Built-in image decoding.
### 🛠️ Included Applications ### Included Applications
* **Productivity:** GUI Text Editor calculator, Markdown Viewer, a simple browser and BoredWord. * **Productivity:** GUI Text Editor calculator, Markdown Viewer, a simple browser and BoredWord.
* **Creativity:** A Paint application. * **Creativity:** A Paint application.
* **Utilities:** Terminal, Task Manager, File Explorer, Clock and a (limited) C Compiler. * **Utilities:** Terminal, Task Manager, File Explorer, Clock and a (limited) C Compiler.
@@ -55,11 +55,11 @@ Explore the internal workings of BoredOS via our comprehensive guides in the [`d
--- ---
## Support the Journey ## Support the Journey
If you find this project interesting or helpful, consider fueling the development with a coffee! If you find this project interesting or helpful, consider fueling the development with a coffee!
<a href="https://buymeacoffee.com/boreddevnl" target="_blank"> <a href="https://buymeacoffee.com/boreddevhq" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" /> <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
</a> </a>
@@ -68,16 +68,16 @@ If you find this project interesting or helpful, consider fueling the developmen
## ⚠️ Project Disclaimer & Heritage ## ⚠️ Project Disclaimer & Heritage
**BoredOS** is the successor to **BrewKernel**, a text-only project initiated in 2023. **BoredOS** is the successor to **BrewKernel**, a project initiated in 2023.
While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable 64-bit system. While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable system.
> [!IMPORTANT] > [!IMPORTANT]
> Please ensure all issues, discussions, and contributions are directed to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS. > Please ensure all issues, discussions, and contributions are directed to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
--- ---
## ⚖️ License ## License
**Copyright (C) 2024-2026 boreddevnl** **Copyright (C) 2024-2026 boreddevnl**

286
build.log Normal file
View File

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

BIN
disk.img

Binary file not shown.

View File

@@ -27,7 +27,9 @@ Instructions for compiling the OS from source.
The SDK and toolchain guides for creating your own `.elf` userland binaries. The SDK and toolchain guides for creating your own `.elf` userland binaries.
- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls. - [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls.
- [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`. - [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`.
- [`Widget API`](appdev/widget_api.md): High-level UI components like buttons, textboxes, and scrollbars using `libwidget.h`.
- [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO. - [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO.
- [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking. - [`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.
--- ---

View File

@@ -10,7 +10,7 @@ This guide explains how to write a new "Hello World" application locally, compil
> [!TIP] > [!TIP]
> **Looking for working code?** Check out the [Examples Directory](examples/README.md) for full source code demonstrating basic CLI, Windows, Animations, and TCP Networking. > **Looking for working code?** Check out the [Examples Directory](examples/README.md) for full source code demonstrating basic CLI, Windows, Animations, and TCP Networking.
## 📝 Step 1: Write the C Source ## Step 1: Write the C Source
Applications reside entirely in the `src/userland/` directory. Create a new file, for example, `src/userland/gui/hello.c`. Applications reside entirely in the `src/userland/` directory. Create a new file, for example, `src/userland/gui/hello.c`.
@@ -52,7 +52,7 @@ int main(void) {
} }
``` ```
## ⚙️ Step 2: Edit the Makefile ## Step 2: Edit the Makefile
Now you need to tell the build system to compile `hello.c`. Fortunately, the `src/userland/Makefile` is designed to detect new C files largely automatically! Now you need to tell the build system to compile `hello.c`. Fortunately, the `src/userland/Makefile` is designed to detect new C files largely automatically!
@@ -64,7 +64,7 @@ Now you need to tell the build system to compile `hello.c`. Fortunately, the `sr
Since you placed the file in `gui/hello.c`, the wildcard logic will pick it up automatically. Since you placed the file in `gui/hello.c`, the wildcard logic will pick it up automatically.
3. The Makefile will generate `bin/hello.elf` during the build phase. 3. The Makefile will generate `bin/hello.elf` during the build phase.
## 📦 Step 3: Bundle it into the OS ## Step 3: Bundle it into the OS
The main overarching `Makefile` (in the project root) takes binaries from `src/userland/bin/*.elf` and places them into the `iso_root/bin/` directory, while also adding them to `limine.conf` as loadable boot modules. The main overarching `Makefile` (in the project root) takes binaries from `src/userland/bin/*.elf` and places them into the `iso_root/bin/` directory, while also adding them to `limine.conf` as loadable boot modules.
@@ -77,7 +77,7 @@ The main overarching `Makefile` (in the project root) takes binaries from `src/u
make clean && make run make clean && make run
``` ```
## 🚀 Step 4: Run it inside BoredOS ## Step 4: Run it inside BoredOS
1. When BoredOS boots, launch the **Terminal** application. 1. When BoredOS boots, launch the **Terminal** application.
2. The OS automatically maps built applications to standard shell commands. Simply type your application's filename (without the `.elf` extension). 2. The OS automatically maps built applications to standard shell commands. Simply type your application's filename (without the `.elf` extension).

345
docs/appdev/grapher.md Normal file
View File

@@ -0,0 +1,345 @@
<div align="center">
<h1>Grapher</h1>
<p><em>An interactive mathematical expression plotter for BoredOS, supporting both 2D and 3D visualizations.</em></p>
</div>
---
Grapher is a built-in GUI application that lets you type any mathematical equation and see it plotted in real time. It supports 2D explicit and implicit curves as well as full 3D surface visualization — including both explicit surfaces (`z = f(x, y)`) and implicit surfaces (`f(x, y, z) = c`).
> [!NOTE]
> Grapher is located at `src/userland/gui/grapher.c`. It runs as a standard BoredOS GUI process and can be launched from the terminal or from the dock.
---
## Features at a Glance
| Feature | Details |
|---|---|
| **2D Explicit** | Plot `y = f(x)` curves |
| **2D Implicit** | Plot any `f(x, y) = g(x, y)` contour via marching squares |
| **3D Explicit** | Plot `z = f(x, y)` surfaces |
| **3D Implicit** | Plot any `f(x, y, z) = c` surface |
| **Rendering modes** | Wireframe and filled polygon modes |
| **Height coloring** | Surfaces are colored by a blue→green→yellow→red gradient based on Z height |
| **Phong-style shading** | Filled mode computes per-face normals and applies diffuse + ambient lighting |
| **Parallel rendering** | Evaluation and projection are distributed across 4 worker threads via `sys_parallel_run` |
| **Preset equations** | 7 built-in presets accessible from the toolbar |
| **Auto-fit** | 2D view auto-fits the Y axis to the plotted curve on first plot |
| **Atomic Color-Depth Buffer** | All 3D drawing uses a 64-bit atomic buffer to prevent depth/color race conditions |
---
## Launching Grapher
From the BoredOS terminal:
```sh
grapher
```
Or click the **Grapher icon** in the system dock.
---
### Toolbar Controls
| Control | Function |
|---|---|
| **Equation box** | Type your mathematical expression, then press **Enter** or **Plot** |
| **Plot button** | Parse and render the current equation |
| **Wire / Filled button** | Toggle wireframe vs. shaded polygon mode (3D only) |
| **Presets button** | Open a dropdown of example equations |
### Status Bar Controls (3D mode)
| Control | Function |
|---|---|
| **`+` button** | Increase the 3D world range (zoom out in world space) |
| **`-` button** | Decrease the 3D world range (zoom in in world space) |
---
## Keyboard Shortcuts
| Shortcut | Action |
|---|---|
| **Enter** (in equation box) | Plot the equation |
| **Ctrl + R** | Reset the view to defaults |
| **F** | Toggle filled / wireframe rendering (3D mode) |
| **Scroll wheel** | Zoom in/out (2D mode adjusts viewport; 3D mode adjusts camera zoom) |
| **Right-click drag** | Rotate the 3D surface |
---
## Writing Equations
Grapher parses equations entered as plain text. It supports a subset of mathematical notation with automatic implicit multiplication.
### Supported Functions
| Syntax | Meaning |
|---|---|
| `sin(x)` | Sine |
| `cos(x)` | Cosine |
| `tan(x)` | Tangent |
| `sqrt(x)` | Square root |
| `abs(x)` | Absolute value |
| `log(x)` | Natural logarithm (base *e*) |
### Supported Operators
| Operator | Meaning |
|---|---|
| `+` `-` `*` `/` | Arithmetic |
| `^` | Exponentiation (right-associative) |
| `(` `)` | Grouping |
### Special Values
| Token | Value |
|---|---|
| `pi` or `PI` | π ≈ 3.14159… |
### Implicit Multiplication
Adjacent tokens that would normally require a `*` are multiplied automatically:
```
2x → 2 * x
3sin(x) → 3 * sin(x)
(x+1)(x) → (x+1) * x
```
### How Equations Are Classified
Grapher looks at which variables appear in your equation to automatically choose the rendering mode:
| Equation form | Auto-detected as |
|---|---|
| `y = f(x)` or just `f(x)` | 2D explicit |
| `f(x, y) = g(x, y)` | 2D implicit |
| `z = f(x, y)` | 3D explicit |
| `f(x, y, z) = c` | 3D implicit |
If you omit the `=` sign, Grapher treats the input as `y = <expression>` when no `y` or `z` is present, or as `<expression> = 0` otherwise.
---
## Example Equations
### 2D Examples
```
y = sin(x)
y = x^2
y = cos(x)*x
y = abs(x) - 2
x^2 + y^2 = 25 ← circle (implicit)
y = log(x)
```
### 3D Explicit Examples
```
z = sin(x)*cos(y)
z = x^2 - y^2 ← saddle surface
z = sqrt(25 - x^2 - y^2)
```
### 3D Implicit Examples
```
x^2 + y^2 + z^2 = 25 ← sphere
x^2 + y^2 = 16 ← cylinder
x^2 + y^2 - z^2 = 1 ← hyperboloid
```
---
## Navigation Controls
### 2D Mode
| Input | Action |
|---|---|
| **Scroll up** | Zoom in |
| **Scroll down** | Zoom out |
| **Ctrl+R** | Reset to default view (`x: [-10, 10]`) |
### 3D Mode
| Input | Action |
|---|---|
| **Right-click drag** | Rotate the surface (orbit camera) |
| **Scroll up** | Zoom camera in |
| **Scroll down** | Zoom camera out |
| **`+` / `-` buttons** | Increase / decrease world range |
| **Ctrl+R** | Reset rotation and zoom |
> [!TIP]
> In 3D mode, the surface auto-rotates slowly by default. This can be disabled by setting `#define ROTATE 0` in the source file.
---
## Architecture Overview
Grapher is implemented as a single self-contained C file. Below is a high-level breakdown of its major components:
### Math Library
Grapher uses the BoredOS freestanding **`libc/math.h`** library, which provides all the math functions it needs without depending on a host standard library:
| Function | Description |
|---|---|
| `sin`, `cos`, `tan` | Trigonometry via Taylor series (8 terms, range-reduced to `[-π, π]`) |
| `sqrt` | Newton-Raphson iteration (25 steps) |
| `log` | Natural logarithm via Padé-style series |
| `log2`, `log10` | Derived from `log` |
| `exp` | Range-reduced Taylor series for `e^x` |
| `pow` | Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))` |
| `fabs`, `fmod` | Absolute value and floating-point remainder |
| `floor`, `ceil` | Rounding |
| `sinh`, `cosh`, `tanh` | Hyperbolic functions |
| `hypot`, `fmin`, `fmax`, `fclamp` | Utility helpers |
The constants `M_PI`, `M_E`, `M_LN2`, `M_SQRT2` are also defined in the header.
This library is automatically linked into every userland ELF — any app can `#include "math.h"` to use it.
### Expression Parser
Equations are parsed in three stages:
1. **Tokenizer** (`tokenize`) — converts the input string into a flat token array. Handles implicit multiplication by inserting `*` tokens where needed.
2. **Recursive Descent Parser** (`parse_expr`, `parse_term`, `parse_power`, `parse_unary`, `parse_atom`) — produces an Abstract Syntax Tree (AST) with up to `MAX_NODES = 128` nodes.
3. **Bytecode Compiler** (`compile_ast`) — walks the AST in post-order and emits a flat instruction sequence for a simple stack machine. This avoids recursive evaluation during rendering hot paths.
The resulting bytecode is then executed by `run_bc` for every sample point.
### Rendering Pipeline
#### 2D Rendering
- **Explicit** — evaluates `y = f(x)` at every pixel column and connects adjacent samples with Bresenham lines.
- **Implicit** — applies **marching squares** on a 200×130 grid to find sign changes in `f(x,y) - g(x,y)` and plots intersection pixels.
#### 3D Rendering
The 3D pipeline uses a multi-pass system parallelized across worker threads:
| Pass | Function | Description |
|---|---|---|
| 1 | **Evaluation** | Samples the surface at grid points. For implicit surfaces, this uses **tri-axis marching**. |
| 2 | **Projection** | Projects 3D world coordinates to 2D screen coordinates with perspective. |
| 3 | **Drawing** | Rasterizes wireframe lines or filled triangles with Z-buffering. |
##### Tri-Axis Marching (Implicit Surfaces)
Unlike explicit surfaces that only need one evaluation per grid point, implicit surfaces require finding roots of $f(x, y, z) = 0$. To ensure complete surface connectivity and eliminate "cracks," Grapher marches along all three primary axes:
1. **X-Axis Pass**: For every $(y, z)$ pair, march along $x$.
2. **Y-Axis Pass**: For every $(x, z)$ pair, march along $y$.
3. **Z-Axis Pass**: For every $(x, y)$ pair, march along $z$.
Each pass uses a multi-stage root finder (170 linear steps followed by 15 bisection iterations). By sampling along all three axes, the engine "catches" surfaces that are nearly parallel to any specific marching direction, ensuring that vertical walls and steep gradients are rendered solidly from any viewing angle.
##### Atomic Color-Depth Buffer
To prevent "z-fighting" and race conditions between parallel threads, Grapher uses a 64-bit atomic buffer (`graph_czb`). Each 64-bit word stores:
- **Upper 32 bits**: Z-depth (integer).
- **Lower 32 bits**: Pixel color (0xAARRGGBB).
A single `__atomic_compare_exchange_n` operation ensures that a pixel's color and depth are updated together only if the new depth is closer to the camera than the existing one.
Surface normals are estimated using central finite differences of the implicit function.
#### Filled Mod
When filled mode is active, each quad cell is split into two triangles. The average surface normal across the four corner vertices is computed and fed into `apply_shading`, which calculates:
```
intensity = ambient(0.3) + diffuse(0.7) * dot(normal, light_direction)
```
The light direction is fixed at `(0.577, 0.707, 0.408)` (normalized diagonal).
#### Z-Buffer
The depth buffer (`graph_zb`) stores integer depth values. `gfb_pixel_z` uses a **compare-and-swap (CAS) loop** via `__atomic_compare_exchange_n` so multiple parallel draw threads cannot produce race conditions.
### Coordinate Systems
#### 2D
World coordinates map linearly to screen pixels:
```c
screen_x = (wx - view_x_min) / (view_x_max - view_x_min) * graph_w
screen_y = (view_y_max - wy) / (view_y_max - view_y_min) * graph_h
```
#### 3D
Points are first rotated by two Euler angles (`rot_y`, `rot_x`) then projected with a simple perspective divide:
```
persp = d / (pz + d) // d = range_3d * 5
sx = px * scale * persp + screen_cx
sy = -py * scale * persp + screen_cy
```
---
## Configuration Constants
These can be changed at the top of `grapher.c` to tune behaviour:
| Constant | Default | Effect |
|---|---|---|
| `ROTATE` | `1` | Set to `0` to disable auto-rotation in 3D mode |
| `GRID_3D` | `41` | Grid resolution for 3D sampling. Higher = more detail, much slower |
> [!WARNING]
> Setting `GRID_3D` too high (e.g. 9000) will exhaust available memory. The `surf` grid and `surf_x`/`surf_y_3d` arrays are statically allocated at compile time: memory usage grows as **O(GRID_3D²)**. Values above ~512 are not recommended.
> [!TIP]
> `GRID_3D = 256` gives a good balance of detail and performance on typical BoredOS hardware emulation.
---
## Color Palette
3D surfaces are colored by height using a 4-stop rainbow ramp:
```
Low → Blue → Cyan → Green → Yellow → Red → High
```
---
## Preset Equations
The built-in presets are shown in the dropdown when you click **Presets**:
| Label | Type |
|---|---|
| `y = sin(x)` | 2D explicit |
| `y = x^2` | 2D explicit |
| `y = cos(x)*x` | 2D explicit |
| `z = sin(x)*cos(y)` | 3D explicit |
| `z = x^2 - y^2` | 3D explicit |
| `x^2+y^2+z^2=25` | 3D implicit (sphere) |
| `x^2+y^2=16` | 3D implicit (cylinder) |
---
## Known Limitations
- **No parameter slider** — equations are static; there is no way to animate a parameter.
- **No multiple equations** — only one equation can be graphed at a time.
- **Implicit surface precision** — extremely thin or high-frequency implicit surfaces may still have small artifacts if the grid resolution (`GRID_3D`) is too low.
- **3D implicit performance** — tri-axis marching evaluates the function significantly more times than explicit rendering; high resolutions will impact frame rate.
- **Integer axis labels only for large values** — very large axis values are capped at `>2G` or `<-2G` due to `itoa` limitations.

View File

@@ -7,9 +7,13 @@
BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features. BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features.
All headers are located in `src/userland/libc/`. All headers are located in `src/userland/libc/` (standard functions) and `src/wm/` (UI and widgets).
- `stdlib.h`: Memory, strings, and basic I/O.
- `math.h`: Freestanding floating-point math library.
- `libui.h`: Core window and drawing API.
- `libwidget.h`: High-level UI components.
## 📚 Standard Library (`stdlib.h` & `string.h`) ## Standard Library (`stdlib.h` & `string.h`)
The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls. The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls.
@@ -45,7 +49,60 @@ The standard library wrappers provide memory management, string manipulation, an
--- ---
## ⚙️ System Calls (`syscall.h`) ## Math Library (`math.h`)
BoredOS ships a freestanding floating-point math library in `libc/math.h`. It uses pure arithmetic — Taylor series, Newton-Raphson, and range-reduction — with no dependency on a host `libm` or hardware math intrinsics. It is automatically linked into every userland ELF.
```c
#include "math.h"
```
### Constants
| Constant | Value | Description |
|---|---|---|
| `M_PI` | 3.14159… | π |
| `M_E` | 2.71828… | Euler's number |
| `M_LN2` | 0.69315… | Natural log of 2 |
| `M_SQRT2` | 1.41421… | √2 |
| `HUGE_VAL` | ~+∞ | Overflow sentinel |
### Functions
#### Absolute value & remainder
* `double fabs(double x);` — Absolute value.
* `double fmod(double x, double y);` — Floating-point remainder. Returns `0` when `y == 0`.
#### Rounding
* `double floor(double x);` — Largest integer ≤ x.
* `double ceil(double x);` — Smallest integer ≥ x.
#### Trigonometry *(arguments in radians)*
* `double sin(double x);` — Sine. Range-reduced to `[-π, π]` then computed via 8-term Taylor series.
* `double cos(double x);` — Cosine. Computed via `sin(x + π/2)`.
* `double tan(double x);` — Tangent. Returns sentinel `1e15` near poles.
#### Exponential & logarithm
* `double sqrt(double x);` — Square root via Newton-Raphson (25 iterations). Returns `0` for `x ≤ 0`.
* `double log(double x);` — Natural logarithm (ln). Returns `-1e30` for `x ≤ 0`.
* `double log2(double x);` — Base-2 logarithm.
* `double log10(double x);` — Base-10 logarithm.
* `double exp(double x);` — e^x. Saturates to `1e300` for `x > 700`, `0` for `x < -700`.
* `double pow(double base, double exponent);` — General power. Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))`.
#### Hyperbolic
* `double sinh(double x);` — Hyperbolic sine.
* `double cosh(double x);` — Hyperbolic cosine.
* `double tanh(double x);` — Hyperbolic tangent.
#### Utility
* `double hypot(double x, double y);``sqrt(x² + y²)` without intermediate overflow.
* `double fmin(double a, double b);` — Minimum of two values.
* `double fmax(double a, double b);` — Maximum of two values.
* `double fclamp(double x, double lo, double hi);` — Clamps `x` into `[lo, hi]`.
> [!NOTE]
> The implementation file is named `libc/libmath.c` (not `libc/math.c`) to avoid a name collision with the `math` CLI calculator app in userland. The public header is still included as `#include "math.h"`.
For advanced operations, `syscall.h` provides direct wrappers into the kernel. For advanced operations, `syscall.h` provides direct wrappers into the kernel.
@@ -108,7 +165,7 @@ BoredOS includes lwIP for hardware TCP/UDP networking.
--- ---
## 📑 Core Data Structures ## Core Data Structures
### `os_info_t` ### `os_info_t`
Contains detailed build and version information about the OS. Contains detailed build and version information about the OS.

View File

@@ -7,7 +7,7 @@
The UI library (`libui.h`) is the sole mechanism for Graphical Userland Applications to draw to the screen and receive input events in BoredOS. It wraps `SYS_GUI` kernel calls. The UI library (`libui.h`) is the sole mechanism for Graphical Userland Applications to draw to the screen and receive input events in BoredOS. It wraps `SYS_GUI` kernel calls.
## 🪟 Window Management ## Window Management
A "Window" is a reserved drawing canvas managed by the compositor. A "Window" is a reserved drawing canvas managed by the compositor.
@@ -21,7 +21,7 @@ A "Window" is a reserved drawing canvas managed by the compositor.
* `void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h);` * `void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h);`
Query the global screen resolution of the display. Query the global screen resolution of the display.
## 🎨 Drawing Primitives ## Drawing Primitives
All drawing functions write to an off-screen buffer associated with the window. **You must call `ui_mark_dirty()` to instruct the compositor to push your changes to the physical screen.** All drawing functions write to an off-screen buffer associated with the window. **You must call `ui_mark_dirty()` to instruct the compositor to push your changes to the physical screen.**
@@ -38,7 +38,7 @@ All drawing functions write to an off-screen buffer associated with the window.
> Colors are defined as 32-bit unsigned integers in **ARGB** format: `0xAARRGGBB`. > Colors are defined as 32-bit unsigned integers in **ARGB** format: `0xAARRGGBB`.
> E.g., `0xFF000000` is opaque black, `0xFFFF0000` is opaque red. > E.g., `0xFF000000` is opaque black, `0xFFFF0000` is opaque red.
## 🔤 Text Rendering ## Text Rendering
BoredOS provides multiple text rendering methodologies, including a default system font and scaled/bitmap alternatives. BoredOS provides multiple text rendering methodologies, including a default system font and scaled/bitmap alternatives.
@@ -60,7 +60,7 @@ Used for calculating layout bounds before drawing:
* `uint32_t ui_get_string_width_scaled(const char *str, float scale);` * `uint32_t ui_get_string_width_scaled(const char *str, float scale);`
* `uint32_t ui_get_font_height_scaled(float scale);` * `uint32_t ui_get_font_height_scaled(float scale);`
## 🔄 Event Handling ## Event Handling
Applications must continuously poll for events inside an infinite `$while(1)` loop. Applications must continuously poll for events inside an infinite `$while(1)` loop.
@@ -103,3 +103,9 @@ typedef struct {
*(Note: Coordinate arguments (`arg1`, `arg2`) for mouse events are typically relative to the top-left corner of the window's client area).* *(Note: Coordinate arguments (`arg1`, `arg2`) for mouse events are typically relative to the top-left corner of the window's client area).*
--- ---
> [!TIP]
> **Looking for Buttons, TextBoxes, or Scrollbars?**
> While `libui.h` provides the foundation for drawing, most applications should use the higher-level [**Widget API**](widget_api.md) (`libwidget.h`) for standard interactive components.
---

108
docs/appdev/widget_api.md Normal file
View File

@@ -0,0 +1,108 @@
<div align="center">
<h1>Widget API (<code>libwidget.h</code>)</h1>
<p><em>High-level UI components for BoredOS applications.</em></p>
</div>
---
The Widget library (`libwidget.h`) provides a set of reusable UI components built on top of `libui.h`. It uses an abstract `widget_context_t` to decouple component logic from specific drawing implementations, making it easier to build complex graphical interfaces.
## Widget Context
To use any widget, you must first define a `widget_context_t`. This structure contains function pointers for basic drawing operations (rects, strings) and theme preferences.
```c
typedef struct {
void *user_data;
void (*draw_rect)(void *user_data, int x, int y, int w, int h, uint32_t color);
void (*draw_rounded_rect_filled)(void *user_data, int x, int y, int w, int h, int r, uint32_t color);
void (*draw_string)(void *user_data, int x, int y, const char *str, uint32_t color);
int (*measure_string_width)(void *user_data, const char *str);
void (*mark_dirty)(void *user_data, int x, int y, int w, int h);
bool use_light_theme;
} widget_context_t;
```
> [!TIP]
> Usually, `user_data` is set to your `ui_window_t` handle, and the functions are simple wrappers around `ui_draw_rect`, `ui_draw_string`, etc.
---
## Button (`widget_button_t`)
Standard interactive button with hover and click states.
* `void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text);`
* `void widget_button_draw(widget_context_t *ctx, widget_button_t *btn);`
* `bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data);`
### Usage Example:
```c
widget_button_t my_btn;
widget_button_init(&my_btn, 10, 10, 80, 25, "Click Me");
my_btn.on_click = my_callback_func;
// In your event loop:
widget_button_handle_mouse(&my_btn, ev.arg1, ev.arg2, is_down, is_clicked, my_data);
```
---
## Scrollbar (`widget_scrollbar_t`)
Vertical scrollbar supporting dragging and track-paging.
* `void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h);`
* `void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y);`
* `void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb);`
* `bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data);`
> [!NOTE]
> The scrollbar automatically calculates the "thumb" size based on the ratio of `h` to `content_height`.
---
## TextBox (`widget_textbox_t`)
Editable text field with focus support and keyboard handling.
* `void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len);`
* `void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb);`
* `bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data);`
* `bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data);`
---
## Dropdown (`widget_dropdown_t`)
Selection menu for picking one item from a list.
* `void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count);`
* `void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd);`
* `bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data);`
---
## Checkbox / Radio (`widget_checkbox_t`)
Toggleable options with support for circular "Radio" style or square "Checkbox" style.
* `void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio);`
* `void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb);`
* `bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data);`
---
## Event Integration
Widgets are designed to be polled within your `libui` event loop. Most handle-mouse functions return `true` if the event was "consumed" by the widget, allowing you to stop further processing for that event.
```c
if (ui_get_event(win, &ev)) {
bool handled = false;
handled |= widget_button_handle_mouse(&btn, ev.arg1, ev.arg2, is_down, is_clicked, NULL);
if (!handled) {
// Handle global window events...
}
}
```

View File

@@ -9,7 +9,7 @@ BoredOS is a 64-bit hobbyist operating system designed for the x86_64 architectu
This document serves as an overview of the core architecture and the layout of the kernel source code. This document serves as an overview of the core architecture and the layout of the kernel source code.
## 📂 Source Code Layout (`src/`) ## Source Code Layout (`src/`)
The OS heavily relies on module separation. The `src/` directory is logically split into several domains: The OS heavily relies on module separation. The `src/` directory is logically split into several domains:
@@ -23,7 +23,7 @@ The OS heavily relies on module separation. The `src/` directory is logically sp
- **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering. - **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering.
- **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`). - **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`).
## 🚀 Boot Process ## Boot Process
BoredOS uses **Limine** as its primary bootloader. BoredOS uses **Limine** as its primary bootloader.
@@ -42,7 +42,7 @@ BoredOS utilizes Symmetric Multi-Processing (SMP) to distribute workloads across
- **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution. - **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution.
- **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions. - **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions.
## 🛡️ Userland Transition ## Userland Transition
The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel: The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel:

View File

@@ -7,7 +7,7 @@
BoredOS implements a rudimentary but functional filesystem layer designed to support reading system assets and user applications during runtime. BoredOS implements a rudimentary but functional filesystem layer designed to support reading system assets and user applications during runtime.
## 🗂️ Virtual File System (VFS) ## Virtual File System (VFS)
The Virtual File System acts as an abstraction layer across different underlying storage mechanisms (even if, currently, only one type is fully utilized). System calls targeting files (`SYS_FS`) route through the VFS rather than interacting with the disk directly. The Virtual File System acts as an abstraction layer across different underlying storage mechanisms (even if, currently, only one type is fully utilized). System calls targeting files (`SYS_FS`) route through the VFS rather than interacting with the disk directly.
@@ -17,11 +17,11 @@ Key VFS functionalities include:
- **Path Parsing**: Resolving absolute and relative paths. - **Path Parsing**: Resolving absolute and relative paths.
- **SMP Safety**: All VFS and underlying FAT32 operations are protected by a global **Spinlock**. This ensures that multiple cores can safely read from the filesystem simultaneously without corrupting internal file seek pointers or directory cache states. - **SMP Safety**: All VFS and underlying FAT32 operations are protected by a global **Spinlock**. This ensures that multiple cores can safely read from the filesystem simultaneously without corrupting internal file seek pointers or directory cache states.
## 💾 FAT32 Implementation ## FAT32 Implementation
The primary filesystem logic in `fat32.c` handles both in-memory RAM-based filesystem simulation and physical ATA block devices. The primary filesystem logic in `fat32.c` handles both in-memory RAM-based filesystem simulation and physical ATA block devices.
### 💿 Storage Support ### Storage Support
BoredOS supports two main types of storage for its FAT32 implementation: BoredOS supports two main types of storage for its FAT32 implementation:
@@ -30,7 +30,7 @@ BoredOS supports two main types of storage for its FAT32 implementation:
- **GPT is NOT supported**: Currently, only **MBR (Master Boot Record)** partition tables or **raw (partitionless)** disks are supported. - **GPT is NOT supported**: Currently, only **MBR (Master Boot Record)** partition tables or **raw (partitionless)** disks are supported.
- **Filesystem**: The partition must be formatted as **FAT32**. - **Filesystem**: The partition must be formatted as **FAT32**.
### 🔍 Auto-detection ### Auto-detection
The `Disk Manager` automatically probes primary and secondary IDE channels during initialization. If a valid FAT32 partition is found (either directly at sector 0 or via an MBR partition table), the disk is assigned a drive letter (starting from `B:`) and becomes accessible to the VFS. The `Disk Manager` automatically probes primary and secondary IDE channels during initialization. If a valid FAT32 partition is found (either directly at sector 0 or via an MBR partition table), the disk is assigned a drive letter (starting from `B:`) and becomes accessible to the VFS.

View File

@@ -7,7 +7,7 @@
Memory management in BoredOS is split into physical and virtual layers, designed to support both kernel operations and userland isolation on the x86_64 architecture. Memory management in BoredOS is split into physical and virtual layers, designed to support both kernel operations and userland isolation on the x86_64 architecture.
## 🧠 Physical Memory Management (PMM) ## Physical Memory Management (PMM)
The PMM is responsible for tracking which physical RAM frames (usually 4KB each) are free and which are in use. The PMM is responsible for tracking which physical RAM frames (usually 4KB each) are free and which are in use.
@@ -19,7 +19,7 @@ The PMM is responsible for tracking which physical RAM frames (usually 4KB each)
> [!NOTE] > [!NOTE]
> 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables. > 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables.
## 🗺️ Virtual Memory Management (VMM) and Paging ## Virtual Memory Management (VMM) and Paging
BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland. BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland.
@@ -30,7 +30,7 @@ BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing
- **User Space**: Userland applications are loaded into lower virtual addresses. - **User Space**: Userland applications are loaded into lower virtual addresses.
- **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame or terminate the process. - **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame or terminate the process.
## 🏗️ Kernel Heap ## Kernel Heap
Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space. Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space.

View File

@@ -7,7 +7,7 @@
BoredOS features a fully custom, graphical Window Manager built directly into the kernel, residing in the `src/wm/` directory. It is responsible for compositing the screen, handling window logic, rendering text, and dispatching UI events. BoredOS features a fully custom, graphical Window Manager built directly into the kernel, residing in the `src/wm/` directory. It is responsible for compositing the screen, handling window logic, rendering text, and dispatching UI events.
## 🖼️ Framebuffer and Rendering ## Framebuffer and Rendering
1. **Limine Framebuffer**: During boot, the Limine bootloader requests a graphical framebuffer from the hardware (e.g., GOP in UEFI environments) and passes a pointer to this linear memory buffer to the kernel. 1. **Limine Framebuffer**: During boot, the Limine bootloader requests a graphical framebuffer from the hardware (e.g., GOP in UEFI environments) and passes a pointer to this linear memory buffer to the kernel.
2. **Double Buffering**: To prevent screen tearing, the WM does not draw directly to the screen. It allocates a "back buffer" in kernel memory equal to the size of the screen. All drawing operations (lines, rectangles, windows) happen on this back buffer. 2. **Double Buffering**: To prevent screen tearing, the WM does not draw directly to the screen. It allocates a "back buffer" in kernel memory equal to the size of the screen. All drawing operations (lines, rectangles, windows) happen on this back buffer.
@@ -24,7 +24,7 @@ The windowing system is built around a linked list of `Window` structures.
- **Window Structures**: Each window object tracks its dimensions (`x`, `y`, `width`, `height`), title, background color, and an internal buffer if it's acting as a canvas for userland apps. - **Window Structures**: Each window object tracks its dimensions (`x`, `y`, `width`, `height`), title, background color, and an internal buffer if it's acting as a canvas for userland apps.
- **Decorations**: The kernel handles drawing window borders, title bars, and close buttons automatically unless a borderless style is specified. - **Decorations**: The kernel handles drawing window borders, title bars, and close buttons automatically unless a borderless style is specified.
## 🖱️ Input Handling and Events ## Input Handling and Events
The WM acts as the central hub for input routing. The WM acts as the central hub for input routing.
@@ -38,11 +38,11 @@ The WM acts as the central hub for input routing.
With the introduction of Symmetric Multi-Processing (SMP), the Window Manager (WM) was redesigned to ensure stability and high performance across multiple cores. With the introduction of Symmetric Multi-Processing (SMP), the Window Manager (WM) was redesigned to ensure stability and high performance across multiple cores.
1. **Global GUI Lock (`wm_lock`)**: To prevent race conditions when multiple cores attempt to create windows, move cursors, or update pixels, the WM utilizes a central spinlock. All `GUI_CMD` system calls are protected by this lock. 1. **Granular Window Locks**: Each `Window` object possesses its own `spinlock_t lock;`. User applications concurrently draw directly into their own window buffers without stalling the rest of the system. The global `wm_lock` is reserved strictly for altering global structures like window z-order or syncing buffers to the screen compositing layer.
2. **Deferred Rendering**: Previously, the desktop was repainted inside the timer interrupt. On multi-core systems, this caused severe "core starvation" as all other CPUs would spin waiting for the GUI lock during the long draw cycle. 2. **Per-CPU Rendering State**: To facilitate simultaneous GUI system calls across all CPU cores, the low-level rendering context (`g_render_target` array) is isolated per-CPU using the core ID. This allows completely lockless multi-core pixel rasterization, drastically reducing rendering bottlenecks.
3. **Kernel Loop Integration**: Final screen composition (`wm_paint`) is now deferred to the main kernel idle loop on the Bootstrap Processor (BSP). This allows application cores to continue processing logic while the GUI asynchronously flips the framebuffer. 3. **Deferred Compositing**: Final screen composition (`wm_paint`) is scheduled to the main kernel idle loop on the Bootstrap Processor (BSP). This enables application cores to continue processing logic seamlessly while the GUI asynchronously handles flipping the physical framebuffer.
> [!IMPORTANT] > [!IMPORTANT]
> Because rendering is now asynchronous to the timer, application performance is significantly higher as they are no longer bottlenecked by interrupt-context drawing. > Because application rendering (rasterizing geometry into a window's backbuffer) is SMP-safe and lock-free across cores, GUI performance scales linearly with the number of CPUs active.
--- ---

View File

@@ -1,7 +0,0 @@
#!/bin/bash
sed -i '' -e 's/asm volatile("pushfq; pop %0; cli" : "=r"(rflags));/rflags = wm_lock_acquire();/g' src/sys/syscall.c
sed -i '' -e 's/asm volatile("push %0; popfq" : : "r"(rflags));/wm_lock_release(rflags);/g' src/sys/syscall.c
sed -i '' -e 's/asm volatile("pushfq; pop %0; cli" : "=r"(rflags));/rflags = wm_lock_acquire();/g' src/wm/wm.c
sed -i '' -e 's/asm volatile("push %0; popfq" : : "r"(rflags));/wm_lock_release(rflags);/g' src/wm/wm.c
sed -i '' -e 's/uint64_t rflags;/uint64_t rflags;/g' src/sys/syscall.c
echo "Done"

View File

@@ -45,7 +45,11 @@ isr%2_wrapper:
push r14 push r14
push r15 push r15
; Save SSE/FPU state test qword [rsp + 144], 3
jz %%skip_swap
swapgs
%%skip_swap:
sub rsp, 512 sub rsp, 512
fxsave [rsp] fxsave [rsp]
@@ -76,6 +80,12 @@ isr%2_wrapper:
pop rcx pop rcx
pop rbx pop rbx
pop rax pop rax
test qword [rsp + 24], 3
jz %%skip_swap_back
swapgs
%%skip_swap_back:
add rsp, 16 ; drop dummy vector and error code add rsp, 16 ; drop dummy vector and error code
iretq iretq
%endmacro %endmacro
@@ -164,7 +174,11 @@ exception_common:
push r14 push r14
push r15 push r15
; Save SSE/FPU state test qword [rsp + 144], 3
jz .skip_swap_exc
swapgs
.skip_swap_exc:
sub rsp, 512 sub rsp, 512
fxsave [rsp] fxsave [rsp]
@@ -196,6 +210,12 @@ exception_common:
pop rcx pop rcx
pop rbx pop rbx
pop rax pop rax
test qword [rsp + 24], 3
jz .skip_swap_back_exc
swapgs
.skip_swap_back_exc:
add rsp, 16 ; drop vector and error code add rsp, 16 ; drop vector and error code
iretq iretq

View File

@@ -15,15 +15,14 @@ section .text
; R9 = arg5 ; R9 = arg5
syscall_entry: syscall_entry:
; 1. Switch to Kernel Stack safely swapgs
; Note: For true SMP safety, we need per-CPU storage (via swapgs).
; For now, we use a global scratch which is only safe because we mask interrupts on entry.
mov [rel user_rsp_scratch], rsp
mov rsp, [rel kernel_syscall_stack]
; 2. Build iretq frame (compatible with registers_t) mov [gs:40], rsp
mov rsp, [gs:48]
; 2. Build iretq frame
push 0x1B ; SS (User Data) push 0x1B ; SS (User Data)
push qword [rel user_rsp_scratch] ; RSP push qword [gs:40] ; RSP
push r11 ; RFLAGS (captured by syscall) push r11 ; RFLAGS (captured by syscall)
push 0x23 ; CS (User Code) push 0x23 ; CS (User Code)
push rcx ; RIP (return address from syscall) push rcx ; RIP (return address from syscall)
@@ -81,14 +80,7 @@ syscall_entry:
pop rax pop rax
add rsp, 16 ; drop int_no/err_code add rsp, 16 ; drop int_no/err_code
; Debug: check RIP before iretq swapgs
; We can't easily print from here without destroying registers,
; but we can at least check if it's canonical.
iretq iretq
section .bss section .bss
global kernel_syscall_stack
global user_rsp_scratch
kernel_syscall_stack: resq 1
user_rsp_scratch: resq 1

View File

@@ -30,6 +30,16 @@ int k_strcmp(const char *s1, const char *s2) {
return *(const unsigned char*)s1 - *(const unsigned char*)s2; return *(const unsigned char*)s1 - *(const unsigned char*)s2;
} }
int k_strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
s1++;
s2++;
n--;
}
if (n == 0) return 0;
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
void k_strcpy(char *dest, const char *src) { void k_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++; while (*src) *dest++ = *src++;
*dest = 0; *dest = 0;

View File

@@ -13,6 +13,7 @@ void k_memset(void *dest, int val, size_t len);
void k_memcpy(void *dest, const void *src, size_t len); void k_memcpy(void *dest, const void *src, size_t len);
size_t k_strlen(const char *str); size_t k_strlen(const char *str);
int k_strcmp(const char *s1, const char *s2); int k_strcmp(const char *s1, const char *s2);
int k_strncmp(const char *s1, const char *s2, size_t n);
void k_strcpy(char *dest, const char *src); void k_strcpy(char *dest, const char *src);
int k_atoi(const char *str); int k_atoi(const char *str);
void k_itoa(int n, char *buf); void k_itoa(int n, char *buf);

View File

@@ -16,12 +16,20 @@
#include "wm.h" #include "wm.h"
#include "io.h" #include "io.h"
#include "fat32.h" #include "fat32.h"
#include "tar.h"
#include "vfs.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "platform.h" #include "platform.h"
#include "wallpaper.h" #include "wallpaper.h"
#include "smp.h" #include "smp.h"
#include "work_queue.h" #include "work_queue.h"
#include "lapic.h" #include "lapic.h"
#include "fs/sysfs.h"
#include "fs/procfs.h"
#include "sys/kernel_subsystem.h"
#include "sys/module_manager.h"
extern void sysfs_init_subsystems(void);
// --- Limine Requests --- // --- Limine Requests ---
__attribute__((used, section(".requests"))) __attribute__((used, section(".requests")))
@@ -132,6 +140,7 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) { void kmain(void) {
init_serial(); init_serial();
vfs_init();
serial_write("\n[DEBUG] Entering kmain...\n"); serial_write("\n[DEBUG] Entering kmain...\n");
platform_init(); platform_init();
@@ -155,6 +164,8 @@ void kmain(void) {
// The memory manager will now scan the memory map and manage all usable regions. // The memory manager will now scan the memory map and manage all usable regions.
memory_manager_init_from_memmap(memmap_request.response); memory_manager_init_from_memmap(memmap_request.response);
serial_write("[DEBUG] memory_manager_init OK\n"); serial_write("[DEBUG] memory_manager_init OK\n");
smp_init_bsp();
serial_write("[DEBUG] smp_init_bsp OK\n");
} else { } else {
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n"); serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
hcf(); hcf();
@@ -197,6 +208,11 @@ void kmain(void) {
fat32_mkdir("/Library/DOOM"); fat32_mkdir("/Library/DOOM");
fat32_mkdir("/docs"); 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);
if (module_request.response == NULL) { if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n"); serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
} else { } else {
@@ -210,6 +226,15 @@ void kmain(void) {
if (fs_starts_with(clean_path, "boot():")) clean_path += 7; if (fs_starts_with(clean_path, "boot():")) clean_path += 7;
else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8; else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8;
int len = 0;
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(clean_path);
serial_write("\n");
tar_parse(mod->address, mod->size);
} else {
char dir_path[256]; char dir_path[256];
int last_slash = -1; int last_slash = -1;
for (int j = 0; clean_path[j]; j++) { for (int j = 0; clean_path[j]; j++) {
@@ -227,6 +252,9 @@ void kmain(void) {
fat32_close(fh); 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 // Initialize fonts now that FAT32 and modules are loaded
@@ -245,7 +273,7 @@ void kmain(void) {
// Initialize LAPIC for IPI support // Initialize LAPIC for IPI support
lapic_init(); lapic_init();
// Initialize SMP — bring up all CPU cores // Initialize SMP
if (smp_request.response != NULL) { if (smp_request.response != NULL) {
uint32_t online = smp_init(smp_request.response); uint32_t online = smp_init(smp_request.response);
serial_write("[DEBUG] SMP init complete, CPUs online: "); serial_write("[DEBUG] SMP init complete, CPUs online: ");
@@ -253,7 +281,6 @@ void kmain(void) {
serial_write("\n"); serial_write("\n");
} else { } else {
serial_write("[DEBUG] No SMP response from bootloader\n"); serial_write("[DEBUG] No SMP response from bootloader\n");
// Still init as single-CPU
smp_init(NULL); smp_init(NULL);
} }

View File

@@ -14,7 +14,7 @@ static size_t man_strlen(const char *str) {
} }
static void write_man_file(const char *name, const char *content) { static void write_man_file(const char *name, const char *content) {
char path[128] = "A:/Library/man/"; char path[128] = "/Library/man/";
int i = 15; int i = 15;
while (*name) path[i++] = *name++; while (*name) path[i++] = *name++;
path[i++] = '.'; path[i++] = '.';
@@ -31,8 +31,8 @@ static void write_man_file(const char *name, const char *content) {
} }
void create_man_entries(void) { void create_man_entries(void) {
fat32_mkdir("A:/Library"); fat32_mkdir("/Library");
fat32_mkdir("A:/Library/man"); fat32_mkdir("/Library/man");
write_man_file("ping", "PING - Send ICMP echo requests\n\nUsage: ping <ip>\n\nSends ICMP echo requests to the specified IP address and displays the response times."); write_man_file("ping", "PING - Send ICMP echo requests\n\nUsage: ping <ip>\n\nSends ICMP echo requests to the specified IP address and displays the response times.");
write_man_file("net", "NET - Network utilities\n\nUsage: net init\nnet info\nnet ipset >ip<\nnet udpsend >ip< >port< >message< net ping >ip< net help\n\nA collection of network-related commands."); write_man_file("net", "NET - Network utilities\n\nUsage: net init\nnet info\nnet ipset >ip<\nnet udpsend >ip< >port< >message< net ping >ip< net help\n\nA collection of network-related commands.");
@@ -55,7 +55,7 @@ void create_man_entries(void) {
write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist."); write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist.");
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("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("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 A:/Library/conf/sysfetch.cfg."); 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("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics."); 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("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."); write_man_file("reboot", "REBOOT - Restart system\n\nUsage: reboot\n\nRestarts the computer immediately.");

View File

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

View File

@@ -6,9 +6,21 @@
#include <stdint.h> #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); void platform_init(void);
uint64_t p2v(uint64_t phys); uint64_t p2v(uint64_t phys);
uint64_t v2p(uint64_t virt); uint64_t v2p(uint64_t virt);
void platform_get_cpu_model(char *model); 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 #endif

View File

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

509
src/dev/ahci.c Normal file
View File

@@ -0,0 +1,509 @@
// 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 "ahci.h"
#include "pci.h"
#include "disk.h"
#include "memory_manager.h"
#include "paging.h"
#include "io.h"
#include <stddef.h>
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
extern void serial_write_hex(uint32_t val);
// ============================================================================
// AHCI Driver State
// ============================================================================
static HBA_MEM *abar = NULL; // MMIO-mapped AHCI Base Address
static bool ahci_initialized = false;
static int active_port_count = 0;
#define MAX_AHCI_PORTS 32
typedef struct {
bool active;
int port_num;
HBA_PORT *port;
HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned
void *fis_base; // 256B, 256B aligned
HBA_CMD_TBL *cmd_tbl; // Command table for slot 0
} ahci_port_state_t;
static ahci_port_state_t ports[MAX_AHCI_PORTS];
// ============================================================================
// String Helpers
// ============================================================================
static void ahci_strcpy(char *d, const char *s) {
while ((*d++ = *s++));
}
// Kernel virtual to physical address conversion
extern uint64_t v2p(uint64_t vaddr);
// ============================================================================
// Port Setup
// ============================================================================
static void ahci_stop_cmd(HBA_PORT *port) {
// Clear ST (Start)
port->cmd &= ~HBA_PORT_CMD_ST;
// Clear FRE (FIS Receive Enable)
port->cmd &= ~HBA_PORT_CMD_FRE;
// Wait until FR and CR clear
int timeout = 500000;
while (timeout-- > 0) {
if (port->cmd & HBA_PORT_CMD_FR) continue;
if (port->cmd & HBA_PORT_CMD_CR) continue;
break;
}
}
static void ahci_start_cmd(HBA_PORT *port) {
// Wait until CR clears
while (port->cmd & HBA_PORT_CMD_CR);
// Set FRE and ST
port->cmd |= HBA_PORT_CMD_FRE;
port->cmd |= HBA_PORT_CMD_ST;
}
static int ahci_check_port_type(HBA_PORT *port) {
uint32_t ssts = port->ssts;
uint8_t ipm = (ssts >> 8) & 0x0F;
uint8_t det = ssts & 0x0F;
if (det != 3) return -1; // No device detected
if (ipm != 1) return -1; // Not in active state
switch (port->sig) {
case SATA_SIG_ATA: return 0; // SATA drive
case SATA_SIG_ATAPI: return 1; // SATAPI drive
case SATA_SIG_SEMB: return 2; // SEMB
case SATA_SIG_PM: return 3; // Port multiplier
default: return -1;
}
}
static void ahci_port_rebase(ahci_port_state_t *ps) {
HBA_PORT *port = ps->port;
ahci_stop_cmd(port);
// Allocate command list (1KB, 1024-byte aligned)
ps->cmd_list = (HBA_CMD_HEADER*)kmalloc_aligned(1024, 1024);
if (!ps->cmd_list) return;
mem_memset(ps->cmd_list, 0, 1024);
uint64_t clb_phys = v2p((uint64_t)ps->cmd_list);
port->clb = (uint32_t)(clb_phys & 0xFFFFFFFF);
port->clbu = (uint32_t)(clb_phys >> 32);
// Allocate FIS receive area (256 bytes, 256-byte aligned)
ps->fis_base = kmalloc_aligned(256, 256);
if (!ps->fis_base) return;
mem_memset(ps->fis_base, 0, 256);
uint64_t fb_phys = v2p((uint64_t)ps->fis_base);
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
port->fbu = (uint32_t)(fb_phys >> 32);
// Allocate command table for slot 0 (256-byte aligned, room for 8 PRDT entries)
int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 8 * sizeof(HBA_PRDT_ENTRY);
ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256);
if (!ps->cmd_tbl) return;
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
// Set command header 0 to point to our command table
uint64_t ctba_phys = v2p((uint64_t)ps->cmd_tbl);
ps->cmd_list[0].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF);
ps->cmd_list[0].ctbau = (uint32_t)(ctba_phys >> 32);
ps->cmd_list[0].prdtl = 1; // 1 PRDT entry default
// Clear error and interrupt status
port->serr = 0xFFFFFFFF;
port->is = 0xFFFFFFFF;
ahci_start_cmd(port);
}
// ============================================================================
// Sector I/O
// ============================================================================
static int ahci_find_free_slot(HBA_PORT *port) {
uint32_t slots = (port->sact | port->ci);
for (int i = 0; i < 32; i++) {
if (!(slots & (1 << i))) return i;
}
return -1;
}
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) {
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
HBA_PORT *port = ps->port;
// Clear any pending interrupts/errors
port->is = 0xFFFFFFFF;
int slot = ahci_find_free_slot(port);
if (slot < 0) return -1;
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
cmd_hdr->w = 0; // Read
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
// Setup PRDT
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count
cmd_tbl->prdt[0].i = 1;
// Setup Command FIS
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1; // Command
fis->command = ATA_CMD_READ_DMA_EX;
fis->lba0 = (uint8_t)(lba);
fis->lba1 = (uint8_t)(lba >> 8);
fis->lba2 = (uint8_t)(lba >> 16);
fis->device = 1 << 6; // LBA mode
fis->lba3 = (uint8_t)(lba >> 24);
fis->lba4 = (uint8_t)(lba >> 32);
fis->lba5 = (uint8_t)(lba >> 40);
fis->countl = (uint8_t)(count);
fis->counth = (uint8_t)(count >> 8);
// Issue command
port->ci = (1 << slot);
// Wait for completion
int timeout = 1000000;
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");
return -1;
}
}
if (timeout <= 0) {
serial_write("[AHCI] Read timeout on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
return 0;
}
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer) {
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
HBA_PORT *port = ps->port;
port->is = 0xFFFFFFFF;
int slot = ahci_find_free_slot(port);
if (slot < 0) return -1;
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
cmd_hdr->w = 1; // Write
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1;
cmd_tbl->prdt[0].i = 1;
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1;
fis->command = ATA_CMD_WRITE_DMA_EX;
fis->lba0 = (uint8_t)(lba);
fis->lba1 = (uint8_t)(lba >> 8);
fis->lba2 = (uint8_t)(lba >> 16);
fis->device = 1 << 6;
fis->lba3 = (uint8_t)(lba >> 24);
fis->lba4 = (uint8_t)(lba >> 32);
fis->lba5 = (uint8_t)(lba >> 40);
fis->countl = (uint8_t)(count);
fis->counth = (uint8_t)(count >> 8);
port->ci = (1 << slot);
int timeout = 1000000;
while (timeout-- > 0) {
if (!(port->ci & (1 << slot))) break;
if (port->is & (1 << 30)) {
serial_write("[AHCI] Write error on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
}
if (timeout <= 0) {
serial_write("[AHCI] Write timeout on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
return 0;
}
// ============================================================================
// AHCI Disk Integration — wrap AHCI into Disk read/write_sector
// ============================================================================
typedef struct {
int ahci_port;
} AHCIDriverData;
static int ahci_disk_read_sector(Disk *disk, uint32_t sector, uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
// For partitions, add offset and use parent's port
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_read_sectors(pdata->ahci_port,
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
}
return ahci_read_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
}
static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_write_sectors(pdata->ahci_port,
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
}
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
}
// ============================================================================
// Initialization
// ============================================================================
int ahci_get_port_count(void) {
return active_port_count;
}
bool ahci_port_is_active(int port_num) {
if (port_num < 0 || port_num >= MAX_AHCI_PORTS) return false;
return ports[port_num].active;
}
void ahci_init(void) {
serial_write("[AHCI] Scanning PCI for AHCI controller...\n");
// Find AHCI controller (Class 0x01, Subclass 0x06)
pci_device_t pci_dev;
if (!pci_find_device_by_class(PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_SATA, &pci_dev)) {
serial_write("[AHCI] No AHCI controller found\n");
return;
}
serial_write("[AHCI] Found AHCI controller (");
serial_write("vendor=0x");
serial_write_hex(pci_dev.vendor_id);
serial_write(", device=0x");
serial_write_hex(pci_dev.device_id);
serial_write(")\n");
// Enable Bus Mastering and MMIO
pci_enable_bus_mastering(&pci_dev);
pci_enable_mmio(&pci_dev);
// Read ABAR (BAR5)
uint32_t abar_raw = pci_get_bar(&pci_dev, 5);
uint64_t abar_phys = abar_raw & 0xFFFFF000; // Mask out lower bits
if (abar_phys == 0) {
serial_write("[AHCI] Invalid ABAR address\n");
return;
}
serial_write("[AHCI] ABAR physical address: 0x");
serial_write_hex((uint32_t)abar_phys);
serial_write("\n");
// Map ABAR region into kernel virtual address space
// Identity-map several pages to cover the HBA memory (at least 0x1100 bytes)
uint64_t abar_virt = abar_phys; // Use identity mapping
for (uint64_t offset = 0; offset < 0x2000; offset += 4096) {
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
abar_phys + offset,
PT_PRESENT | PT_RW | PT_CACHE_DISABLE);
}
abar = (HBA_MEM*)abar_virt;
// Enable AHCI mode
abar->ghc |= (1 << 31); // AE (AHCI Enable)
serial_write("[AHCI] Version: ");
serial_write_num(abar->vs >> 16);
serial_write(".");
serial_write_num(abar->vs & 0xFFFF);
serial_write("\n");
// Probe ports
uint32_t pi = abar->pi;
active_port_count = 0;
for (int i = 0; i < 32; i++) {
ports[i].active = false;
if (!(pi & (1 << i))) continue;
HBA_PORT *port = &abar->ports[i];
int type = ahci_check_port_type(port);
if (type == 0) { // SATA drive
serial_write("[AHCI] Port ");
serial_write_num(i);
serial_write(": SATA drive detected\n");
ports[i].port_num = i;
ports[i].port = port;
ahci_port_rebase(&ports[i]);
ports[i].active = true;
active_port_count++;
// Register as a block device
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
if (disk) {
AHCIDriverData *drv = (AHCIDriverData*)kmalloc(sizeof(AHCIDriverData));
drv->ahci_port = i;
disk->devname[0] = 0; // Auto-assign
disk->type = DISK_TYPE_SATA;
ahci_strcpy(disk->label, "SATA Drive");
disk->read_sector = ahci_disk_read_sector;
disk->write_sector = ahci_disk_write_sector;
disk->driver_data = drv;
disk->partition_lba_offset = 0;
disk->total_sectors = 0;
disk->parent = NULL;
disk->is_partition = false;
disk->is_fat32 = false;
disk_register(disk);
// Let disk_manager parse partitions — we call a scan function
extern void disk_manager_scan_partitions(Disk *disk);
// Inline MBR parse for this disk
extern void serial_write(const char *str);
serial_write("[AHCI] Probing partitions on /dev/");
serial_write(disk->devname);
serial_write("...\n");
// Read MBR sector 0
uint8_t *mbr_buf = (uint8_t*)kmalloc(512);
if (mbr_buf) {
if (ahci_disk_read_sector(disk, 0, mbr_buf) == 0) {
if (mbr_buf[510] == 0x55 && mbr_buf[511] == 0xAA) {
// Parse MBR partition table
typedef struct {
uint8_t status;
uint8_t chs_first[3];
uint8_t type;
uint8_t chs_last[3];
uint32_t lba_start;
uint32_t sector_count;
} __attribute__((packed)) MBR_Part;
MBR_Part *parts = (MBR_Part*)&mbr_buf[446];
int pn = 1;
for (int p = 0; p < 4; p++) {
if (parts[p].type == 0x00 || parts[p].sector_count == 0)
continue;
bool fat32 = false;
if (parts[p].type == 0x0B || parts[p].type == 0x0C) {
// Verify BPB
uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (pbuf) {
if (ahci_disk_read_sector(disk, parts[p].lba_start, pbuf) == 0) {
if (pbuf[510] == 0x55 && pbuf[511] == 0xAA) {
uint16_t bps = *(uint16_t*)&pbuf[11];
uint16_t spf16 = *(uint16_t*)&pbuf[22];
uint32_t spf32 = *(uint32_t*)&pbuf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0)
fat32 = true;
}
}
kfree(pbuf);
}
}
disk_register_partition(disk, parts[p].lba_start,
parts[p].sector_count, fat32, pn);
pn++;
}
// Fallback: raw FAT32
if (pn == 1) {
uint16_t bps = *(uint16_t*)&mbr_buf[11];
uint16_t spf16 = *(uint16_t*)&mbr_buf[22];
uint32_t spf32 = *(uint32_t*)&mbr_buf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) {
disk->is_fat32 = true;
disk->partition_lba_offset = 0;
serial_write("[AHCI] Raw FAT32 volume detected\n");
}
}
}
}
kfree(mbr_buf);
}
}
} else if (type == 1) {
serial_write("[AHCI] Port ");
serial_write_num(i);
serial_write(": SATAPI drive (ignored)\n");
}
}
if (active_port_count > 0) {
ahci_initialized = true;
serial_write("[AHCI] Initialization complete: ");
serial_write_num(active_port_count);
serial_write(" SATA port(s) active\n");
} else {
serial_write("[AHCI] No active SATA ports found\n");
}
}

174
src/dev/ahci.h Normal file
View File

@@ -0,0 +1,174 @@
// 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 AHCI_H
#define AHCI_H
#include <stdint.h>
#include <stdbool.h>
// ============================================================================
// FIS (Frame Information Structure) Types
// ============================================================================
typedef enum {
FIS_TYPE_REG_H2D = 0x27, // Register FIS — Host to Device
FIS_TYPE_REG_D2H = 0x34, // Register FIS — Device to Host
FIS_TYPE_DMA_ACT = 0x39, // DMA Activate FIS
FIS_TYPE_DMA_SETUP = 0x41, // DMA Setup FIS
FIS_TYPE_DATA = 0x46, // Data FIS
FIS_TYPE_BIST = 0x58, // BIST Activate FIS
FIS_TYPE_PIO_SETUP = 0x5F, // PIO Setup FIS
FIS_TYPE_DEV_BITS = 0xA1, // Set Device Bits FIS
} FIS_TYPE;
// ============================================================================
// HBA Register Structures (MMIO-mapped from ABAR)
// ============================================================================
// Port Registers (one set per port, at ABAR + 0x100 + portno*0x80)
typedef volatile struct {
uint32_t clb; // 0x00: Command List Base Address (lower 32 bits)
uint32_t clbu; // 0x04: Command List Base Address (upper 32 bits)
uint32_t fb; // 0x08: FIS Base Address (lower 32 bits)
uint32_t fbu; // 0x0C: FIS Base Address (upper 32 bits)
uint32_t is; // 0x10: Interrupt Status
uint32_t ie; // 0x14: Interrupt Enable
uint32_t cmd; // 0x18: Command and Status
uint32_t rsv0; // 0x1C: Reserved
uint32_t tfd; // 0x20: Task File Data
uint32_t sig; // 0x24: Signature
uint32_t ssts; // 0x28: SATA Status (SStatus)
uint32_t sctl; // 0x2C: SATA Control (SControl)
uint32_t serr; // 0x30: SATA Error (SError)
uint32_t sact; // 0x34: SATA Active (SCR3)
uint32_t ci; // 0x38: Command Issue
uint32_t sntf; // 0x3C: SATA Notification (SCR4)
uint32_t fbs; // 0x40: FIS-based Switch Control
uint32_t rsv1[11]; // 0x44~0x6F
uint32_t vendor[4]; // 0x70~0x7F
} HBA_PORT;
// Global HBA Memory Registers (at ABAR)
typedef volatile struct {
uint32_t cap; // 0x00: Host Capability
uint32_t ghc; // 0x04: Global Host Control
uint32_t is; // 0x08: Interrupt Status
uint32_t pi; // 0x0C: Port Implemented
uint32_t vs; // 0x10: Version
uint32_t ccc_ctl; // 0x14: Command Completion Coalescing Control
uint32_t ccc_pts; // 0x18: Command Completion Coalescing Ports
uint32_t em_loc; // 0x1C: Enclosure Management Location
uint32_t em_ctl; // 0x20: Enclosure Management Control
uint32_t cap2; // 0x24: Host Capabilities Extended
uint32_t bohc; // 0x28: BIOS/OS Handoff Control and Status
uint8_t rsv[0xA0 - 0x2C];
uint8_t vendor[0x100 - 0xA0];
HBA_PORT ports[]; // Port 0 at offset 0x100 (flexible array member)
} HBA_MEM;
// ============================================================================
// Command List / Table Structures (DMA)
// ============================================================================
// Command Header (32 bytes each, 32 entries per port = 1KB)
typedef struct {
uint8_t cfl:5; // Command FIS Length (in DWORDs)
uint8_t a:1; // ATAPI
uint8_t w:1; // Write (1=H2D, 0=D2H)
uint8_t p:1; // Prefetchable
uint8_t r:1; // Reset
uint8_t b:1; // BIST
uint8_t c:1; // Clear Busy upon R_OK
uint8_t rsv0:1;
uint8_t pmp:4; // Port Multiplier Port
uint16_t prdtl; // Physical Region Descriptor Table Length (entries)
volatile uint32_t prdbc; // PRD Byte Count transferred
uint32_t ctba; // Command Table Descriptor Base Address (lower 32)
uint32_t ctbau; // Command Table Descriptor Base Address (upper 32)
uint32_t rsv1[4]; // Reserved
} __attribute__((packed)) HBA_CMD_HEADER;
// Physical Region Descriptor Table Entry
typedef struct {
uint32_t dba; // Data Base Address (lower 32)
uint32_t dbau; // Data Base Address (upper 32)
uint32_t rsv0; // Reserved
uint32_t dbc:22; // Byte Count (0-based, max 4MB)
uint32_t rsv1:9; // Reserved
uint32_t i:1; // Interrupt on Completion
} __attribute__((packed)) HBA_PRDT_ENTRY;
// Host-to-Device Register FIS
typedef struct {
uint8_t fis_type; // FIS_TYPE_REG_H2D
uint8_t pmport:4; // Port Multiplier
uint8_t rsv0:3; // Reserved
uint8_t c:1; // 1=Command, 0=Control
uint8_t command; // Command register
uint8_t featurel; // Feature register (7:0)
uint8_t lba0; // LBA (7:0)
uint8_t lba1; // LBA (15:8)
uint8_t lba2; // LBA (23:16)
uint8_t device; // Device register
uint8_t lba3; // LBA (31:24)
uint8_t lba4; // LBA (39:32)
uint8_t lba5; // LBA (47:40)
uint8_t featureh; // Feature register (15:8)
uint8_t countl; // Count (7:0)
uint8_t counth; // Count (15:8)
uint8_t icc; // Isochronous Command Completion
uint8_t control; // Control register
uint8_t rsv1[4]; // Reserved
} __attribute__((packed)) FIS_REG_H2D;
// Command Table (256-byte aligned)
typedef struct {
uint8_t cfis[64]; // Command FIS
uint8_t acmd[16]; // ATAPI Command
uint8_t rsv[48]; // Reserved
HBA_PRDT_ENTRY prdt[]; // PRDT entries (variable, at least 1)
} __attribute__((packed)) HBA_CMD_TBL;
// ============================================================================
// Port Signature Values
// ============================================================================
#define SATA_SIG_ATA 0x00000101 // SATA drive
#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive
#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge
#define SATA_SIG_PM 0x96690101 // Port multiplier
// ============================================================================
// Port Command Bits
// ============================================================================
#define HBA_PORT_CMD_ST 0x0001 // Start
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
// ============================================================================
// ATA Commands
// ============================================================================
#define ATA_CMD_READ_DMA_EX 0x25
#define ATA_CMD_WRITE_DMA_EX 0x35
#define ATA_CMD_IDENTIFY 0xEC
// ============================================================================
// Public API
// ============================================================================
void ahci_init(void);
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer);
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer);
int ahci_get_port_count(void);
bool ahci_port_is_active(int port_num);
#endif

View File

@@ -8,6 +8,7 @@
#include <stdbool.h> #include <stdbool.h>
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define MAX_DISKS 16
typedef enum { typedef enum {
DISK_TYPE_RAM, DISK_TYPE_RAM,
@@ -17,11 +18,12 @@ typedef enum {
} DiskType; } DiskType;
typedef struct Disk { typedef struct Disk {
char letter; char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
DiskType type; DiskType type;
bool is_fat32; bool is_fat32;
char name[32]; char label[32]; // Human-readable label
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw) uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
uint32_t total_sectors; // Total sectors on this device/partition
// Function pointers for driver operations // Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer); int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
@@ -29,14 +31,32 @@ typedef struct Disk {
// Private driver data // Private driver data
void *driver_data; void *driver_data;
// Parent disk (for partitions — points to the whole-disk Disk)
struct Disk *parent;
bool is_partition;
bool registered;
} Disk; } Disk;
// Initialization and scanning
void disk_manager_init(void); void disk_manager_init(void);
void disk_manager_scan(void); // Scans for new disks void disk_manager_scan(void);
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void); // Device registration
void disk_register(Disk *disk); void disk_register(Disk *disk);
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, int part_num);
// Lookup
Disk* disk_get_by_name(const char *devname);
int disk_get_count(void); int disk_get_count(void);
Disk* disk_get_by_index(int index); Disk* disk_get_by_index(int index);
// Auto-naming helpers
const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc.
// Backward compat (deprecated — wraps disk_get_by_name)
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
#endif #endif

View File

@@ -6,14 +6,41 @@
#include "memory_manager.h" #include "memory_manager.h"
#include "io.h" #include "io.h"
#include "wm.h" #include "wm.h"
#include "ahci.h"
#include "../fs/vfs.h"
#include "../fs/fat32.h"
#include "../sys/spinlock.h"
#include <stddef.h> #include <stddef.h>
#define MAX_DISKS 26 static spinlock_t ide_lock = SPINLOCK_INIT;
static Disk *disks[MAX_DISKS]; static Disk *disks[MAX_DISKS];
static int disk_count = 0; static int disk_count = 0;
static int next_drive_letter_idx = 0; // For backward compat
static int next_sd_index = 0; // For sda, sdb, sdc...
// === ATA Definitions === extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
// === String Helpers ===
static void dm_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
static int dm_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static int dm_strlen(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
// === ATA Definitions (Legacy IDE PIO — kept as fallback) ===
#define ATA_PRIMARY_IO 0x1F0 #define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6 #define ATA_PRIMARY_CTRL 0x3F6
@@ -35,91 +62,68 @@ static int disk_count = 0;
#define ATA_CMD_WRITE_PIO 0x30 #define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC #define ATA_CMD_IDENTIFY 0xEC
#define ATA_SR_BSY 0x80 // Busy #define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40 // Drive ready #define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20 // Drive write fault #define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10 // Drive seek complete #define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08 // Data request ready #define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04 // Corrected data #define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02 // Index #define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01 // Error #define ATA_SR_ERR 0x01
typedef struct { typedef struct {
uint16_t port_base; uint16_t port_base;
bool slave; bool slave;
} ATADriverData; } ATADriverData;
// === Helpers === // === ATA PIO Driver ===
static void dm_strcpy(char *dest, const char *src) { static int ata_wait_bsy(uint16_t port_base) {
while (*src) *dest++ = *src++; int timeout = 10000000;
*dest = 0; while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0);
return timeout <= 0 ? -1 : 0;
} }
void disk_register(Disk *disk); static int ata_wait_drq(uint16_t port_base) {
int timeout = 10000000;
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)) && --timeout > 0);
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) { if (timeout <= 0 || (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR)) return -1;
(void)disk; (void)sector; (void)buffer;
return 0; return 0;
} }
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
return 0;
}
static void ata_wait_bsy(uint16_t port_base) {
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
}
static void ata_wait_drq(uint16_t port_base) {
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
}
// Returns 1 if drive exists, 0 otherwise
static int ata_identify(uint16_t port_base, bool slave) { static int ata_identify(uint16_t port_base, bool slave) {
// Select Drive
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0); outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
// Zero out sector count and LBA registers
outb(port_base + ATA_REG_SEC_COUNT0, 0); outb(port_base + ATA_REG_SEC_COUNT0, 0);
outb(port_base + ATA_REG_LBA0, 0); outb(port_base + ATA_REG_LBA0, 0);
outb(port_base + ATA_REG_LBA1, 0); outb(port_base + ATA_REG_LBA1, 0);
outb(port_base + ATA_REG_LBA2, 0); outb(port_base + ATA_REG_LBA2, 0);
// Send Identify command
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
// Check if status is 0 (no drive)
uint8_t status = inb(port_base + ATA_REG_STATUS); uint8_t status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0; if (status == 0) return 0;
// Wait until BSY clears
int timeout = 10000; int timeout = 10000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) { while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
status = inb(port_base + ATA_REG_STATUS); status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0; // Check again if (status == 0) return 0;
} }
if (timeout <= 0) return 0; // Hardware didn't respond if (timeout <= 0) return 0;
// Check for error
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
return 0; // Error, likely not ATA
}
// Wait for DRQ or ERR
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0; if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
// Read 256 words (512 bytes) of identity data if (ata_wait_drq(port_base) != 0) return 0;
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
uint32_t sectors = 0;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
uint16_t data = inw(port_base + ATA_REG_DATA); uint16_t data = inw(port_base + ATA_REG_DATA);
(void)data; if (i == 60) sectors |= (uint32_t)data;
if (i == 61) sectors |= (uint32_t)data << 16;
} }
return 1; return sectors;
} }
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) { static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
@@ -127,9 +131,22 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
uint16_t port_base = data->port_base; uint16_t port_base = data->port_base;
bool slave = data->slave; bool slave = data->slave;
ata_wait_bsy(port_base); // For partition reads, add the partition LBA offset
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
// Use parent's driver
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
// Select drive and send highest 4 bits of LBA
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1); outb(port_base + ATA_REG_SEC_COUNT0, 1);
@@ -138,15 +155,22 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ata_wait_bsy(port_base); if (ata_wait_bsy(port_base) != 0) {
ata_wait_drq(port_base); spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
if (ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
uint16_t *ptr = (uint16_t*)buffer; uint16_t *ptr = (uint16_t*)buffer;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
ptr[i] = inw(port_base + ATA_REG_DATA); ptr[i] = inw(port_base + ATA_REG_DATA);
} }
return 0; // Success spinlock_release_irqrestore(&ide_lock, flags);
return 0;
} }
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) { static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
@@ -154,7 +178,20 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
uint16_t port_base = data->port_base; uint16_t port_base = data->port_base;
bool slave = data->slave; bool slave = data->slave;
ata_wait_bsy(port_base); // For partition writes, add the partition LBA offset
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_FEATURES, 0x00);
@@ -164,74 +201,130 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait_bsy(port_base); if (ata_wait_bsy(port_base) != 0) {
ata_wait_drq(port_base); spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
if (ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
const uint16_t *ptr = (const uint16_t*)buffer; const uint16_t *ptr = (const uint16_t*)buffer;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, ptr[i]); outw(port_base + ATA_REG_DATA, ptr[i]);
} }
// Flush / Sync
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
ata_wait_bsy(port_base); if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
spinlock_release_irqrestore(&ide_lock, flags);
return 0; return 0;
} }
// === Device Naming ===
char disk_get_next_free_letter(void) { const char* disk_get_next_dev_name(void) {
for (int i = 0; i < MAX_DISKS; i++) { static char name[8];
char letter = 'A' + i; name[0] = 's';
bool used = false; name[1] = 'd';
for (int j = 0; j < disk_count; j++) { name[2] = 'a' + next_sd_index;
if (disks[j]->letter == letter) { name[3] = 0;
used = true; next_sd_index++;
break; return name;
}
}
if (!used) return letter;
}
return 0; // No free letters
} }
// === Registration ===
void disk_register(Disk *disk) { void disk_register(Disk *disk) {
if (disk_count >= MAX_DISKS) return; if (disk_count >= MAX_DISKS) return;
// Ensure letter is unique // Auto-assign devname if empty
if (disk->letter == 0) { if (disk->devname[0] == 0) {
disk->letter = disk_get_next_free_letter(); const char *n = disk_get_next_dev_name();
dm_strcpy(disk->devname, n);
} }
disk->registered = true;
disks[disk_count++] = disk; disks[disk_count++] = disk;
serial_write("[DISK] Registered /dev/");
serial_write(disk->devname);
serial_write(" (");
serial_write(disk->label);
serial_write(")\n");
} }
void disk_manager_init(void) { void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
for (int i = 0; i < MAX_DISKS; i++) { bool is_fat32, int part_num) {
disks[i] = NULL; if (disk_count >= MAX_DISKS) return;
Disk *part = (Disk*)kmalloc(sizeof(Disk));
if (!part) return;
// Build name: parent_devname + partition number (e.g. "sda1")
int len = dm_strlen(parent->devname);
for (int i = 0; i < len; i++) part->devname[i] = parent->devname[i];
part->devname[len] = '0' + part_num;
part->devname[len + 1] = 0;
part->type = parent->type;
part->is_fat32 = is_fat32;
dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition");
part->partition_lba_offset = lba_offset;
part->total_sectors = sector_count;
part->read_sector = parent->read_sector;
part->write_sector = parent->write_sector;
part->driver_data = parent->driver_data;
part->parent = parent;
part->is_partition = true;
part->registered = true;
disks[disk_count++] = part;
serial_write("[DISK] Registered /dev/");
serial_write(part->devname);
serial_write(" (LBA offset ");
serial_write_num(lba_offset);
serial_write(", ");
serial_write_num(sector_count);
serial_write(" sectors, FAT32=");
serial_write(" sectors, FAT32=");
serial_write(is_fat32 ? "yes" : "no");
serial_write(")\n");
if (is_fat32) {
// Try to initialize and mount FAT32 volume to VFS
void *vol = fat32_mount_volume(part);
if (vol) {
char mount_path[32];
mount_path[0] = '/';
mount_path[1] = 'd'; mount_path[2] = 'e'; mount_path[3] = 'v'; mount_path[4] = '/';
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");
wm_notify_fs_change();
} else {
serial_write("[VFS] Failed to mount ");
serial_write(mount_path);
serial_write("\n");
}
}
} }
disk_count = 0;
// Register A: (Ramdisk)
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
ramdisk->letter = 'A';
ramdisk->type = DISK_TYPE_RAM;
ramdisk->is_fat32 = true; // Ramdisk is always formatted
dm_strcpy(ramdisk->name, "RAM");
ramdisk->read_sector = ramdisk_read;
ramdisk->write_sector = ramdisk_write;
ramdisk->driver_data = NULL;
ramdisk->partition_lba_offset = 0;
disk_register(ramdisk);
} }
Disk* disk_get_by_letter(char letter) { // === Lookup ===
// Uppercase
if (letter >= 'a' && letter <= 'z') letter -= 32;
Disk* disk_get_by_name(const char *devname) {
if (!devname) return NULL;
for (int i = 0; i < disk_count; i++) { for (int i = 0; i < disk_count; i++) {
if (disks[i]->letter == letter) { if (dm_strcmp(disks[i]->devname, devname) == 0) {
return disks[i]; return disks[i];
} }
} }
@@ -247,36 +340,57 @@ Disk* disk_get_by_index(int index) {
return disks[index]; return disks[index];
} }
// === Backward Compat (deprecated) ===
// === MBR Partition Table Structures === char disk_get_next_free_letter(void) {
char letter = 'B' + next_drive_letter_idx++;
if (letter > 'Z') return 0;
return letter;
}
Disk* disk_get_by_letter(char letter) {
// Maps old letter scheme: A=ramfs (not a block device), B+=first real disk, etc.
if (letter >= 'a' && letter <= 'z') letter -= 32;
// A: was the ramdisk — return NULL since ramfs is now VFS-managed
if (letter == 'A') return NULL;
// B-Z map to disk indices 0, 1, 2...
// Find real disks (non-RAM, non-partition-parent)
int real_idx = 0;
for (int i = 0; i < disk_count; i++) {
if (disks[i]->is_partition && disks[i]->is_fat32) {
if (real_idx == (letter - 'B')) {
return disks[i];
}
real_idx++;
}
}
return NULL;
}
// === MBR Partition Table ===
typedef struct { typedef struct {
uint8_t status; // 0x80 = bootable, 0x00 = inactive uint8_t status;
uint8_t chs_first[3]; // CHS of first sector uint8_t chs_first[3];
uint8_t type; // Partition type uint8_t type;
uint8_t chs_last[3]; // CHS of last sector uint8_t chs_last[3];
uint32_t lba_start; // LBA of first sector uint32_t lba_start;
uint32_t sector_count; // Number of sectors uint32_t sector_count;
} __attribute__((packed)) MBR_PartitionEntry; } __attribute__((packed)) MBR_PartitionEntry;
// FAT32 partition type codes
#define PART_TYPE_FAT32 0x0B #define PART_TYPE_FAT32 0x0B
#define PART_TYPE_FAT32_LBA 0x0C #define PART_TYPE_FAT32_LBA 0x0C
// Check if sector contains a valid FAT32 BPB (Volume Boot Record)
static bool is_fat32_bpb(const uint8_t *sector) { static bool is_fat32_bpb(const uint8_t *sector) {
// Must have 0xAA55 boot signature
if (sector[510] != 0x55 || sector[511] != 0xAA) return false; if (sector[510] != 0x55 || sector[511] != 0xAA) return false;
// Check for FAT32 filesystem string at offset 82
// "FAT32 " in the fs_type field of the BPB
if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' && if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
sector[85] == '3' && sector[86] == '2') { sector[85] == '3' && sector[86] == '2') {
return true; return true;
} }
// Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0
// (FAT32 always has sectors_per_fat_16 == 0)
uint16_t bps = *(uint16_t*)&sector[11]; uint16_t bps = *(uint16_t*)&sector[11];
uint16_t spf16 = *(uint16_t*)&sector[22]; uint16_t spf16 = *(uint16_t*)&sector[22];
uint32_t spf32 = *(uint32_t*)&sector[36]; uint32_t spf32 = *(uint32_t*)&sector[36];
@@ -287,60 +401,68 @@ static bool is_fat32_bpb(const uint8_t *sector) {
return false; return false;
} }
// Parse MBR partition table and find a FAT32 partition. // Parse MBR and register each partition as a child block device
// Sets disk->partition_lba_offset and returns true if found. static void parse_mbr_partitions(Disk *disk) {
static bool detect_fat32_partition(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512); uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return false; if (!buffer) return;
// Read sector 0 (MBR or raw BPB)
if (disk->read_sector(disk, 0, buffer) != 0) { if (disk->read_sector(disk, 0, buffer) != 0) {
kfree(buffer); kfree(buffer);
return false; return;
} }
// Must have 0xAA55 boot signature // Check for valid MBR signature
if (buffer[510] != 0x55 || buffer[511] != 0xAA) { if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
kfree(buffer); kfree(buffer);
return false; return;
} }
// Check MBR partition table entries (4 entries at offset 446)
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446]; MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
int part_num = 1;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (partitions[i].type == PART_TYPE_FAT32 || uint32_t start = partitions[i].lba_start;
partitions[i].type == PART_TYPE_FAT32_LBA) { uint32_t size = partitions[i].sector_count;
uint8_t type = partitions[i].type;
uint32_t part_lba = partitions[i].lba_start; if (type == 0x00) continue; // Empty entry
if (size == 0) continue;
if (start >= disk->total_sectors) continue; // Invalid start
// Read the partition's first sector to verify it's a valid FAT32 BPB bool fat32 = false;
if (type == PART_TYPE_FAT32 || type == PART_TYPE_FAT32_LBA) {
// Verify by reading the BPB
uint8_t *pbuf = (uint8_t*)kmalloc(512); uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (!pbuf) { kfree(buffer); return false; } if (pbuf) {
if (disk->read_sector(disk, start, pbuf) == 0) {
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) { fat32 = is_fat32_bpb(pbuf);
disk->partition_lba_offset = part_lba;
kfree(pbuf);
kfree(buffer);
return true;
} }
kfree(pbuf); kfree(pbuf);
} }
} }
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table) disk_register_partition(disk, partitions[i].lba_start,
if (is_fat32_bpb(buffer)) { partitions[i].sector_count, fat32, part_num);
part_num++;
}
// Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
if (part_num == 1 && is_fat32_bpb(buffer)) {
serial_write("[DISK] No MBR partitions — raw FAT32 volume on /dev/");
serial_write(disk->devname);
serial_write("\n");
disk->is_fat32 = true;
disk->partition_lba_offset = 0; disk->partition_lba_offset = 0;
kfree(buffer);
return true;
} }
kfree(buffer); kfree(buffer);
return false;
} }
// === ATA Drive Discovery ===
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) { static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
if (ata_identify(port, slave)) { uint32_t sectors = ata_identify(port, slave);
if (sectors > 0) {
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk)); Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
if (!new_disk) return; if (!new_disk) return;
@@ -348,29 +470,51 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
data->port_base = port; data->port_base = port;
data->slave = slave; data->slave = slave;
new_disk->letter = 0; // Auto-assign new_disk->devname[0] = 0; // Auto-assign
new_disk->type = DISK_TYPE_IDE; new_disk->type = DISK_TYPE_IDE;
dm_strcpy(new_disk->name, name); dm_strcpy(new_disk->label, name);
new_disk->read_sector = ata_read_sector; new_disk->read_sector = ata_read_sector;
new_disk->write_sector = ata_write_sector; new_disk->write_sector = ata_write_sector;
new_disk->driver_data = data; new_disk->driver_data = data;
new_disk->partition_lba_offset = 0; new_disk->partition_lba_offset = 0;
new_disk->total_sectors = sectors;
new_disk->parent = NULL;
new_disk->is_partition = false;
new_disk->is_fat32 = false;
// Detect FAT32 (with MBR partition support)
if (detect_fat32_partition(new_disk)) {
new_disk->is_fat32 = true;
disk_register(new_disk); disk_register(new_disk);
} else {
kfree(data); // Parse MBR to find partitions
kfree(new_disk); parse_mbr_partitions(new_disk);
} }
}
// === Init & Scan ===
void disk_manager_init(void) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
} }
disk_count = 0;
next_sd_index = 0;
next_drive_letter_idx = 0;
serial_write("[DISK] Disk manager initialized (VFS mode)\n");
// NOTE: Ramdisk (A:) is no longer registered here.
// RAMFS is managed directly by fat32.c and mounted at "/" via VFS.
} }
void disk_manager_scan(void) { void disk_manager_scan(void) {
// Probe Standard ATA Ports serial_write("[DISK] Initializing AHCI (SATA DMA)...\n");
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1"); ahci_init();
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3"); if (ahci_get_port_count() == 0) {
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4"); serial_write("[DISK] No AHCI ports found, falling back to legacy IDE PIO...\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");
} else {
serial_write("[DISK] AHCI initialized successfully, skipping legacy IDE.\n");
}
} }

View File

@@ -97,3 +97,23 @@ int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t*
} }
return 0; return 0;
} }
uint32_t pci_get_bar(pci_device_t *dev, int bar_num) {
if (!dev || bar_num < 0 || bar_num > 5) return 0;
uint8_t offset = 0x10 + (bar_num * 4);
return pci_read_config(dev->bus, dev->device, dev->function, offset);
}
void pci_enable_bus_mastering(pci_device_t *dev) {
if (!dev) return;
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
cmd |= (1 << 2); // Set Bus Master bit
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
}
void pci_enable_mmio(pci_device_t *dev) {
if (!dev) return;
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
cmd |= (1 << 1); // Set Memory Space bit
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
}

View File

@@ -22,6 +22,9 @@ typedef struct {
#define PCI_CLASS_NETWORK_CONTROLLER 0x02 #define PCI_CLASS_NETWORK_CONTROLLER 0x02
#define PCI_CLASS_ETHERNET_CONTROLLER 0x00 #define PCI_CLASS_ETHERNET_CONTROLLER 0x00
#define PCI_CLASS_MASS_STORAGE 0x01
#define PCI_SUBCLASS_SATA 0x06
#define PCI_SUBCLASS_IDE 0x01
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
@@ -35,4 +38,9 @@ int pci_enumerate_devices(pci_device_t* devices, int max_devices);
int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device); int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device);
int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device); int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device);
// BAR access and bus mastering helpers
uint32_t pci_get_bar(pci_device_t *dev, int bar_num);
void pci_enable_bus_mastering(pci_device_t *dev);
void pci_enable_mmio(pci_device_t *dev);
#endif #endif

View File

@@ -16,18 +16,19 @@ extern void serial_print_hex(uint64_t n);
volatile uint64_t kernel_ticks = 0; volatile uint64_t kernel_ticks = 0;
uint64_t timer_handler(registers_t *regs) { uint64_t timer_handler(registers_t *regs) {
if (smp_this_cpu_id() == 0) {
kernel_ticks++; kernel_ticks++;
wm_timer_tick(); wm_timer_tick();
network_process_frames(); network_process_frames();
extern void k_beep_process(void); extern void k_beep_process(void);
k_beep_process(); k_beep_process();
}
outb(0x20, 0x20); // EOI after processing to prevent nested timer interrupts outb(0x20, 0x20);
extern uint64_t process_schedule(uint64_t current_rsp); extern uint64_t process_schedule(uint64_t current_rsp);
uint64_t new_rsp = process_schedule((uint64_t)regs); uint64_t new_rsp = process_schedule((uint64_t)regs);
// SMP: Wake AP cores to run their assigned processes
if (smp_cpu_count() > 1) { if (smp_cpu_count() > 1) {
lapic_send_ipi_all(); lapic_send_ipi_all();
} }
@@ -37,9 +38,27 @@ uint64_t timer_handler(registers_t *regs) {
// --- Keyboard --- // --- Keyboard ---
static bool shift_pressed = false; static bool shift_pressed = false;
static bool caps_lock_on = false;
bool ps2_ctrl_pressed = false; bool ps2_ctrl_pressed = false;
static bool extended_scancode = false; static bool extended_scancode = false;
static void ps2_kbd_wait_write(void) {
uint32_t timeout = 100000;
while (timeout--) {
if ((inb(0x64) & 2) == 0) return;
}
}
static void ps2_update_leds(void) {
uint8_t led_status = 0;
if (caps_lock_on) led_status |= 4;
ps2_kbd_wait_write();
outb(0x60, 0xED);
ps2_kbd_wait_write();
outb(0x60, led_status);
}
static char scancode_map[128] = { static char scancode_map[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
@@ -67,7 +86,7 @@ uint64_t keyboard_handler(registers_t *regs) {
if (scancode == 0x1D) { if (scancode == 0x1D) {
ps2_ctrl_pressed = true; ps2_ctrl_pressed = true;
extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug) extended_scancode = false;
} else if (scancode == 0x9D) { } else if (scancode == 0x9D) {
ps2_ctrl_pressed = false; ps2_ctrl_pressed = false;
extended_scancode = false; extended_scancode = false;
@@ -77,10 +96,9 @@ uint64_t keyboard_handler(registers_t *regs) {
extern process_t* process_get_current(void); extern process_t* process_get_current(void);
process_t* proc = process_get_current(); process_t* proc = process_get_current();
if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) { if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) {
// Only kill if the associated terminal window is focused
if (((Window*)proc->ui_window)->focused) { if (((Window*)proc->ui_window)->focused) {
extern uint64_t process_terminate_current(void); extern uint64_t process_terminate_current(void);
outb(0x20, 0x20); // EOI before context switch outb(0x20, 0x20);
return process_terminate_current(); return process_terminate_current();
} }
} }
@@ -90,7 +108,10 @@ uint64_t keyboard_handler(registers_t *regs) {
shift_pressed = true; shift_pressed = true;
} else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up } else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up
shift_pressed = false; shift_pressed = false;
} else if (!(scancode & 0x80)) { // Key Press (not release) } else if (scancode == 0x3A) { // Caps Lock Down
caps_lock_on = !caps_lock_on;
ps2_update_leds();
} else if (!(scancode & 0x80)) { // Key Press
if (extended_scancode) { if (extended_scancode) {
extended_scancode = false; extended_scancode = false;
switch (scancode) { switch (scancode) {
@@ -102,6 +123,10 @@ uint64_t keyboard_handler(registers_t *regs) {
} else { } else {
char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode]; char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode];
if (c) { if (c) {
if (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, true); wm_handle_key(c, true);
} }
} }
@@ -115,8 +140,13 @@ uint64_t keyboard_handler(registers_t *regs) {
case 0x4D: wm_handle_key(20, false); break; // Right arrow case 0x4D: wm_handle_key(20, false); break; // Right arrow
} }
} else { } else {
char c = shift_pressed ? scancode_map_shift[scancode & 0x7F] : scancode_map[scancode & 0x7F]; uint8_t base_scancode = scancode & 0x7F;
char c = shift_pressed ? scancode_map_shift[base_scancode] : scancode_map[base_scancode];
if (c) { if (c) {
if (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, false); wm_handle_key(c, false);
} }
} }
@@ -177,7 +207,7 @@ void mouse_init(void) {
mouse_write(0xF6); mouse_write(0xF6);
mouse_read(); mouse_read();
// Enable Wheel - Magic Sequence // Enable Wheel
mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read(); mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read();
mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read(); mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read();
mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read(); mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read();

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,18 @@ typedef struct {
uint32_t file_size; // File size uint32_t file_size; // File size
} __attribute__((packed)) FAT32_DirEntry; } __attribute__((packed)) FAT32_DirEntry;
// Long File Name Directory Entry (32 bytes)
typedef struct {
uint8_t order; // Sequence number (0x40 = last, | index)
uint16_t name1[5]; // Characters 1-5 (UCS-2)
uint8_t attr; // Always 0x0F
uint8_t type; // Always 0x00
uint8_t checksum; // Checksum of short name
uint16_t name2[6]; // Characters 6-11 (UCS-2)
uint16_t first_cluster; // Always 0x0000
uint16_t name3[2]; // Characters 12-13 (UCS-2)
} __attribute__((packed)) FAT32_LFNEntry;
// File Attributes // File Attributes
#define ATTR_READ_ONLY 0x01 #define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02 #define ATTR_HIDDEN 0x02
@@ -70,6 +82,7 @@ typedef struct {
#define ATTR_ARCHIVE 0x20 #define ATTR_ARCHIVE 0x20
#define ATTR_DEVICE 0x40 #define ATTR_DEVICE 0x40
#define ATTR_RESERVED 0x80 #define ATTR_RESERVED 0x80
#define ATTR_LFN 0x0F // LFN marker (all of the above ORed)
// FAT32 Constants // FAT32 Constants
#define FAT32_SECTOR_SIZE 512 #define FAT32_SECTOR_SIZE 512
@@ -88,7 +101,9 @@ typedef struct {
bool valid; // Is this handle valid? bool valid; // Is this handle valid?
uint32_t dir_sector; // Sector containing the directory entry uint32_t dir_sector; // Sector containing the directory entry
uint32_t dir_offset; // Offset within that sector uint32_t dir_offset; // Offset within that sector
char drive; // Drive letter (A, B, ...) bool is_directory; // Is this a directory?
uint8_t attributes; // File attributes
void *volume; // Pointer to owning FAT32_Volume (or NULL for ramfs)
} FAT32_FileHandle; } FAT32_FileHandle;
// Directory Entry Info (for listing) // Directory Entry Info (for listing)
@@ -101,12 +116,23 @@ typedef struct {
uint16_t write_time; uint16_t write_time;
} FAT32_FileInfo; } FAT32_FileInfo;
// === VFS Integration ===
// Forward-declared VFS ops type (defined in vfs.h)
struct vfs_fs_ops;
// Get VFS ops structs for registration
struct vfs_fs_ops* fat32_get_ramfs_ops(void);
struct vfs_fs_ops* fat32_get_realfs_ops(void);
// Mount a real FAT32 volume from a block device — returns fs_private for VFS
void* fat32_mount_volume(void *disk_ptr);
// === Function Declarations === // === Function Declarations ===
// Initialization // Initialization
void fat32_init(void); void fat32_init(void);
// File Operations // File Operations (backward-compat wrappers — dispatch through VFS)
FAT32_FileHandle* fat32_open(const char *path, const char *mode); FAT32_FileHandle* fat32_open(const char *path, const char *mode);
void fat32_close(FAT32_FileHandle *handle); void fat32_close(FAT32_FileHandle *handle);
int fat32_read(FAT32_FileHandle *handle, void *buffer, int size); int fat32_read(FAT32_FileHandle *handle, void *buffer, int size);
@@ -124,7 +150,10 @@ bool fat32_is_directory(const char *path);
// Listing // Listing
int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries); int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries);
// Working Directory // Info
int fat32_get_info(const char *path, FAT32_FileInfo *info);
// Working Directory (backward compat — wraps VFS path tracking)
bool fat32_chdir(const char *path); bool fat32_chdir(const char *path);
void fat32_get_current_dir(char *buffer, int size); void fat32_get_current_dir(char *buffer, int size);
bool fat32_change_drive(char drive); bool fat32_change_drive(char drive);

447
src/fs/procfs.c Normal file
View File

@@ -0,0 +1,447 @@
#include "vfs.h"
#include "../sys/process.h"
#include "../sys/syscall.h"
#include "../dev/disk.h"
#include "memory_manager.h"
#include "core/kutils.h"
#include "core/platform.h"
typedef struct {
uint32_t pid;
char type[32];
int offset;
bool is_root;
} procfs_handle_t;
void* procfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
procfs_handle_t *h = (procfs_handle_t*)kmalloc(sizeof(procfs_handle_t));
k_memset(h, 0, sizeof(procfs_handle_t));
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
return h;
}
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
h->pid = k_atoi(pid_str);
if (path[i] == '/') {
k_strcpy(h->type, path + i + 1);
} else {
h->type[0] = 0;
}
return h;
}
h->pid = 0xFFFFFFFF;
k_strcpy(h->type, path);
return h;
}
void procfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
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 = (char*)kmalloc(16384);
if (!out) return -1;
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
if (k_strcmp(h->type, "version") == 0) {
extern void get_os_info(os_info_t *info);
os_info_t info;
get_os_info(&info);
k_strcpy(out, info.os_name);
k_strcpy(out + k_strlen(out), " [");
k_strcpy(out + k_strlen(out), info.os_codename);
k_strcpy(out + k_strlen(out), "] Version ");
k_strcpy(out + k_strlen(out), info.os_version);
k_strcpy(out + k_strlen(out), "\nKernel: ");
k_strcpy(out + k_strlen(out), info.kernel_name);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.kernel_version);
k_strcpy(out + k_strlen(out), "\nBuild: ");
k_strcpy(out + k_strlen(out), info.build_date);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.build_time);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "uptime") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_itoa(ticks / 60, out);
k_strcpy(out + k_strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; k_itoa(ticks, t_s);
k_strcpy(out + k_strlen(out), t_s);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *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);
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), "\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();
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\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\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\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\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), "\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");
} else if (k_strcmp(h->type, "devices") == 0) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
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 && !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) { 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, "status") == 0) {
k_strcpy(out, "Name: ");
k_strcpy(out + k_strlen(out), proc->name);
k_strcpy(out + k_strlen(out), "\nPID: ");
char pid_s[16]; k_itoa(proc->pid, pid_s);
k_strcpy(out + k_strlen(out), pid_s);
k_strcpy(out + k_strlen(out), "\nState: RUNNING\nMemory: ");
uint64_t mem_val = proc->used_memory;
if (h->pid == 0) {
extern MemStats memory_get_stats(void);
mem_val = memory_get_stats().used_memory;
}
char mem_s[32]; k_itoa(mem_val / 1024, mem_s);
k_strcpy(out + k_strlen(out), mem_s);
k_strcpy(out + k_strlen(out), " KB\nTicks: ");
char tick_s[32]; k_itoa(proc->ticks, tick_s);
k_strcpy(out + k_strlen(out), tick_s);
k_strcpy(out + k_strlen(out), "\nIdle: ");
k_strcpy(out + k_strlen(out), proc->is_idle ? "1" : "0");
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = k_strlen(out);
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;
}
int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h || h->pid == 0xFFFFFFFF) return -1;
if (k_strcmp(h->type, "signal") == 0) {
char cmd[16];
int to_copy = size < 15 ? size : 15;
k_memcpy(cmd, buf, to_copy);
cmd[to_copy] = 0;
if (k_strcmp(cmd, "9") == 0 || k_strcmp(cmd, "kill") == 0) {
process_t *proc = process_get_by_pid(h->pid);
if (proc && proc->pid != 0) {
process_terminate(proc);
return size;
}
}
}
return -1;
}
int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
if (path[0] == '\0') {
int out = 0;
k_strcpy(entries[out++].name, "version");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "uptime");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "cpuinfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "meminfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "devices");
entries[out-1].is_directory = 0;
extern process_t processes[];
for (int i = 0; i < 16 && out < max; i++) {
if (processes[i].pid != 0xFFFFFFFF) {
k_itoa(processes[i].pid, entries[out].name);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
return out;
}
if (path[0] >= '0' && path[0] <= '9') {
int out = 0;
k_strcpy(entries[out++].name, "name");
k_strcpy(entries[out++].name, "status");
k_strcpy(entries[out++].name, "cmdline");
k_strcpy(entries[out++].name, "signal");
for(int i=0; i<out; i++) entries[i].is_directory = 0;
return out;
}
return 0;
}
bool procfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
uint32_t pid = k_atoi(pid_str);
if (process_get_by_pid(pid)) return true;
}
if (k_strcmp(path, "version") == 0 || k_strcmp(path, "uptime") == 0) return true;
if (k_strcmp(path, "cpuinfo") == 0 || k_strcmp(path, "meminfo") == 0) return true;
if (k_strcmp(path, "devices") == 0) return true;
return false;
}
bool procfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
int i = 0;
while (path[i] && path[i] != '/') i++;
if (path[i] == '\0') return true;
return false;
}
return false;
}
vfs_fs_ops_t procfs_ops = {
.open = procfs_open,
.close = procfs_close,
.read = procfs_read,
.write = procfs_write,
.readdir = procfs_readdir,
.exists = procfs_exists,
.is_dir = procfs_is_dir
};
vfs_fs_ops_t* procfs_get_ops(void) {
return &procfs_ops;
}

8
src/fs/procfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef PROCFS_H
#define PROCFS_H
#include "vfs.h"
vfs_fs_ops_t* procfs_get_ops(void);
#endif

181
src/fs/sysfs.c Normal file
View File

@@ -0,0 +1,181 @@
#include "vfs.h"
#include "../sys/kernel_subsystem.h"
#include "memory_manager.h"
#include "core/kutils.h"
typedef struct {
kernel_subsystem_t *sub;
subsystem_file_t *file;
int offset;
} sysfs_handle_t;
static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
if (path[0] == '\0') return NULL;
kernel_subsystem_t *sub = NULL;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) {
sysfs_handle_t *h = (sysfs_handle_t*)kmalloc(sizeof(sysfs_handle_t));
h->sub = sub;
h->file = &sub->files[j];
h->offset = 0;
return h;
}
}
}
}
return NULL;
}
static void sysfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->read) return -1;
int bytes = h->file->read((char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->write) return -1;
int bytes = h->file->write((const char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
kernel_subsystem_t *exact_sub = subsystem_get_by_name(path);
int out = 0;
if (exact_sub) {
for (int i = 0; i < exact_sub->file_count && out < max; i++) {
k_strcpy(entries[out].name, exact_sub->files[i].name);
entries[out].is_directory = 0;
entries[out].size = 0;
out++;
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count && out < max; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
const char *sub_path = s->name + (path_len ? path_len + 1 : 0);
char comp[64];
int j = 0;
while (sub_path[j] && sub_path[j] != '/' && j < 63) {
comp[j] = sub_path[j];
j++;
}
comp[j] = 0;
if (comp[0] == '\0') continue;
bool found = false;
for (int k = 0; k < out; k++) {
if (k_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found) {
k_strcpy(entries[out].name, comp);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
}
return out;
}
static bool sysfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (subsystem_get_by_name(path)) return true;
// File check
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return true;
}
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
}
return false;
}
static bool sysfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return false;
}
}
}
return sysfs_exists(fs_private, path);
}
vfs_fs_ops_t sysfs_ops = {
.open = sysfs_open,
.close = sysfs_close,
.read = sysfs_read,
.write = sysfs_write,
.readdir = sysfs_readdir,
.exists = sysfs_exists,
.is_dir = sysfs_is_dir
};
vfs_fs_ops_t* sysfs_get_ops(void) {
return &sysfs_ops;
}

8
src/fs/sysfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SYSFS_H
#define SYSFS_H
#include "vfs.h"
vfs_fs_ops_t* sysfs_get_ops(void);
#endif

128
src/fs/tar.c Normal file
View File

@@ -0,0 +1,128 @@
// 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 "tar.h"
#include "fat32.h"
// The standard TAR header block is 512 bytes.
struct tar_header {
char filename[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char pad[12];
} __attribute__((packed));
// Helper: parse tar octal field representation
static uint64_t tar_parse_octal(const char *str, int size) {
uint64_t result = 0;
while (size-- > 0) {
if (*str >= '0' && *str <= '7') {
result = (result << 3) + (*str - '0');
}
str++;
}
return result;
}
// Helper: Make directories sequentially for nested paths
static void tar_mkdir_recursive(const char *path) {
char temp[256];
int i = 0;
if (path[0] == '/') {
temp[0] = '/';
i = 1;
}
while (path[i] && i < 255) {
temp[i] = path[i];
if (path[i] == '/') {
temp[i] = '\0';
fat32_mkdir(temp);
temp[i] = '/';
}
i++;
}
if (i > 0 && temp[i - 1] != '/') {
temp[i] = '\0';
fat32_mkdir(temp);
}
}
void tar_parse(void *archive, uint64_t archive_size) {
uint8_t *ptr = (uint8_t *)archive;
uint8_t *end = ptr + archive_size;
while (ptr + 512 <= end) {
struct tar_header *header = (struct tar_header *)ptr;
// End of archive is marked by empty blocks
if (header->filename[0] == '\0') {
break;
}
uint64_t file_size = tar_parse_octal(header->size, 11);
char full_path[256];
// Ensure path starts with a '/' for VFS consistency
if (header->filename[0] != '/') {
full_path[0] = '/';
int j = 0;
while (header->filename[j] && j < 254) {
full_path[j + 1] = header->filename[j];
j++;
}
full_path[j + 1] = '\0';
} else {
int j = 0;
while (header->filename[j] && j < 255) {
full_path[j] = header->filename[j];
j++;
}
full_path[j] = '\0';
}
if (header->typeflag == '5') {
// It's a directory
tar_mkdir_recursive(full_path);
} else if (header->typeflag == '0' || header->typeflag == '\0') {
// It's a normal file
// First ensure the parent directory exists
char parent_path[256];
int last_slash = -1;
for (int j = 0; full_path[j]; j++) {
parent_path[j] = full_path[j];
if (full_path[j] == '/') {
last_slash = j;
}
}
if (last_slash > 0) {
parent_path[last_slash] = '\0';
tar_mkdir_recursive(parent_path);
}
// Extract the file data block directly into the VFS
FAT32_FileHandle *fh = fat32_open(full_path, "w");
if (fh && fh->valid) {
fat32_write(fh, ptr + 512, file_size);
fat32_close(fh);
}
}
// Advance pointer to the next file header
// Header block (512) + File data (padded to 512-byte multiples)
uint64_t data_blocks = (file_size + 511) / 512;
ptr += 512 + (data_blocks * 512);
}
}

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

@@ -0,0 +1,13 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef TAR_H
#define TAR_H
#include <stdint.h>
#include <stddef.h>
// Parse a TAR archive located in memory and extract its contents into the current filesystem (fatal32 RAM disk).
void tar_parse(void *archive, uint64_t archive_size);
#endif

816
src/fs/vfs.c Normal file
View File

@@ -0,0 +1,816 @@
// 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 "vfs.h"
#include "memory_manager.h"
#include "spinlock.h"
#include <stddef.h>
#include "disk.h"
#include "process.h"
static vfs_mount_t mounts[VFS_MAX_MOUNTS];
static int mount_count = 0;
static vfs_file_t open_files[VFS_MAX_OPEN_FILES];
static spinlock_t vfs_lock = SPINLOCK_INIT;
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
static int vfs_strlen(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
static void vfs_strcpy(char *d, const char *s) {
while ((*d++ = *s++));
}
static int vfs_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static int vfs_strncmp(const char *a, const char *b, int n) {
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i];
if (!a[i]) return 0;
}
return 0;
}
static bool vfs_starts_with(const char *str, const char *prefix) {
while (*prefix) {
if (*str++ != *prefix++) return false;
}
return true;
}
static bool vfs_path_is_parent(const char *parent, const char *child) {
int plen = vfs_strlen(parent);
if (vfs_strncmp(parent, child, plen) != 0) return false;
if (child[plen] == '\0') return true;
if (child[plen] == '/') return true;
if (plen == 1 && parent[0] == '/') return true;
return false;
}
void vfs_normalize_path(const char *cwd, const char *path, char *normalized) {
char parts[32][64]; // Reduced size to save stack, 64 is enough for most names
int depth = 0;
int i = 0;
// Handle relative path by starting with CWD
if (path[0] != '/' && cwd) {
int ci = 0;
if (cwd[0] == '/') ci = 1;
while (cwd[ci]) {
if (cwd[ci] == '/') { ci++; continue; }
int j = 0;
while (cwd[ci] && cwd[ci] != '/' && j < 63) {
parts[depth][j++] = cwd[ci++];
}
parts[depth][j] = 0;
if (j > 0) depth++;
if (depth >= 32) break;
if (cwd[ci] == '/') ci++;
}
}
if (path[0] == '/') i = 1;
while (path[i]) {
if (path[i] == '/') { i++; continue; }
int j = 0;
while (path[i] && path[i] != '/' && j < 63) {
parts[depth][j++] = path[i++];
}
parts[depth][j] = 0;
if (parts[depth][0] == '.' && parts[depth][1] == 0) {
// "." skip
} else if (parts[depth][0] == '.' && parts[depth][1] == '.' && parts[depth][2] == 0) {
// ".." pop
if (depth > 0) depth--;
} else {
if (j > 0) {
depth++;
if (depth >= 32) break;
}
}
if (path[i] == '/') i++;
}
normalized[0] = '/';
int pos = 1;
for (int k = 0; k < depth; k++) {
int l = 0;
while (parts[k][l] && pos < VFS_MAX_PATH - 2) {
normalized[pos++] = parts[k][l++];
}
if (k < depth - 1) normalized[pos++] = '/';
}
normalized[pos] = 0;
if (pos == 1 && normalized[0] == '/') {
normalized[1] = 0;
}
}
static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_out) {
vfs_mount_t *best = NULL;
int best_len = -1;
for (int i = 0; i < mount_count; i++) {
if (!mounts[i].active) continue;
int mlen = mounts[i].path_len;
if (mlen == 1 && mounts[i].path[0] == '/') {
if (best_len < 1) {
best = &mounts[i];
best_len = 1;
}
continue;
}
if (vfs_strncmp(path, mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0') {
if (mlen > best_len) {
best = &mounts[i];
best_len = mlen;
}
}
}
}
if (best && rel_path_out) {
const char *rel = path + best_len;
while (*rel == '/') rel++;
*rel_path_out = rel;
}
return best;
}
static vfs_file_t* vfs_alloc_file(void) {
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
if (!open_files[i].valid) {
open_files[i].valid = true;
open_files[i].fs_handle = NULL;
open_files[i].mount = NULL;
open_files[i].position = 0;
open_files[i].is_device = false;
return &open_files[i];
}
}
return NULL;
}
static void vfs_free_file(vfs_file_t *f) {
if (f) {
f->valid = false;
f->fs_handle = NULL;
f->mount = NULL;
f->position = 0;
f->is_device = false;
}
}
void vfs_init(void) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
mounts[i].active = false;
}
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
open_files[i].valid = false;
}
mount_count = 0;
serial_write("[VFS] Virtual File System initialized\n");
}
// ===============
// Mount / Unmount
// ===============
bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
vfs_fs_ops_t *ops, void *fs_private) {
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
if (mount_count >= VFS_MAX_MOUNTS) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: Mount table full\n");
return false;
}
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: Mount point already in use: ");
serial_write(mount_path);
serial_write("\n");
return false;
}
}
vfs_mount_t *m = &mounts[mount_count];
vfs_strcpy(m->path, mount_path);
m->path_len = vfs_strlen(mount_path);
m->ops = ops;
m->fs_private = fs_private;
vfs_strcpy(m->device, device ? device : "none");
vfs_strcpy(m->fs_type, fs_type ? fs_type : "unknown");
m->active = true;
mount_count++;
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] Mounted ");
serial_write(fs_type);
serial_write(" (");
serial_write(device ? device : "none");
serial_write(") at ");
serial_write(mount_path);
serial_write("\n");
return true;
}
bool vfs_umount(const char *mount_path) {
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
for (int j = 0; j < VFS_MAX_OPEN_FILES; j++) {
if (open_files[j].valid && open_files[j].mount == &mounts[i]) {
if (mounts[i].ops->close) {
mounts[i].ops->close(mounts[i].fs_private, open_files[j].fs_handle);
}
vfs_free_file(&open_files[j]);
}
}
serial_write("[VFS] Unmounted ");
serial_write(mounts[i].path);
serial_write("\n");
mounts[i].active = false;
// Compact array
for (int k = i; k < mount_count - 1; k++) {
mounts[k] = mounts[k + 1];
}
mount_count--;
spinlock_release_irqrestore(&vfs_lock, flags);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags);
return false;
}
// ==============
// File Operations
// ==============
vfs_file_t* vfs_open(const char *path, const char *mode) {
if (!path || !mode) return NULL;
char normalized[VFS_MAX_PATH];
process_t *proc = process_get_current();
vfs_normalize_path(proc ? proc->cwd : "/", path, normalized);
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// Fallback for block devices (/dev/sda etc)
if (vfs_starts_with(normalized, "/dev/")) {
const char *devname = normalized + 5;
Disk *d = disk_get_by_name(devname);
if (d && (!mount || mount->path_len == 1)) {
vfs_file_t *vf = vfs_alloc_file();
if (vf) {
vf->mount = &mounts[0];
vf->fs_handle = (void*)d;
vf->is_device = true;
vf->position = 0;
spinlock_release_irqrestore(&vfs_lock, flags);
return vf;
}
}
}
if (!mount || !mount->ops->open) {
spinlock_release_irqrestore(&vfs_lock, flags);
return NULL;
}
if (!rel_path || rel_path[0] == '\0') {
rel_path = "/";
}
vfs_file_t *vf = vfs_alloc_file();
if (!vf) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: No free file handles\n");
return NULL;
}
vf->mount = mount;
spinlock_release_irqrestore(&vfs_lock, flags);
void *fs_handle = mount->ops->open(mount->fs_private, rel_path, mode);
if (!fs_handle) {
flags = spinlock_acquire_irqsave(&vfs_lock);
vfs_free_file(vf);
spinlock_release_irqrestore(&vfs_lock, flags);
return NULL;
}
vf->fs_handle = fs_handle;
return vf;
}
void vfs_close(vfs_file_t *file) {
if (!file || !file->valid) return;
vfs_mount_t *mount = file->mount;
if (mount && mount->ops->close) {
mount->ops->close(mount->fs_private, file->fs_handle);
}
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
vfs_free_file(file);
spinlock_release_irqrestore(&vfs_lock, flags);
}
int vfs_read(vfs_file_t *file, void *buf, int size) {
if (!file || !file->valid || !file->mount) return -1;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint32_t total_read = 0;
uint32_t sector = (uint32_t)(file->position / 512);
uint32_t offset = (uint32_t)(file->position % 512);
uint8_t sector_buf[512];
while (total_read < (uint32_t)size) {
if (sector >= d->total_sectors) break;
if (d->read_sector(d, sector, sector_buf) != 0) break;
uint32_t to_copy = 512 - offset;
if (to_copy > (uint32_t)size - total_read) to_copy = (uint32_t)size - total_read;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy((uint8_t*)buf + total_read, sector_buf + offset, to_copy);
total_read += to_copy;
file->position += to_copy;
sector++;
offset = 0;
}
return (int)total_read;
}
if (!file->mount->ops->read) return -1;
int ret = file->mount->ops->read(file->mount->fs_private, file->fs_handle, buf, size);
if (ret > 0) file->position += ret;
return ret;
}
int vfs_write(vfs_file_t *file, const void *buf, int size) {
if (!file || !file->valid || !file->mount) return -1;
if (!file->mount->ops->write) return -1;
return file->mount->ops->write(file->mount->fs_private, file->fs_handle, buf, size);
}
int vfs_seek(vfs_file_t *file, int offset, int whence) {
if (!file || !file->valid || !file->mount) return -1;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint64_t new_pos = file->position;
if (whence == 0) new_pos = (uint64_t)offset; // SET
else if (whence == 1) new_pos += (uint64_t)offset; // CUR
else if (whence == 2) new_pos = (uint64_t)(d->total_sectors * 512 + offset); // END
if (new_pos > (uint64_t)d->total_sectors * 512) new_pos = (uint64_t)d->total_sectors * 512;
file->position = new_pos;
return 0;
}
if (!file->mount->ops->seek) return -1;
int ret = file->mount->ops->seek(file->mount->fs_private, file->fs_handle, offset, whence);
if (ret == 0) {
// Sync position back from driver if possible
if (file->mount->ops->get_position) {
file->position = file->mount->ops->get_position(file->fs_handle);
} else {
// Manual sync if driver doesn't support get_position but seek succeeded
if (whence == 0) file->position = offset;
else if (whence == 1) file->position += offset;
}
}
return ret;
}
uint32_t vfs_file_position(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) return (uint32_t)file->position;
if (!file->mount->ops->get_position) return 0;
return file->mount->ops->get_position(file->fs_handle);
}
uint32_t vfs_file_size(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
return d ? d->total_sectors * 512 : 0;
}
if (!file->mount->ops->get_size) return 0;
return file->mount->ops->get_size(file->fs_handle);
}
int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
if (!path || !entries) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
int count = 0;
if (mount && mount->ops->readdir) {
if (!rel_path || rel_path[0] == '\0') rel_path = "/";
count = mount->ops->readdir(mount->fs_private, rel_path, entries, max);
if (count < 0) count = 0;
}
uint64_t v_flags = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (!mounts[i].active) continue;
if (vfs_strcmp(mounts[i].path, normalized) == 0) continue;
if (vfs_path_is_parent(normalized, mounts[i].path)) {
const char *sub = mounts[i].path + vfs_strlen(normalized);
if (*sub == '/') sub++;
if (*sub != '\0') {
char comp[VFS_MAX_NAME];
int j = 0;
while (sub[j] && sub[j] != '/' && j < VFS_MAX_NAME - 1) {
comp[j] = sub[j];
j++;
}
comp[j] = 0;
bool found = false;
for (int k = 0; k < count; k++) {
if (vfs_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found && count < max) {
vfs_strcpy(entries[count].name, comp);
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
}
}
spinlock_release_irqrestore(&vfs_lock, v_flags);
// Special case: Ensure "dev", "sys", "proc" are visible in "/"
if (vfs_strcmp(normalized, "/") == 0) {
const char *virtual_dirs[] = {"dev", "sys", "proc"};
for (int v = 0; v < 3; v++) {
bool found = false;
for (int i = 0; i < count; i++) {
if (vfs_strcmp(entries[i].name, virtual_dirs[v]) == 0) {
found = true;
break;
}
}
if (!found && count < max) {
vfs_strcpy(entries[count].name, virtual_dirs[v]);
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
}
// Special case: /dev listing for block devices
if (vfs_strcmp(normalized, "/dev") == 0) {
int dcount = disk_get_count();
for (int i = 0; i < dcount && count < max; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
bool found = false;
for (int k = 0; k < count; k++) {
if (vfs_strcmp(entries[k].name, d->devname) == 0) {
found = true;
break;
}
}
if (!found) {
vfs_strcpy(entries[count].name, d->devname);
entries[count].size = d->total_sectors * 512;
entries[count].is_directory = 0;
entries[count].start_cluster = 0;
entries[count].write_date = 0;
entries[count].write_time = 0;
count++;
}
}
}
}
return count;
}
bool vfs_mkdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->mkdir) return false;
return mount->ops->mkdir(mount->fs_private, rel_path);
}
bool vfs_rmdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->rmdir) return false;
return mount->ops->rmdir(mount->fs_private, rel_path);
}
bool vfs_delete(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->unlink) return false;
return mount->ops->unlink(mount->fs_private, rel_path);
}
bool vfs_rename(const char *old_path, const char *new_path) {
if (!old_path || !new_path) return false;
char norm_old[VFS_MAX_PATH], norm_new[VFS_MAX_PATH];
vfs_normalize_path("/", old_path, norm_old);
vfs_normalize_path("/", new_path, norm_new);
const char *rel_old = NULL, *rel_new = NULL;
vfs_mount_t *mount_old = vfs_resolve_mount(norm_old, &rel_old);
vfs_mount_t *mount_new = vfs_resolve_mount(norm_new, &rel_new);
if (!mount_old || mount_old != mount_new) return false;
if (!mount_old->ops->rename) return false;
if (!rel_old || rel_old[0] == '\0') return false;
if (!rel_new || rel_new[0] == '\0') return false;
return mount_old->ops->rename(mount_old->fs_private, rel_old, rel_new);
}
bool vfs_exists(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return true;
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) {
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
if (disk_get_by_name(dev)) return true;
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount || !mount->ops->exists) return false;
if (!rel_path || rel_path[0] == '\0') return true;
return mount->ops->exists(mount->fs_private, rel_path);
}
bool vfs_is_directory(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return true;
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) == 0) {
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
// If normalized is a parent of a mount, it's a virtual directory
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
Disk *d = disk_get_by_name(dev);
if (d) return false;
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount) return false;
if (!rel_path || rel_path[0] == '\0') return true;
if (!mount->ops->is_dir) return false;
return mount->ops->is_dir(mount->fs_private, rel_path);
}
int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (!path || !info) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') {
vfs_strcpy(info->name, "/");
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) {
const char *name = normalized + 1;
vfs_strcpy(info->name, name);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) != 0) {
const char *p = normalized + vfs_strlen(normalized);
while (p > normalized && *(p-1) != '/') p--;
vfs_strcpy(info->name, p);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return 0;
}
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
// Device check
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
Disk *d = disk_get_by_name(dev);
if (d) {
vfs_strcpy(info->name, d->devname);
info->size = d->total_sectors * 512;
info->is_directory = 0;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount || !mount->ops->get_info) return -1;
if (!rel_path || rel_path[0] == '\0') {
// Info about mount root
vfs_strcpy(info->name, mount->device);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
return mount->ops->get_info(mount->fs_private, rel_path, info);
}
int vfs_get_mount_count(void) {
return mount_count;
}
vfs_mount_t* vfs_get_mount(int index) {
if (index < 0 || index >= mount_count) return NULL;
if (!mounts[index].active) return NULL;
return &mounts[index];
}
void vfs_automount_partition(const char *devname) {
char mount_path[64] = "/mnt/";
int i = 5;
const char *d = devname;
while (*d && i < 62) mount_path[i++] = *d++;
mount_path[i] = 0;
serial_write("[VFS] Auto-mount requested for ");
serial_write(devname);
serial_write(" at ");
serial_write(mount_path);
serial_write("\n");
}

117
src/fs/vfs.h Normal file
View File

@@ -0,0 +1,117 @@
// 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 VFS_H
#define VFS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define VFS_MAX_PATH 1024
#define VFS_MAX_NAME 256
#define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 64
// Forward declarations
typedef struct vfs_mount vfs_mount_t;
typedef struct vfs_file vfs_file_t;
// Directory entry for readdir
typedef struct vfs_dirent {
char name[VFS_MAX_NAME];
uint32_t size;
uint8_t is_directory;
uint32_t start_cluster;
uint16_t write_date;
uint16_t write_time;
} vfs_dirent_t;
// Filesystem operations — implemented by each filesystem type
typedef struct vfs_fs_ops {
// File operations — return opaque FS handle
void* (*open)(void *fs_private, const char *rel_path, const char *mode);
void (*close)(void *fs_private, void *file_handle);
int (*read)(void *fs_private, void *file_handle, void *buf, int size);
int (*write)(void *fs_private, void *file_handle, const void *buf, int size);
int (*seek)(void *fs_private, void *file_handle, int offset, int whence);
// Directory operations
int (*readdir)(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max);
bool (*mkdir)(void *fs_private, const char *rel_path);
bool (*rmdir)(void *fs_private, const char *rel_path);
bool (*unlink)(void *fs_private, const char *rel_path);
bool (*rename)(void *fs_private, const char *old_path, const char *new_path);
// Query operations
bool (*exists)(void *fs_private, const char *rel_path);
bool (*is_dir)(void *fs_private, const char *rel_path);
int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info);
// Handle info (for backward compat with syscall position/size queries)
uint32_t (*get_position)(void *file_handle);
uint32_t (*get_size)(void *file_handle);
} vfs_fs_ops_t;
// VFS file handle
struct vfs_file {
void *fs_handle; // FS-specific handle (e.g. FAT32_FileHandle*)
vfs_mount_t *mount; // Mount this file belongs to
bool valid;
uint64_t position; // Current Seek Position (for raw devices/fallbacks)
bool is_device; // Is this a raw device handle?
};
// Mount entry
struct vfs_mount {
char path[256]; // Mount point (e.g. "/", "/mnt/sda1")
int path_len;
vfs_fs_ops_t *ops;
void *fs_private; // FS-specific data (e.g. FAT32_Volume*)
char device[32]; // Device name (e.g. "ramfs", "sda1")
char fs_type[16]; // "ramfs", "fat32"
bool active;
};
// Initialization
void vfs_init(void);
// Mount/unmount
bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
vfs_fs_ops_t *ops, void *fs_private);
bool vfs_umount(const char *mount_path);
// File operations
vfs_file_t* vfs_open(const char *path, const char *mode);
void vfs_close(vfs_file_t *file);
int vfs_read(vfs_file_t *file, void *buf, int size);
int vfs_write(vfs_file_t *file, const void *buf, int size);
int vfs_seek(vfs_file_t *file, int offset, int whence);
// Directory operations
int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max);
bool vfs_mkdir(const char *path);
bool vfs_rmdir(const char *path);
bool vfs_delete(const char *path);
bool vfs_rename(const char *old_path, const char *new_path);
// Query operations
bool vfs_exists(const char *path);
bool vfs_is_directory(const char *path);
int vfs_get_info(const char *path, vfs_dirent_t *info);
// Mount enumeration
int vfs_get_mount_count(void);
vfs_mount_t* vfs_get_mount(int index);
// Block device auto-mount
void vfs_automount_partition(const char *devname);
// Path utilities
void vfs_normalize_path(const char *cwd, const char *path, char *normalized);
// Backward compat: get position/size from vfs_file
uint32_t vfs_file_position(vfs_file_t *file);
uint32_t vfs_file_size(vfs_file_t *file);
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

View File

@@ -224,7 +224,7 @@ int network_tcp_connect(const ipv4_address_t *ip, uint16_t port) {
uint32_t start = sys_now(); uint32_t start = sys_now();
asm volatile("sti"); asm volatile("sti");
while (sys_now() - start < 5000) { // 5 second timeout while (sys_now() - start < 15000) { // 15 second timeout
network_poll_internal(); network_poll_internal();
if (tcp_connect_done) { asm volatile("cli"); network_processing = 0; return 0; } if (tcp_connect_done) { asm volatile("cli"); network_processing = 0; return 0; }
if (tcp_connect_error) { asm volatile("cli"); network_processing = 0; return -1; } if (tcp_connect_error) { asm volatile("cli"); network_processing = 0; return -1; }
@@ -253,7 +253,7 @@ int network_tcp_recv(void *buf, size_t max_len) {
if (tcp_closed) { network_processing = 0; return 0; } // End of stream if (tcp_closed) { network_processing = 0; return 0; } // End of stream
uint32_t start = sys_now(); uint32_t start = sys_now();
asm volatile("sti"); asm volatile("sti");
while (sys_now() - start < 5000) { // 5 second timeout while (sys_now() - start < 30000) { // 30 second timeout
network_poll_internal(); network_poll_internal();
if (tcp_recv_queue) break; if (tcp_recv_queue) break;
if (tcp_closed) break; if (tcp_closed) break;
@@ -281,7 +281,10 @@ int network_tcp_recv_nb(void *buf, size_t max_len) {
if (network_processing) return -1; if (network_processing) return -1;
network_processing = 1; network_processing = 1;
network_poll_internal();
if (!tcp_recv_queue) { if (!tcp_recv_queue) {
if (tcp_closed) { network_processing = 0; return -2; }
network_processing = 0; network_processing = 0;
return 0; return 0;
} }

View File

@@ -0,0 +1,73 @@
#include "kernel_subsystem.h"
#include "memory_manager.h"
#include "spinlock.h"
static kernel_subsystem_t subsystems[MAX_SUBSYSTEMS];
static int subsystem_count = 0;
static spinlock_t sub_lock = SPINLOCK_INIT;
extern void mem_memset(void *dest, int val, size_t len);
extern void mem_memcpy(void *dest, const void *src, size_t len);
static void sub_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
static int sub_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
void subsystem_register(const char *name, kernel_subsystem_t **out_sub) {
uint64_t flags = spinlock_acquire_irqsave(&sub_lock);
if (subsystem_count >= MAX_SUBSYSTEMS) {
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = NULL;
return;
}
// Check if already exists
for (int i = 0; i < subsystem_count; i++) {
if (sub_strcmp(subsystems[i].name, name) == 0) {
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = &subsystems[i];
return;
}
}
kernel_subsystem_t *s = &subsystems[subsystem_count++];
mem_memset(s, 0, sizeof(kernel_subsystem_t));
sub_strcpy(s->name, name);
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = s;
}
void subsystem_add_file(kernel_subsystem_t *sub, const char *name,
int (*read)(char*, int, int),
int (*write)(const char*, int, int)) {
if (!sub || sub->file_count >= MAX_SUBSYSTEM_FILES) return;
subsystem_file_t *f = &sub->files[sub->file_count++];
sub_strcpy(f->name, name);
f->read = read;
f->write = write;
}
kernel_subsystem_t* subsystem_get_by_name(const char *name) {
for (int i = 0; i < subsystem_count; i++) {
if (sub_strcmp(subsystems[i].name, name) == 0) return &subsystems[i];
}
return NULL;
}
int subsystem_get_count(void) {
return subsystem_count;
}
kernel_subsystem_t* subsystem_get_by_index(int index) {
if (index < 0 || index >= subsystem_count) return NULL;
return &subsystems[index];
}

View File

@@ -0,0 +1,32 @@
#ifndef KERNEL_SUBSYSTEM_H
#define KERNEL_SUBSYSTEM_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define MAX_SUBSYSTEMS 16
#define MAX_SUBSYSTEM_FILES 32
typedef struct {
char name[64];
int (*read)(char *buffer, int size, int offset);
int (*write)(const char *buffer, int size, int offset);
} subsystem_file_t;
typedef struct {
char name[64];
subsystem_file_t files[MAX_SUBSYSTEM_FILES];
int file_count;
} kernel_subsystem_t;
void subsystem_register(const char *name, kernel_subsystem_t **out_sub);
void subsystem_add_file(kernel_subsystem_t *sub, const char *name,
int (*read)(char*, int, int),
int (*write)(const char*, int, int));
kernel_subsystem_t* subsystem_get_by_name(const char *name);
int subsystem_get_count(void);
kernel_subsystem_t* subsystem_get_by_index(int index);
#endif

29
src/sys/module_manager.c Normal file
View File

@@ -0,0 +1,29 @@
#include "module_manager.h"
#include "memory_manager.h"
#define MAX_MODULES 32
static kernel_module_t modules[MAX_MODULES];
static int module_count = 0;
static void mod_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
void module_manager_register(const char *name, uint64_t addr, uint64_t size) {
if (module_count >= MAX_MODULES) return;
kernel_module_t *m = &modules[module_count++];
mod_strcpy(m->name, name);
m->address = addr;
m->size = size;
}
int module_manager_get_count(void) {
return module_count;
}
kernel_module_t* module_manager_get_index(int index) {
if (index < 0 || index >= module_count) return NULL;
return &modules[index];
}

17
src/sys/module_manager.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef MODULE_MANAGER_H
#define MODULE_MANAGER_H
#include <stdint.h>
#include <stddef.h>
typedef struct {
char name[64];
uint64_t address;
uint64_t size;
} kernel_module_t;
void module_manager_register(const char *name, uint64_t addr, uint64_t size);
int module_manager_get_count(void);
kernel_module_t* module_manager_get_index(int index);
#endif

View File

@@ -20,12 +20,12 @@ extern void serial_write(const char *str);
#define MAX_PROCESSES 16 #define MAX_PROCESSES 16
#define MAX_CPUS_SCHED 32 #define MAX_CPUS_SCHED 32
process_t processes[MAX_PROCESSES] __attribute__((aligned(16))); process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
int process_count = 0;
static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU
static uint32_t next_pid = 0; static uint32_t next_pid = 0;
static void *free_kernel_stack_later = NULL; static void *free_kernel_stack_later[MAX_CPUS_SCHED] = {0};
static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
static spinlock_t runqueue_lock = SPINLOCK_INIT; static spinlock_t runqueue_lock = SPINLOCK_INIT;
static uint32_t next_cpu_assign = 1; // Round-robin CPU assignment (start from CPU 1) static uint32_t next_cpu_assign = 1;
void process_init(void) { void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
@@ -33,9 +33,10 @@ void process_init(void) {
} }
// Current kernel execution is PID 0 // Current kernel execution is PID 0
process_t *kernel_proc = &processes[process_count++]; process_t *kernel_proc = &processes[0];
kernel_proc->pid = next_pid++; kernel_proc->pid = next_pid++;
kernel_proc->is_user = false; kernel_proc->is_user = false;
kernel_proc->is_idle = true;
// We don't have its RSP or PML4 yet, but it's already running. // 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! // The timer interrupt will naturally capture its context on the first tick!
@@ -54,21 +55,39 @@ void process_init(void) {
kernel_proc->next = kernel_proc; // Circular linked list kernel_proc->next = kernel_proc; // Circular linked list
kernel_proc->cpu_affinity = 0; // Kernel always on BSP kernel_proc->cpu_affinity = 0; // Kernel always on BSP
mem_memset(kernel_proc->cwd, 0, 1024);
kernel_proc->cwd[0] = '/';
current_process[0] = kernel_proc; current_process[0] = kernel_proc;
} }
process_t* process_create(void (*entry_point)(void), bool is_user) { process_t* process_create(void (*entry_point)(void), bool is_user) {
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
if (process_count >= MAX_PROCESSES) { process_t *new_proc = NULL;
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i];
break;
}
}
if (!new_proc) {
spinlock_release_irqrestore(&runqueue_lock, rflags); spinlock_release_irqrestore(&runqueue_lock, rflags);
return NULL; return NULL;
} }
process_t *new_proc = &processes[process_count++];
new_proc->pid = next_pid++; new_proc->pid = next_pid++;
new_proc->is_user = is_user; new_proc->is_user = is_user;
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 {
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
}
// 1. Setup Page Table // 1. Setup Page Table
if (is_user) { if (is_user) {
new_proc->pml4_phys = paging_create_user_pml4_phys(); new_proc->pml4_phys = paging_create_user_pml4_phys();
@@ -162,9 +181,8 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
// Find an available slot // Find an available slot
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF || i >= process_count) { if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i]; new_proc = &processes[i];
if (i >= process_count) process_count = i + 1;
break; break;
} }
} }
@@ -375,16 +393,35 @@ process_t* process_get_current(void) {
} }
uint64_t process_schedule(uint64_t current_rsp) { uint64_t process_schedule(uint64_t current_rsp) {
if (free_kernel_stack_later) { uint32_t my_cpu = smp_this_cpu_id();
kfree(free_kernel_stack_later); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
free_kernel_stack_later = NULL;
void *cleanup_stack = NULL;
uint64_t cleanup_pml4 = 0;
if (free_kernel_stack_later[my_cpu]) {
cleanup_stack = free_kernel_stack_later[my_cpu];
free_kernel_stack_later[my_cpu] = NULL;
}
if (free_pml4_later[my_cpu]) {
cleanup_pml4 = free_pml4_later[my_cpu];
free_pml4_later[my_cpu] = 0;
} }
uint32_t my_cpu = smp_this_cpu_id();
process_t *cur = current_process[my_cpu]; process_t *cur = current_process[my_cpu];
if (!cur || !cur->next || cur == cur->next) if (!cur || !cur->next || cur == cur->next) {
spinlock_release_irqrestore(&runqueue_lock, rflags);
// Perform cleanup outside the lock
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 current_rsp; return current_rsp;
}
// Save context // Save context
cur->rsp = current_rsp; cur->rsp = current_rsp;
@@ -406,11 +443,8 @@ uint64_t process_schedule(uint64_t current_rsp) {
next_proc = next_proc->next; next_proc = next_proc->next;
} }
// If we didn't find a ready process for our CPU, stay on current (unless we are terminated)
if (next_proc->cpu_affinity != my_cpu || next_proc->pid == 0xFFFFFFFF) { if (next_proc->cpu_affinity != my_cpu || next_proc->pid == 0xFFFFFFFF) {
// Fallback to idle if current is terminated
if (cur && cur->pid == 0xFFFFFFFF) { if (cur && cur->pid == 0xFFFFFFFF) {
// Find the idle process for this CPU
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) { if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
next_proc = &processes[i]; next_proc = &processes[i];
@@ -418,18 +452,25 @@ uint64_t process_schedule(uint64_t current_rsp) {
} }
} }
} else { } else {
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 current_rsp; return current_rsp;
} }
} }
current_process[my_cpu] = next_proc; current_process[my_cpu] = next_proc;
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) { 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); tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) { cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
extern uint64_t kernel_syscall_stack; if (cpu_state) {
kernel_syscall_stack = current_process[my_cpu]->kernel_stack; cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
} }
} }
@@ -437,8 +478,16 @@ uint64_t process_schedule(uint64_t current_rsp) {
paging_switch_directory(current_process[my_cpu]->pml4_phys); paging_switch_directory(current_process[my_cpu]->pml4_phys);
current_process[my_cpu]->ticks++; current_process[my_cpu]->ticks++;
uint64_t next_rsp = current_process[my_cpu]->rsp;
return 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;
} }
process_t* process_get_by_pid(uint32_t pid) { process_t* process_get_by_pid(uint32_t pid) {
@@ -469,6 +518,9 @@ static void process_cleanup_inner(process_t *proc) {
extern void cmd_process_finished(void); extern void cmd_process_finished(void);
cmd_process_finished(); cmd_process_finished();
extern void network_cleanup(void);
network_cleanup();
extern void network_cleanup_pcb(void *pcb); extern void network_cleanup_pcb(void *pcb);
// TODO: We need per-process PCB tracking to call this safely // TODO: We need per-process PCB tracking to call this safely
// For now, let's NOT call global network_cleanup // For now, let's NOT call global network_cleanup
@@ -521,9 +573,8 @@ void process_terminate(process_t *to_delete) {
to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc); if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
if (to_delete->kernel_stack_alloc) { // Defer kernel stack until we switch away from it
kfree(to_delete->kernel_stack_alloc); to_delete->kernel_stack_alloc = NULL;
}
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) { if (to_delete->pml4_phys && to_delete->is_user) {
@@ -586,29 +637,23 @@ uint64_t process_terminate_current(void) {
// Mark slot as free // Mark slot as free
to_delete->pid = 0xFFFFFFFF; to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->ui_window = NULL;
to_delete->is_terminal_proc = false;
// 4. Load context for the NEXT process // 4. Load context for the NEXT process
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) { 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); tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) { cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
extern uint64_t kernel_syscall_stack; if (cpu_state) {
kernel_syscall_stack = current_process[my_cpu]->kernel_stack; cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
} }
} }
paging_switch_directory(current_process[my_cpu]->pml4_phys); paging_switch_directory(current_process[my_cpu]->pml4_phys);
// 5. Free memory free_kernel_stack_later[my_cpu] = to_delete->kernel_stack_alloc;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
paging_destroy_user_pml4_phys(to_delete->pml4_phys);
}
to_delete->user_stack_alloc = NULL;
free_kernel_stack_later = to_delete->kernel_stack_alloc;
to_delete->kernel_stack_alloc = NULL; to_delete->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = to_delete->pml4_phys;
to_delete->pml4_phys = 0; to_delete->pml4_phys = 0;
uint64_t next_rsp = current_process[my_cpu]->rsp; uint64_t next_rsp = current_process[my_cpu]->rsp;
@@ -656,4 +701,3 @@ process_t* process_get_by_ui_window(void *win) {
} }
return NULL; return NULL;
} }

View File

@@ -52,7 +52,9 @@ typedef struct process {
uint64_t ticks; uint64_t ticks;
uint64_t sleep_until; uint64_t sleep_until;
size_t used_memory; size_t used_memory;
uint32_t cpu_affinity; // Which CPU this process runs on (0 = BSP) uint32_t cpu_affinity;
bool is_idle;
char cwd[1024];
} __attribute__((aligned(16))) process_t; } __attribute__((aligned(16))) process_t;
typedef struct { typedef struct {
@@ -60,6 +62,7 @@ typedef struct {
char name[64]; char name[64];
uint64_t ticks; uint64_t ticks;
size_t used_memory; size_t used_memory;
bool is_idle;
} ProcessInfo; } ProcessInfo;
void process_init(void); void process_init(void);
@@ -73,7 +76,7 @@ uint64_t process_terminate_current(void);
void process_terminate(process_t *proc); void process_terminate(process_t *proc);
process_t* process_get_by_pid(uint32_t pid); process_t* process_get_by_pid(uint32_t pid);
// SMP: IPI handler for AP scheduling (called from ISR) // SMP: IPI handler for AP scheduling
uint64_t sched_ipi_handler(registers_t *regs); uint64_t sched_ipi_handler(registers_t *regs);
void process_push_gui_event(process_t *proc, gui_event_t *ev); void process_push_gui_event(process_t *proc, gui_event_t *ev);

View File

@@ -9,17 +9,27 @@
#include "platform.h" #include "platform.h"
#include "paging.h" #include "paging.h"
#include "process.h" #include "process.h"
#include "work_queue.h"
#include "core/kutils.h"
extern void serial_write(const char *str); extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n); extern void serial_write_num(uint32_t n);
extern void serial_write_hex(uint64_t n); extern void serial_write_hex(uint64_t n);
// --- Dynamically allocated per-CPU state --- static cpu_state_t *cpu_states = NULL;
static cpu_state_t *cpu_states = NULL; // Array[cpu_count]
static uint32_t total_cpus = 0; static uint32_t total_cpus = 0;
static uint32_t bsp_lapic_id = 0; static uint32_t bsp_lapic_id = 0;
static cpu_state_t *bsp_cpu_state = NULL;
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
static inline void wrmsr(uint32_t msr, uint64_t value) {
uint32_t low = (uint32_t)value;
uint32_t high = (uint32_t)(value >> 32);
asm volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high));
}
// Get LAPIC ID via CPUID leaf 0x01 (works on all x86_64)
static uint32_t read_lapic_id(void) { static uint32_t read_lapic_id(void) {
uint32_t eax, ebx, ecx, edx; uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
@@ -28,6 +38,12 @@ static uint32_t read_lapic_id(void) {
uint32_t smp_this_cpu_id(void) { uint32_t smp_this_cpu_id(void) {
if (total_cpus <= 1) return 0; 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;
uint32_t lapic = read_lapic_id(); uint32_t lapic = read_lapic_id();
for (uint32_t i = 0; i < total_cpus; i++) { for (uint32_t i = 0; i < total_cpus; i++) {
if (cpu_states[i].lapic_id == lapic) return i; if (cpu_states[i].lapic_id == lapic) return i;
@@ -44,50 +60,46 @@ cpu_state_t *smp_get_cpu(uint32_t cpu_id) {
return &cpu_states[cpu_id]; return &cpu_states[cpu_id];
} }
// --- AP Entry Point ---
// Called by Limine on each Application Processor.
// The limine_smp_info* is passed as a parameter.
static void ap_entry(struct limine_smp_info *info) { static void ap_entry(struct limine_smp_info *info) {
// 1. Figure out which CPU we are
uint32_t my_id = (uint32_t)(info->extra_argument); uint32_t my_id = (uint32_t)(info->extra_argument);
// 2. Enable FPU/SSE on this core (same as BSP does in platform_init)
uint64_t cr0; uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0)); asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1ULL << 2); // Clear EM cr0 &= ~(1ULL << 2);
cr0 |= (1ULL << 1); // Set MP cr0 |= (1ULL << 1);
cr0 |= (1ULL << 5); // Set NE cr0 |= (1ULL << 5);
asm volatile("mov %0, %%cr0" : : "r"(cr0)); asm volatile("mov %0, %%cr0" : : "r"(cr0));
uint64_t cr4; uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4)); asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9); // OSFXSR cr4 |= (1ULL << 9);
cr4 |= (1ULL << 10); // OSXMMEXCPT cr4 |= (1ULL << 10);
asm volatile("mov %0, %%cr4" : : "r"(cr4)); asm volatile("mov %0, %%cr4" : : "r"(cr4));
asm volatile("fninit"); asm volatile("fninit");
// 3. Load the shared GDT and properly reload all segments (including CS=0x08)
extern struct gdt_ptr gdtr; extern struct gdt_ptr gdtr;
extern void gdt_flush(uint64_t); extern void gdt_flush(uint64_t);
gdt_flush((uint64_t)&gdtr); gdt_flush((uint64_t)&gdtr);
// 4. Load per-CPU TSS
gdt_load_ap_tss(my_id); gdt_load_ap_tss(my_id);
// 5. Load the shared IDT
extern void idt_load(void); extern void idt_load(void);
idt_load(); idt_load();
// 6. Load the kernel page tables (same CR3 as BSP — shared kernel space) extern void syscall_init(void);
syscall_init();
uint64_t kernel_cr3 = paging_get_pml4_phys(); uint64_t kernel_cr3 = paging_get_pml4_phys();
asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3)); asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3));
// 7. Enable LAPIC on this core so it can receive IPIs
extern void lapic_enable(void); extern void lapic_enable(void);
lapic_enable(); lapic_enable();
// 8. Mark ourselves as online cpu_states[my_id].self = &cpu_states[my_id];
cpu_states[my_id].online = true; cpu_states[my_id].online = true;
cpu_states[my_id].kernel_syscall_stack = cpu_states[my_id].kernel_stack;
wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[my_id]);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[my_id]);
serial_write("[SMP] AP "); serial_write("[SMP] AP ");
serial_write_num(my_id); serial_write_num(my_id);
@@ -95,25 +107,35 @@ static void ap_entry(struct limine_smp_info *info) {
serial_write_num(cpu_states[my_id].lapic_id); serial_write_num(cpu_states[my_id].lapic_id);
serial_write(")\n"); serial_write(")\n");
// 9. Initialize the current_process pointer for this CPU process_t *ap_idle = process_create(NULL, false);
// Create a dedicated idle task for this AP (PID 0 is reserved for the BSP)
process_t *ap_idle = process_create(NULL, false); // Idle process
ap_idle->cpu_affinity = my_id; ap_idle->cpu_affinity = my_id;
process_set_current_for_cpu(my_id, ap_idle); ap_idle->is_idle = true;
k_strcpy(ap_idle->name, "idle:");
char id_s[8]; k_itoa(my_id, id_s);
k_strcpy(ap_idle->name + 5, id_s);
// 10. Enable interrupts and enter idle halt loop. process_set_current_for_cpu(my_id, ap_idle);
// APs will be woken by scheduling IPIs from BSP (vector 0x41).
// The IPI handler does context switching for this CPU's processes.
asm volatile("sti"); asm volatile("sti");
// Idle loop — APs halt and wait for IPI work_queue_drain_loop();
for (;;) { asm volatile("hlt"); } }
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_state_static.self = &bsp_state_static;
bsp_state_static.online = true;
wrmsr(MSR_GS_BASE, (uint64_t)&bsp_state_static);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&bsp_state_static);
bsp_cpu_state = &bsp_state_static;
} }
// --- SMP Initialization --- // --- SMP Initialization ---
uint32_t smp_init(struct limine_smp_response *smp_resp) { uint32_t smp_init(struct limine_smp_response *smp_resp) {
if (!smp_resp || smp_resp->cpu_count <= 1) { if (!smp_resp || smp_resp->cpu_count <= 1) {
// Single CPU system — just set up the BSP entry
total_cpus = 1; total_cpus = 1;
cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t)); cpu_states = (cpu_state_t *)kmalloc(sizeof(cpu_state_t));
if (!cpu_states) return 1; if (!cpu_states) return 1;
@@ -135,7 +157,6 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
serial_write_num(bsp_lapic_id); serial_write_num(bsp_lapic_id);
serial_write("\n"); serial_write("\n");
// Allocate per-CPU state array
cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t)); cpu_states = (cpu_state_t *)kmalloc(total_cpus * sizeof(cpu_state_t));
if (!cpu_states) { if (!cpu_states) {
serial_write("[SMP] ERROR: Failed to allocate CPU state array!\n"); serial_write("[SMP] ERROR: Failed to allocate CPU state array!\n");
@@ -145,10 +166,8 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
extern void mem_memset(void *, int, size_t); extern void mem_memset(void *, int, size_t);
mem_memset(cpu_states, 0, total_cpus * sizeof(cpu_state_t)); mem_memset(cpu_states, 0, total_cpus * sizeof(cpu_state_t));
// Initialize per-CPU GDT/TSS entries for all CPUs
gdt_init_ap_tss(total_cpus); gdt_init_ap_tss(total_cpus);
// Fill in CPU state and start APs
uint32_t bsp_index = 0; uint32_t bsp_index = 0;
for (uint32_t i = 0; i < total_cpus; i++) { for (uint32_t i = 0; i < total_cpus; i++) {
struct limine_smp_info *cpu = smp_resp->cpus[i]; struct limine_smp_info *cpu = smp_resp->cpus[i];
@@ -156,16 +175,21 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
cpu_states[i].lapic_id = cpu->lapic_id; cpu_states[i].lapic_id = cpu->lapic_id;
if (cpu->lapic_id == bsp_lapic_id) { if (cpu->lapic_id == bsp_lapic_id) {
// This is the BSP — already running cpu_states[i] = *bsp_cpu_state; // Copy early BSP state
cpu_states[i].online = true; cpu_states[i].self = &cpu_states[i];
cpu_states[i].kernel_stack = 0; // Limine stack for now
cpu_states[i].kernel_syscall_stack = 0;
bsp_index = i; bsp_index = i;
wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[i]);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[i]);
serial_write("[SMP] BSP CPU "); serial_write("[SMP] BSP CPU ");
serial_write_num(i); serial_write_num(i);
serial_write(" (LAPIC "); serial_write(" (LAPIC ");
serial_write_num(cpu->lapic_id); serial_write_num(cpu->lapic_id);
serial_write(") online\n"); serial_write(") online\n");
} else { } else {
// Allocate a kernel stack for this AP
void *ap_stack = kmalloc_aligned(65536, 65536); void *ap_stack = kmalloc_aligned(65536, 65536);
if (!ap_stack) { if (!ap_stack) {
serial_write("[SMP] ERROR: Failed to allocate AP stack!\n"); serial_write("[SMP] ERROR: Failed to allocate AP stack!\n");
@@ -175,27 +199,18 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
cpu_states[i].kernel_stack_alloc = ap_stack; cpu_states[i].kernel_stack_alloc = ap_stack;
cpu_states[i].online = false; cpu_states[i].online = false;
// Set extra_argument so the AP knows its index
cpu->extra_argument = i; cpu->extra_argument = i;
// Tell Limine to start this AP. Limine sets up the AP's stack
// from extra_argument's stack, but we need the goto_address.
// Limine will jump to ap_entry with the AP's limine_smp_info*.
// Important: Limine creates a temporary stack for the AP, and the
// goto_address is where the AP starts executing.
serial_write("[SMP] Starting AP "); serial_write("[SMP] Starting AP ");
serial_write_num(i); serial_write_num(i);
serial_write(" (LAPIC "); serial_write(" (LAPIC ");
serial_write_num(cpu->lapic_id); serial_write_num(cpu->lapic_id);
serial_write(")...\n"); serial_write(")...\n");
// This atomic write triggers the AP to start executing at ap_entry
__atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST); __atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST);
} }
} }
// Wait for all APs to come online (with timeout)
volatile uint32_t timeout = 10000000; volatile uint32_t timeout = 10000000;
uint32_t online_count = 0; uint32_t online_count = 0;
while (timeout-- > 0) { while (timeout-- > 0) {

View File

@@ -8,29 +8,27 @@
#include <stdbool.h> #include <stdbool.h>
#include "spinlock.h" #include "spinlock.h"
// Per-CPU state. Dynamically allocated at boot based on actual CPU count.
typedef struct cpu_state { typedef struct cpu_state {
uint32_t cpu_id; // Logical CPU index (0 = BSP) struct cpu_state *self;
uint32_t lapic_id; // Local APIC ID from Limine uint32_t cpu_id;
uint64_t kernel_stack; // Top of kernel stack for this CPU uint32_t lapic_id;
void *kernel_stack_alloc; // Base allocation for kfree uint64_t kernel_stack;
volatile bool online; // True once AP is fully initialized void *kernel_stack_alloc;
volatile bool online;
uint64_t user_rsp_scratch;
uint64_t kernel_syscall_stack;
} cpu_state_t; } cpu_state_t;
// Initialize SMP — call after GDT/IDT/memory init but before wm_init. void smp_init_bsp(void);
// Pass the Limine SMP response. APs will be started and will enter their
// idle loops. Returns the number of CPUs brought online.
struct limine_smp_response; struct limine_smp_response;
uint32_t smp_init(struct limine_smp_response *smp_resp); uint32_t smp_init(struct limine_smp_response *smp_resp);
// Get the current CPU index (0 = BSP). Uses CPUID to read LAPIC ID,
// then looks up in the cpu table.
uint32_t smp_this_cpu_id(void); uint32_t smp_this_cpu_id(void);
// Total number of CPUs online.
uint32_t smp_cpu_count(void); uint32_t smp_cpu_count(void);
// Get per-CPU state by index.
cpu_state_t *smp_get_cpu(uint32_t cpu_id); cpu_state_t *smp_get_cpu(uint32_t cpu_id);
#endif #endif

View File

@@ -8,7 +8,10 @@
#include "process.h" #include "process.h"
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "paging.h" #include "paging.h"
#include "work_queue.h"
#include "smp.h"
#include "platform.h" #include "platform.h"
#include "io.h" #include "io.h"
#include "pci.h" #include "pci.h"
@@ -19,6 +22,8 @@
#include "font_manager.h" #include "font_manager.h"
#include "graphics.h" #include "graphics.h"
extern bool ps2_ctrl_pressed;
// Read MSR // Read MSR
static inline uint64_t rdmsr(uint32_t msr) { static inline uint64_t rdmsr(uint32_t msr) {
uint32_t low, high; uint32_t low, high;
@@ -34,55 +39,98 @@ static inline void wrmsr(uint32_t msr, uint64_t value) {
} }
extern void isr128_wrapper(void); extern void isr128_wrapper(void);
extern void* kmalloc(size_t size);
extern void kfree(void* ptr);
typedef struct {
void (*fn)(void *);
void *arg;
uint64_t pml4_phys;
volatile int *completion_counter;
} smp_user_task_t;
static void smp_user_wrapper(void *arg) {
smp_user_task_t *task = (smp_user_task_t *)arg;
if (!task) return;
uint64_t old_cr3;
asm volatile("mov %%cr3, %0" : "=r"(old_cr3));
// Switch to user address space if necessary
bool switch_cr3 = (task->pml4_phys != 0 && task->pml4_phys != old_cr3);
if (switch_cr3) {
asm volatile("mov %0, %%cr3" :: "r"(task->pml4_phys) : "memory");
}
if (task->fn) {
task->fn(task->arg);
}
if (switch_cr3) {
asm volatile("mov %0, %%cr3" :: "r"(old_cr3) : "memory");
}
if (task->completion_counter) {
__sync_fetch_and_add(task->completion_counter, -1);
}
}
void syscall_init(void) { void syscall_init(void) {
// SMP-Safe System Calls using int 0x80 (configured in idt.c) uint64_t efer = rdmsr(MSR_EFER);
efer |= 1;
wrmsr(MSR_EFER, efer);
uint64_t star = ((uint64_t)0x001B << 48) | ((uint64_t)0x0008 << 32);
wrmsr(MSR_STAR, star);
extern void syscall_entry(void);
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
} }
static void user_window_close(Window *win) { static void user_window_close(Window *win) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_CLOSE }; gui_event_t ev = { .type = GUI_EVENT_CLOSE };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_paint(Window *win) { static void user_window_paint(Window *win) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_PAINT }; gui_event_t ev = { .type = GUI_EVENT_PAINT };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_click(Window *win, int x, int y) { static void user_window_click(Window *win, int x, int y) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_CLICK, .arg1 = x, .arg2 = y }; gui_event_t ev = { .type = GUI_EVENT_CLICK, .arg1 = x, .arg2 = y };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_right_click(Window *win, int x, int y) { static void user_window_right_click(Window *win, int x, int y) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_RIGHT_CLICK, .arg1 = x, .arg2 = y }; gui_event_t ev = { .type = GUI_EVENT_RIGHT_CLICK, .arg1 = x, .arg2 = y };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_mouse_down(Window *win, int x, int y) { static void user_window_mouse_down(Window *win, int x, int y) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_MOUSE_DOWN, .arg1 = x, .arg2 = y }; gui_event_t ev = { .type = GUI_EVENT_MOUSE_DOWN, .arg1 = x, .arg2 = y };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_mouse_up(Window *win, int x, int y) { static void user_window_mouse_up(Window *win, int x, int y) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_MOUSE_UP, .arg1 = x, .arg2 = y }; gui_event_t ev = { .type = GUI_EVENT_MOUSE_UP, .arg1 = x, .arg2 = y };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
static void user_window_mouse_move(Window *win, int x, int y, uint8_t buttons) { static void user_window_mouse_move(Window *win, int x, int y, uint8_t buttons) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_MOUSE_MOVE, .arg1 = x, .arg2 = y, .arg3 = buttons }; gui_event_t ev = { .type = GUI_EVENT_MOUSE_MOVE, .arg1 = x, .arg2 = y, .arg3 = buttons };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
@@ -90,24 +138,24 @@ static void user_window_mouse_move(Window *win, int x, int y, uint8_t buttons) {
// Helper function for WM to send mouse events // Helper function for WM to send mouse events
void syscall_send_mouse_move_event(Window *win, int x, int y, uint8_t buttons) { void syscall_send_mouse_move_event(Window *win, int x, int y, uint8_t buttons) {
if (!win || !win->data) return; if (!win) return;
user_window_mouse_move(win, x, y, buttons); user_window_mouse_move(win, x, y, buttons);
} }
void syscall_send_mouse_down_event(Window *win, int x, int y) { void syscall_send_mouse_down_event(Window *win, int x, int y) {
if (!win || !win->data) return; if (!win) return;
user_window_mouse_down(win, x, y); user_window_mouse_down(win, x, y);
} }
void syscall_send_mouse_up_event(Window *win, int x, int y) { void syscall_send_mouse_up_event(Window *win, int x, int y) {
if (!win || !win->data) return; if (!win) return;
user_window_mouse_up(win, x, y); user_window_mouse_up(win, x, y);
} }
static void user_window_key(Window *win, char c, bool pressed) { static void user_window_key(Window *win, char c, bool pressed) {
process_t *proc = (process_t *)win->data; process_t *proc = process_get_by_ui_window(win);
if (!proc) return; if (!proc) return;
gui_event_t ev = { .type = pressed ? GUI_EVENT_KEY : GUI_EVENT_KEYUP, .arg1 = (int)c }; gui_event_t ev = { .type = pressed ? GUI_EVENT_KEY : GUI_EVENT_KEYUP, .arg1 = (int)c, .arg3 = (int)ps2_ctrl_pressed };
process_push_gui_event(proc, &ev); process_push_gui_event(proc, &ev);
} }
@@ -119,15 +167,18 @@ static void user_window_resize(Window *win, int w, int h) {
extern void kfree(void* ptr); extern void kfree(void* ptr);
extern void serial_write(const char *str); extern void serial_write(const char *str);
if (win->pixels) kfree(win->pixels); if (win->pixels) kfree(win->pixels);
if (win->comp_pixels) kfree(win->comp_pixels); if (win->comp_pixels) kfree(win->comp_pixels);
win->pixels = (uint32_t *)kmalloc(w * h * sizeof(uint32_t)); win->pixels = (uint32_t *)kmalloc(w * h * sizeof(uint32_t));
win->comp_pixels = (uint32_t *)kmalloc(w * h * sizeof(uint32_t)); win->comp_pixels = (uint32_t *)kmalloc(w * h * sizeof(uint32_t));
win->w = w;
win->h = h;
if (win->pixels) { if (win->pixels) {
for (int i = 0; i < w * h; i++) win->pixels[i] = 0; extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->pixels, 0, w * h * sizeof(uint32_t));
} }
} }
@@ -215,6 +266,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->cursor_pos = 0; win->cursor_pos = 0;
win->data = proc; win->data = proc;
win->font = NULL; win->font = NULL;
win->lock = SPINLOCK_INIT;
serial_write("Kernel: Dims initialized.\n"); serial_write("Kernel: Dims initialized.\n");
@@ -269,7 +321,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void graphics_set_render_target(uint32_t *buffer, int w, int h); extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
if (win->pixels) { if (win->pixels) {
// Strict user-to-window relative clamping // Strict user-to-window relative clamping
@@ -289,7 +343,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
draw_rect(win->x + params[0], win->y + params[1], params[2], params[3], color); draw_rect(win->x + params[0], win->y + params[1], params[2], params[3], color);
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == GUI_CMD_DRAW_ROUNDED_RECT_FILLED) { } else if (cmd == GUI_CMD_DRAW_ROUNDED_RECT_FILLED) {
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -303,7 +358,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void graphics_set_render_target(uint32_t *buffer, int w, int h); extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
if (win->pixels) { if (win->pixels) {
int rx = (int)params[0]; int ry = (int)params[1]; int rx = (int)params[0]; int ry = (int)params[1];
@@ -321,7 +378,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
} }
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == GUI_CMD_DRAW_STRING) { } else if (cmd == GUI_CMD_DRAW_STRING) {
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -344,7 +402,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
kernel_str[i] = 0; kernel_str[i] = 0;
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf(); ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf();
@@ -355,11 +415,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 baseline = uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
} }
}
} else { } else {
draw_string(ux, uy, kernel_str, color); draw_string(ux, uy, kernel_str, color);
} }
@@ -370,17 +438,26 @@ 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 baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
} }
}
} else { } else {
draw_string(win->x + ux, win->y + uy, kernel_str, color); draw_string(win->x + ux, win->y + uy, kernel_str, color);
} }
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == 10) { // GUI_CMD_DRAW_STRING_BITMAP } else if (cmd == 10) { // GUI_CMD_DRAW_STRING_BITMAP
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -403,7 +480,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
kernel_str[i] = 0; kernel_str[i] = 0;
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
if (win->pixels) { if (win->pixels) {
if (ux >= -100 && ux < win->w && uy >= -100 && uy < (win->h - 20)) { if (ux >= -100 && ux < win->w && uy >= -100 && uy < (win->h - 20)) {
@@ -415,7 +494,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
draw_string_bitmap(win->x + ux, win->y + uy, kernel_str, color); draw_string_bitmap(win->x + ux, win->y + uy, kernel_str, color);
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == 11) { // GUI_CMD_DRAW_STRING_SCALED } else if (cmd == 11) { // GUI_CMD_DRAW_STRING_SCALED
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -442,7 +522,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
kernel_str[i] = 0; kernel_str[i] = 0;
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf(); ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf();
@@ -453,11 +535,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
} }
}
} else { } else {
draw_string_scaled(ux, uy, kernel_str, color, scale); draw_string_scaled(ux, uy, kernel_str, color, scale);
} }
@@ -468,17 +558,26 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
} }
}
} else { } else {
draw_string_scaled(win->x + ux, win->y + uy, kernel_str, color, scale); draw_string_scaled(win->x + ux, win->y + uy, kernel_str, color, scale);
} }
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == 18) { // GUI_CMD_DRAW_STRING_SCALED_SLOPED } else if (cmd == 18) { // GUI_CMD_DRAW_STRING_SCALED_SLOPED
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -515,7 +614,9 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
kernel_str[i] = 0; kernel_str[i] = 0;
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf(); ttf_font_t *font = win->font ? (ttf_font_t*)win->font : graphics_get_current_ttf();
@@ -526,12 +627,20 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { 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)); 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); 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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
} }
}
} else { } else {
draw_string_scaled_sloped(ux, uy, kernel_str, color, scale, slope); draw_string_scaled_sloped(ux, uy, kernel_str, color, scale, slope);
} }
@@ -542,18 +651,27 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { 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)); 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); 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); 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); cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
} }
}
} else { } else {
draw_string_scaled_sloped(win->x + ux, win->y + uy, kernel_str, color, scale, slope); draw_string_scaled_sloped(win->x + ux, win->y + uy, kernel_str, color, scale, slope);
} }
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == GUI_CMD_DRAW_IMAGE) { } else if (cmd == GUI_CMD_DRAW_IMAGE) {
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -564,14 +682,17 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
for (int i = 0; i < 4; i++) params[i] = u_params[i]; for (int i = 0; i < 4; i++) params[i] = u_params[i];
uint64_t rflags; uint64_t rflags;
rflags = wm_lock_acquire(); bool use_wm_lock = (win->pixels == NULL);
if (use_wm_lock) rflags = wm_lock_acquire();
else rflags = spinlock_acquire_irqsave(&win->lock);
if (win->pixels) { if (win->pixels) {
int rx = (int)params[0]; int ry = (int)params[1]; int rx = (int)params[0]; int ry = (int)params[1];
int rw = (int)params[2]; int rh = (int)params[3]; int rw = (int)params[2]; int rh = (int)params[3];
int src_w = rw;
int src_x_offset = 0; int src_x_offset = 0;
int src_y_offset = 0; int src_y_offset = 0;
if (rx < 0) { src_x_offset = -rx; rw += rx; rx = 0; } if (rx < 0) { src_x_offset = -rx; rw += rx; rx = 0; }
if (ry < 0) { src_y_offset = -ry; rh += ry; ry = 0; } if (ry < 0) { src_y_offset = -ry; rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx; if (rx + rw > win->w) rw = win->w - rx;
@@ -580,15 +701,13 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
if (rw > 0 && rh > 0) { if (rw > 0 && rh > 0) {
for (int y = 0; y < rh; y++) { for (int y = 0; y < rh; y++) {
uint32_t *dest = &win->pixels[(ry + y) * win->w + rx]; uint32_t *dest = &win->pixels[(ry + y) * win->w + rx];
uint32_t *src = &image_data[(src_y_offset + y) * (int)params[2] + src_x_offset]; uint32_t *src = &image_data[(src_y_offset + y) * src_w + src_x_offset];
for (int x = 0; x < rw; x++) { for (int x = 0; x < rw; x++) {
uint32_t s = src[x]; uint32_t s = src[x];
uint8_t alpha = (s >> 24) & 0xFF; uint8_t alpha = (s >> 24) & 0xFF;
if (alpha == 0xFF) { if (alpha == 0xFF) {
dest[x] = s; dest[x] = s;
} else if (alpha == 0) { } else if (alpha > 0) {
// Skip
} else {
uint32_t d = dest[x]; uint32_t d = dest[x];
uint32_t rb = ((s & 0xFF00FF) * alpha + (d & 0xFF00FF) * (255 - alpha)) >> 8; uint32_t rb = ((s & 0xFF00FF) * alpha + (d & 0xFF00FF) * (255 - alpha)) >> 8;
uint32_t g = ((s & 0x00FF00) * alpha + (d & 0x00FF00) * (255 - alpha)) >> 8; uint32_t g = ((s & 0x00FF00) * alpha + (d & 0x00FF00) * (255 - alpha)) >> 8;
@@ -599,7 +718,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
} }
} }
wm_lock_release(rflags); if (use_wm_lock) wm_lock_release(rflags);
else spinlock_release_irqrestore(&win->lock, rflags);
} }
} else if (cmd == GUI_CMD_MARK_DIRTY) { } else if (cmd == GUI_CMD_MARK_DIRTY) {
uint64_t rflags = wm_lock_acquire(); uint64_t rflags = wm_lock_acquire();
@@ -611,8 +731,10 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
// Dual-buffer commit: copy pixels to comp_pixels // Dual-buffer commit: copy pixels to comp_pixels
if (win->pixels && win->comp_pixels) { if (win->pixels && win->comp_pixels) {
uint64_t win_rflags = spinlock_acquire_irqsave(&win->lock);
extern void mem_memcpy(void *dest, const void *src, size_t len); extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(win->comp_pixels, win->pixels, (size_t)win->w * (win->h - 20) * 4); mem_memcpy(win->comp_pixels, win->pixels, (size_t)win->w * (win->h - 20) * 4);
spinlock_release_irqrestore(&win->lock, win_rflags);
} }
wm_mark_dirty(win->x + (int)params[0], win->y + (int)params[1], (int)params[2], (int)params[3]); wm_mark_dirty(win->x + (int)params[0], win->y + (int)params[1], (int)params[2], (int)params[3]);
} }
@@ -787,33 +909,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
const char *mode = (const char *)arg3; const char *mode = (const char *)arg3;
if (!path || !mode) return -1; if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode); // vfs_open now handles normalization internally with process_get_current()
if (!fh) return -1; // but let's be explicit if we can.
vfs_file_t *vf = vfs_open(path, mode);
if (!vf) return -1;
for (int i = 0; i < MAX_PROCESS_FDS; i++) { for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) { if (proc->fds[i] == NULL) {
proc->fds[i] = fh; proc->fds[i] = vf;
return (uint64_t)i; return (uint64_t)i;
} }
} }
fat32_close(fh); vfs_close(vf);
return -1; return -1;
} else if (cmd == FS_CMD_READ) { } else if (cmd == FS_CMD_READ) {
int fd = (int)arg2; int fd = (int)arg2;
void *buf = (void *)arg3; void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_read((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_WRITE) { } else if (cmd == FS_CMD_WRITE) {
int fd = (int)arg2; int fd = (int)arg2;
const void *buf = (const void *)arg3; const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_write((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_CLOSE) { } else if (cmd == FS_CMD_CLOSE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
fat32_close((FAT32_FileHandle*)proc->fds[fd]); vfs_close((vfs_file_t*)proc->fds[fd]);
proc->fds[fd] = NULL; proc->fds[fd] = NULL;
return 0; return 0;
} else if (cmd == FS_CMD_SEEK) { } else if (cmd == FS_CMD_SEEK) {
@@ -821,49 +945,107 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int offset = (int)arg3; int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_seek((FAT32_FileHandle*)proc->fds[fd], offset, whence); return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence);
} else if (cmd == FS_CMD_TELL) { } else if (cmd == FS_CMD_TELL) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->position; return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]);
} else if (cmd == FS_CMD_SIZE) { } else if (cmd == FS_CMD_SIZE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size; return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
} } else if (cmd == FS_CMD_GETCWD) {
else if (cmd == FS_CMD_LIST) { char *buf = (char *)arg2;
int size = (int)arg3;
if (!buf || size <= 0) return -1;
int len = (int)k_strlen(proc->cwd);
if (len >= size) return -1;
k_strcpy(buf, proc->cwd);
return (uint64_t)len;
} else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3; if (!path) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
if (vfs_is_directory(normalized)) {
k_strcpy(proc->cwd, normalized);
return 0;
}
return -1;
} else if (cmd == FS_CMD_LIST) {
const char *path = (const char *)arg2;
FAT32_FileInfo *u_entries = (FAT32_FileInfo *)arg3;
int max_entries = (int)arg4; int max_entries = (int)arg4;
if (!path || !entries) return -1; if (!path || !u_entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
// Safety cap for kernel allocation
if (max_entries > 256) max_entries = 256;
if (max_entries <= 0) return 0;
vfs_dirent_t *v_entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * max_entries);
if (!v_entries) return -1;
int count = vfs_list_directory(normalized, v_entries, max_entries);
if (count > 0) {
for (int i = 0; i < count; i++) {
// Direct copy as layouts are now aligned
k_strcpy(u_entries[i].name, v_entries[i].name);
u_entries[i].size = v_entries[i].size;
u_entries[i].is_directory = v_entries[i].is_directory;
u_entries[i].start_cluster = v_entries[i].start_cluster;
u_entries[i].write_date = v_entries[i].write_date;
u_entries[i].write_time = v_entries[i].write_time;
}
}
kfree(v_entries);
return (uint64_t)count;
} else if (cmd == FS_CMD_DELETE) { } else if (cmd == FS_CMD_DELETE) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_delete(path) ? 0 : -1; char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
return vfs_delete(normalized) ? 0 : -1;
} else if (cmd == FS_CMD_GET_INFO) { } else if (cmd == FS_CMD_GET_INFO) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *info = (FAT32_FileInfo *)arg3; FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3;
if (!path || !info) return -1; if (!path || !u_info) return -1;
extern int fat32_get_info(const char *path, FAT32_FileInfo *info);
return (uint64_t)fat32_get_info(path, info); char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
vfs_dirent_t v_info;
int res = vfs_get_info(normalized, &v_info);
if (res == 0) {
k_strcpy(u_info->name, v_info.name);
u_info->size = v_info.size;
u_info->is_directory = v_info.is_directory;
u_info->start_cluster = v_info.start_cluster;
u_info->write_date = v_info.write_date;
u_info->write_time = v_info.write_time;
}
return (uint64_t)res;
} else if (cmd == FS_CMD_MKDIR) { } else if (cmd == FS_CMD_MKDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1; return vfs_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) { } else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return 0; if (!path) return 0;
return fat32_exists(path) ? 1 : 0; return vfs_exists(path) ? 1 : 0;
} else if (cmd == FS_CMD_GETCWD) { } else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2; char *buf = (char *)arg2;
int size = (int)arg3; int size = (int)arg3;
if (!buf) return -1; if (!buf) return -1;
extern void fat32_get_current_dir(char *buf, int size);
fat32_get_current_dir(buf, size); fat32_get_current_dir(buf, size);
return 0; return 0;
} else if (cmd == FS_CMD_CHDIR) { } else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
extern bool fat32_chdir(const char *path);
return fat32_chdir(path) ? 0 : -1; return fat32_chdir(path) ? 0 : -1;
} }
return 0; return 0;
@@ -990,15 +1172,6 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void k_beep(int freq, int ms); extern void k_beep(int freq, int ms);
k_beep(freq, ms); k_beep(freq, ms);
return 0; return 0;
} else if (cmd == 15) { // SYSTEM_CMD_MEMINFO
uint64_t *out = (uint64_t *)arg2;
if (!out) return -1;
MemStats stats = memory_get_stats();
out[0] = stats.total_memory;
out[1] = stats.used_memory;
return 0;
} else if (cmd == 16) { // SYSTEM_CMD_UPTIME
return wm_get_ticks();
} else if (cmd == 17) { // SYSTEM_CMD_PCI_LIST } else if (cmd == 17) { // SYSTEM_CMD_PCI_LIST
typedef struct { typedef struct {
uint16_t vendor; uint16_t vendor;
@@ -1159,54 +1332,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
size_t max_len = (size_t)arg3; size_t max_len = (size_t)arg3;
extern int network_tcp_recv_nb(void *buf, size_t max_len); extern int network_tcp_recv_nb(void *buf, size_t max_len);
return (uint64_t)network_tcp_recv_nb(buf, max_len); return (uint64_t)network_tcp_recv_nb(buf, max_len);
} else if (cmd == SYSTEM_CMD_PROCESS_LIST) { return -1;
ProcessInfo *out = (ProcessInfo *)arg2;
int max_procs = (int)arg3;
if (!out) return 0;
extern process_t processes[];
// Dynamically calculate kernel usage as: Total System Used - User Process Sum
MemStats stats = memory_get_stats();
size_t total_used = stats.used_memory;
size_t user_used = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].is_user) {
user_used += processes[i].used_memory;
}
}
if (total_used > user_used) processes[0].used_memory = total_used - user_used;
else processes[0].used_memory = 0;
int count = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && (processes[i].is_user || processes[i].pid == 0)) {
out[count].pid = processes[i].pid;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(out[count].name, processes[i].name, 64);
if (processes[i].pid == 0) {
out[count].name[0] = 'k'; out[count].name[1] = 'e'; out[count].name[2] = 'r';
out[count].name[3] = 'n'; out[count].name[4] = 'e'; out[count].name[5] = 'l';
out[count].name[6] = '\0';
}
out[count].ticks = processes[i].ticks;
out[count].used_memory = processes[i].used_memory;
count++;
if (count >= max_procs) break;
}
}
return (uint64_t)count;
} else if (cmd == SYSTEM_CMD_GET_CPU_MODEL) {
char *user_buf = (char *)arg2;
if (!user_buf) return -1;
char model[64];
platform_get_cpu_model(model);
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(user_buf, model, 49);
return 0;
} else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION } else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION
uint16_t req_w = (uint16_t)arg2; uint16_t req_w = (uint16_t)arg2;
uint16_t req_h = (uint16_t)arg3; uint16_t req_h = (uint16_t)arg3;
@@ -1245,11 +1371,36 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return 0; return 0;
} }
return -1; return -1;
} else if (cmd == 49) { // SYSTEM_CMD_GET_OS_INFO return -1;
os_info_t *info = (os_info_t *)arg2; } else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
if (!info) return -1; void (*user_fn)(void*) = (void (*)(void*))arg2;
extern void get_os_info(os_info_t *info); void **args = (void **)arg3;
get_os_info(info); int count = (int)arg4;
if (count <= 0) return 0;
if (count > 64) count = 64;
volatile int completion_counter = count;
uint64_t current_pml4 = proc->pml4_phys;
smp_user_task_t tasks[64];
for (int i = 0; i < count; i++) {
tasks[i].fn = user_fn;
tasks[i].arg = args[i];
tasks[i].pml4_phys = current_pml4;
tasks[i].completion_counter = &completion_counter;
extern void work_queue_submit(void (*fn)(void*), void *arg);
work_queue_submit(smp_user_wrapper, &tasks[i]);
}
extern bool work_queue_drain_one(void);
while (completion_counter > 0) {
if (!work_queue_drain_one()) {
asm volatile("pause");
}
}
return 0; return 0;
} }
return -1; return -1;

View File

@@ -6,7 +6,6 @@
#include <stdint.h> #include <stdint.h>
// Forward declarations
typedef struct Window Window; typedef struct Window Window;
typedef struct registers_t registers_t; typedef struct registers_t registers_t;
@@ -53,11 +52,9 @@ typedef struct {
#define SYSTEM_CMD_SET_RAW_MODE 41 #define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42 #define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43 #define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RESOLUTION 47 #define SYSTEM_CMD_SET_RESOLUTION 47
#define SYSTEM_CMD_GET_OS_INFO 49 #define SYSTEM_CMD_PARALLEL_RUN 50
void syscall_init(void); void syscall_init(void);
uint64_t syscall_handler_c(registers_t *regs); uint64_t syscall_handler_c(registers_t *regs);

365
src/sys/sysfs_init.c Normal file
View File

@@ -0,0 +1,365 @@
#include "kernel_subsystem.h"
#include "smp.h"
#include "pci.h"
#include "memory_manager.h"
#include "module_manager.h"
#include "io.h"
#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) {
k_itoa(n, s);
}
// --- Graphics Implementation ---
static int read_gfx_drm(char *buf, int size, int offset) {
char out[512];
k_memset(out, 0, 512);
k_strcpy(out, "Driver: Simple Framebuffer\n");
k_strcpy(out + k_strlen(out), "Resolution: ");
char s[32]; k_itoa(get_screen_width(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "x");
k_itoa(get_screen_height(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nDepth: ");
k_itoa(graphics_get_fb_bpp(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), " bpp\nAddress: 0x");
k_itoa_hex(graphics_get_fb_addr(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\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;
}
// --- Memory Tracking Implementation ---
static int read_mem_tracking(char *buf, int size, int offset) {
MemStats stats = memory_get_stats();
char out[1024];
k_memset(out, 0, 1024);
k_strcpy(out, "--- Kernel Heap Tracking ---\n");
k_strcpy(out + k_strlen(out), "Allocated Blocks: ");
char s[32]; k_itoa(stats.allocated_blocks, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_itoa(stats.fragmentation_percent, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "%\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;
}
// --- Module Implementation ---
static int read_sys_modules(char *buf, int size, int offset) {
int count = module_manager_get_count();
char out[2048] = "Loaded Modules:\n";
for (int i = 0; i < count; i++) {
kernel_module_t *mod = module_manager_get_index(i);
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), mod->name);
k_strcpy(out + k_strlen(out), " (");
char sz_s[16]; k_itoa(mod->size / 1024, sz_s);
k_strcpy(out + k_strlen(out), sz_s);
k_strcpy(out + k_strlen(out), " KB)\n");
}
int len = 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;
}
// --- PCI Bus Implementation ---
static int read_pci_bus(char *buf, int size, int offset) {
pci_device_t devices[64];
int count = pci_enumerate_devices(devices, 64);
char out[4096];
k_memset(out, 0, 4096);
k_strcpy(out, "PCI Bus Devices:\n");
for (int i = 0; i < count; i++) {
char line[128];
k_strcpy(line, " [");
char b_s[8]; k_itoa(devices[i].bus, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].device, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].function, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "] Vendor:");
k_itoa_hex(devices[i].vendor_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Device:");
k_itoa_hex(devices[i].device_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Class:");
k_itoa_hex(devices[i].class_code, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "\n");
if (k_strlen(out) + k_strlen(line) < 4095) {
k_strcpy(out + k_strlen(out), line);
}
}
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;
}
// --- 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);
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;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- GPIO Implementation ---
static int read_gpio_debug(char *buf, int size, int offset) {
uint8_t p64 = inb(0x64);
char out[64] = "Port 0x64 Status: ";
char s[16]; k_itoa(p64, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\n");
int len = 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;
}
void sysfs_init_subsystems(void) {
kernel_subsystem_t *kernel, *devices, *bus, *class, *debug, *mem_debug;
subsystem_register("kernel", &kernel);
subsystem_register("devices", &devices);
subsystem_register("bus", &bus);
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);
// Bus info
kernel_subsystem_t *pci_bus;
subsystem_register("bus/pci", &pci_bus);
subsystem_add_file(pci_bus, "devices", read_pci_bus, NULL);
// Module info
kernel_subsystem_t *modules_sub;
subsystem_register("module", &modules_sub);
subsystem_add_file(modules_sub, "loaded", read_sys_modules, NULL);
// Memory Tracking
subsystem_register("kernel/debug/memory", &mem_debug);
subsystem_add_file(mem_debug, "tracking", read_mem_tracking, NULL);
// Graphics DRM
kernel_subsystem_t *gfx_debug;
subsystem_register("kernel/debug/graphics", &gfx_debug);
subsystem_add_file(gfx_debug, "drm", read_gfx_drm, NULL);
// GPIO
subsystem_add_file(debug, "gpio", read_gpio_debug, NULL);
}

View File

@@ -11,7 +11,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic
BIN_DIR = bin BIN_DIR = bin
LIBC_SOURCES = $(wildcard libc/*.c) LIBC_SOURCES = $(wildcard libc/*.c)
LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o $(BIN_DIR)/libwidget.o
VPATH = cli gui sys games libc net VPATH = cli gui sys games libc net
vpath %.c cli gui sys games libc net vpath %.c cli gui sys games libc net
@@ -34,6 +34,9 @@ $(BIN_DIR)/crt0.o: crt0.asm
$(BIN_DIR)/%.o: libc/%.c $(BIN_DIR)/%.o: libc/%.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
$(BIN_DIR)/libwidget.o: ../wm/libwidget.c
$(CC) $(CFLAGS) -c $< -o $@
$(BIN_DIR)/stb_image.o: stb_image.c $(BIN_DIR)/stb_image.o: stb_image.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@

View File

@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
printf("Manual for: %s\n", argv[1]); printf("Manual for: %s\n", argv[1]);
printf("---------------------------\n"); printf("---------------------------\n");
strcpy(path, "A:/Library/man/"); strcpy(path, "/Library/man/");
strcat(path, argv[1]); strcat(path, argv[1]);
strcat(path, ".txt"); strcat(path, ".txt");

View File

@@ -9,15 +9,6 @@
#define MAX_ASCII_WIDTH 80 #define MAX_ASCII_WIDTH 80
#define MAX_INFO_LINES 10 #define MAX_INFO_LINES 10
static char* strchr(const char *s, int c) {
while (*s != (char)c) {
if (!*s++) {
return 0;
}
}
return (char *)s;
}
static char* strncpy(char *dest, const char *src, size_t n) { static char* strncpy(char *dest, const char *src, size_t n) {
size_t i; size_t i;
for (i = 0; i < n && src[i] != '\0'; i++) dest[i] = src[i]; for (i = 0; i < n && src[i] != '\0'; i++) dest[i] = src[i];
@@ -117,7 +108,7 @@ static char* trim(char *str) {
} }
static void set_config_defaults() { static void set_config_defaults() {
strcpy(config.ascii_art_file, "A:/Library/art/boredos.txt"); strcpy(config.ascii_art_file, "/Library/art/boredos.txt");
strcpy(config.user_host_string, "root@boredos"); strcpy(config.user_host_string, "root@boredos");
strcpy(config.separator, "------------"); strcpy(config.separator, "------------");
strcpy(config.os_label, "OS"); strcpy(config.os_label, "OS");
@@ -160,7 +151,7 @@ static void parse_config(char* buffer) {
static void load_config() { static void load_config() {
set_config_defaults(); set_config_defaults();
int fd = sys_open("A:/Library/conf/sysfetch.cfg", "r"); int fd = sys_open("/Library/conf/sysfetch.cfg", "r");
if (fd < 0) return; if (fd < 0) return;
char *buffer = malloc(4096); char *buffer = malloc(4096);
@@ -237,52 +228,82 @@ int main(int argc, char **argv) {
if (config.separator[0]) { if (config.separator[0]) {
strcpy(info_lines[info_line_count++], config.separator); strcpy(info_lines[info_line_count++], config.separator);
} }
os_info_t os_info; // Helper for proc parsing
sys_get_os_info(&os_info); auto int find_v(const char *b, const char *k) {
char *p = (char*)b; int kl = strlen(k);
while (*p) {
if (memcmp(p, k, kl) == 0 && p[kl] == ':') {
p += kl + 1; while (*p == ' ') p++; return atoi(p);
}
while (*p && *p != '\n') p++; if (*p == '\n') p++;
}
return 0;
}
int fd_v = sys_open("/proc/version", "r");
char v_buf[512];
if (fd_v >= 0) {
int b = sys_read(fd_v, v_buf, 511);
v_buf[b] = 0;
sys_close(fd_v);
} else strcpy(v_buf, "Unknown");
if (config.os_label[0]) { if (config.os_label[0]) {
strcpy(info_lines[info_line_count], config.os_label); strcpy(info_lines[info_line_count], config.os_label);
strcat(info_lines[info_line_count], ": "); strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.os_name); // Parse "BoredOS [codename] Version X.Y.Z"
strcat(info_lines[info_line_count], " V"); strcat(info_lines[info_line_count], v_buf);
strcat(info_lines[info_line_count], os_info.os_version); // Truncate at newline
strcat(info_lines[info_line_count], " '"); char *nl = strchr(info_lines[info_line_count], '\n');
strcat(info_lines[info_line_count], os_info.os_codename); if (nl) *nl = 0;
strcat(info_lines[info_line_count], "'");
info_line_count++; info_line_count++;
} }
if (config.kernel_label[0]) { if (config.kernel_label[0]) {
strcpy(info_lines[info_line_count], config.kernel_label); strcpy(info_lines[info_line_count], config.kernel_label);
strcat(info_lines[info_line_count], ": "); strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.kernel_name); char *kstart = strchr(v_buf, '\n');
strcat(info_lines[info_line_count], " V"); if (kstart) {
strcat(info_lines[info_line_count], os_info.kernel_version); strcat(info_lines[info_line_count], kstart + 1);
strcat(info_lines[info_line_count], " "); char *knext = strchr(info_lines[info_line_count], '\n');
strcat(info_lines[info_line_count], os_info.build_arch); if (knext) *knext = 0;
} else strcat(info_lines[info_line_count], "Unknown");
info_line_count++; info_line_count++;
} }
if (config.uptime_label[0]) { if (config.uptime_label[0]) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0); int fd_u = sys_open("/proc/uptime", "r");
int minutes = ticks / 3600; // 60Hz timer if (fd_u >= 0) {
char u_buf[64];
int b = sys_read(fd_u, u_buf, 63);
u_buf[b] = 0;
sys_close(fd_u);
int sec = atoi(u_buf);
int mins = sec / 60;
strcpy(info_lines[info_line_count], config.uptime_label); strcpy(info_lines[info_line_count], config.uptime_label);
strcat(info_lines[info_line_count], ": "); strcat(info_lines[info_line_count], ": ");
itoa(minutes, temp_buf); itoa(mins, temp_buf);
strcat(info_lines[info_line_count], temp_buf); strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], " mins"); strcat(info_lines[info_line_count++], " mins");
} }
}
if (config.shell_label[0]) { if (config.shell_label[0]) {
strcpy(info_lines[info_line_count], config.shell_label); strcpy(info_lines[info_line_count], config.shell_label);
strcat(info_lines[info_line_count++], ": bsh"); strcat(info_lines[info_line_count++], ": bsh");
} }
if (config.memory_label[0]) { if (config.memory_label[0]) {
uint64_t mem[2]; int fd_m = sys_open("/proc/meminfo", "r");
if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) { if (fd_m >= 0) {
char m_buf[512];
int b = sys_read(fd_m, m_buf, 511);
m_buf[b] = 0;
sys_close(fd_m);
int total = find_v(m_buf, "MemTotal");
int used = find_v(m_buf, "MemUsed");
strcpy(info_lines[info_line_count], config.memory_label); strcpy(info_lines[info_line_count], config.memory_label);
strcat(info_lines[info_line_count], ": "); strcat(info_lines[info_line_count], ": ");
itoa((int)(mem[1] / 1024 / 1024), temp_buf); itoa(used / 1024, temp_buf);
strcat(info_lines[info_line_count], temp_buf); strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count], "MiB / "); strcat(info_lines[info_line_count], "MiB / ");
itoa((int)(mem[0] / 1024 / 1024), temp_buf); itoa(total / 1024, temp_buf);
strcat(info_lines[info_line_count], temp_buf); strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], "MiB"); strcat(info_lines[info_line_count++], "MiB");
} }

View File

@@ -149,26 +149,12 @@ char *strrchr(const char *s, int c) {
if (c == 0) last = s; if (c == 0) last = s;
return (char*)last; return (char*)last;
} }
char *strchr(const char *s, int c) {
while (*s) { if (*s == c) return (char*)s; s++; }
if (c == 0) return (char*)s;
return NULL;
}
char *strdup(const char *s) { char *strdup(const char *s) {
size_t len = strlen(s) + 1; size_t len = strlen(s) + 1;
char *dup = malloc(len); char *dup = malloc(len);
if (dup) memcpy(dup, s, len); if (dup) memcpy(dup, s, len);
return dup; return dup;
} }
char *strstr(const char *haystack, const char *needle) {
size_t n = strlen(needle);
if (!n) return (char *)haystack;
while (*haystack) {
if (!strncmp(haystack, needle, n)) return (char *)haystack;
haystack++;
}
return NULL;
}
int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; } int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; }
int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; } int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }

View File

@@ -1,339 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{95A126D2-CC94-4840-BF05-80305041B5FB}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>doomgeneric</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4146;4996</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4146;4996</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4146;4996</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4146;4996</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="am_map.c" />
<ClCompile Include="doomdef.c" />
<ClCompile Include="doomgeneric.c" />
<ClCompile Include="doomgeneric_win.c" />
<ClCompile Include="doomstat.c" />
<ClCompile Include="dstrings.c" />
<ClCompile Include="dummy.c" />
<ClCompile Include="d_event.c" />
<ClCompile Include="d_items.c" />
<ClCompile Include="d_iwad.c" />
<ClCompile Include="d_loop.c" />
<ClCompile Include="d_main.c" />
<ClCompile Include="d_mode.c" />
<ClCompile Include="d_net.c" />
<ClCompile Include="f_finale.c" />
<ClCompile Include="f_wipe.c" />
<ClCompile Include="gusconf.c" />
<ClCompile Include="g_game.c" />
<ClCompile Include="hu_lib.c" />
<ClCompile Include="hu_stuff.c" />
<ClCompile Include="icon.c" />
<ClCompile Include="info.c" />
<ClCompile Include="i_cdmus.c" />
<ClCompile Include="i_endoom.c" />
<ClCompile Include="i_input.c" />
<ClCompile Include="i_joystick.c" />
<ClCompile Include="i_scale.c" />
<ClCompile Include="i_sound.c" />
<ClCompile Include="i_system.c" />
<ClCompile Include="i_timer.c" />
<ClCompile Include="i_video.c" />
<ClCompile Include="memio.c" />
<ClCompile Include="m_argv.c" />
<ClCompile Include="m_bbox.c" />
<ClCompile Include="m_cheat.c" />
<ClCompile Include="m_config.c" />
<ClCompile Include="m_controls.c" />
<ClCompile Include="m_fixed.c" />
<ClCompile Include="m_menu.c" />
<ClCompile Include="m_misc.c" />
<ClCompile Include="m_random.c" />
<ClCompile Include="p_ceilng.c" />
<ClCompile Include="p_doors.c" />
<ClCompile Include="p_enemy.c" />
<ClCompile Include="p_floor.c" />
<ClCompile Include="p_inter.c" />
<ClCompile Include="p_lights.c" />
<ClCompile Include="p_map.c" />
<ClCompile Include="p_maputl.c" />
<ClCompile Include="p_mobj.c" />
<ClCompile Include="p_plats.c" />
<ClCompile Include="p_pspr.c" />
<ClCompile Include="p_saveg.c" />
<ClCompile Include="p_setup.c" />
<ClCompile Include="p_sight.c" />
<ClCompile Include="p_spec.c" />
<ClCompile Include="p_switch.c" />
<ClCompile Include="p_telept.c" />
<ClCompile Include="p_tick.c" />
<ClCompile Include="p_user.c" />
<ClCompile Include="r_bsp.c" />
<ClCompile Include="r_data.c" />
<ClCompile Include="r_draw.c" />
<ClCompile Include="r_main.c" />
<ClCompile Include="r_plane.c" />
<ClCompile Include="r_segs.c" />
<ClCompile Include="r_sky.c" />
<ClCompile Include="r_things.c" />
<ClCompile Include="sha1.c" />
<ClCompile Include="sounds.c" />
<ClCompile Include="statdump.c" />
<ClCompile Include="st_lib.c" />
<ClCompile Include="st_stuff.c" />
<ClCompile Include="s_sound.c" />
<ClCompile Include="tables.c" />
<ClCompile Include="v_video.c" />
<ClCompile Include="wi_stuff.c" />
<ClCompile Include="w_checksum.c" />
<ClCompile Include="w_file.c" />
<ClCompile Include="w_file_stdc.c" />
<ClCompile Include="w_main.c" />
<ClCompile Include="w_wad.c" />
<ClCompile Include="z_zone.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="am_map.h" />
<ClInclude Include="config.h" />
<ClInclude Include="deh_main.h" />
<ClInclude Include="deh_misc.h" />
<ClInclude Include="deh_str.h" />
<ClInclude Include="doom.h" />
<ClInclude Include="doomdata.h" />
<ClInclude Include="doomdef.h" />
<ClInclude Include="doomfeatures.h" />
<ClInclude Include="doomgeneric.h" />
<ClInclude Include="doomkeys.h" />
<ClInclude Include="doomstat.h" />
<ClInclude Include="doomtype.h" />
<ClInclude Include="dstrings.h" />
<ClInclude Include="d_englsh.h" />
<ClInclude Include="d_event.h" />
<ClInclude Include="d_items.h" />
<ClInclude Include="d_iwad.h" />
<ClInclude Include="d_loop.h" />
<ClInclude Include="d_main.h" />
<ClInclude Include="d_mode.h" />
<ClInclude Include="d_player.h" />
<ClInclude Include="d_textur.h" />
<ClInclude Include="d_think.h" />
<ClInclude Include="d_ticcmd.h" />
<ClInclude Include="f_finale.h" />
<ClInclude Include="f_wipe.h" />
<ClInclude Include="gusconf.h" />
<ClInclude Include="g_game.h" />
<ClInclude Include="hu_lib.h" />
<ClInclude Include="hu_stuff.h" />
<ClInclude Include="info.h" />
<ClInclude Include="i_cdmus.h" />
<ClInclude Include="i_endoom.h" />
<ClInclude Include="i_joystick.h" />
<ClInclude Include="i_scale.h" />
<ClInclude Include="i_sound.h" />
<ClInclude Include="i_swap.h" />
<ClInclude Include="i_system.h" />
<ClInclude Include="i_timer.h" />
<ClInclude Include="i_video.h" />
<ClInclude Include="memio.h" />
<ClInclude Include="m_argv.h" />
<ClInclude Include="m_bbox.h" />
<ClInclude Include="m_cheat.h" />
<ClInclude Include="m_config.h" />
<ClInclude Include="m_controls.h" />
<ClInclude Include="m_fixed.h" />
<ClInclude Include="m_menu.h" />
<ClInclude Include="m_misc.h" />
<ClInclude Include="m_random.h" />
<ClInclude Include="net_client.h" />
<ClInclude Include="net_dedicated.h" />
<ClInclude Include="net_defs.h" />
<ClInclude Include="net_gui.h" />
<ClInclude Include="net_io.h" />
<ClInclude Include="net_loop.h" />
<ClInclude Include="net_packet.h" />
<ClInclude Include="net_query.h" />
<ClInclude Include="net_sdl.h" />
<ClInclude Include="net_server.h" />
<ClInclude Include="p_inter.h" />
<ClInclude Include="p_local.h" />
<ClInclude Include="p_mobj.h" />
<ClInclude Include="p_pspr.h" />
<ClInclude Include="p_saveg.h" />
<ClInclude Include="p_setup.h" />
<ClInclude Include="p_spec.h" />
<ClInclude Include="p_tick.h" />
<ClInclude Include="r_bsp.h" />
<ClInclude Include="r_data.h" />
<ClInclude Include="r_defs.h" />
<ClInclude Include="r_draw.h" />
<ClInclude Include="r_local.h" />
<ClInclude Include="r_main.h" />
<ClInclude Include="r_plane.h" />
<ClInclude Include="r_segs.h" />
<ClInclude Include="r_sky.h" />
<ClInclude Include="r_state.h" />
<ClInclude Include="r_things.h" />
<ClInclude Include="sha1.h" />
<ClInclude Include="sounds.h" />
<ClInclude Include="statdump.h" />
<ClInclude Include="st_lib.h" />
<ClInclude Include="st_stuff.h" />
<ClInclude Include="s_sound.h" />
<ClInclude Include="tables.h" />
<ClInclude Include="v_patch.h" />
<ClInclude Include="v_video.h" />
<ClInclude Include="wi_stuff.h" />
<ClInclude Include="w_checksum.h" />
<ClInclude Include="w_file.h" />
<ClInclude Include="w_main.h" />
<ClInclude Include="w_merge.h" />
<ClInclude Include="w_wad.h" />
<ClInclude Include="z_zone.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -1,558 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="am_map.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_event.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_items.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_iwad.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_loop.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_mode.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d_net.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="doomdef.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="doomstat.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dstrings.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dummy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="f_finale.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="f_wipe.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="g_game.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gusconf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hu_lib.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hu_stuff.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_cdmus.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_endoom.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_joystick.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_scale.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_sound.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_system.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_timer.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="icon.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="info.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_argv.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_bbox.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_cheat.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_config.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_controls.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_fixed.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_menu.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_misc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="m_random.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="memio.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_ceilng.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_doors.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_enemy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_floor.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_inter.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_lights.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_map.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_maputl.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_mobj.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_plats.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_pspr.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_saveg.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_setup.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_sight.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_spec.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_switch.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_telept.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_tick.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p_user.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_bsp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_data.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_draw.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_plane.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_segs.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_sky.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="r_things.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="s_sound.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sha1.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sounds.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="st_lib.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="st_stuff.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="statdump.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tables.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="v_video.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="w_checksum.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="w_file.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="w_file_stdc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="w_main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="w_wad.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wi_stuff.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="z_zone.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="doomgeneric.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="doomgeneric_win.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_input.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="i_video.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="am_map.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_englsh.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_event.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_items.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_iwad.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_loop.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_mode.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_player.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_textur.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_think.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d_ticcmd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="deh_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="deh_misc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="deh_str.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doom.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomdata.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomfeatures.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomkeys.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomstat.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomtype.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dstrings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="f_finale.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="f_wipe.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="g_game.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="gusconf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hu_lib.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hu_stuff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_cdmus.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_endoom.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_joystick.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_scale.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_sound.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_swap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_system.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="i_video.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="info.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_argv.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_bbox.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_cheat.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_controls.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_fixed.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_menu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_misc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="m_random.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="memio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_client.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_dedicated.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_defs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_gui.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_io.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_loop.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_packet.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_query.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_sdl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="net_server.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_inter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_local.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_mobj.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_pspr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_saveg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_setup.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_spec.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p_tick.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_bsp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_data.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_defs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_draw.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_local.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_plane.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_segs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_sky.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="r_things.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="s_sound.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sha1.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sounds.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="st_lib.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="st_stuff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="statdump.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tables.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="v_patch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="v_video.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="w_checksum.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="w_file.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="w_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="w_merge.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="w_wad.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wi_stuff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="z_zone.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doomgeneric.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -6,26 +6,13 @@
static ui_window_t doom_win = 0; static ui_window_t doom_win = 0;
void DG_Init(void) { void DG_Init(void) {
doom_win = ui_window_create("Yes, it's DOOM.", (1920 - DOOMGENERIC_RESX) / 2, (1080 - DOOMGENERIC_RESY) / 2, DOOMGENERIC_RESX, DOOMGENERIC_RESY); doom_win = ui_window_create("DOOM", (1920 - DOOMGENERIC_RESX) / 2, (1080 - DOOMGENERIC_RESY) / 2, DOOMGENERIC_RESX, DOOMGENERIC_RESY);
} }
static uint32_t scaled_buffer[600 * 900]; // DOOMGENERIC_RESX * DOOMGENERIC_RESY static uint32_t scaled_buffer[600 * 900]; // DOOMGENERIC_RESX * DOOMGENERIC_RESY
void DG_DrawFrame(void) { void DG_DrawFrame(void) {
if (doom_win) { if (doom_win) {
// Doom's internal rendering is always 320x200.
// But doomgeneric seems to expect DOOMGENERIC_RESX x DOOMGENERIC_RESY.
// Actually, if we set DOOMGENERIC_RESX = 600, doom builds its internal tables based on DOOMGENERIC_RESX.
// Wait, Doom's standard resolution is 320x200. Let's find out what DG_ScreenBuffer dimensions are.
// According to Doom source, SCREENWIDTH and SCREENHEIGHT define the buffer size.
// So DG_ScreenBuffer is indeed DOOMGENERIC_RESX x DOOMGENERIC_RESY.
// However, the issue shown in the image is that the *game itself* only drew a 320x200 or 640x400 block in the corner!
// So the image wasn't scaled by Doom. Let's let LibUI do the scaling!
// We will tell Doom its resolution is 640x400 (which it knows how to handle correctly for the 16:10 aspect ratio),
// but we'll scale it to 600x900 (or whatever window size) before sending it to ui_draw_image.
// Wait, ui_draw_image doesn't scale natively yet. Let's do nearest-neighbor scaling.
int src_w = 640; int src_w = 640;
int src_h = 400; int src_h = 400;
int dst_w = DOOMGENERIC_RESX; int dst_w = DOOMGENERIC_RESX;
@@ -52,8 +39,19 @@ void DG_SleepMs(uint32_t ms) {
} }
uint32_t DG_GetTicksMs(void) { uint32_t DG_GetTicksMs(void) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME = 16 (100Hz) int fd = sys_open("/proc/uptime", "r");
return (uint32_t)(ticks * 10); if (fd < 0) return 0;
char buf[128];
int bytes = sys_read(fd, buf, 127);
sys_close(fd);
if (bytes <= 0) return 0;
buf[bytes] = 0;
char *p = strstr(buf, "Raw_Ticks:");
if (!p) return 0;
uint32_t ticks = atoi(p + 10);
// 60Hz to ms: ticks * 1000 / 60 = ticks * 50 / 3
return (ticks * 50) / 3;
} }
void DG_SetWindowTitle(const char * title) { void DG_SetWindowTitle(const char * title) {
@@ -121,7 +119,7 @@ int DG_GetKey(int* pressed, unsigned char* key) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
(void)argc; (void)argc;
(void)argv; (void)argv;
char* fake_argv[] = {"doom", "-iwad", "A:/Library/DOOM/doom1.wad"}; char* fake_argv[] = {"doom", "-iwad", "/Library/DOOM/doom1.wad"};
doomgeneric_Create(3, fake_argv); doomgeneric_Create(3, fake_argv);
while (1) { while (1) {

View File

@@ -95,36 +95,39 @@ static void about_paint(ui_window_t win) {
draw_ascii_logo(win, 14, offset_y); draw_ascii_logo(win, 14, offset_y);
int fh = ui_get_font_height(); int fh = ui_get_font_height();
os_info_t os_info; int fd_v = sys_open("/proc/version", "r");
sys_get_os_info(&os_info); char v_buf[1024]; v_buf[0] = 0;
if (fd_v >= 0) {
int b = sys_read(fd_v, v_buf, 1023);
v_buf[b] = 0;
sys_close(fd_v);
}
char os_name_str[128]; char os_name_str[128] = "Unknown OS";
os_name_str[0] = 0; char os_version_str[128] = "Unknown Version";
strcat(os_name_str, os_info.os_name); char kernel_version_str[128] = "Unknown Kernel";
strcat(os_name_str, " '"); char build_date_str[128] = "Unknown Build";
strcat(os_name_str, os_info.os_codename);
strcat(os_name_str, "'");
char os_version_str[128]; if (v_buf[0]) {
os_version_str[0] = 0; char *line1 = v_buf;
strcat(os_version_str, os_info.os_name); char *line2 = strchr(line1, '\n'); if (line2) { *line2 = 0; line2++; }
strcat(os_version_str, " Version "); char *line3 = line2 ? strchr(line2, '\n') : NULL; if (line3) { *line3 = 0; line3++; }
strcat(os_version_str, os_info.os_version);
char kernel_version_str[128]; strcpy(os_name_str, line1);
kernel_version_str[0] = 0; if (line2) {
strcat(kernel_version_str, os_info.kernel_name); strcpy(os_version_str, line2);
strcat(kernel_version_str, " Version "); }
strcat(kernel_version_str, os_info.kernel_version); if (line3) {
strcat(kernel_version_str, " "); strcpy(kernel_version_str, line3);
strcat(kernel_version_str, os_info.build_arch); char *line4 = strchr(line3, '\n');
if (line4) {
char build_date_str[128]; *line4 = 0; line4++;
build_date_str[0] = 0; strcpy(build_date_str, line4);
strcat(build_date_str, "Build Date: "); char *line5 = strchr(build_date_str, '\n');
strcat(build_date_str, os_info.build_date); if (line5) *line5 = 0;
strcat(build_date_str, " "); }
strcat(build_date_str, os_info.build_time); }
}
ui_draw_string(win, offset_x, offset_y + 105, os_name_str, 0xFFFFFFFF); ui_draw_string(win, offset_x, offset_y + 105, os_name_str, 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh, os_version_str, 0xFFFFFFFF); ui_draw_string(win, offset_x, offset_y + 105 + fh, os_version_str, 0xFFFFFFFF);

View File

@@ -4,6 +4,7 @@
#include "libc/syscall.h" #include "libc/syscall.h"
#include "libc/libui.h" #include "libc/libui.h"
#include "libc/stdlib.h" #include "libc/stdlib.h"
#include "../../wm/libwidget.h"
#include <stddef.h> #include <stddef.h>
#define COLOR_DARK_PANEL 0xFF202020 #define COLOR_DARK_PANEL 0xFF202020
@@ -90,8 +91,30 @@ static char open_filename[256] = "";
static _Bool file_modified = 0; static _Bool file_modified = 0;
static int scroll_y = 0; static int scroll_y = 0;
static _Bool is_dragging_scrollbar = 0; static widget_scrollbar_t doc_scrollbar;
static int scrollbar_drag_offset_y = 0;
static void word_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) {
ui_draw_rect((ui_window_t)user_data, x, y, w, h, color);
}
static void word_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) {
ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color);
}
static void word_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) {
ui_draw_string((ui_window_t)user_data, x, y, str, color);
}
static widget_context_t word_ctx = {
.user_data = 0,
.draw_rect = word_draw_rect,
.draw_rounded_rect_filled = word_draw_rounded_rect_filled,
.draw_string = word_draw_string,
.mark_dirty = NULL
};
static void word_on_scroll(void *user_data, int new_scroll_y) {
(void)user_data;
scroll_y = new_scroll_y;
}
static _Bool is_in_selection(int p, int r, int c); static _Bool is_in_selection(int p, int r, int c);
@@ -1301,20 +1324,13 @@ static void draw_document(ui_window_t win) {
set_active_font(win, 0); set_active_font(win, 0);
int content_h = current_page * (page_h + 20) + page_h + 20; int content_h = current_page * (page_h + 20) + page_h + 20;
if (content_h > win_h - 40) { doc_scrollbar.x = win_w - 12;
int sb_x = win_w - 12; doc_scrollbar.y = 40;
int sb_w = 12; doc_scrollbar.w = 12;
int sb_h = win_h - 40; doc_scrollbar.h = win_h - 40;
float ratio = (float)(win_h - 40) / (float)content_h; doc_scrollbar.on_scroll = word_on_scroll;
int thumb_h = (int)(sb_h * ratio); widget_scrollbar_update(&doc_scrollbar, content_h, scroll_y);
if (thumb_h < 20) thumb_h = 20; widget_scrollbar_draw(&word_ctx, &doc_scrollbar);
int max_scroll = content_h - (win_h - 40);
if (scroll_y > max_scroll) scroll_y = max_scroll;
int thumb_y = 40 + (int)(((float)scroll_y / max_scroll) * (sb_h - thumb_h));
ui_draw_rect(win, sb_x, 40, sb_w, sb_h, 0xFF303030);
ui_draw_rounded_rect_filled(win, sb_x+2, thumb_y+2, sb_w-4, thumb_h-4, 4, 0xFF606060);
}
} }
static void ensure_cursor_visible(ui_window_t win) { static void ensure_cursor_visible(ui_window_t win) {
@@ -1645,32 +1661,8 @@ static void handle_click(ui_window_t win, int x, int y) {
} }
} }
content_h = dummy_page * (page_h + 20) + page_h + 20; content_h = dummy_page * (page_h + 20) + page_h + 20;
widget_scrollbar_update(&doc_scrollbar, content_h, scroll_y);
int sb_x = win_w - 12; if (widget_scrollbar_handle_mouse(&doc_scrollbar, x, y, true, NULL)) {
int sb_w = 12;
int sb_h = win_h - 40;
int thumb_y = 40;
int thumb_h = 0;
int max_scroll = 0;
if (content_h > win_h - 40) {
float ratio = (float)(win_h - 40) / (float)content_h;
thumb_h = (int)(sb_h * ratio);
if (thumb_h < 20) thumb_h = 20;
max_scroll = content_h - (win_h - 40);
if (scroll_y > max_scroll) scroll_y = max_scroll;
thumb_y = 40 + (int)(((float)scroll_y / max_scroll) * (sb_h - thumb_h));
}
if (content_h > win_h - 40 && x >= sb_x && x < sb_x + sb_w) {
if (y >= thumb_y && y < thumb_y + thumb_h) {
is_dragging_scrollbar = 1;
scrollbar_drag_offset_y = y - thumb_y;
} else {
if (y < thumb_y) scroll_y -= (win_h - 40);
else scroll_y += (win_h - 40);
if (scroll_y < 0) scroll_y = 0;
if (scroll_y > max_scroll) scroll_y = max_scroll;
}
return; return;
} }
@@ -1848,12 +1840,15 @@ int main(int argc, char **argv) {
(void)argv; (void)argv;
ui_window_t win = ui_window_create("BoredWord", 100, 100, win_w, win_h); ui_window_t win = ui_window_create("BoredWord", 100, 100, win_w, win_h);
if (!win) return 1; if (!win) return 1;
word_ctx.user_data = (void*)win;
ui_window_set_resizable(win, 1); ui_window_set_resizable(win, 1);
load_fonts(); load_fonts();
set_active_font(win, 0); set_active_font(win, 0);
init_doc(); init_doc();
widget_scrollbar_init(&doc_scrollbar, win_w - 12, 40, 12, win_h - 40);
if (argc > 1) { if (argc > 1) {
load_file(win, argv[1]); load_file(win, argv[1]);
} }
@@ -1892,61 +1887,12 @@ int main(int argc, char **argv) {
needs_repaint = 1; needs_repaint = 1;
} else if (ev.type == GUI_EVENT_MOUSE_UP) { } else if (ev.type == GUI_EVENT_MOUSE_UP) {
is_dragging = 0; is_dragging = 0;
is_dragging_scrollbar = 0; widget_scrollbar_handle_mouse(&doc_scrollbar, ev.arg1, ev.arg2, false, NULL);
needs_repaint = 1; needs_repaint = 1;
} else if (ev.type == GUI_EVENT_MOUSE_MOVE) { } else if (ev.type == GUI_EVENT_MOUSE_MOVE) {
if (is_dragging_scrollbar) { if (doc_scrollbar.is_dragging) {
int pw, ph; get_page_size(&pw, &ph); widget_scrollbar_handle_mouse(&doc_scrollbar, ev.arg1, ev.arg2, true, NULL);
int doc_view_w = win_w - 40;
float scale = (float)doc_view_w / (float)pw; if (scale > 1.0f) scale = 1.0f;
int page_w = (int)(pw * scale);
int page_h = (int)(ph * scale);
int content_h = 0;
int dummy_y = 10; int dummy_page = 0;
for(int p=0; p<para_count; p++) {
Paragraph *para = &paragraphs[p]; int start_run = 0; int start_char = 0;
while(start_run < para->run_count) {
int max_h = 16; int end_run = start_run; int end_char = start_char; int line_w = 0;
int r_idx = start_run; int c_idx = start_char; int last_space_run = -1; int last_space_char = -1; int last_space_w = 0;
while(r_idx < para->run_count) {
TextRun *run = &para->runs[r_idx]; set_active_font(win, run->font_idx);
int fh = ui_get_font_height_scaled(run->font_size); if (fh > max_h) max_h = fh;
while(c_idx < run->len) {
char buf[2] = {run->text[c_idx], 0}; int cw = ui_get_string_width_scaled(buf, run->font_size);
if (run->text[c_idx] == ' ') { last_space_run = r_idx; last_space_char = c_idx; last_space_w = line_w + cw; }
if (line_w + cw > page_w - 20) break;
line_w += cw; c_idx++;
}
if (c_idx < run->len) break;
r_idx++; c_idx = 0;
}
if (r_idx < para->run_count || (r_idx == para->run_count - 1 && c_idx < para->runs[r_idx].len)) {
if (last_space_run != -1 && (last_space_run > start_run || last_space_char > start_char)) { end_run = last_space_run; end_char = last_space_char; line_w = last_space_w; }
else { end_run = r_idx; end_char = c_idx; }
} else { end_run = para->run_count; end_char = 0; }
int line_h = (int)(max_h * para->spacing) + 4;
if (dummy_y + line_h > dummy_page * (page_h + 20) + page_h - 10) { dummy_page++; dummy_y = dummy_page * (page_h + 20) + 10; }
dummy_y += line_h; start_run = end_run; start_char = end_char;
if (start_run < para->run_count && para->runs[start_run].text[start_char] == ' ') { start_char++; if (start_char >= para->runs[start_run].len) { start_char = 0; start_run++; } }
}
}
content_h = dummy_page * (page_h + 20) + page_h + 20;
if (content_h > win_h - 40) {
int sb_h = win_h - 40;
float ratio = (float)(win_h - 40) / (float)content_h;
int thumb_h = (int)(sb_h * ratio);
if (thumb_h < 20) thumb_h = 20;
int max_scroll = content_h - (win_h - 40);
int new_thumb_y = ev.arg2 - scrollbar_drag_offset_y;
if (new_thumb_y < 40) new_thumb_y = 40;
if (new_thumb_y > 40 + sb_h - thumb_h) new_thumb_y = 40 + sb_h - thumb_h;
scroll_y = (int)(((float)(new_thumb_y - 40) / (sb_h - thumb_h)) * max_scroll);
needs_repaint = 1; needs_repaint = 1;
}
} else if (is_dragging && ev.arg2 >= 40 && active_dialog == 0 && active_dropdown == 0) { } else if (is_dragging && ev.arg2 >= 40 && active_dialog == 0 && active_dropdown == 0) {
handle_click(win, ev.arg1, ev.arg2); handle_click(win, ev.arg1, ev.arg2);
needs_repaint = 1; needs_repaint = 1;

View File

@@ -32,19 +32,6 @@ static int win_h = 960;
static char history_stack[HISTORY_MAX][512]; static char history_stack[HISTORY_MAX][512];
static int history_count = 0; static int history_count = 0;
static char* 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;
}
static char* str_istrstr(const char* haystack, const char* needle) { static char* str_istrstr(const char* haystack, const char* needle) {
if (!*needle) return (char*)haystack; if (!*needle) return (char*)haystack;
for (; *haystack; haystack++) { for (; *haystack; haystack++) {
@@ -113,6 +100,7 @@ typedef struct {
float scale; float scale;
int list_depth; int list_depth;
int blockquote_depth; int blockquote_depth;
int attr_w;
bool img_loading; bool img_loading;
bool img_failed; bool img_failed;
uint32_t **img_frames; uint32_t **img_frames;
@@ -137,6 +125,42 @@ static int scroll_y = 0;
static int total_content_height = 0; static int total_content_height = 0;
static int focused_element = -1; static int focused_element = -1;
#include "../../wm/libwidget.h"
static void browser_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) {
ui_draw_rect((ui_window_t)user_data, x, y, w, h, color);
}
static void browser_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) {
ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color);
}
static void browser_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) {
ui_draw_string((ui_window_t)user_data, x, y, str, color);
}
static int browser_measure_string_width(void *user_data, const char *str) {
(void)user_data;
return (int)ui_get_string_width(str);
}
static void browser_mark_dirty(void *user_data, int x, int y, int w, int h) {
ui_mark_dirty((ui_window_t)user_data, x, y, w, h);
}
static widget_context_t browser_ctx = {
.draw_rect = browser_draw_rect,
.draw_rounded_rect_filled = browser_draw_rounded_rect_filled,
.draw_string = browser_draw_string,
.measure_string_width = browser_measure_string_width,
.mark_dirty = browser_mark_dirty
};
static widget_scrollbar_t browser_scrollbar;
static void browser_on_scroll(void *user_data, int new_scroll_y) {
(void)user_data;
scroll_y = new_scroll_y;
}
static widget_textbox_t url_tb;
static widget_button_t btn_back;
static widget_button_t btn_home;
static void parse_html(const char *html); static void parse_html(const char *html);
static void parse_html_incremental(const char *html, int safe_len); static void parse_html_incremental(const char *html, int safe_len);
static void browser_reflow(void); static void browser_reflow(void);
@@ -219,6 +243,9 @@ static int parse_ip(const char* str, net_ipv4_address_t* ip) {
return 0; return 0;
} }
static char dns_cache_host[256] = "";
static net_ipv4_address_t dns_cache_ip;
static int fetch_content(const char *url, char *dest_buf, int max_len, bool progressive) { static int fetch_content(const char *url, char *dest_buf, int max_len, bool progressive) {
const char* host_start = url; const char* host_start = url;
if (url[0] == 'h' && url[1] == 't' && url[2] == 't' && url[3] == 'p') { if (url[0] == 'h' && url[1] == 't' && url[2] == 't' && url[3] == 'p') {
@@ -253,7 +280,13 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
net_ipv4_address_t ip; net_ipv4_address_t ip;
if (parse_ip(hostname, &ip) != 0) { if (parse_ip(hostname, &ip) != 0) {
if (str_iequals(hostname, dns_cache_host)) {
ip = dns_cache_ip;
} else {
if (sys_dns_lookup(hostname, &ip) != 0) return 0; if (sys_dns_lookup(hostname, &ip) != 0) return 0;
int k=0; while(hostname[k]) { dns_cache_host[k] = hostname[k]; k++; } dns_cache_host[k] = 0;
dns_cache_ip = ip;
}
} }
if (sys_tcp_connect(&ip, port) != 0) return 0; if (sys_tcp_connect(&ip, port) != 0) return 0;
@@ -280,18 +313,100 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
int total = 0; int total = 0;
int last_render = 0; int last_render = 0;
if (progressive) inc_parse_offset = 0; if (progressive) inc_parse_offset = 0;
long long last_data_tick = sys_system(16, 0, 0, 0, 0);
while (1) { while (1) {
int len = sys_tcp_recv(dest_buf + total, max_len - 1 - total); int len = sys_tcp_recv_nb(dest_buf + total, max_len - 1 - total);
if (len <= 0) break; if (len < 0 && len != -2) break;
if (len == -2) break;
if (len == 0) {
long long now = sys_system(16, 0, 0, 0, 0);
if (now > last_data_tick + 1800) break; // 30 sec timeout
gui_event_t ev;
bool scrolled = false;
while (ui_get_event(win_browser, &ev)) {
if (ev.type == 9) { // GUI_EVENT_MOUSE_WHEEL
scroll_y += ev.arg1 * 20;
scrolled = true;
} else if (ev.type == 12) { // GUI_EVENT_CLOSE
sys_exit(0);
} else if (ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_MOUSE_UP || ev.type == GUI_EVENT_MOUSE_MOVE) {
bool is_down = (ev.type == GUI_EVENT_MOUSE_DOWN || (ev.type == GUI_EVENT_MOUSE_MOVE && browser_scrollbar.is_dragging));
if (widget_scrollbar_handle_mouse(&browser_scrollbar, ev.arg1, ev.arg2, is_down, &browser_ctx)) {
scroll_y = browser_scrollbar.scroll_y;
scrolled = true;
}
}
}
if (scrolled) {
int max_scroll = total_content_height - (win_h - URL_BAR_H);
if (max_scroll < 0) max_scroll = 0;
if (scroll_y > max_scroll) scroll_y = max_scroll;
if (scroll_y < 0) scroll_y = 0;
browser_reflow(); // Needs reflow in case of dimensions changing, but mostly just paint
browser_paint();
ui_mark_dirty(win_browser, 0, 0, win_w, win_h);
}
sleep(10);
continue;
}
last_data_tick = sys_system(16, 0, 0, 0, 0);
total += len; total += len;
if (total >= max_len - 1) break; if (total >= max_len - 1) break;
dest_buf[total] = 0;
char *body = strstr(dest_buf, "\r\n\r\n");
if (body) {
char temp = body[0];
body[0] = 0; // Null-terminate headers
int expected = -1;
char *cl = str_istrstr(dest_buf, "Content-Length:");
if (cl) {
cl += 15;
while (*cl == ' ') cl++;
expected = 0;
while (*cl >= '0' && *cl <= '9') {
expected = expected * 10 + (*cl - '0');
cl++;
}
}
int is_chunked = 0;
char *te = str_istrstr(dest_buf, "Transfer-Encoding:");
if (te && str_istrstr(te, "chunked")) {
is_chunked = 1;
}
body[0] = temp; // Restore body
body += 4;
int body_len = total - (body - dest_buf);
if (expected != -1) {
if (body_len >= expected) break;
} else if (is_chunked) {
if (total >= 5 && dest_buf[total-5] == '0' && dest_buf[total-4] == '\r' &&
dest_buf[total-3] == '\n' && dest_buf[total-2] == '\r' && dest_buf[total-1] == '\n') {
break;
}
}
}
if (progressive && total - last_render > 32768) { if (progressive && total - last_render > 32768) {
dest_buf[total] = 0; dest_buf[total] = 0;
char *body = strstr(dest_buf, "\r\n\r\n"); char *body = strstr(dest_buf, "\r\n\r\n");
if (body) { if (body) {
char temp = body[0];
body[0] = 0;
int is_chunked = strstr(dest_buf, "Transfer-Encoding: chunked") != NULL;
body[0] = temp;
body += 4; body += 4;
if (!strstr(dest_buf, "Transfer-Encoding: chunked")) { if (!is_chunked) {
int body_len = total - (body - dest_buf); int body_len = total - (body - dest_buf);
int safe_len = body_len; int safe_len = body_len;
while (safe_len > 0 && body[safe_len - 1] != '>') safe_len--; while (safe_len > 0 && body[safe_len - 1] != '>') safe_len--;
@@ -333,8 +448,13 @@ static void decode_image(unsigned char *data, int len, RenderElement *el) {
if (rgba && img_w_orig > 0 && img_h_orig > 0) { if (rgba && img_w_orig > 0 && img_h_orig > 0) {
int fit_w = img_w_orig; int fit_h = img_h_orig; int fit_w = img_w_orig; int fit_h = img_h_orig;
if (el->attr_w > 0) {
fit_w = el->attr_w;
fit_h = (img_h_orig * fit_w) / img_w_orig;
} else {
if (fit_w > win_w - 60) { fit_h = fit_h * (win_w - 60) / fit_w; fit_w = win_w - 60; } if (fit_w > win_w - 60) { fit_h = fit_h * (win_w - 60) / fit_w; fit_w = win_w - 60; }
if (fit_h > 400) { fit_w = fit_w * 400 / fit_h; fit_h = 400; } if (fit_h > 400) { fit_w = fit_w * 400 / fit_h; fit_h = 400; }
}
if (frame_count > 1 && delays) { if (frame_count > 1 && delays) {
el->img_frames = malloc(frame_count * sizeof(uint32_t *)); el->img_frames = malloc(frame_count * sizeof(uint32_t *));
@@ -479,6 +599,7 @@ static int inc_list_type[16];
static int inc_list_index[16]; static int inc_list_index[16];
static int inc_center_depth = 0; static int inc_center_depth = 0;
static int inc_table_depth = 0; static int inc_table_depth = 0;
static int inc_table_float_depth = 0;
static int inc_blockquote_depth = 0; static int inc_blockquote_depth = 0;
static bool inc_is_bold = false; static bool inc_is_bold = false;
static bool inc_is_italic = false; static bool inc_is_italic = false;
@@ -521,7 +642,7 @@ static void flush_line(void) {
if (el->tag == TAG_IMG && el->img_h + 10 > max_h) max_h = el->img_h + 10; if (el->tag == TAG_IMG && el->img_h + 10 > max_h) max_h = el->img_h + 10;
if ((el->tag == TAG_INPUT || el->tag == TAG_BUTTON) && 20 + 10 > max_h) max_h = 20 + 10; if ((el->tag == TAG_INPUT || el->tag == TAG_BUTTON) && 20 + 10 > max_h) max_h = 20 + 10;
if (el->tag == TAG_NONE) { if (el->tag == TAG_NONE) {
int fh = ui_get_font_height_scaled(el->scale); int fh = el->h;
if (fh + 4 > max_h) max_h = fh + 4; if (fh + 4 > max_h) max_h = fh + 4;
if (fh > max_baseline) max_baseline = fh; if (fh > max_baseline) max_baseline = fh;
} }
@@ -531,7 +652,7 @@ static void flush_line(void) {
RenderElement *el = &elements[line_elements[i]]; RenderElement *el = &elements[line_elements[i]];
el->x = offset_x; el->x = offset_x;
if (el->tag == TAG_NONE) { if (el->tag == TAG_NONE) {
int fh = ui_get_font_height_scaled(el->scale); int fh = el->h;
el->y = cur_line_y + (max_baseline - fh); el->y = cur_line_y + (max_baseline - fh);
} else { } else {
el->y = cur_line_y; el->y = cur_line_y;
@@ -550,9 +671,41 @@ static void browser_reflow(void) {
line_element_count = 0; line_element_count = 0;
total_content_height = 0; total_content_height = 0;
int float_right_bottom_y = 0;
int float_start_idx = -1;
int float_start_y = 0;
for (int i = 0; i < element_count; i++) { for (int i = 0; i < element_count; i++) {
RenderElement *el = &elements[i]; RenderElement *el = &elements[i];
if (el->tag == 8) {
flush_line();
float_start_idx = i;
float_start_y = cur_line_y;
continue;
} else if (el->tag == 9) {
if (float_start_idx != -1) {
flush_line();
float_right_bottom_y = cur_line_y;
int float_offset = win_w - SCROLL_BAR_W - 320 - 10;
elements[float_start_idx].x = float_offset > 0 ? float_offset + 10 : 10;
elements[float_start_idx].y = float_start_y;
elements[float_start_idx].w = 320;
elements[float_start_idx].h = cur_line_y - float_start_y;
if (float_offset > 0) {
for (int j = float_start_idx; j < i; j++) {
elements[j].x += float_offset;
}
}
cur_line_y = float_start_y;
cur_line_x = 10;
float_start_idx = -1;
}
continue;
}
if (el->tag == TAG_BR) { if (el->tag == TAG_BR) {
flush_line(); flush_line();
cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20); cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20);
@@ -562,18 +715,24 @@ static void browser_reflow(void) {
if (el->tag == TAG_HR) { if (el->tag == TAG_HR) {
flush_line(); flush_line();
el->w = win_w - SCROLL_BAR_W - 40 - (el->blockquote_depth * 40); el->w = win_w - SCROLL_BAR_W - 40 - (el->blockquote_depth * 40);
if (float_start_idx != -1) el->w = 300 - 20;
else if (cur_line_y < float_right_bottom_y) el->w = win_w - 320 - SCROLL_BAR_W - 20;
line_elements[line_element_count++] = i; line_elements[line_element_count++] = i;
flush_line(); flush_line();
cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20); cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20);
continue; continue;
} }
int max_x = win_w - SCROLL_BAR_W - 20 - (el->blockquote_depth * 40);
if (float_start_idx != -1) max_x = 310;
else if (cur_line_y < float_right_bottom_y) max_x = win_w - 320 - SCROLL_BAR_W - 20;
if (el->tag == TAG_NONE && el->content[0] == ' ' && el->content[1] == 0) { if (el->tag == TAG_NONE && el->content[0] == ' ' && el->content[1] == 0) {
if (line_element_count == 0) continue; if (line_element_count == 0) continue;
if (cur_line_x + el->w > win_w - SCROLL_BAR_W - 20 - (el->blockquote_depth * 40)) continue; if (cur_line_x + el->w > max_x) continue;
} }
if (cur_line_x + el->w > win_w - SCROLL_BAR_W - 20 - (el->blockquote_depth * 40)) { if (cur_line_x + el->w > max_x) {
flush_line(); flush_line();
cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20); cur_line_x = 10 + (el->list_depth * 20) + (el->blockquote_depth * 20);
} }
@@ -714,7 +873,7 @@ static void parse_html(const char *html) {
browser_clear(); browser_clear();
list_depth = 0; list_depth = 0;
cur_line_y = 10; cur_line_x = 10; line_element_count = 0; cur_line_y = 10; cur_line_x = 10; line_element_count = 0;
int i = 0; int center_depth = 0; int table_depth = 0; int blockquote_depth = 0; bool is_bold = false; bool is_italic = false; bool is_underline = false; int i = 0; int center_depth = 0; int table_depth = 0; int table_float_depth = 0; int blockquote_depth = 0; bool is_bold = false; bool is_italic = false; bool is_underline = false;
uint32_t current_color = COLOR_TEXT; uint32_t current_color = COLOR_TEXT;
char current_link[256] = ""; char current_link[256] = "";
float current_scale = 15.0f; float base_scale = 15.0f; float current_scale = 15.0f; float base_scale = 15.0f;
@@ -758,7 +917,14 @@ static void parse_html(const char *html) {
if (tag_name[0] == '/') { if (tag_name[0] == '/') {
if (str_iequals(tag_name+1, "center")) { emit_br(); if (center_depth > 0) center_depth--; } if (str_iequals(tag_name+1, "center")) { emit_br(); if (center_depth > 0) center_depth--; }
else if (str_iequals(tag_name+1, "table")) { emit_br(); if (table_depth > 0) table_depth--; table_col = 0; } else if (str_iequals(tag_name+1, "table")) {
emit_br();
if (table_depth > 0 && table_depth == table_float_depth) {
table_float_depth = 0;
if (element_count < MAX_ELEMENTS) { RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement)); el->tag = 9; }
}
if (table_depth > 0) table_depth--; table_col = 0;
}
else if (str_iequals(tag_name+1, "tr")) { emit_br(); table_col = 0; } else if (str_iequals(tag_name+1, "tr")) { emit_br(); table_col = 0; }
else if (str_iequals(tag_name+1, "td") || str_iequals(tag_name+1, "th")) { else if (str_iequals(tag_name+1, "td") || str_iequals(tag_name+1, "th")) {
table_col++; table_col++;
@@ -796,7 +962,7 @@ static void parse_html(const char *html) {
current_form_id = 0; current_form_action[0] = 0; current_form_id = 0; current_form_action[0] = 0;
} }
else if (str_iequals(tag_name+1, "a")) current_link[0] = 0; else if (str_iequals(tag_name+1, "a")) current_link[0] = 0;
else if (str_iequals(tag_name+1, "p") || str_iequals(tag_name+1, "li") || str_iequals(tag_name+1, "div") || str_iequals(tag_name+1, "address")) emit_br(); else if (str_iequals(tag_name+1, "p") || str_iequals(tag_name+1, "li") || str_iequals(tag_name+1, "div") || str_iequals(tag_name+1, "address")) { emit_br(); }
else if (str_iequals(tag_name+1, "pre") || str_iequals(tag_name+1, "xmp") || str_iequals(tag_name+1, "listing")) { emit_br(); is_pre = false; } else if (str_iequals(tag_name+1, "pre") || str_iequals(tag_name+1, "xmp") || str_iequals(tag_name+1, "listing")) { emit_br(); is_pre = false; }
else if (str_iequals(tag_name+1, "font") || str_iequals(tag_name+1, "tt") || str_iequals(tag_name+1, "code") || str_iequals(tag_name+1, "samp") || str_iequals(tag_name+1, "kbd")) { else if (str_iequals(tag_name+1, "font") || str_iequals(tag_name+1, "tt") || str_iequals(tag_name+1, "code") || str_iequals(tag_name+1, "samp") || str_iequals(tag_name+1, "kbd")) {
if (font_ptr > 0) { if (font_ptr > 0) {
@@ -816,7 +982,17 @@ static void parse_html(const char *html) {
} }
} else { } else {
if (str_iequals(tag_name, "center")) { emit_br(); center_depth++; } if (str_iequals(tag_name, "center")) { emit_br(); center_depth++; }
else if (str_iequals(tag_name, "table")) { emit_br(); table_depth++; table_col = 0; } else if (str_iequals(tag_name, "table")) {
emit_br(); table_depth++; table_col = 0;
if (str_istrstr(attr_buf, "align=\"right\"") && table_float_depth == 0) {
table_float_depth = table_depth;
if (element_count < MAX_ELEMENTS) {
RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement)); el->tag = 8;
char *bg_str = str_istrstr(attr_buf, "bgcolor=\"");
if (bg_str) el->color = parse_html_color(bg_str + 9);
}
}
}
else if (str_iequals(tag_name, "tr")) { emit_br(); table_col = 0; } else if (str_iequals(tag_name, "tr")) { emit_br(); table_col = 0; }
else if (str_iequals(tag_name, "td") || str_iequals(tag_name, "th")) { else if (str_iequals(tag_name, "td") || str_iequals(tag_name, "th")) {
if (table_col > 0) { if (table_col > 0) {
@@ -920,7 +1096,9 @@ static void parse_html(const char *html) {
} }
} }
else if (str_iequals(tag_name, "br")) emit_br(); else if (str_iequals(tag_name, "br")) emit_br();
else if (str_iequals(tag_name, "p") || str_iequals(tag_name, "div")) emit_br(); else if (str_iequals(tag_name, "p") || str_iequals(tag_name, "div")) {
emit_br();
}
else if (str_iequals(tag_name, "pre")) { emit_br(); is_pre = true; current_scale = 14.0f; } else if (str_iequals(tag_name, "pre")) { emit_br(); is_pre = true; current_scale = 14.0f; }
else if (str_iequals(tag_name, "li")) { else if (str_iequals(tag_name, "li")) {
emit_br(); emit_br();
@@ -983,6 +1161,8 @@ static void parse_html(const char *html) {
RenderElement *el = &elements[element_count++]; RenderElement *el = &elements[element_count++];
memset(el, 0, sizeof(RenderElement)); memset(el, 0, sizeof(RenderElement));
el->tag = TAG_IMG; el->w = 100; el->h = 80; el->centered = EFF_CENTER; el->tag = TAG_IMG; el->w = 100; el->h = 80; el->centered = EFF_CENTER;
char *width_str = str_istrstr(attr_buf, "width=\"");
if (width_str) { int w = atoi(width_str + 7); if (w > 0) el->attr_w = w; }
char *src = str_istrstr(attr_buf, "src=\""); char *src = str_istrstr(attr_buf, "src=\"");
if (src) { if (src) {
src += 5; int l = 0; src += 5; int l = 0;
@@ -995,6 +1175,8 @@ static void parse_html(const char *html) {
RenderElement *el = &elements[element_count++]; RenderElement *el = &elements[element_count++];
memset(el, 0, sizeof(RenderElement)); memset(el, 0, sizeof(RenderElement));
el->tag = TAG_INPUT; el->w = 160; el->h = 20; el->centered = EFF_CENTER; el->tag = TAG_INPUT; el->w = 160; el->h = 20; el->centered = EFF_CENTER;
char *size_str = str_istrstr(attr_buf, "size=\"");
if (size_str) { int sz = atoi(size_str + 6); if (sz > 0) el->w = sz * 8; }
char *val = str_istrstr(attr_buf, "value=\""); char *val = str_istrstr(attr_buf, "value=\"");
char *ph = str_istrstr(attr_buf, "placeholder=\""); char *ph = str_istrstr(attr_buf, "placeholder=\"");
char *type = str_istrstr(attr_buf, "type=\""); char *type = str_istrstr(attr_buf, "type=\"");
@@ -1145,7 +1327,7 @@ static void parse_html_incremental(const char *html, int safe_len) {
browser_clear(); browser_clear();
list_depth = 0; list_depth = 0;
cur_line_y = 10; cur_line_x = 10; line_element_count = 0; cur_line_y = 10; cur_line_x = 10; line_element_count = 0;
inc_center_depth = 0; inc_table_depth = 0; inc_blockquote_depth = 0; inc_center_depth = 0; inc_table_depth = 0; inc_table_float_depth = 0; inc_blockquote_depth = 0;
inc_is_bold = false; inc_is_italic = false; inc_is_underline = false; inc_is_bold = false; inc_is_italic = false; inc_is_underline = false;
inc_current_color = COLOR_TEXT; inc_current_link[0] = 0; inc_current_color = COLOR_TEXT; inc_current_link[0] = 0;
inc_current_scale = 15.0f; inc_base_scale = 15.0f; inc_current_scale = 15.0f; inc_base_scale = 15.0f;
@@ -1162,6 +1344,7 @@ static void parse_html_incremental(const char *html, int safe_len) {
int i = inc_parse_offset; int i = inc_parse_offset;
int center_depth = inc_center_depth; int center_depth = inc_center_depth;
int table_depth = inc_table_depth; int table_depth = inc_table_depth;
int table_float_depth = inc_table_float_depth;
int blockquote_depth = inc_blockquote_depth; int blockquote_depth = inc_blockquote_depth;
bool is_bold = inc_is_bold; bool is_bold = inc_is_bold;
bool is_italic = inc_is_italic; bool is_italic = inc_is_italic;
@@ -1207,7 +1390,14 @@ static void parse_html_incremental(const char *html, int safe_len) {
if (tag_name[0] == '/') { if (tag_name[0] == '/') {
if (str_iequals(tag_name+1, "center")) { emit_br(); if (center_depth > 0) center_depth--; } if (str_iequals(tag_name+1, "center")) { emit_br(); if (center_depth > 0) center_depth--; }
else if (str_iequals(tag_name+1, "table")) { emit_br(); if (table_depth > 0) table_depth--; table_col = 0; } else if (str_iequals(tag_name+1, "table")) {
emit_br();
if (table_depth > 0 && table_depth == table_float_depth) {
table_float_depth = 0;
if (element_count < MAX_ELEMENTS) { RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement)); el->tag = 9; }
}
if (table_depth > 0) table_depth--; table_col = 0;
}
else if (str_iequals(tag_name+1, "tr")) { emit_br(); table_col = 0; } else if (str_iequals(tag_name+1, "tr")) { emit_br(); table_col = 0; }
else if (str_iequals(tag_name+1, "td") || str_iequals(tag_name+1, "th")) { else if (str_iequals(tag_name+1, "td") || str_iequals(tag_name+1, "th")) {
table_col++; table_col++;
@@ -1235,7 +1425,7 @@ static void parse_html_incremental(const char *html, int safe_len) {
else if (tag_name[1] == 'h' && tag_name[2] >= '1' && tag_name[2] <= '6') { emit_br(); emit_br(); is_bold = false; is_italic = false; is_underline = false; base_scale = 15.0f; current_scale = 15.0f; } else if (tag_name[1] == 'h' && tag_name[2] >= '1' && tag_name[2] <= '6') { emit_br(); emit_br(); is_bold = false; is_italic = false; is_underline = false; base_scale = 15.0f; current_scale = 15.0f; }
else if (str_iequals(tag_name+1, "form")) { emit_br(); current_form_id = 0; current_form_action[0] = 0; } else if (str_iequals(tag_name+1, "form")) { emit_br(); current_form_id = 0; current_form_action[0] = 0; }
else if (str_iequals(tag_name+1, "a")) current_link[0] = 0; else if (str_iequals(tag_name+1, "a")) current_link[0] = 0;
else if (str_iequals(tag_name+1, "p") || str_iequals(tag_name+1, "li") || str_iequals(tag_name+1, "div")) emit_br(); else if (str_iequals(tag_name+1, "p") || str_iequals(tag_name+1, "li") || str_iequals(tag_name+1, "div")) { emit_br(); }
else if (str_iequals(tag_name+1, "pre") || str_iequals(tag_name+1, "xmp") || str_iequals(tag_name+1, "listing")) { emit_br(); is_pre = false; } else if (str_iequals(tag_name+1, "pre") || str_iequals(tag_name+1, "xmp") || str_iequals(tag_name+1, "listing")) { emit_br(); is_pre = false; }
else if (str_iequals(tag_name+1, "font") || str_iequals(tag_name+1, "tt") || str_iequals(tag_name+1, "code") || str_iequals(tag_name+1, "samp") || str_iequals(tag_name+1, "kbd")) { else if (str_iequals(tag_name+1, "font") || str_iequals(tag_name+1, "tt") || str_iequals(tag_name+1, "code") || str_iequals(tag_name+1, "samp") || str_iequals(tag_name+1, "kbd")) {
if (inc_font_ptr > 0) { if (inc_font_ptr > 0) {
@@ -1255,7 +1445,17 @@ static void parse_html_incremental(const char *html, int safe_len) {
} }
} else { } else {
if (str_iequals(tag_name, "center")) { emit_br(); center_depth++; } if (str_iequals(tag_name, "center")) { emit_br(); center_depth++; }
else if (str_iequals(tag_name, "table")) { emit_br(); table_depth++; table_col = 0; } else if (str_iequals(tag_name, "table")) {
emit_br(); table_depth++; table_col = 0;
if (str_istrstr(attr_buf, "align=\"right\"") && table_float_depth == 0) {
table_float_depth = table_depth;
if (element_count < MAX_ELEMENTS) {
RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement)); el->tag = 8;
char *bg_str = str_istrstr(attr_buf, "bgcolor=\"");
if (bg_str) el->color = parse_html_color(bg_str + 9);
}
}
}
else if (str_iequals(tag_name, "tr")) { emit_br(); table_col = 0; } else if (str_iequals(tag_name, "tr")) { emit_br(); table_col = 0; }
else if (str_iequals(tag_name, "td") || str_iequals(tag_name, "th")) { else if (str_iequals(tag_name, "td") || str_iequals(tag_name, "th")) {
if (table_col > 0) { if (table_col > 0) {
@@ -1314,10 +1514,13 @@ static void parse_html_incremental(const char *html, int safe_len) {
} }
} }
else if (str_iequals(tag_name, "br")) emit_br(); else if (str_iequals(tag_name, "br")) emit_br();
else if (str_iequals(tag_name, "p") || str_iequals(tag_name, "div")) emit_br(); else if (str_iequals(tag_name, "p") || str_iequals(tag_name, "div")) {
emit_br();
}
else if (str_iequals(tag_name, "pre")) { emit_br(); is_pre = true; current_scale = 14.0f; } else if (str_iequals(tag_name, "pre")) { emit_br(); is_pre = true; current_scale = 14.0f; }
else if (str_iequals(tag_name, "li")) { else if (str_iequals(tag_name, "li")) {
emit_br(); RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement)); emit_br();
RenderElement *el = &elements[element_count++]; memset(el, 0, sizeof(RenderElement));
el->tag = TAG_NONE; if (list_depth > 0 && list_type[list_depth - 1] == 1) { char num[16]; itoa(list_index[list_depth - 1]++, num); int l=0; while(num[l]) { el->content[l] = num[l]; l++; } el->content[l++] = '.'; el->content[l++] = ' '; el->content[l] = 0; } el->tag = TAG_NONE; if (list_depth > 0 && list_type[list_depth - 1] == 1) { char num[16]; itoa(list_index[list_depth - 1]++, num); int l=0; while(num[l]) { el->content[l] = num[l]; l++; } el->content[l++] = '.'; el->content[l++] = ' '; el->content[l] = 0; }
else if (list_depth > 0 && list_type[list_depth - 1] == 2) { el->content[0] = ' '; el->content[1] = 0; } else { el->content[0] = (char)130; el->content[1] = ' '; el->content[2] = 0; } else if (list_depth > 0 && list_type[list_depth - 1] == 2) { el->content[0] = ' '; el->content[1] = 0; } else { el->content[0] = (char)130; el->content[1] = ' '; el->content[2] = 0; }
el->w = ui_get_string_width_scaled(el->content, current_scale); el->h = ui_get_font_height_scaled(current_scale); el->color = current_color; el->centered = EFF_CENTER; el->bold = is_bold; el->scale = current_scale; el->list_depth = list_depth; el->blockquote_depth = blockquote_depth; el->w = ui_get_string_width_scaled(el->content, current_scale); el->h = ui_get_font_height_scaled(current_scale); el->color = current_color; el->centered = EFF_CENTER; el->bold = is_bold; el->scale = current_scale; el->list_depth = list_depth; el->blockquote_depth = blockquote_depth;
@@ -1330,11 +1533,13 @@ static void parse_html_incremental(const char *html, int safe_len) {
else if (str_iequals(tag_name, "hr")) { emit_br(); RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_HR; el->list_depth = list_depth; el->blockquote_depth = blockquote_depth; el->h = 10; el->centered = true; emit_br(); } else if (str_iequals(tag_name, "hr")) { emit_br(); RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_HR; el->list_depth = list_depth; el->blockquote_depth = blockquote_depth; el->h = 10; el->centered = true; emit_br(); }
else if (str_iequals(tag_name, "img")) { else if (str_iequals(tag_name, "img")) {
RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_IMG; el->w = 100; el->h = 80; el->centered = EFF_CENTER; RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_IMG; el->w = 100; el->h = 80; el->centered = EFF_CENTER;
char *width_str = str_istrstr(attr_buf, "width=\""); if (width_str) { int w = atoi(width_str + 7); if (w > 0) el->attr_w = w; }
char *src = str_istrstr(attr_buf, "src=\""); if (src) { src += 5; int l = 0; while(src[l] && src[l] != '"' && l < 255) { el->attr_value[l] = src[l]; l++; } el->attr_value[l] = 0; el->img_loading = true; } char *src = str_istrstr(attr_buf, "src=\""); if (src) { src += 5; int l = 0; while(src[l] && src[l] != '"' && l < 255) { el->attr_value[l] = src[l]; l++; } el->attr_value[l] = 0; el->img_loading = true; }
el->blockquote_depth = blockquote_depth; el->blockquote_depth = blockquote_depth;
} }
else if (str_iequals(tag_name, "input")) { else if (str_iequals(tag_name, "input")) {
RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_INPUT; el->w = 160; el->h = 20; el->centered = EFF_CENTER; RenderElement *el = &elements[element_count++]; for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0; el->tag = TAG_INPUT; el->w = 160; el->h = 20; el->centered = EFF_CENTER;
char *size_str = str_istrstr(attr_buf, "size=\""); if (size_str) { int sz = atoi(size_str + 6); if (sz > 0) el->w = sz * 8; }
char *val = str_istrstr(attr_buf, "value=\""); char *ph = str_istrstr(attr_buf, "placeholder=\""); char *type = str_istrstr(attr_buf, "type=\""); char *name = str_istrstr(attr_buf, "name=\""); char *val = str_istrstr(attr_buf, "value=\""); char *ph = str_istrstr(attr_buf, "placeholder=\""); char *type = str_istrstr(attr_buf, "type=\""); char *name = str_istrstr(attr_buf, "name=\"");
el->form_id = current_form_id; el->input_cursor = 0; el->input_scroll = 0; int l; l = 0; while(current_form_action[l]) { el->form_action[l] = current_form_action[l]; l++; } el->form_action[l] = 0; el->form_id = current_form_id; el->input_cursor = 0; el->input_scroll = 0; int l; l = 0; while(current_form_action[l]) { el->form_action[l] = current_form_action[l]; l++; } el->form_action[l] = 0;
if (name) { name += 6; l = 0; while(name[l] && name[l] != '"' && l < 63) { el->input_name[l] = name[l]; l++; } el->input_name[l] = 0; } else { l = 0; const char *dn = "q"; while(dn[l]) { el->input_name[l] = dn[l]; l++; } el->input_name[l] = 0; } if (name) { name += 6; l = 0; while(name[l] && name[l] != '"' && l < 63) { el->input_name[l] = name[l]; l++; } el->input_name[l] = 0; } else { l = 0; const char *dn = "q"; while(dn[l]) { el->input_name[l] = dn[l]; l++; } el->input_name[l] = 0; }
@@ -1420,7 +1625,7 @@ static void parse_html_incremental(const char *html, int safe_len) {
} }
emit_br(); emit_br();
inc_parse_offset = i; inc_center_depth = center_depth; inc_table_depth = table_depth; inc_blockquote_depth = blockquote_depth; inc_parse_offset = i; inc_center_depth = center_depth; inc_table_depth = table_depth; inc_table_float_depth = table_float_depth; inc_blockquote_depth = blockquote_depth;
inc_is_bold = is_bold; inc_is_italic = is_italic; inc_is_underline = is_underline; inc_current_color = current_color; inc_is_bold = is_bold; inc_is_italic = is_italic; inc_is_underline = is_underline; inc_current_color = current_color;
{ int k=0; while(current_link[k]) { inc_current_link[k] = current_link[k]; k++; } inc_current_link[k] = 0; } { int k=0; while(current_link[k]) { inc_current_link[k] = current_link[k]; k++; } inc_current_link[k] = 0; }
inc_current_scale = current_scale; inc_base_scale = base_scale; inc_is_space_pending = is_space_pending; inc_current_scale = current_scale; inc_base_scale = base_scale; inc_is_space_pending = is_space_pending;
@@ -1431,65 +1636,66 @@ static void parse_html_incremental(const char *html, int safe_len) {
} }
static void browser_paint(void) { static void browser_paint(void) {
browser_ctx.user_data = (void *)win_browser;
ui_draw_rect(win_browser, 0, 0, win_w, win_h, COLOR_BG); ui_draw_rect(win_browser, 0, 0, win_w, win_h, COLOR_BG);
for (int i = 0; i < element_count; i++) { for (int i = 0; i < element_count; i++) {
RenderElement *el = &elements[i]; RenderElement *el = &elements[i];
int draw_y = el->y - scroll_y + URL_BAR_H; int draw_y = el->y - scroll_y + URL_BAR_H;
if (draw_y < URL_BAR_H - 400 || draw_y > win_h) continue; int el_h = el->h;
if (el->tag == TAG_IMG && el->img_h > el_h) el_h = el->img_h;
if (draw_y + el_h < URL_BAR_H || draw_y > win_h) continue;
if (el->tag == 8) {
if (el->color != 0 && el->color != COLOR_BG) {
ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h + 10, el->color);
}
continue;
}
if (el->tag == TAG_IMG) { if (el->tag == TAG_IMG) {
uint32_t *pixels = el->img_pixels; uint32_t *pixels = el->img_pixels;
if (el->img_frames) pixels = el->img_frames[el->img_current_frame]; if (el->img_frames) pixels = el->img_frames[el->img_current_frame];
if (pixels) ui_draw_image(win_browser, el->x, draw_y, el->img_w, el->img_h, pixels); if (pixels) ui_draw_image(win_browser, el->x, draw_y, el->img_w, el->img_h, pixels);
else ui_draw_rect(win_browser, el->x, draw_y, 100, 80, 0xFFCCCCCC); else ui_draw_rect(win_browser, el->x, draw_y, 100, 80, 0xFFCCCCCC);
} else if (el->tag == TAG_INPUT) { } else if (el->tag == TAG_INPUT) {
ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFFFFFFFF); browser_ctx.use_light_theme = true;
uint32_t border = (focused_element == i) ? 0xFF0000FF : 0xFF808080; char visible[128];
ui_draw_rect(win_browser, el->x, draw_y, el->w, 1, border);
ui_draw_rect(win_browser, el->x, draw_y + el->h - 1, el->w, 1, border);
ui_draw_rect(win_browser, el->x, draw_y, 1, el->h, border);
ui_draw_rect(win_browser, el->x + el->w - 1, draw_y, 1, el->h, border);
char visible[64];
int v_len = 0; int v_len = 0;
int max_v = (el->w - 10) / 8; int max_v = (el->w - 10) / 8;
if (max_v > 63) max_v = 63; if (max_v > 127) max_v = 127;
for (int k = el->input_scroll; el->attr_value[k] && v_len < max_v; k++) { for (int k = el->input_scroll; el->attr_value[k] && v_len < max_v; k++) {
visible[v_len++] = el->attr_value[k]; visible[v_len++] = el->attr_value[k];
} }
visible[v_len] = 0; visible[v_len] = 0;
ui_draw_string(win_browser, el->x + 5, draw_y + 2, visible, (focused_element == i) ? 0xFF000000 : 0xFF808080);
if (focused_element == i) { widget_textbox_t tb;
int cursor_pos = el->input_cursor - el->input_scroll; widget_textbox_init(&tb, el->x, draw_y, el->w, el->h, visible, max_v);
if (cursor_pos >= 0 && cursor_pos < max_v) { tb.cursor_pos = el->input_cursor - el->input_scroll;
char sub[64]; if (tb.cursor_pos < 0) tb.cursor_pos = 0;
int k; tb.focused = (focused_element == i);
for (k = 0; k < cursor_pos && visible[k]; k++) sub[k] = visible[k]; widget_textbox_draw(&browser_ctx, &tb);
sub[k] = 0; browser_ctx.use_light_theme = false;
int cx = ui_get_string_width(sub);
ui_draw_rect(win_browser, el->x + 5 + cx, draw_y + 16, 8, 2, 0xFF000000);
}
}
} else if (el->tag == TAG_BUTTON) { } else if (el->tag == TAG_BUTTON) {
ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFFDDDDDD); browser_ctx.use_light_theme = true;
ui_draw_rect(win_browser, el->x, draw_y, el->w, 1, 0xFFFFFFFF); widget_button_t btn;
ui_draw_rect(win_browser, el->x, draw_y + el->h - 1, el->w, 1, 0xFF888888); widget_button_init(&btn, el->x, draw_y, el->w, el->h, el->attr_value);
ui_draw_rect(win_browser, el->x, draw_y, 1, el->h, 0xFFFFFFFF); widget_button_draw(&browser_ctx, &btn);
ui_draw_rect(win_browser, el->x + el->w - 1, draw_y, 1, el->h, 0xFF888888); browser_ctx.use_light_theme = false;
ui_draw_string(win_browser, el->x + 10, draw_y + 4, el->attr_value, 0xFF000000);
} else if (el->tag == TAG_RADIO) { } else if (el->tag == TAG_RADIO) {
ui_draw_rounded_rect_filled(win_browser, el->x, draw_y, el->w, el->h, el->w/2, 0xFF808080); browser_ctx.use_light_theme = true;
ui_draw_rounded_rect_filled(win_browser, el->x + 1, draw_y + 1, el->w - 2, el->h - 2, (el->w-2)/2, 0xFFFFFFFF); widget_checkbox_t cb;
if (el->checked) { widget_checkbox_init(&cb, el->x, draw_y, el->w, el->h, "", true);
ui_draw_rounded_rect_filled(win_browser, el->x + 4, draw_y + 4, el->w - 8, el->h - 8, (el->w-8)/2, 0xFF000000); cb.checked = el->checked;
} widget_checkbox_draw(&browser_ctx, &cb);
browser_ctx.use_light_theme = false;
} else if (el->tag == TAG_CHECKBOX) { } else if (el->tag == TAG_CHECKBOX) {
ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFF808080); browser_ctx.use_light_theme = true;
ui_draw_rect(win_browser, el->x + 1, draw_y + 1, el->w - 2, el->h - 2, 0xFFFFFFFF); widget_checkbox_t cb;
if (el->checked) { widget_checkbox_init(&cb, el->x, draw_y, el->w, el->h, "", false);
ui_draw_rect(win_browser, el->x + 4, draw_y + 4, el->w - 8, el->h - 8, 0xFF000000); cb.checked = el->checked;
} widget_checkbox_draw(&browser_ctx, &cb);
browser_ctx.use_light_theme = false;
} else if (el->tag == TAG_HR) { } else if (el->tag == TAG_HR) {
ui_draw_rect(win_browser, el->x, draw_y + el->h / 2, el->w, 2, 0xFF888888); ui_draw_rect(win_browser, el->x, draw_y + el->h / 2, el->w, 2, 0xFF888888);
ui_draw_rect(win_browser, el->x, draw_y + (el->h / 2) + 2, el->w, 1, 0xFFFFFFFF); ui_draw_rect(win_browser, el->x, draw_y + (el->h / 2) + 2, el->w, 1, 0xFFFFFFFF);
@@ -1504,47 +1710,37 @@ static void browser_paint(void) {
ui_draw_string_scaled(win_browser, el->x + 1, draw_y - 1, el->content, el->color, el->scale); ui_draw_string_scaled(win_browser, el->x + 1, draw_y - 1, el->content, el->color, el->scale);
} }
if (el->underline) { if (el->underline) {
int fh = ui_get_font_height_scaled(el->scale); int fh = el->h;
ui_draw_rect(win_browser, el->x, draw_y + fh - 1, el->w, 1, el->color); ui_draw_rect(win_browser, el->x, draw_y + fh - 1, el->w, 1, el->color);
} }
} }
} }
ui_draw_rect(win_browser, 0, 0, win_w, URL_BAR_H, COLOR_URL_BAR); ui_draw_rect(win_browser, 0, 0, win_w, URL_BAR_H, COLOR_URL_BAR);
ui_draw_string(win_browser, 10, 8, url_input_buffer, COLOR_URL_TEXT);
if (focused_element == -1) { widget_textbox_init(&url_tb, 10, 5, win_w - SCROLL_BAR_W - BTN_W*2 - BTN_PAD*2 - 20, 20, url_input_buffer, 511);
char sub[512]; url_tb.cursor_pos = url_cursor;
int k; url_tb.focused = (focused_element == -1);
for (k = 0; k < url_cursor && url_input_buffer[k]; k++) sub[k] = url_input_buffer[k]; widget_textbox_draw(&browser_ctx, &url_tb);
sub[k] = 0;
int cx = ui_get_string_width(sub);
ui_draw_rect(win_browser, 10 + cx, 22, 8, 2, COLOR_URL_TEXT);
}
// Back button // Back button
int btn_y = (URL_BAR_H - BTN_H) / 2; int btn_y = (URL_BAR_H - BTN_H) / 2;
uint32_t back_col = history_count > 0 ? 0xFF505050 : 0xFF404040; widget_button_init(&btn_back, BACK_BTN_X, btn_y, BTN_W, BTN_H, "<");
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, BTN_H, back_col); widget_button_draw(&browser_ctx, &btn_back);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, 1, 0xFF606060);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, 1, BTN_H, 0xFF606060);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020);
ui_draw_rect(win_browser, BACK_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020);
ui_draw_string(win_browser, BACK_BTN_X + 10, btn_y + 4, "<", history_count > 0 ? 0xFFFFFFFF : 0xFF808080);
// Home button // Home button
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, BTN_H, 0xFF505050); widget_button_init(&btn_home, HOME_BTN_X, btn_y, BTN_W, BTN_H, "H");
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, 1, 0xFF606060); widget_button_draw(&browser_ctx, &btn_home);
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, 1, BTN_H, 0xFF606060);
ui_draw_rect(win_browser, HOME_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020);
ui_draw_rect(win_browser, HOME_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020);
ui_draw_string(win_browser, HOME_BTN_X + 10, btn_y + 4, "H", 0xFFFFFFFF);
// Scroll bar // Scroll bar
ui_draw_rect(win_browser, win_w - SCROLL_BAR_W, URL_BAR_H, SCROLL_BAR_W, win_h - URL_BAR_H, COLOR_SCROLL_BG); int viewport_h = win_h - URL_BAR_H;
int thumb_h = (win_h - URL_BAR_H) * (win_h - URL_BAR_H) / (total_content_height > win_h ? total_content_height : win_h); browser_scrollbar.x = win_w - SCROLL_BAR_W;
if (thumb_h < 20) thumb_h = 20; browser_scrollbar.y = URL_BAR_H;
int thumb_y = URL_BAR_H + (scroll_y * (win_h - URL_BAR_H - thumb_h)) / (total_content_height > win_h - URL_BAR_H ? total_content_height - (win_h - URL_BAR_H) : 1); browser_scrollbar.w = SCROLL_BAR_W;
ui_draw_rect(win_browser, win_w - SCROLL_BAR_W + 2, thumb_y, SCROLL_BAR_W - 4, thumb_h, COLOR_SCROLL_BTN); browser_scrollbar.h = viewport_h;
browser_scrollbar.on_scroll = browser_on_scroll;
widget_scrollbar_update(&browser_scrollbar, total_content_height, scroll_y);
widget_scrollbar_draw(&browser_ctx, &browser_scrollbar);
} }
static void navigate(const char *url) { static void navigate(const char *url) {
@@ -1593,34 +1789,53 @@ int main(int argc, char **argv) {
continue; continue;
} }
else if (ev.type == GUI_EVENT_CLICK) { else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_MOUSE_UP || ev.type == GUI_EVENT_MOUSE_MOVE) {
int mx = ev.arg1; int mx = ev.arg1;
if (mx >= win_w - SCROLL_BAR_W) { int my = ev.arg2;
if (ev.arg2 < URL_BAR_H + (win_h - URL_BAR_H)/2) scroll_y -= 100; bool is_down = (ev.type == GUI_EVENT_MOUSE_DOWN || (ev.type == GUI_EVENT_MOUSE_MOVE && browser_scrollbar.is_dragging));
else scroll_y += 100; bool is_click = (ev.type == GUI_EVENT_CLICK);
if (scroll_y < 0) scroll_y = 0;
int old_scroll = scroll_y;
bool was_dragging = browser_scrollbar.is_dragging;
if (widget_scrollbar_handle_mouse(&browser_scrollbar, mx, my, is_down, &browser_ctx)) {
if (scroll_y != old_scroll || browser_scrollbar.is_dragging || was_dragging) {
needs_repaint = true;
}
if (ev.type == GUI_EVENT_MOUSE_MOVE) continue;
if (is_down || is_click) continue;
}
if (ev.type != GUI_EVENT_CLICK && ev.type != GUI_EVENT_MOUSE_DOWN) continue;
if (my < URL_BAR_H) {
if (widget_textbox_handle_mouse(&url_tb, mx, my, is_click, NULL)) {
focused_element = -1;
needs_repaint = true; needs_repaint = true;
continue; continue;
} }
if (ev.arg2 < URL_BAR_H) { if (widget_button_handle_mouse(&btn_back, mx, my, is_down, is_click, NULL)) {
// Check back button if (is_click && history_count > 0) {
if (mx >= BACK_BTN_X && mx < BACK_BTN_X + BTN_W && history_count > 0) {
history_count--; history_count--;
int j=0; while(history_stack[history_count][j]) { url_input_buffer[j] = history_stack[history_count][j]; j++; } url_input_buffer[j] = 0; url_cursor = j; int j=0; while(history_stack[history_count][j]) { url_input_buffer[j] = history_stack[history_count][j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1; navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
}
needs_repaint = true; continue; needs_repaint = true; continue;
} }
// Check home button if (widget_button_handle_mouse(&btn_home, mx, my, is_down, is_click, NULL)) {
if (mx >= HOME_BTN_X && mx < HOME_BTN_X + BTN_W) { if (is_click) {
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; } if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
const char *home = "http://find.boreddev.nl"; const char *home = "http://find.boreddev.nl";
int j=0; while(home[j]) { url_input_buffer[j] = home[j]; j++; } url_input_buffer[j] = 0; url_cursor = j; int j=0; while(home[j]) { url_input_buffer[j] = home[j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1; navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
}
needs_repaint = true; continue; needs_repaint = true; continue;
} }
focused_element = -1; needs_repaint = true; continue; if (is_click) {
focused_element = -1; needs_repaint = true;
} }
int my = ev.arg2 - URL_BAR_H + scroll_y; continue;
}
my = ev.arg2 - URL_BAR_H + scroll_y;
bool found = false; bool found = false;
for (int i = 0; i < element_count; i++) { for (int i = 0; i < element_count; i++) {
RenderElement *el = &elements[i]; RenderElement *el = &elements[i];
@@ -1637,15 +1852,38 @@ int main(int argc, char **argv) {
if (el->tag == TAG_RADIO) { if (el->tag == TAG_RADIO) {
for (int k = 0; k < element_count; k++) { for (int k = 0; k < element_count; k++) {
if (elements[k].tag == TAG_RADIO && elements[k].form_id == el->form_id && str_iequals(elements[k].input_name, el->input_name)) { if (elements[k].tag == TAG_RADIO && elements[k].form_id == el->form_id && str_iequals(elements[k].input_name, el->input_name)) {
if (elements[k].checked) {
elements[k].checked = false; elements[k].checked = false;
widget_checkbox_t cb;
widget_checkbox_init(&cb, elements[k].x, elements[k].y - scroll_y + URL_BAR_H, elements[k].w, elements[k].h, "", true);
cb.checked = false;
browser_ctx.use_light_theme = true;
widget_checkbox_draw(&browser_ctx, &cb);
browser_ctx.use_light_theme = false;
ui_mark_dirty(win_browser, cb.x, cb.y, cb.w, cb.h);
}
} }
} }
el->checked = true; el->checked = true;
needs_repaint = true; found = true; break; widget_checkbox_t cb;
widget_checkbox_init(&cb, el->x, el->y - scroll_y + URL_BAR_H, el->w, el->h, "", true);
cb.checked = true;
browser_ctx.use_light_theme = true;
widget_checkbox_draw(&browser_ctx, &cb);
browser_ctx.use_light_theme = false;
ui_mark_dirty(win_browser, cb.x, cb.y, cb.w, cb.h);
found = true; break;
} }
if (el->tag == TAG_CHECKBOX) { if (el->tag == TAG_CHECKBOX) {
el->checked = !el->checked; el->checked = !el->checked;
needs_repaint = true; found = true; break; widget_checkbox_t cb;
widget_checkbox_init(&cb, el->x, el->y - scroll_y + URL_BAR_H, el->w, el->h, "", false);
cb.checked = el->checked;
browser_ctx.use_light_theme = true;
widget_checkbox_draw(&browser_ctx, &cb);
browser_ctx.use_light_theme = false;
ui_mark_dirty(win_browser, cb.x, cb.y, cb.w, cb.h);
found = true; break;
} }
if (el->tag == TAG_BUTTON) { if (el->tag == TAG_BUTTON) {
int fid = el->form_id; int fid = el->form_id;
@@ -1759,6 +1997,7 @@ int main(int argc, char **argv) {
if (c == 13 || c == 10) { if (c == 13 || c == 10) {
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; } if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
navigate(url_input_buffer); scroll_y = 0; navigate(url_input_buffer); scroll_y = 0;
needs_repaint = true;
} }
else if (c == 19) { if (url_cursor > 0) url_cursor--; } else if (c == 19) { if (url_cursor > 0) url_cursor--; }
else if (c == 20) { int len = 0; while(url_input_buffer[len]) len++; if (url_cursor < len) url_cursor++; } else if (c == 20) { int len = 0; while(url_input_buffer[len]) len++; if (url_cursor < len) url_cursor++; }

View File

@@ -3,6 +3,7 @@
// This header needs to maintain in any file it is present in, as per the GPL license terms. // This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "syscall.h" #include "syscall.h"
#include "libui.h" #include "libui.h"
#include "../../wm/libwidget.h"
#include <stdbool.h> #include <stdbool.h>
#include "stdlib.h" #include "stdlib.h"
@@ -25,6 +26,33 @@ static long long calc_decimal_divisor = 10;
static char display_buffer[1024]; static char display_buffer[1024];
static int display_buf_len = 0; static int display_buf_len = 0;
static widget_button_t buttons[20];
static const char *labels[] = {
"C", "sqr", "rt", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "BS", "="
};
static void calc_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) {
ui_draw_rect((ui_window_t)user_data, x, y, w, h, color);
}
static void calc_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) {
ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color);
}
static void calc_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) {
ui_draw_string((ui_window_t)user_data, x, y, str, color);
}
static widget_context_t calc_ctx = {
.user_data = 0,
.draw_rect = calc_draw_rect,
.draw_rounded_rect_filled = calc_draw_rounded_rect_filled,
.draw_string = calc_draw_string,
.mark_dirty = NULL
};
static long long isqrt(long long n) { static long long isqrt(long long n) {
if (n < 0) return -1; if (n < 0) return -1;
if (n == 0) return 0; if (n == 0) return 0;
@@ -97,27 +125,8 @@ static void calculator_paint(void) {
int text_x = w - 15 - text_w; int text_x = w - 15 - text_w;
ui_draw_string(win_calculator, text_x, 18, display_buffer, COLOR_DARK_TEXT); ui_draw_string(win_calculator, text_x, 18, display_buffer, COLOR_DARK_TEXT);
const char *labels[] = {
"C", "sqr", "rt", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "BS", "="
};
int bw = 35;
int bh = 25;
int gap = 5;
int start_x = 10;
int start_y = 40;
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
int r = i / 4; widget_button_draw(&calc_ctx, &buttons[i]);
int c = i % 4;
ui_draw_rounded_rect_filled(win_calculator, start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER);
int label_x = start_x + c*(bw+gap) + 5;
int label_y = start_y + r*(bh+gap) + 9;
ui_draw_string(win_calculator, label_x, label_y, labels[i], COLOR_DARK_TEXT);
} }
} }
@@ -135,28 +144,15 @@ static void do_op(void) {
} }
} }
static void calculator_click(int x, int y) { static void handle_button_click(int idx) {
int bw = 35; const char *labels_map[] = {
int bh = 25;
int gap = 5;
int start_x = 10;
int start_y = 35; // Matches the hitboxes
for (int i = 0; i < 20; i++) {
int r = i / 4;
int c = i % 4;
int bx = start_x + c*(bw+gap);
int by = start_y + r*(bh+gap);
if (x >= bx && x < bx + bw && y >= by && y < by + bh) {
const char *labels[] = {
"C", "s", "r", "/", "C", "s", "r", "/",
"7", "8", "9", "*", "7", "8", "9", "*",
"4", "5", "6", "-", "4", "5", "6", "-",
"1", "2", "3", "+", "1", "2", "3", "+",
"0", ".", "B", "=" "0", ".", "B", "="
}; };
char lbl = labels[i][0]; char lbl = labels_map[idx][0];
if (lbl >= '0' && lbl <= '9') { if (lbl >= '0' && lbl <= '9') {
if (calc_new_entry || calc_error) { if (calc_new_entry || calc_error) {
@@ -222,17 +218,18 @@ static void calculator_click(int x, int y) {
} }
calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false; calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false;
} }
update_display();
calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
return;
}
}
} }
int main(void) { int main(void) {
win_calculator = ui_window_create("Calculator", 200, 200, 180, 230); win_calculator = ui_window_create("Calculator", 200, 200, 180, 230);
calc_ctx.user_data = (void *)win_calculator;
int bw = 35, bh = 25, gap = 5, start_x = 10, start_y = 40;
for (int i = 0; i < 20; i++) {
int r = i / 4;
int c = i % 4;
widget_button_init(&buttons[i], start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, labels[i]);
}
calc_curr = 0; calc_curr = 0;
calc_acc = 0; calc_acc = 0;
@@ -249,8 +246,21 @@ int main(void) {
if (ev.type == GUI_EVENT_PAINT) { if (ev.type == GUI_EVENT_PAINT) {
calculator_paint(); calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230); ui_mark_dirty(win_calculator, 0, 0, 180, 230);
} else if (ev.type == GUI_EVENT_CLICK) { } else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_MOUSE_UP) {
calculator_click(ev.arg1, ev.arg2); bool needs_paint = false;
for (int i=0; i<20; i++) {
if (widget_button_handle_mouse(&buttons[i], ev.arg1, ev.arg2, ev.type == GUI_EVENT_MOUSE_DOWN, ev.type == GUI_EVENT_CLICK, NULL)) {
needs_paint = true;
if (ev.type == GUI_EVENT_CLICK) {
handle_button_click(i);
update_display();
}
}
}
if (needs_paint) {
calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
}
} else if (ev.type == GUI_EVENT_CLOSE) { } else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0); sys_exit(0);
} }

1768
src/userland/gui/grapher.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ static void notepad_ensure_cursor_visible(int h) {
} }
static void notepad_load_state() { static void notepad_load_state() {
int fd = sys_open("A:/tmp/notepad_state.txt", "r"); int fd = sys_open("/tmp/notepad_state.txt", "r");
if (fd >= 0) { if (fd >= 0) {
sys_serial_write("Notepad: Loading state...\n"); sys_serial_write("Notepad: Loading state...\n");
buf_len = sys_read(fd, buffer, NOTEPAD_BUF_SIZE - 1); buf_len = sys_read(fd, buffer, NOTEPAD_BUF_SIZE - 1);
@@ -50,8 +50,8 @@ static void notepad_load_state() {
static void notepad_save_state() { static void notepad_save_state() {
// Ensure dir exists // Ensure dir exists
sys_mkdir("A:/tmp"); sys_mkdir("/tmp");
int fd = sys_open("A:/tmp/notepad_state.txt", "w"); int fd = sys_open("/tmp/notepad_state.txt", "w");
if (fd >= 0) { if (fd >= 0) {
sys_write_fs(fd, buffer, buf_len); sys_write_fs(fd, buffer, buf_len);
sys_close(fd); sys_close(fd);

View File

@@ -15,9 +15,9 @@
#define COLOR_BLACK 0xFF000000 #define COLOR_BLACK 0xFF000000
#define COLOR_WHITE 0xFFFFFFFF #define COLOR_WHITE 0xFFFFFFFF
#define COLOR_RED 0xFFFF0000 #define COLOR_RED 0xFFFF0000
#define COLOR_APPLE_GREEN 0xFF4CD964 #define COLOR_GREEN 0xFF4CD964
#define COLOR_APPLE_BLUE 0xFF007AFF #define COLOR_BLUE 0xFF007AFF
#define COLOR_APPLE_YELLOW 0xFFFFCC00 #define COLOR_YELLOW 0xFFFFCC00
#define COLOR_DARK_BG 0xFF121212 #define COLOR_DARK_BG 0xFF121212
#define COLOR_DARK_PANEL 0xFF202020 #define COLOR_DARK_PANEL 0xFF202020
@@ -28,7 +28,7 @@ static uint32_t *canvas_buffer = NULL;
static uint32_t current_color = COLOR_BLACK; static uint32_t current_color = COLOR_BLACK;
static int last_mx = -1; static int last_mx = -1;
static int last_my = -1; static int last_my = -1;
static char current_file_path[256] = "/Desktop/drawing.pnt"; static char current_file_path[256] = "/root/Desktop/drawing.pnt";
static void paint_strcpy(char *dest, const char *src) { static void paint_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++; while (*src) *dest++ = *src++;
@@ -58,7 +58,7 @@ static void paint_paint(ui_window_t win) {
ui_draw_rounded_rect_filled(win, canvas_x - 2, canvas_y - 2, CANVAS_W + 4, CANVAS_H + 4, 4, COLOR_DARK_BG); ui_draw_rounded_rect_filled(win, canvas_x - 2, canvas_y - 2, CANVAS_W + 4, CANVAS_H + 4, 4, COLOR_DARK_BG);
ui_draw_rounded_rect_filled(win, 10, 0, 40, 230, 6, COLOR_DARK_PANEL); ui_draw_rounded_rect_filled(win, 10, 0, 40, 230, 6, COLOR_DARK_PANEL);
uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_WHITE};
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
int cy = 10 + (i * 25); int cy = 10 + (i * 25);
ui_draw_rounded_rect_filled(win, 15, cy, 30, 20, 3, colors[i]); ui_draw_rounded_rect_filled(win, 15, cy, 30, 20, 3, colors[i]);
@@ -79,12 +79,7 @@ static void paint_paint(ui_window_t win) {
// Draw canvas content // Draw canvas content
if (canvas_buffer) { if (canvas_buffer) {
for (int y = 0; y < CANVAS_H; y++) { ui_draw_image(win, canvas_x, canvas_y, CANVAS_W, CANVAS_H, canvas_buffer);
for (int x = 0; x < CANVAS_W; x++) {
uint32_t color = canvas_buffer[y * CANVAS_W + x];
ui_draw_rect(win, canvas_x + x, canvas_y + y, 1, 1, color);
}
}
} }
} }
@@ -96,7 +91,6 @@ static void paint_put_brush(ui_window_t win, int cx, int cy, int *min_x, int *mi
int py = cy + dy; int py = cy + dy;
if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) { if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) {
canvas_buffer[py * CANVAS_W + px] = current_color; canvas_buffer[py * CANVAS_W + px] = current_color;
ui_draw_rect(win, 60 + px, 0 + py, 1, 1, current_color);
if (px < *min_x) *min_x = px; if (px < *min_x) *min_x = px;
if (py < *min_y) *min_y = py; if (py < *min_y) *min_y = py;
@@ -140,6 +134,7 @@ void paint_handle_mouse(ui_window_t win, int x, int y) {
} }
if (min_x <= max_x && min_y <= max_y) { if (min_x <= max_x && min_y <= max_y) {
ui_draw_image(win, 60, 0, CANVAS_W, CANVAS_H, canvas_buffer);
ui_mark_dirty(win, 60 + min_x, 0 + min_y, (max_x - min_x) + 1, (max_y - min_y) + 1); ui_mark_dirty(win, 60 + min_x, 0 + min_y, (max_x - min_x) + 1, (max_y - min_y) + 1);
} }
@@ -202,7 +197,7 @@ static void paint_click(ui_window_t win, int x, int y) {
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
int cy = 10 + (i * 25); int cy = 10 + (i * 25);
if (y >= cy && y < cy + 20) { if (y >= cy && y < cy + 20) {
uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_WHITE};
current_color = colors[i]; current_color = colors[i];
paint_paint(win); paint_paint(win);
ui_mark_dirty(win, 0, 0, 380, 230); ui_mark_dirty(win, 0, 0, 380, 230);

View File

@@ -9,16 +9,38 @@
#include "stb_image_write.h" #include "stb_image_write.h"
#include <string.h> #include <string.h>
#define GUI_CMD_GET_SCREEN_SIZE 17 #define GUI_CMD_GET_SCREEN_SIZE 50
#define GUI_CMD_GET_SCREENBUFFER 18 #define GUI_CMD_GET_SCREENBUFFER 51
#define GUI_CMD_SHOW_NOTIFICATION 19 #define GUI_CMD_SHOW_NOTIFICATION 52
#define GUI_CMD_GET_DATETIME 20 #define GUI_CMD_GET_DATETIME 53
#define PNG_WRITE_BUF_SIZE (8 * 1024 * 1024) // 8MB buffer to hold entire PNG
static uint8_t *png_output_buf = NULL;
static int png_output_idx = 0;
void png_write_func(void *context, void *data, int size) { void png_write_func(void *context, void *data, int size) {
int fd = *(int*)context; (void)context;
sys_write_fs(fd, data, size); if (!png_output_buf) return;
if (png_output_idx + size < PNG_WRITE_BUF_SIZE) {
memcpy(png_output_buf + png_output_idx, data, size);
png_output_idx += size;
}
} }
// Global filename for helper functions
static char g_filename[128];
void append_num(int num, int digits) {
int len = 0; while (g_filename[len]) len++;
if (digits == 4) {
g_filename[len++] = '0' + (num / 1000) % 10;
g_filename[len++] = '0' + (num / 100) % 10;
}
g_filename[len++] = '0' + (num / 10) % 10;
g_filename[len++] = '0' + (num % 10);
g_filename[len] = '\0';
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
(void)argc; (void)argc;
@@ -29,28 +51,25 @@ int main(int argc, char **argv) {
syscall3(SYS_GUI, GUI_CMD_GET_SCREEN_SIZE, (uint64_t)&w, (uint64_t)&h); syscall3(SYS_GUI, GUI_CMD_GET_SCREEN_SIZE, (uint64_t)&w, (uint64_t)&h);
if (w == 0 || h == 0 || w > 4096 || h > 4096) { if (w == 0 || h == 0 || w > 4096 || h > 4096) {
printf("Failed to get screen size %d x %d\n", (int)w, (int)h);
return 1; return 1;
} }
// 2. Allocate buffer for 0xAARRGGBB // 2. Allocate buffers
uint32_t *pixels = (uint32_t *)malloc(w * h * sizeof(uint32_t)); uint32_t *pixels = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (!pixels) { uint8_t *rgb_pixels = (uint8_t *)malloc(w * h * 3);
printf("Failed to allocate memory for %d x %d pixels\n", (int)w, (int)h); png_output_buf = (uint8_t *)malloc(PNG_WRITE_BUF_SIZE);
if (!pixels || !rgb_pixels || !png_output_buf) {
if (pixels) free(pixels);
if (rgb_pixels) free(rgb_pixels);
if (png_output_buf) free(png_output_buf);
return 1; return 1;
} }
// 3. Request screenbuffer // 3. Request screenbuffer
syscall2(SYS_GUI, GUI_CMD_GET_SCREENBUFFER, (uint64_t)pixels); syscall2(SYS_GUI, GUI_CMD_GET_SCREENBUFFER, (uint64_t)pixels);
// 4. Convert 0xAARRGGBB to RGB for stb_image_write // 4. Convert 0xAARRGGBB to RGB
uint8_t *rgb_pixels = (uint8_t *)malloc(w * h * 3);
if (!rgb_pixels) {
printf("Failed to allocate RGB buffer\n");
free(pixels);
return 1;
}
for (int y = 0; y < (int)h; y++) { for (int y = 0; y < (int)h; y++) {
for (int x = 0; x < (int)w; x++) { for (int x = 0; x < (int)w; x++) {
uint32_t px = pixels[y * w + x]; uint32_t px = pixels[y * w + x];
@@ -61,62 +80,50 @@ int main(int argc, char **argv) {
} }
} }
// 5. Get Datetime for filename // 5. Get Datetime and construct filename
uint64_t dt[6] = {0}; uint64_t dt[6] = {0};
syscall2(SYS_GUI, GUI_CMD_GET_DATETIME, (uint64_t)dt); syscall2(SYS_GUI, GUI_CMD_GET_DATETIME, (uint64_t)dt);
char filename[128] = "A:/Desktop/screenshot-"; strcpy(g_filename, "/root/Desktop/screenshot-");
append_num((int)dt[0], 4); // Year
append_num((int)dt[1], 2); // Month
append_num((int)dt[2], 2); // Day
// Quick helper to append 4-digit and 2-digit numbers int len = strlen(g_filename);
auto void append_num(int num, int digits); g_filename[len++] = '-'; g_filename[len] = '\0';
void append_num(int num, int digits) {
int len = 0; while (filename[len]) len++;
if (digits == 4) {
filename[len++] = '0' + (num / 1000) % 10;
filename[len++] = '0' + (num / 100) % 10;
}
filename[len++] = '0' + (num / 10) % 10;
filename[len++] = '0' + (num % 10);
filename[len] = '\0';
}
append_num((int)dt[0], 4); append_num((int)dt[3], 2); // Hour
append_num((int)dt[1], 2); append_num((int)dt[4], 2); // Min
append_num((int)dt[2], 2); append_num((int)dt[5], 2); // Sec
int len = 0; while (filename[len]) len++; strcat(g_filename, ".png");
filename[len++] = '-'; filename[len] = '\0';
append_num((int)dt[3], 2);
append_num((int)dt[4], 2);
append_num((int)dt[5], 2);
len = 0; while (filename[len]) len++;
filename[len++] = '.'; filename[len++] = 'p'; filename[len++] = 'n'; filename[len++] = 'g'; filename[len] = '\0';
// 6. Write to PNG // 6. Generate PNG in memory
int fd = sys_open(filename, "w"); // Open file png_output_idx = 0;
int res = 0; int res = stbi_write_png_to_func(png_write_func, NULL, (int)w, (int)h, 3, rgb_pixels, (int)w * 3);
// 7. Perform a SINGLE write to the filesystem
if (res && png_output_idx > 0) {
int fd = sys_open(g_filename, "w");
if (fd >= 0) { if (fd >= 0) {
res = stbi_write_png_to_func(png_write_func, &fd, (int)w, (int)h, 3, rgb_pixels, (int)w * 3); sys_write_fs(fd, png_output_buf, png_output_idx);
sys_close(fd); // Close file sys_close(fd);
// Show notification
char notif[256] = "Saved ";
strcat(notif, g_filename + 14); // Skip "/root/Desktop/"
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)notif);
} else {
res = 0;
}
} }
if (!res) {
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)"Failed to save screenshot");
}
free(png_output_buf);
free(rgb_pixels); free(rgb_pixels);
free(pixels); free(pixels);
if (res) { return res ? 0 : 1;
char notif[256] = "Saved ";
int nlen = 6;
int flen = 0;
while (filename[11 + flen]) {
notif[nlen + flen] = filename[11 + flen];
flen++;
}
notif[nlen + flen] = '\0';
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)notif);
} else {
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)"Failed to save screenshot");
return 1;
}
return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -32,38 +32,111 @@ static uint64_t kernel_ticks_prev = 0;
static uint64_t total_mem_system = 0; static uint64_t total_mem_system = 0;
static uint64_t used_mem_system = 0; static uint64_t used_mem_system = 0;
static char cpu_model_name[64] = "Unknown CPU"; static char cpu_model_name[64] = "Unknown CPU";
static int cpu_cores = 1;
typedef struct { static int find_value(const char *buf, const char *key) {
size_t total_memory; char *p = (char*)buf;
size_t used_memory; int key_len = strlen(key);
size_t available_memory; while (*p) {
size_t allocated_blocks; if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') {
size_t free_blocks; p += key_len + 1;
size_t largest_free_block; while (*p == ' ') p++;
size_t smallest_free_block; return atoi(p);
size_t fragmentation_percent; }
size_t peak_memory_used; while (*p && *p != '\n') p++;
} MemStats; if (*p == '\n') p++;
}
return 0;
}
static void find_string(const char *buf, const char *key, char *out, int max_len) {
char *p = (char*)buf;
int key_len = strlen(key);
while (*p) {
if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') {
p += key_len + 1;
while (*p == ' ') p++;
int i = 0;
while (*p && *p != '\n' && i < max_len - 1) {
out[i++] = *p++;
}
out[i] = 0;
return;
}
while (*p && *p != '\n') p++;
if (*p == '\n') p++;
}
strcpy(out, "Unknown");
}
static void update_proc_list(void) { static void update_proc_list(void) {
proc_count = sys_system(SYSTEM_CMD_PROCESS_LIST, (uint64_t)proc_list, 32, 0, 0); FAT32_FileInfo entries[64];
int count = sys_list("/proc", entries, 64);
if (count < 0) return;
uint64_t uptime_now = sys_system(SYSTEM_CMD_UPTIME, 0, 0, 0, 0); proc_count = 0;
uint64_t user_ticks_now = 0; uint64_t user_ticks_now = 0;
for (int i = 0; i < proc_count; i++) { for (int i = 0; i < count; i++) {
if (proc_list[i].pid != 0) { if (entries[i].is_directory) {
user_ticks_now += proc_list[i].ticks; // Check if name is numeric (PID)
bool numeric = true;
for (int j = 0; entries[i].name[j]; j++) {
if (entries[i].name[j] < '0' || entries[i].name[j] > '9') {
numeric = false;
break;
}
}
if (!numeric) continue;
int pid = atoi(entries[i].name);
char path[64];
strcpy(path, "/proc/");
strcat(path, entries[i].name);
strcat(path, "/status");
int fd = sys_open(path, "r");
if (fd >= 0) {
char buf[512];
int bytes = sys_read(fd, buf, 511);
sys_close(fd);
if (bytes > 0) {
buf[bytes] = 0;
proc_list[proc_count].pid = pid;
find_string(buf, "Name", proc_list[proc_count].name, 64);
proc_list[proc_count].used_memory = (size_t)find_value(buf, "Memory") * 1024;
uint64_t ticks = (uint64_t)find_value(buf, "Ticks");
proc_list[proc_count].ticks = ticks;
proc_list[proc_count].is_idle = find_value(buf, "Idle") == 1;
if (!proc_list[proc_count].is_idle) user_ticks_now += ticks;
proc_count++;
if (proc_count >= 32) break;
}
}
}
}
// Global stats
int fd_u = sys_open("/proc/uptime", "r");
uint64_t uptime_now = 0;
if (fd_u >= 0) {
char buf[256];
int bytes = sys_read(fd_u, buf, 255);
sys_close(fd_u);
if (bytes > 0) {
buf[bytes] = 0;
uptime_now = (uint64_t)find_value(buf, "Raw_Ticks");
} }
} }
if (uptime_prev > 0) { if (uptime_prev > 0) {
uint64_t total_delta = uptime_now - uptime_prev; uint64_t total_delta = uptime_now - uptime_prev;
if (total_delta > 0) { if (total_delta > 0) {
uint64_t used_delta = user_ticks_now - kernel_ticks_prev; // Reusing the global state variable for prev user_ticks uint64_t used_delta = user_ticks_now - kernel_ticks_prev;
int cores = cpu_cores > 0 ? cpu_cores : 1;
// On a 4 CPU system, theoretically used_delta can be 4x total_delta int usage = (int)((used_delta * 100) / (total_delta * cores));
int usage = (int)((used_delta * 100) / (total_delta * 4));
if (usage > 100) usage = 100; if (usage > 100) usage = 100;
cpu_history[history_idx] = usage; cpu_history[history_idx] = usage;
} }
@@ -72,11 +145,18 @@ static void update_proc_list(void) {
uptime_prev = uptime_now; uptime_prev = uptime_now;
kernel_ticks_prev = user_ticks_now; kernel_ticks_prev = user_ticks_now;
MemStats stats; int fd_m = sys_open("/proc/meminfo", "r");
sys_system(SYSTEM_CMD_MEMINFO, (uint64_t)&stats, 0, 0, 0); if (fd_m >= 0) {
total_mem_system = stats.total_memory; char buf[1024];
used_mem_system = stats.used_memory; int bytes = sys_read(fd_m, buf, 1023);
mem_history[history_idx] = (int)(stats.used_memory / 1024); sys_close(fd_m);
if (bytes > 0) {
buf[bytes] = 0;
total_mem_system = (uint64_t)find_value(buf, "MemTotal") * 1024;
used_mem_system = (uint64_t)find_value(buf, "MemUsed") * 1024;
mem_history[history_idx] = (int)(used_mem_system / 1024);
}
}
history_idx = (history_idx + 1) % GRAPH_POINTS; history_idx = (history_idx + 1) % GRAPH_POINTS;
} }
@@ -168,9 +248,12 @@ static void draw_taskman(void) {
// Memory Graph Area // Memory Graph Area
ui_draw_string(win_taskman, 205, 10, "MEMORY", COLOR_MEM); ui_draw_string(win_taskman, 205, 10, "MEMORY", COLOR_MEM);
char mem_pct_label[16]; char mem_pct_label[16];
int current_mem_pct = 0; int current_mem_pct_x10 = 0;
if (total_mem_system > 0) current_mem_pct = (int)((used_mem_system * 100) / total_mem_system); if (total_mem_system > 0) current_mem_pct_x10 = (int)((used_mem_system * 1000) / total_mem_system);
itoa(current_mem_pct, mem_pct_label); itoa(current_mem_pct_x10 / 10, mem_pct_label);
strcat(mem_pct_label, ".");
char frac[4]; itoa(current_mem_pct_x10 % 10, frac);
strcat(mem_pct_label, frac);
strcat(mem_pct_label, "%"); strcat(mem_pct_label, "%");
ui_draw_string(win_taskman, 340, 10, mem_pct_label, COLOR_MEM); ui_draw_string(win_taskman, 340, 10, mem_pct_label, COLOR_MEM);
@@ -255,8 +338,18 @@ static void draw_taskman(void) {
int main(void) { int main(void) {
win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480); win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480);
// Fetch CPU model int fd_c = sys_open("/proc/cpuinfo", "r");
sys_system(SYSTEM_CMD_GET_CPU_MODEL, (uint64_t)cpu_model_name, 0, 0, 0); if (fd_c >= 0) {
char buf[1024];
int bytes = sys_read(fd_c, buf, 1023);
sys_close(fd_c);
if (bytes > 0) {
buf[bytes] = 0;
find_string(buf, "Processor", cpu_model_name, 64);
int cores = find_value(buf, "Cores");
if (cores > 0) cpu_cores = cores;
}
}
for(int i=0; i<GRAPH_POINTS; i++) { cpu_history[i] = 0; mem_history[i] = 0; } for(int i=0; i<GRAPH_POINTS; i++) { cpu_history[i] = 0; mem_history[i] = 0; }

View File

@@ -9,8 +9,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define VIEWER_MAX_W 800 #define VIEWER_MAX_W 4096
#define VIEWER_MAX_H 600 #define VIEWER_MAX_H 4096
static uint32_t *viewer_pixels = NULL; static uint32_t *viewer_pixels = NULL;
static uint32_t **viewer_frames = NULL; static uint32_t **viewer_frames = NULL;
@@ -24,6 +24,8 @@ static int viewer_img_h = 0;
static char viewer_title[64] = "Viewer"; static char viewer_title[64] = "Viewer";
static bool viewer_has_image = false; static bool viewer_has_image = false;
static char viewer_file_path[256]; static char viewer_file_path[256];
static bool resize_pending = false;
static uint64_t last_resize_tick = 0;
static int win_w = 500; static int win_w = 500;
static int win_h = 400; static int win_h = 400;
@@ -80,11 +82,13 @@ static void viewer_scale_rgba_to_argb(const unsigned char *rgba, int src_w, int
} }
static void viewer_paint(ui_window_t win) { static void viewer_paint(ui_window_t win) {
int cx = 4; int cx = 0;
int cy = 0; int cy = 0;
int cw = win_w - 8; int cw = win_w;
int ch = win_h - 28; int ch = win_h - 20; // 20px header
// Clear background
ui_draw_rect(win, 0, 0, win_w, win_h, 0xFF000000); // Black background
if (!viewer_has_image) { if (!viewer_has_image) {
ui_draw_string(win, cx + 20, cy + ch / 2, "No image loaded", 0xFF888888); ui_draw_string(win, cx + 20, cy + ch / 2, "No image loaded", 0xFF888888);
@@ -94,27 +98,24 @@ static void viewer_paint(ui_window_t win) {
uint32_t *pixels = viewer_pixels; uint32_t *pixels = viewer_pixels;
if (viewer_frames) pixels = viewer_frames[viewer_current_frame]; if (viewer_frames) pixels = viewer_frames[viewer_current_frame];
// Maintain aspect ratio while fitting to window
int disp_w = viewer_img_w; int disp_w = viewer_img_w;
int disp_h = viewer_img_h; int disp_h = viewer_img_h;
if (disp_w > cw - 8) { float sw = (float)cw / (float)viewer_img_w;
disp_h = disp_h * (cw - 8) / disp_w; float sh = (float)ch / (float)viewer_img_h;
disp_w = cw - 8; float scale = (sw < sh) ? sw : sh;
}
if (disp_h > ch - 40) { disp_w = (int)(viewer_img_w * scale);
disp_w = disp_w * (ch - 40) / disp_h; disp_h = (int)(viewer_img_h * scale);
disp_h = ch - 40;
}
int ox = cx + (cw - disp_w) / 2; int ox = cx + (cw - disp_w) / 2;
int oy = cy + (ch - disp_h) / 2; int oy = cy + (ch - disp_h) / 2;
if (disp_w <= 0 || disp_h <= 0) return;
uint32_t *temp_buf = malloc(disp_w * disp_h * sizeof(uint32_t)); uint32_t *temp_buf = malloc(disp_w * disp_h * sizeof(uint32_t));
if (temp_buf) { if (temp_buf) {
if (disp_w == viewer_img_w && disp_h == viewer_img_h) {
// Fast path: 1:1
for (int i = 0; i < disp_w * disp_h; i++) temp_buf[i] = pixels[i];
} else {
// Fixed-point 16.16 // Fixed-point 16.16
uint32_t step_x = (viewer_img_w << 16) / disp_w; uint32_t step_x = (viewer_img_w << 16) / disp_w;
uint32_t step_y = (viewer_img_h << 16) / disp_h; uint32_t step_y = (viewer_img_h << 16) / disp_h;
@@ -134,7 +135,6 @@ static void viewer_paint(ui_window_t win) {
} }
curr_y += step_y; curr_y += step_y;
} }
}
ui_draw_image(win, ox, oy, disp_w, disp_h, temp_buf); ui_draw_image(win, ox, oy, disp_w, disp_h, temp_buf);
free(temp_buf); free(temp_buf);
} }
@@ -271,8 +271,13 @@ void viewer_open_file(const char *path) {
viewer_title[ti] = 0; viewer_title[ti] = 0;
win_w = fit_w + 16; win_w = fit_w + 16;
if (win_w < 200) win_w = 200;
win_h = fit_h + 34; win_h = fit_h + 34;
// Cap initial window size to 1024x768 (or image size if smaller)
if (win_w > 1024) win_w = 1024;
if (win_h > 768) win_h = 768;
if (win_w < 200) win_w = 200;
if (win_h < 100) win_h = 100; if (win_h < 100) win_h = 100;
} }
@@ -284,18 +289,37 @@ int main(int argc, char **argv) {
ui_window_t win = ui_window_create(viewer_title, 100, 50, win_w, win_h); ui_window_t win = ui_window_create(viewer_title, 100, 50, win_w, win_h);
if (!win) return 1; if (!win) return 1;
ui_window_set_resizable(win, true);
gui_event_t ev; gui_event_t ev;
while (1) { while (1) {
if (ui_get_event(win, &ev)) { if (ui_get_event(win, &ev)) {
if (ev.type == GUI_EVENT_PAINT) { if (ev.type == GUI_EVENT_PAINT) {
viewer_paint(win); viewer_paint(win);
ui_mark_dirty(win, 0, 0, win_w, win_h - 20); ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
} else if (ev.type == GUI_EVENT_RESIZE) {
win_w = ev.arg1;
win_h = ev.arg2;
resize_pending = true;
last_resize_tick = sys_system(16, 0, 0, 0, 0);
// Fast background clear during active resize
ui_draw_rect(win, 0, 0, win_w, win_h, 0xFF000000);
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
} else if (ev.type == GUI_EVENT_CLICK) { } else if (ev.type == GUI_EVENT_CLICK) {
// No actions currently // No actions currently
} else if (ev.type == GUI_EVENT_CLOSE) { } else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0); sys_exit(0);
} }
} else { } else {
if (resize_pending) {
uint64_t now = sys_system(16, 0, 0, 0, 0);
if (now > last_resize_tick + 10) {
viewer_paint(win);
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
resize_pending = false;
}
}
if (viewer_has_image && viewer_frame_count > 1) { if (viewer_has_image && viewer_frame_count > 1) {
uint64_t now = sys_system(16, 0, 0, 0, 0); uint64_t now = sys_system(16, 0, 0, 0, 0);
if (now >= viewer_next_frame_tick) { if (now >= viewer_next_frame_tick) {

157
src/userland/libc/libmath.c Normal file
View File

@@ -0,0 +1,157 @@
// 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 "math.h"
static double _pow_int(double b, int e) {
if (e == 0) return 1.0;
if (e < 0) { return 1.0 / _pow_int(b, -e); }
double r = 1.0;
while (e > 0) {
if (e & 1) r *= b;
b *= b;
e >>= 1;
}
return r;
}
double fabs(double x) {
return x < 0.0 ? -x : x;
}
double fmod(double x, double y) {
if (y == 0.0) return 0.0;
return x - (int)(x / y) * y;
}
double floor(double x) {
int i = (int)x;
return (x < 0.0 && (double)i != x) ? (double)(i - 1) : (double)i;
}
double ceil(double x) {
int i = (int)x;
return (x > 0.0 && (double)i != x) ? (double)(i + 1) : (double)i;
}
double sin(double x) {
x = fmod(x, 2.0 * M_PI);
if (x > M_PI) x -= 2.0 * M_PI;
if (x < -M_PI) x += 2.0 * M_PI;
double x2 = x * x;
double term = x;
double sum = x;
for (int i = 1; i <= 8; i++) {
term *= -x2 / ((2*i) * (2*i + 1));
sum += term;
}
return sum;
}
double cos(double x) {
return sin(x + M_PI / 2.0);
}
double tan(double x) {
double c = cos(x);
if (fabs(c) < 1e-10) return 1e15;
return sin(x) / c;
}
double sqrt(double x) {
if (x <= 0.0) return 0.0;
double g = x * 0.5;
for (int i = 0; i < 25; i++) {
g = (g + x / g) * 0.5;
}
return g;
}
double log(double x) {
if (x <= 0.0) return -1e30;
int e = 0;
while (x > 2.0) { x /= 2.0; e++; }
while (x < 0.5) { x *= 2.0; e--; }
double t = (x - 1.0) / (x + 1.0);
double t2 = t * t;
double sum = t, term = t;
for (int i = 1; i <= 20; i++) {
term *= t2;
sum += term / (2*i + 1);
}
return 2.0 * sum + e * M_LN2;
}
double log2(double x) {
// log2(x) = ln(x) / ln(2)
return log(x) / M_LN2;
}
double log10(double x) {
return log(x) / 2.302585092994046;
}
double exp(double x) {
if (x > 700.0) return 1e300;
if (x < -700.0) return 0.0;
int k = (int)(x / M_LN2);
if (x < 0.0 && (double)k * M_LN2 > x) k--;
double r = x - (double)k * M_LN2;
double sum = 1.0, term = 1.0;
for (int i = 1; i <= 20; i++) {
term *= r / (double)i;
sum += term;
}
double result = sum;
if (k >= 0) { for (int i = 0; i < k; i++) result *= 2.0; }
else { for (int i = 0; i < -k; i++) result /= 2.0; }
return result;
}
double pow(double base, double exponent) {
if (base == 0.0) return 0.0;
if (exponent == 0.0) return 1.0;
int ie = (int)exponent;
if ((double)ie == exponent) return _pow_int(base, ie);
if (base < 0.0) return 0.0;
return exp(exponent * log(base));
}
double sinh(double x) {
double ep = exp(x);
double em = exp(-x);
return (ep - em) * 0.5;
}
double cosh(double x) {
double ep = exp(x);
double em = exp(-x);
return (ep + em) * 0.5;
}
double tanh(double x) {
double e2 = exp(2.0 * x);
return (e2 - 1.0) / (e2 + 1.0);
}
double hypot(double x, double y) {
return sqrt(x*x + y*y);
}
double fmin(double a, double b) {
return a < b ? a : b;
}
double fmax(double a, double b) {
return a > b ? a : b;
}
double fclamp(double x, double lo, double hi) {
if (x < lo) return lo;
if (x > hi) return hi;
return x;
}

40
src/userland/libc/math.h Normal file
View File

@@ -0,0 +1,40 @@
// 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 MATH_H
#define MATH_H
#define M_PI 3.14159265358979323846
#define M_E 2.71828182845904523536
#define M_LN2 0.69314718055994530942
#define M_SQRT2 1.41421356237309504880
#define HUGE_VAL (1e300 * 1e300)
double fabs(double x);
double fmod(double x, double y);
double floor(double x);
double ceil(double x);
double sin(double x);
double cos(double x);
double tan(double x);
double sqrt(double x);
double log(double x);
double log2(double x);
double log10(double x);
double exp(double x);
double pow(double base, double exponent);
double sinh(double x);
double cosh(double x);
double tanh(double x);
double hypot(double x, double y);
double fmin(double a, double b);
double fmax(double a, double b);
double fclamp(double x, double lo, double hi);
#endif /* MATH_H */

View File

@@ -202,9 +202,27 @@ char* strcat(char *dest, const char *src) {
return ret; return ret;
} }
char *strchr(const char *s, int c) {
while (*s != (char)c) {
if (!*s++) return NULL;
}
return (char *)s;
}
char *strstr(const char *haystack, const char *needle) {
size_t needle_len = strlen(needle);
if (!needle_len) return (char *)haystack;
while (*haystack) {
if (memcmp(haystack, needle, needle_len) == 0) return (char *)haystack;
haystack++;
}
return NULL;
}
int atoi(const char *nptr) { int atoi(const char *nptr) {
int res = 0; int res = 0;
int sign = 1; int sign = 1;
while (*nptr == ' ' || *nptr == '\t' || *nptr == '\n' || *nptr == '\r') nptr++;
if (*nptr == '-') { if (*nptr == '-') {
sign = -1; sign = -1;
nptr++; nptr++;

View File

@@ -11,11 +11,9 @@ void* realloc(void* ptr, size_t size);
void *memset(void *s, int c, size_t n); void *memset(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n); void *memcpy(void *dest, const void *src, size_t n);
// String functions #include "string.h"
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2); // Math/Utility functions
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
int atoi(const char *nptr); int atoi(const char *nptr);
void itoa(int n, char *buf); void itoa(int n, char *buf);

View File

@@ -7,5 +7,11 @@ void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n); int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *dest, const void *src, size_t n); void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n); void *memset(void *s, int c, size_t n);
char *strchr(const char *s, int c);
char *strstr(const char *haystack, const char *needle);
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
#endif #endif

View File

@@ -251,7 +251,7 @@ void sys_yield(void) {
syscall1(SYS_SYSTEM, SYSTEM_CMD_YIELD); syscall1(SYS_SYSTEM, SYSTEM_CMD_YIELD);
} }
int sys_get_os_info(os_info_t *info) { void sys_parallel_run(void (*fn)(void*), void **args, int count) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_GET_OS_INFO, (uint64_t)info, 0, 0, 0); syscall5(SYS_SYSTEM, SYSTEM_CMD_PARALLEL_RUN, (uint64_t)fn, (uint64_t)args, (uint64_t)count, 0);
} }

View File

@@ -44,8 +44,6 @@
#define SYSTEM_CMD_REBOOT 12 #define SYSTEM_CMD_REBOOT 12
#define SYSTEM_CMD_SHUTDOWN 13 #define SYSTEM_CMD_SHUTDOWN 13
#define SYSTEM_CMD_BEEP 14 #define SYSTEM_CMD_BEEP 14
#define SYSTEM_CMD_MEMINFO 15
#define SYSTEM_CMD_UPTIME 16
#define SYSTEM_CMD_PCI_LIST 17 #define SYSTEM_CMD_PCI_LIST 17
#define SYSTEM_CMD_NETWORK_DHCP 18 #define SYSTEM_CMD_NETWORK_DHCP 18
#define SYSTEM_CMD_NETWORK_GET_MAC 19 #define SYSTEM_CMD_NETWORK_GET_MAC 19
@@ -69,13 +67,11 @@
#define SYSTEM_CMD_DNS_LOOKUP 37 #define SYSTEM_CMD_DNS_LOOKUP 37
#define SYSTEM_CMD_SET_DNS 38 #define SYSTEM_CMD_SET_DNS 38
#define SYSTEM_CMD_NET_UNLOCK 39 #define SYSTEM_CMD_NET_UNLOCK 39
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RAW_MODE 41 #define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42 #define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43 #define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_GET_OS_INFO 49 #define SYSTEM_CMD_PARALLEL_RUN 50
// Internal assembly entry into Ring 0 // Internal assembly entry into Ring 0
extern uint64_t syscall0(uint64_t sys_num); extern uint64_t syscall0(uint64_t sys_num);
@@ -136,6 +132,7 @@ typedef struct {
char name[64]; char name[64];
uint64_t ticks; uint64_t ticks;
size_t used_memory; size_t used_memory;
uint32_t is_idle;
} ProcessInfo; } ProcessInfo;
// Network API // Network API

View File

@@ -6,11 +6,18 @@
int main(int argc, char **argv) { int main(int argc, char **argv) {
(void)argc; (void)argv; (void)argc; (void)argv;
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME int fd = sys_open("/proc/uptime", "r");
uint64_t seconds = ticks / 100; // 100Hz timer assumed if (fd < 0) return 1;
uint64_t minutes = seconds / 60; char buf[128];
uint64_t hours = minutes / 60; int bytes = sys_read(fd, buf, 127);
uint64_t days = hours / 24; sys_close(fd);
if (bytes <= 0) return 1;
buf[bytes] = 0;
int seconds = atoi(buf);
int minutes = seconds / 60;
int hours = minutes / 60;
int days = hours / 24;
printf("Uptime: %d days, %d hours, %d minutes, %d seconds\n", printf("Uptime: %d days, %d hours, %d minutes, %d seconds\n",
(int)days, (int)(hours % 24), (int)(minutes % 60), (int)(seconds % 60)); (int)days, (int)(hours % 24), (int)(minutes % 60), (int)(seconds % 60));

View File

@@ -8,6 +8,7 @@
#include "rtc.h" #include "rtc.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "disk.h" #include "disk.h"
#include "kutils.h" #include "kutils.h"
#include <stddef.h> #include <stddef.h>
@@ -75,7 +76,6 @@ static ShellConfig shell_config;
// CMD Window State (per-window context) // CMD Window State (per-window context)
typedef struct { typedef struct {
char current_drive;
char current_dir[256]; char current_dir[256];
} CmdState; } CmdState;
@@ -374,7 +374,7 @@ static void cmd_init_config_defaults(void) {
shell_config.default_text_color = 0xFFFFFFFF; // White shell_config.default_text_color = 0xFFFFFFFF; // White
shell_config.bg_color = 0xFF1E1E1E; // Dark background shell_config.bg_color = 0xFF1E1E1E; // Dark background
shell_config.cursor_color = 0xFFFFFFFF; shell_config.cursor_color = 0xFFFFFFFF;
shell_config.show_drive = true; shell_config.show_drive = false;
shell_config.show_dir = true; shell_config.show_dir = true;
shell_config.dir_color = 0xFF569CD6; shell_config.dir_color = 0xFF569CD6;
shell_config.file_color = 0xFFFFFFFF; shell_config.file_color = 0xFFFFFFFF;
@@ -554,10 +554,7 @@ void cmd_print_prompt(void) {
cursor_col = 0; cursor_col = 0;
if (shell_config.show_drive) { if (shell_config.show_drive) {
current_color = shell_config.prompt_drive_color; // No more drive letter in VFS
cmd_putchar(cmd_state->current_drive);
current_color = shell_config.prompt_colon_color;
cmd_putchar(':');
} }
if (shell_config.show_dir) { if (shell_config.show_dir) {
@@ -951,11 +948,7 @@ static void internal_cmd_cd(char *args) {
if (!args || !args[0]) { if (!args || !args[0]) {
// No argument - show current directory // No argument - show current directory
if (cmd_state) { if (cmd_state) {
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir); cmd_write(cmd_state->current_dir);
cmd_write("\n"); cmd_write("\n");
} else { } else {
@@ -985,9 +978,7 @@ static void internal_cmd_cd(char *args) {
cmd_strcpy(full_path, path); cmd_strcpy(full_path, path);
} else if (path[0] == '/') { } else if (path[0] == '/') {
// Absolute path // Absolute path
full_path[0] = cmd_state->current_drive; int j = 0;
full_path[1] = ':';
int j = 2;
int k = 0; int k = 0;
while (path[k] && j < 509) { while (path[k] && j < 509) {
full_path[j++] = path[k++]; full_path[j++] = path[k++];
@@ -995,9 +986,7 @@ static void internal_cmd_cd(char *args) {
full_path[j] = 0; full_path[j] = 0;
} else { } else {
// Relative path - resolve from current directory // Relative path - resolve from current directory
full_path[0] = cmd_state->current_drive; int j = 0;
full_path[1] = ':';
int j = 2;
// Copy current directory // Copy current directory
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
@@ -1019,18 +1008,14 @@ static void internal_cmd_cd(char *args) {
} }
// Validate directory exists // Validate directory exists
if (fat32_is_directory(full_path)) { if (vfs_is_directory(full_path)) {
// Normalize the path to resolve .. and . // Normalize the path to resolve .. and .
char normalized_path[512]; char normalized_path[512];
fat32_normalize_path(full_path, normalized_path); vfs_normalize_path(cmd_state->current_dir, full_path, normalized_path);
cmd_update_dir(normalized_path); cmd_update_dir(normalized_path);
cmd_write("Changed to: "); cmd_write("Changed to: ");
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir); cmd_write(cmd_state->current_dir);
cmd_write("\n"); cmd_write("\n");
} else { } else {
@@ -1086,7 +1071,7 @@ static void internal_cmd_txtedit(char *args) {
cmd_write("\n"); cmd_write("\n");
cmd_is_waiting_for_process = true; cmd_is_waiting_for_process = true;
process_t *proc = process_create_elf("A:/bin/txtedit.elf", normalized_path); process_t *proc = process_create_elf("/bin/txtedit.elf", normalized_path);
if (proc) { if (proc) {
proc->is_terminal_proc = true; proc->is_terminal_proc = true;
proc->ui_window = &win_cmd; proc->ui_window = &win_cmd;
@@ -1105,15 +1090,13 @@ void cmd_exec_elf(char *args) {
char full_exec_path[512] = {0}; char full_exec_path[512] = {0};
int i = 0; int i = 0;
if (args[0] && args[1] != ':') { if (args[0] && args[0] != '/') {
if (cmd_state) { if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_exec_path[i++] = *dir++; full_exec_path[i++] = *dir++;
} }
if (i > 2 && full_exec_path[i-1] != '/') { if (i > 0 && full_exec_path[i-1] != '/') {
full_exec_path[i++] = '/'; full_exec_path[i++] = '/';
} }
} }
@@ -1215,23 +1198,10 @@ static const CommandEntry commands[] = {
// Helper to sync cmd window directory after cd
static void cmd_update_dir(const char *path) { static void cmd_update_dir(const char *path) {
if (!cmd_state || !path) return; if (!cmd_state || !path) return;
// Extract drive if provided
const char *p = path; const char *p = path;
char drive = cmd_state->current_drive;
if (p[0] && p[1] == ':') {
drive = p[0];
if (drive >= 'a' && drive <= 'z') drive -= 32;
p += 2;
}
// Update drive
cmd_state->current_drive = drive;
// Update directory
if (*p) { if (*p) {
// Remove trailing slashes and copy // Remove trailing slashes and copy
int len = 0; int len = 0;
@@ -1269,20 +1239,7 @@ static void cmd_exec_single(char *cmd) {
// Check for drive switch (e.g. "A:", "B:") // Check for drive switch (e.g. "A:", "B:")
if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) { if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) {
char letter = cmd[0]; cmd_write("Drive letters are no longer supported. Use VFS paths (e.g. /dev/sda1)\n");
if (letter >= 'a' && letter <= 'z') letter -= 32;
// Check if drive exists (don't change global, just check)
if (disk_get_by_letter(letter)) {
// Update cmd window's drive, not global
if (cmd_state) {
cmd_state->current_drive = letter;
cmd_state->current_dir[0] = '/';
cmd_state->current_dir[1] = 0;
}
} else {
cmd_write("Invalid drive.\n");
}
return; return;
} }
@@ -1336,19 +1293,15 @@ static void cmd_exec_single(char *cmd) {
char full_exec_path[512]; char full_exec_path[512];
int i = 0; int i = 0;
// Add drive letter
if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
// Add current directory // Add current directory
if (cmd_state) {
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_exec_path[i++] = *dir++; full_exec_path[i++] = *dir++;
} }
// Add separator if current dir doesn't end with / // Add separator if current dir doesn't end with /
if (i > 2 && full_exec_path[i-1] != '/') { if (i > 0 && full_exec_path[i-1] != '/') {
full_exec_path[i++] = '/'; full_exec_path[i++] = '/';
} }
} }
@@ -1436,15 +1389,11 @@ static void cmd_exec_single(char *cmd) {
i += 2; i += 2;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; } while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative // Prepend directory to filename if relative
if (args[i] && args[i+1] != ':') { if (args[i] && args[i] != '/') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
if (args[i] != '/') {
const char *d = cmd_state->current_dir; const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++; while (*d && j < 509) temp_args[j++] = *d++;
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/'; if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
} }
in_redirect = true; in_redirect = true;
} else if (args[i] == '>' && args[i+1] != '>') { } else if (args[i] == '>' && args[i+1] != '>') {
@@ -1453,15 +1402,11 @@ static void cmd_exec_single(char *cmd) {
i++; i++;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; } while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative // Prepend directory to filename if relative
if (args[i] && args[i+1] != ':') { if (args[i] && args[i] != '/') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
if (args[i] != '/') {
const char *d = cmd_state->current_dir; const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++; while (*d && j < 509) temp_args[j++] = *d++;
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/'; if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
} }
in_redirect = true; in_redirect = true;
} else { } else {
@@ -1488,10 +1433,8 @@ static void cmd_exec_single(char *cmd) {
// Already has drive letter // Already has drive letter
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') { } else if (args[0] == '/') {
// Absolute path, just prepend drive // Absolute path
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@@ -1500,8 +1443,6 @@ static void cmd_exec_single(char *cmd) {
} else { } else {
// Relative path - need to build from current directory // Relative path - need to build from current directory
int i = 0; int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory // Add current directory
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
@@ -1510,7 +1451,7 @@ static void cmd_exec_single(char *cmd) {
} }
// Add separator if current dir doesn't end with / // Add separator if current dir doesn't end with /
if (i > 2 && full_path_arg[i-1] != '/') { if (i > 0 && full_path_arg[i-1] != '/') {
full_path_arg[i++] = '/'; full_path_arg[i++] = '/';
} }
@@ -1529,10 +1470,8 @@ static void cmd_exec_single(char *cmd) {
// Has drive letter, use as-is // Has drive letter, use as-is
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') { } else if (args[0] == '/') {
// Absolute path, just prepend drive // Absolute path
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@@ -1541,8 +1480,6 @@ static void cmd_exec_single(char *cmd) {
} else { } else {
// Relative path - need to build from current directory // Relative path - need to build from current directory
int i = 0; int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory // Add current directory
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
@@ -1551,7 +1488,7 @@ static void cmd_exec_single(char *cmd) {
} }
// Add separator if current dir doesn't end with / // Add separator if current dir doesn't end with /
if (i > 2 && full_path_arg[i-1] != '/') { if (i > 0 && full_path_arg[i-1] != '/') {
full_path_arg[i++] = '/'; full_path_arg[i++] = '/';
} }
@@ -1568,10 +1505,10 @@ static void cmd_exec_single(char *cmd) {
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
args = full_path_arg; args = full_path_arg;
} else { } else {
// Add drive letter // Try to use arg as is (it's either absolute or relative)
full_path_arg[0] = cmd_state->current_drive; // If it's relative, we should actually build it...
full_path_arg[1] = ':'; // Let's just use it as is for now since later path resolving will catch it
int i = 2; int i = 0;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@@ -1580,10 +1517,8 @@ static void cmd_exec_single(char *cmd) {
args = full_path_arg; args = full_path_arg;
} }
} else if (is_ls_command || is_cd_command) { } else if (is_ls_command || is_cd_command) {
// For ls and cd with no args, pass current directory with drive // For ls and cd with no args, pass current directory
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_path_arg[i++] = *dir++; full_path_arg[i++] = *dir++;
@@ -1602,7 +1537,7 @@ static void cmd_exec_single(char *cmd) {
} }
} }
// Check for executable in Current Directory or A:/bin/ // Check for executable in Current Directory or /bin/
char search_path[512]; char search_path[512];
// Check if the command already ends in .elf (case insensitive) // Check if the command already ends in .elf (case insensitive)
@@ -1619,11 +1554,9 @@ static void cmd_exec_single(char *cmd) {
// 1. Try Current Directory + .elf // 1. Try Current Directory + .elf
if (cmd_state) { if (cmd_state) {
int idx = 0; int idx = 0;
search_path[idx++] = cmd_state->current_drive;
search_path[idx++] = ':';
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && idx < 500) search_path[idx++] = *dir++; while (*dir && idx < 500) search_path[idx++] = *dir++;
if (idx > 2 && search_path[idx-1] != '/') search_path[idx++] = '/'; if (idx > 0 && search_path[idx-1] != '/') search_path[idx++] = '/';
const char *c = cmd; const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++; while (*c && idx < 500) search_path[idx++] = *c++;
if (!has_elf_ext) { if (!has_elf_ext) {
@@ -1644,10 +1577,10 @@ static void cmd_exec_single(char *cmd) {
} }
} }
// 2. Try A:/bin/ + .elf // 2. Try /bin/ + .elf
{ {
int idx = 0; int idx = 0;
const char *bin_prefix = "A:/bin/"; const char *bin_prefix = "/bin/";
while (*bin_prefix) search_path[idx++] = *bin_prefix++; while (*bin_prefix) search_path[idx++] = *bin_prefix++;
const char *c = cmd; const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++; while (*c && idx < 500) search_path[idx++] = *c++;
@@ -2171,11 +2104,12 @@ void cmd_reset(void) {
} }
static void create_ramfs_files(void) { static void create_ramfs_files(void) {
if (!fat32_exists("Documents")) fat32_mkdir("Documents"); if (!fat32_exists("root")) fat32_mkdir("root");
if (!fat32_exists("Projects")) fat32_mkdir("Projects"); if (!fat32_exists("root/Documents")) fat32_mkdir("root/Documents");
if (!fat32_exists("Documents/Important")) fat32_mkdir("Documents/Important"); if (!fat32_exists("root/Documents/Important")) fat32_mkdir("root/Documents/Important");
if (!fat32_exists("Apps")) fat32_mkdir("Apps"); if (!fat32_exists("root/Apps")) fat32_mkdir("root/Apps");
if (!fat32_exists("Desktop")) fat32_mkdir("Desktop"); if (!fat32_exists("root/Desktop")) fat32_mkdir("root/Desktop");
if (!fat32_exists("root/projects")) fat32_mkdir("root/projects");
if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin"); if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin");
if (!fat32_exists("Library/conf")) fat32_mkdir("Library/conf"); if (!fat32_exists("Library/conf")) fat32_mkdir("Library/conf");
@@ -2223,7 +2157,7 @@ static void create_ramfs_files(void) {
"// BoredOS System Fetch Configuration\n" "// BoredOS System Fetch Configuration\n"
"// ----------------------------------\n" "// ----------------------------------\n"
"// To use custom ascii art, uncomment the line below and point it to your file.\n" "// To use custom ascii art, uncomment the line below and point it to your file.\n"
"ascii_art_file=A:/Library/art/boredos.txt\n" "ascii_art_file=/Library/art/boredos.txt\n"
"user_host_string=root@boredos\n" "user_host_string=root@boredos\n"
"separator=------------\n" "separator=------------\n"
"\n" "\n"
@@ -2259,7 +2193,7 @@ static void create_ramfs_files(void) {
FAT32_FileHandle *fh = fat32_open("Apps/README.md", "w"); FAT32_FileHandle *fh = fat32_open("root/Apps/README.md", "w");
if (fh) { if (fh) {
const char *content = const char *content =
"# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n" "# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n"
@@ -2269,24 +2203,24 @@ static void create_ramfs_files(void) {
fat32_close(fh); fat32_close(fh);
} }
fh = fat32_open("Documents/notes.txt", "w"); fh = fat32_open("root/Documents/notes.txt", "w");
if (fh) { if (fh) {
const char *content = "My Notes\n\n- First note\n- Second note\n"; const char *content = "My Notes\n\n- First note\n- Second note\n";
fat32_write(fh, (void *)content, 39); fat32_write(fh, (void *)content, 39);
fat32_close(fh); fat32_close(fh);
} }
fh = fat32_open("Projects/project1.txt", "w"); fh = fat32_open("root/projects/project1.txt", "w");
if (fh) { if (fh) {
const char *content = "Project 1\n\nStatus: In Progress\n"; const char *content = "Project 1\n\nStatus: In Progress\n";
fat32_write(fh, (void *)content, 32); fat32_write(fh, (void *)content, 32);
fat32_close(fh); fat32_close(fh);
} }
fat32_open("Desktop/Recycle Bin.shortcut", "w"); fat32_open("root/Desktop/Recycle Bin.shortcut", "w");
fh = fat32_open("Apps/wordofgod.c", "w"); fh = fat32_open("root/Apps/wordofgod.c", "w");
if (fh) { if (fh) {
// Buffer the entire file content to write in one go // Buffer the entire file content to write in one go
// This prevents issues with multiple small writes causing truncation // This prevents issues with multiple small writes causing truncation
@@ -2340,7 +2274,7 @@ static void create_ramfs_files(void) {
fat32_close(fh); fat32_close(fh);
} }
fh = fat32_open("Apps/DOOM.c", "w"); fh = fat32_open("root/Apps/DOOM.c", "w");
if (fh) { if (fh) {
const char *content = const char *content =
"int main(){\n" "int main(){\n"
@@ -2389,9 +2323,13 @@ void cmd_init(void) {
// Initialize cmd state (per-window context) // Initialize cmd state (per-window context)
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState)); CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));
if (state) { if (state) {
state->current_drive = 'A';
state->current_dir[0] = '/'; state->current_dir[0] = '/';
state->current_dir[1] = 0; state->current_dir[1] = 'r';
state->current_dir[2] = 'o';
state->current_dir[3] = 'o';
state->current_dir[4] = 't';
state->current_dir[5] = 0;
win_cmd.data = state; win_cmd.data = state;
cmd_state = state; // Set static pointer cmd_state = state; // Set static pointer
} }

View File

@@ -5,6 +5,7 @@
#include "graphics.h" #include "graphics.h"
#include "font_manager.h" #include "font_manager.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "disk.h" #include "disk.h"
#include "wm.h" #include "wm.h"
#include "memory_manager.h" #include "memory_manager.h"
@@ -42,6 +43,28 @@ static int dropdown_menu_item_height = 25;
#define FILE_CONTEXT_MENU_HEIGHT 50 #define FILE_CONTEXT_MENU_HEIGHT 50
#define CONTEXT_MENU_ITEM_HEIGHT 25 #define CONTEXT_MENU_ITEM_HEIGHT 25
static void wm_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) {
(void)user_data; draw_rect(x, y, w, h, color);
}
static void wm_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) {
(void)user_data; draw_rounded_rect_filled(x, y, w, h, r, color);
}
static void wm_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) {
(void)user_data; draw_string(x, y, str, color);
}
static int wm_measure_string_width(void *user_data, const char *str) {
(void)user_data;
return font_manager_get_string_width(graphics_get_current_ttf(), str);
}
static widget_context_t wm_widget_ctx = {
.user_data = NULL,
.draw_rect = wm_draw_rect,
.draw_rounded_rect_filled = wm_draw_rounded_rect_filled,
.draw_string = wm_draw_string,
.measure_string_width = wm_measure_string_width,
.mark_dirty = NULL
};
static char clipboard_path[FAT32_MAX_PATH] = ""; static char clipboard_path[FAT32_MAX_PATH] = "";
static int clipboard_action = 0; static int clipboard_action = 0;
#define FILE_CONTEXT_ITEMS 2 #define FILE_CONTEXT_ITEMS 2
@@ -217,8 +240,8 @@ static void explorer_draw_icon_label(int x, int y, const char *label, uint32_t c
static bool check_desktop_limit_explorer(Window *win) { static bool check_desktop_limit_explorer(Window *win) {
ExplorerState *state = (ExplorerState*)win->data; ExplorerState *state = (ExplorerState*)win->data;
if (explorer_str_starts_with(state->current_path, "/Desktop")) { if (explorer_str_starts_with(state->current_path, "/root/Desktop")) {
if (explorer_strcmp(state->current_path, "/Desktop") == 0 || explorer_strcmp(state->current_path, "/Desktop/") == 0) { // Check if root desktop if (explorer_strcmp(state->current_path, "/root/Desktop") == 0 || explorer_strcmp(state->current_path, "/root/Desktop/") == 0) { // Check if root desktop
if (state->item_count >= desktop_max_cols * (desktop_max_rows_per_col > 1 ? desktop_max_rows_per_col - 1 : 0)) { if (state->item_count >= desktop_max_cols * (desktop_max_rows_per_col > 1 ? desktop_max_rows_per_col - 1 : 0)) {
state->dialog_state = DIALOG_ERROR; state->dialog_state = DIALOG_ERROR;
explorer_strcpy(state->dialog_input, "Desktop is full!"); explorer_strcpy(state->dialog_input, "Desktop is full!");
@@ -280,14 +303,14 @@ static void dialog_confirm_create_file(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); explorer_strcat(full_path, state->dialog_input);
if (fat32_exists(full_path)) { if (vfs_exists(full_path)) {
state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM; state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM;
return; return;
} }
FAT32_FileHandle *file = fat32_open(full_path, "w"); vfs_file_t *file = vfs_open(full_path, "w");
if (file) { if (file) {
fat32_close(file); vfs_close(file);
explorer_refresh_all(); explorer_refresh_all();
} }
@@ -303,9 +326,9 @@ static void dialog_force_create_file(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); explorer_strcat(full_path, state->dialog_input);
FAT32_FileHandle *file = fat32_open(full_path, "w"); vfs_file_t *file = vfs_open(full_path, "w");
if (file) { if (file) {
fat32_close(file); vfs_close(file);
explorer_refresh_all(); explorer_refresh_all();
} }
dialog_close(win); dialog_close(win);
@@ -324,7 +347,7 @@ static void dialog_confirm_create_folder(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); explorer_strcat(full_path, state->dialog_input);
if (fat32_mkdir(full_path)) { if (vfs_mkdir(full_path)) {
explorer_refresh_all(); explorer_refresh_all();
} }
@@ -332,18 +355,18 @@ static void dialog_confirm_create_folder(Window *win) {
} }
bool explorer_delete_permanently(const char *path) { bool explorer_delete_permanently(const char *path) {
if (fat32_is_directory(path)) { if (vfs_is_directory(path)) {
int capacity = 64; int capacity = 64;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return false; if (!entries) return false;
int count = fat32_list_directory(path, entries, capacity); int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) { while (count == capacity) {
capacity *= 2; capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return false; } if (!new_entries) { kfree(entries); return false; }
entries = new_entries; entries = new_entries;
count = fat32_list_directory(path, entries, capacity); count = vfs_list_directory(path, entries, capacity);
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@@ -359,13 +382,13 @@ bool explorer_delete_permanently(const char *path) {
if (entries[i].is_directory) { if (entries[i].is_directory) {
explorer_delete_permanently(child_path); explorer_delete_permanently(child_path);
} else { } else {
fat32_delete(child_path); vfs_delete(child_path);
} }
} }
kfree(entries); kfree(entries);
return fat32_rmdir(path); return vfs_rmdir(path);
} else { } else {
return fat32_delete(path); return vfs_delete(path);
} }
} }
@@ -386,7 +409,7 @@ bool explorer_delete_recursive(const char *path) {
for (int k = i + 1; k < len; k++) filename[j++] = path[k]; for (int k = i + 1; k < len; k++) filename[j++] = path[k];
filename[j] = 0; filename[j] = 0;
char drive_prefix[3] = "A:"; char drive_prefix[3] = "/";
if (path[0] && path[1] == ':') { if (path[0] && path[1] == ':') {
drive_prefix[0] = path[0]; drive_prefix[0] = path[0];
} }
@@ -399,10 +422,10 @@ bool explorer_delete_recursive(const char *path) {
char origin_path[FAT32_MAX_PATH]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path); explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w"); vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) { if (fh) {
fat32_write(fh, path, explorer_strlen(path)); vfs_write(fh, path, explorer_strlen(path));
fat32_close(fh); vfs_close(fh);
} }
explorer_copy_recursive(path, dest_path); explorer_copy_recursive(path, dest_path);
@@ -445,19 +468,19 @@ bool explorer_clipboard_has_content(void) {
} }
static bool explorer_copy_recursive(const char *src_path, const char *dest_path) { static bool explorer_copy_recursive(const char *src_path, const char *dest_path) {
if (fat32_is_directory(src_path)) { if (vfs_is_directory(src_path)) {
if (!fat32_mkdir(dest_path)) return false; if (!vfs_mkdir(dest_path)) return false;
int capacity = 64; int capacity = 64;
FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *files = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!files) return false; if (!files) return false;
int count = fat32_list_directory(src_path, files, capacity); int count = vfs_list_directory(src_path, files, capacity);
while (count == capacity) { while (count == capacity) {
capacity *= 2; capacity *= 2;
FAT32_FileInfo *new_files = (FAT32_FileInfo*)krealloc(files, capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *new_files = (vfs_dirent_t*)krealloc(files, capacity * sizeof(vfs_dirent_t));
if (!new_files) { kfree(files); return false; } if (!new_files) { kfree(files); return false; }
files = new_files; files = new_files;
count = fat32_list_directory(src_path, files, capacity); count = vfs_list_directory(src_path, files, capacity);
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue; if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue;
@@ -476,22 +499,22 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path)
kfree(files); kfree(files);
return true; return true;
} else { } else {
FAT32_FileHandle *src = fat32_open(src_path, "r"); vfs_file_t *src = vfs_open(src_path, "r");
FAT32_FileHandle *dst = fat32_open(dest_path, "w"); vfs_file_t *dst = vfs_open(dest_path, "w");
bool success = false; bool success = false;
if (src && dst) { if (src && dst) {
uint8_t *buf = (uint8_t*)kmalloc(4096); uint8_t *buf = (uint8_t*)kmalloc(4096);
if (buf) { if (buf) {
int bytes; int bytes;
success = true; success = true;
while ((bytes = fat32_read(src, buf, 4096)) > 0) { while ((bytes = vfs_read(src, buf, 4096)) > 0) {
if (fat32_write(dst, buf, bytes) != bytes) { success = false; break; } if (vfs_write(dst, buf, bytes) != bytes) { success = false; break; }
} }
kfree(buf); kfree(buf);
} }
} }
if (src) fat32_close(src); if (src) vfs_close(src);
if (dst) fat32_close(dst); if (dst) vfs_close(dst);
return success; return success;
} }
} }
@@ -522,10 +545,10 @@ static void explorer_perform_paste(Window *win, const char *dest_dir) {
explorer_copy_file_internal(clipboard_path, dest_dir); explorer_copy_file_internal(clipboard_path, dest_dir);
if (clipboard_action == 2) { if (clipboard_action == 2) {
if (fat32_is_directory(clipboard_path)) { if (vfs_is_directory(clipboard_path)) {
explorer_delete_permanently(clipboard_path); explorer_delete_permanently(clipboard_path);
} else { } else {
fat32_delete(clipboard_path); vfs_delete(clipboard_path);
} }
clipboard_action = 0; clipboard_action = 0;
} }
@@ -558,7 +581,7 @@ void explorer_clipboard_paste(Window *win, const char *dest_dir) {
} }
explorer_strcat(dest_path, filename); explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path)) { if (vfs_exists(dest_path)) {
state->dialog_state = DIALOG_REPLACE_CONFIRM; state->dialog_state = DIALOG_REPLACE_CONFIRM;
explorer_strcpy(state->dialog_dest_dir, dest_dir); explorer_strcpy(state->dialog_dest_dir, dest_dir);
return; return;
@@ -583,10 +606,10 @@ void explorer_create_shortcut(Window *win, const char *target_path) {
explorer_strcat(shortcut_path, filename); explorer_strcat(shortcut_path, filename);
explorer_strcat(shortcut_path, ".shortcut"); explorer_strcat(shortcut_path, ".shortcut");
FAT32_FileHandle *fh = fat32_open(shortcut_path, "w"); vfs_file_t *fh = vfs_open(shortcut_path, "w");
if (fh) { if (fh) {
fat32_write(fh, target_path, explorer_strlen(target_path)); vfs_write(fh, target_path, explorer_strlen(target_path));
fat32_close(fh); vfs_close(fh);
explorer_refresh_all(); explorer_refresh_all();
} }
} }
@@ -658,18 +681,18 @@ static void explorer_restore_file(Window *win, int item_idx) {
explorer_strcat(origin_file_path, ".origin"); explorer_strcat(origin_file_path, ".origin");
char original_path[FAT32_MAX_PATH] = {0}; char original_path[FAT32_MAX_PATH] = {0};
FAT32_FileHandle *fh = fat32_open(origin_file_path, "r"); vfs_file_t *fh = vfs_open(origin_file_path, "r");
if (fh) { if (fh) {
int len = fat32_read(fh, original_path, FAT32_MAX_PATH - 1); int len = vfs_read(fh, original_path, FAT32_MAX_PATH - 1);
if (len > 0) original_path[len] = 0; if (len > 0) original_path[len] = 0;
fat32_close(fh); vfs_close(fh);
} }
if (original_path[0] == 0) return; if (original_path[0] == 0) return;
explorer_copy_recursive(recycle_path, original_path); explorer_copy_recursive(recycle_path, original_path);
explorer_delete_permanently(recycle_path); explorer_delete_permanently(recycle_path);
fat32_delete(origin_file_path); vfs_delete(origin_file_path);
explorer_refresh_all(); explorer_refresh_all();
} }
@@ -682,16 +705,17 @@ static void explorer_load_directory(Window *win, const char *path) {
state->item_count = 0; state->item_count = 0;
int capacity = EXPLORER_INITIAL_CAPACITY; int capacity = EXPLORER_INITIAL_CAPACITY;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return; if (!entries) return;
int count = fat32_list_directory(path, entries, capacity); int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) { while (count == capacity) {
capacity *= 2; capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo)); vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return; } if (!new_entries) { kfree(entries); return; }
entries = new_entries; entries = new_entries;
count = fat32_list_directory(path, entries, capacity); count = vfs_list_directory(path, entries, capacity);
} }
if (state->items_capacity < count) { if (state->items_capacity < count) {
@@ -715,7 +739,7 @@ static void explorer_load_directory(Window *win, const char *path) {
explorer_strcpy(state->items[temp_count].name, entries[i].name); explorer_strcpy(state->items[temp_count].name, entries[i].name);
state->items[temp_count].is_directory = entries[i].is_directory; state->items[temp_count].is_directory = entries[i].is_directory;
state->items[temp_count].size = entries[i].size; state->items[temp_count].size = entries[i].size;
state->items[temp_count].color = entries[i].is_directory ? COLOR_APPLE_BLUE : COLOR_APPLE_YELLOW; state->items[temp_count].color = entries[i].is_directory ? COLOR_BLUE : COLOR_YELLOW;
temp_count++; temp_count++;
} }
@@ -782,21 +806,21 @@ void explorer_open_directory(const char *path) {
} }
static void explorer_open_target(const char *path) { static void explorer_open_target(const char *path) {
if (fat32_is_directory(path)) { if (vfs_is_directory(path)) {
explorer_open_directory(path); explorer_open_directory(path);
} else { } else {
if (explorer_str_ends_with(path, ".elf")) { if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path, NULL); process_create_elf(path, NULL);
} else if (explorer_str_ends_with(path, ".pdf")) { } else if (explorer_str_ends_with(path, ".pdf")) {
process_create_elf("A:/bin/boredword.elf", path); process_create_elf("/bin/boredword.elf", path);
} else if (explorer_is_markdown_file(path)) { } else if (explorer_is_markdown_file(path)) {
process_create_elf("A:/bin/markdown.elf", path); process_create_elf("/bin/markdown.elf", path);
} else if (explorer_str_ends_with(path, ".pnt")) { } else if (explorer_str_ends_with(path, ".pnt")) {
process_create_elf("A:/bin/paint.elf", path); process_create_elf("/bin/paint.elf", path);
} else if (explorer_is_image_file(path)) { } else if (explorer_is_image_file(path)) {
process_create_elf("A:/bin/viewer.elf", path); process_create_elf("/bin/viewer.elf", path);
} else { } else {
process_create_elf("A:/bin/txtedit.elf", path); process_create_elf("/bin/txtedit.elf", path);
} }
} }
} }
@@ -842,11 +866,11 @@ static void explorer_open_item(Window *win, int index) {
return; return;
} }
FAT32_FileHandle *fh = fat32_open(full_path, "r"); vfs_file_t *fh = vfs_open(full_path, "r");
if (fh) { if (fh) {
char buf[FAT32_MAX_PATH]; char buf[FAT32_MAX_PATH];
int len = fat32_read(fh, buf, 255); int len = vfs_read(fh, buf, 255);
fat32_close(fh); vfs_close(fh);
if (len > 0) { if (len > 0) {
buf[len] = 0; buf[len] = 0;
explorer_open_target(buf); explorer_open_target(buf);
@@ -898,72 +922,34 @@ static void explorer_paint(Window *win) {
DirtyRect dirty = graphics_get_dirty_rect(); DirtyRect dirty = graphics_get_dirty_rect();
graphics_set_clipping(offset_x, offset_y, win->w - 8, win->h - 28); graphics_push_clipping(offset_x, offset_y, win->w - 8, win->h - 28);
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG); draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
char drive_label[20];
char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') {
current_drv = state->current_path[0];
} else if (state->current_path[0] && (state->current_path[0] >= 'A' && state->current_path[0] <= 'Z')) {
current_drv = state->current_path[0];
}
const char *type_str = "RAM";
Disk *drv = disk_get_by_letter(current_drv);
if (drv) {
switch (drv->type) {
case DISK_TYPE_RAM: type_str = "RAM"; break;
case DISK_TYPE_IDE: type_str = "IDE"; break;
case DISK_TYPE_SATA: type_str = "SATA"; break;
case DISK_TYPE_USB: type_str = "USB"; break;
default: type_str = "???"; break;
}
}
int di = 0;
drive_label[di++] = '[';
drive_label[di++] = ' ';
drive_label[di++] = current_drv;
drive_label[di++] = ':';
const char *ts = type_str;
while (*ts) drive_label[di++] = *ts++;
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ']';
drive_label[di] = 0;
ttf_font_t *ttf_ = graphics_get_current_ttf(); ttf_font_t *ttf_ = graphics_get_current_ttf();
int drive_label_w = ttf_ ? font_manager_get_string_width(ttf_, drive_label) + 16 : 80;
if (drive_label_w < 60) drive_label_w = 60;
draw_rounded_rect_filled(win->x + 4, offset_y + 3, drive_label_w, 22, 5, COLOR_DARK_PANEL);
draw_string(win->x + 12, offset_y + 8, drive_label, COLOR_DARK_TEXT);
int path_height = 22; int path_height = 22;
int path_x = offset_x + drive_label_w + 8; int path_x = offset_x;
int path_w = win->w - 16 - drive_label_w - 8; int path_w = win->w - 16 - 8;
draw_rounded_rect_filled(path_x, offset_y + 3, path_w, path_height, 5, COLOR_DARK_PANEL); draw_rounded_rect_filled(path_x, offset_y + 3, path_w, path_height, 5, COLOR_DARK_PANEL);
draw_string(path_x + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT); draw_string(path_x + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT);
int path_label_w = ttf_ ? font_manager_get_string_width(ttf_, "Path:") : 40; int path_label_w = ttf_ ? font_manager_get_string_width(ttf_, "Path:") : 40;
draw_string(path_x + 6 + path_label_w + 6, offset_y + 8, state->current_path, COLOR_DARK_TEXT); draw_string(path_x + 6 + path_label_w + 6, offset_y + 8, state->current_path, COLOR_DARK_TEXT);
int dropdown_btn_x = win->x + win->w - 90; int dropdown_btn_x = win->x + win->w - 90;
draw_rounded_rect_filled(dropdown_btn_x, offset_y + 3, 35, 22, 5, COLOR_DARK_PANEL); widget_button_init(&state->btn_dropdown, dropdown_btn_x, offset_y + 3, 35, 22, "...");
draw_string(dropdown_btn_x + 10, offset_y + 8, "...", COLOR_DARK_TEXT); widget_button_init(&state->btn_back, win->x + win->w - 40, offset_y + 3, 30, 22, "<");
widget_button_init(&state->btn_up, win->x + win->w - 160, offset_y + 3, 30, 22, "^");
widget_button_init(&state->btn_fwd, win->x + win->w - 125, offset_y + 3, 30, 22, "v");
draw_rounded_rect_filled(win->x + win->w - 40, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL); widget_button_draw(&wm_widget_ctx, &state->btn_dropdown);
draw_string(win->x + win->w - 32, offset_y + 8, "<", COLOR_DARK_TEXT); widget_button_draw(&wm_widget_ctx, &state->btn_back);
widget_button_draw(&wm_widget_ctx, &state->btn_up);
draw_rounded_rect_filled(win->x + win->w - 160, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL); widget_button_draw(&wm_widget_ctx, &state->btn_fwd);
draw_string(win->x + win->w - 150, offset_y + 8, "^", COLOR_DARK_TEXT);
draw_rounded_rect_filled(win->x + win->w - 125, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL);
draw_string(win->x + win->w - 115, offset_y + 8, "v", COLOR_DARK_TEXT);
int content_start_y = offset_y + 30; int content_start_y = offset_y + 30;
graphics_set_clipping(win->x + 4, content_start_y, win->w - 8, win->h - 54 - 4); graphics_push_clipping(win->x + 4, content_start_y, win->w - 8, win->h - 54 - 4);
for (int i = 0; i < state->item_count; i++) { for (int i = 0; i < state->item_count; i++) {
int row = i / EXPLORER_COLS; int row = i / EXPLORER_COLS;
@@ -989,40 +975,8 @@ static void explorer_paint(Window *win) {
} }
if (dirty.active) { graphics_pop_clipping(); // Pop content clipping
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h); graphics_pop_clipping(); // Pop main window clipping
} else {
graphics_clear_clipping();
}
if (state->drive_menu_visible) {
int menu_x = win->x + 4;
int menu_y = offset_y + 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
draw_rounded_rect_filled(menu_x, menu_y, menu_w, menu_h, 6, COLOR_DARK_PANEL);
for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
char buf[16];
buf[0] = d->letter;
buf[1] = ':';
buf[2] = ' ';
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
buf[3+n] = 0;
if (d->letter == current_drv) {
draw_rounded_rect_filled(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, 4, 0xFF4A90E2);
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
} else {
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_DARK_TEXT);
}
}
}
}
if (state->dropdown_menu_visible) { if (state->dropdown_menu_visible) {
int menu_x = dropdown_btn_x; int menu_x = dropdown_btn_x;
@@ -1043,25 +997,15 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 10, "Create New File", COLOR_WHITE); draw_string(dlg_x + 10, dlg_y + 10, "Create New File", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); state->dialog_textbox.focused = true;
{ int max_w = 265; state->dialog_textbox.cursor_pos = state->dialog_input_cursor;
ttf_font_t *ttf_ = graphics_get_current_ttf(); widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox);
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0;
if (total_w > max_w) scroll_x = total_w - max_w;
char sub_[128]; int k_=0;
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_];
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Create");
draw_string(dlg_x + 70, dlg_y + 72, "Create", COLOR_WHITE); widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); widget_button_draw(&wm_widget_ctx, &state->btn_primary);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
} else if (state->dialog_state == DIALOG_CREATE_FOLDER) { } else if (state->dialog_state == DIALOG_CREATE_FOLDER) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
@@ -1070,25 +1014,15 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 10, "Create New Folder", COLOR_WHITE); draw_string(dlg_x + 10, dlg_y + 10, "Create New Folder", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); state->dialog_textbox.focused = true;
{ int max_w = 265; state->dialog_textbox.cursor_pos = state->dialog_input_cursor;
ttf_font_t *ttf_ = graphics_get_current_ttf(); widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox);
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0;
if (total_w > max_w) scroll_x = total_w - max_w;
char sub_[128]; int k_=0;
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_];
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Create");
draw_string(dlg_x + 70, dlg_y + 72, "Create", COLOR_WHITE); widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); widget_button_draw(&wm_widget_ctx, &state->btn_primary);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
} else if (state->dialog_state == DIALOG_DELETE_CONFIRM) { } else if (state->dialog_state == DIALOG_DELETE_CONFIRM) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
@@ -1107,8 +1041,14 @@ static void explorer_paint(Window *win) {
} }
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, 0xFF8B2020); draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, 0xFF8B2020);
draw_string(dlg_x + 68, dlg_y + 72, "Delete", COLOR_WHITE); draw_string(dlg_x + 68, dlg_y + 72, "Delete", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); // Use libwidget only for the cancel button, or do we want to use libwidget for the delete button too?
// Let's use libwidget but the delete button needs red styling so maybe just keep it manual or make it secondary.
// Actually wait, I will use libwidget for both and let the text dictate the action, we can't style individual buttons yet.
widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Delete");
widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
widget_button_draw(&wm_widget_ctx, &state->btn_primary);
widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) { } else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
@@ -1120,10 +1060,10 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", 0xFFAAAAAA);
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA);
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Replace");
draw_string(dlg_x + 63, dlg_y + 77, "Replace", COLOR_WHITE); widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); widget_button_draw(&wm_widget_ctx, &state->btn_primary);
draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE); widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) { } else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
@@ -1150,10 +1090,10 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 35, "Overwrite existing file?", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 35, "Overwrite existing file?", 0xFFAAAAAA);
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA);
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Overwrite");
draw_string(dlg_x + 57, dlg_y + 77, "Overwrite", COLOR_WHITE); widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); widget_button_draw(&wm_widget_ctx, &state->btn_primary);
draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE); widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
} else if (state->dialog_state == DIALOG_ERROR) { } else if (state->dialog_state == DIALOG_ERROR) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
@@ -1163,32 +1103,23 @@ static void explorer_paint(Window *win) {
draw_string(dlg_x + 10, dlg_y + 10, "Error", 0xFFFF6B6B); draw_string(dlg_x + 10, dlg_y + 10, "Error", 0xFFFF6B6B);
draw_string(dlg_x + 10, dlg_y + 40, state->dialog_input, 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 40, state->dialog_input, 0xFFAAAAAA);
draw_rounded_rect_filled(dlg_x + 110, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); widget_button_init(&state->btn_primary, dlg_x + 110, dlg_y + 70, 80, 25, "OK");
draw_string(dlg_x + 138, dlg_y + 77, "OK", COLOR_WHITE); widget_button_draw(&wm_widget_ctx, &state->btn_primary);
} else if (state->dialog_state == DIALOG_RENAME) { } else if (state->dialog_state == DIALOG_RENAME) {
int dlg_x = win->x + win->w / 2 - 150; int dlg_x = win->x + win->w / 2 - 150;
int dlg_y = win->y + win->h / 2 - 60; int dlg_y = win->y + win->h / 2 - 60;
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL); draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
draw_string(dlg_x + 10, dlg_y + 10, "Rename", COLOR_WHITE); draw_string(dlg_x + 10, dlg_y + 10, "Rename", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX);
draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); state->dialog_textbox.focused = true;
{ int max_w = 265; state->dialog_textbox.cursor_pos = state->dialog_input_cursor;
ttf_font_t *ttf_ = graphics_get_current_ttf(); widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox);
int total_w = font_manager_get_string_width(ttf_, state->dialog_input);
int scroll_x = 0; widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Rename");
if (total_w > max_w) scroll_x = total_w - max_w; widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel");
char sub_[128]; int k_=0; widget_button_draw(&wm_widget_ctx, &state->btn_primary);
for(k_=0; k_<state->dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_]; widget_button_draw(&wm_widget_ctx, &state->btn_secondary);
sub_[k_]=0;
int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x;
if (cx_ < 0) cx_ = 0;
if (cx_ > max_w) cx_ = max_w;
draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); }
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
draw_string(dlg_x + 68, dlg_y + 72, "Rename", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE);
} }
if (state->file_context_menu_visible) { if (state->file_context_menu_visible) {
@@ -1220,6 +1151,9 @@ static void explorer_paint(Window *win) {
} }
#define WIDGET_CLICKED(btn, cx, cy) ((cx) >= (btn)->x && (cx) < (btn)->x + (btn)->w && (cy) >= (btn)->y && (cy) < (btn)->y + (btn)->h)
#define TEXTBOX_CLICKED(tb, cx, cy) ((cx) >= (tb)->x && (cx) < (tb)->x + (tb)->w && (cy) >= (tb)->y && (cy) < (tb)->y + (tb)->h)
static void explorer_handle_click(Window *win, int x, int y) { static void explorer_handle_click(Window *win, int x, int y) {
ExplorerState *state = (ExplorerState*)win->data; ExplorerState *state = (ExplorerState*)win->data;
if (state->file_context_menu_visible) { if (state->file_context_menu_visible) {
@@ -1228,156 +1162,117 @@ static void explorer_handle_click(Window *win, int x, int y) {
} }
if (state->dialog_state == DIALOG_CREATE_FILE || state->dialog_state == DIALOG_CREATE_FOLDER) { if (state->dialog_state == DIALOG_CREATE_FILE || state->dialog_state == DIALOG_CREATE_FOLDER) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (state->dialog_state == DIALOG_CREATE_FILE) dialog_confirm_create_file(win);
if (x >= dlg_x + 50 && x < dlg_x + 130 && else dialog_confirm_create_folder(win);
y >= dlg_y + 65 && y < dlg_y + 90) {
if (state->dialog_state == DIALOG_CREATE_FILE) {
dialog_confirm_create_file(win);
} else {
dialog_confirm_create_folder(win);
}
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y)) {
y >= dlg_y + 65 && y < dlg_y + 90) { state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
if (x >= dlg_x + 10 && x < dlg_x + 290 && if (TEXTBOX_CLICKED(&state->dialog_textbox, win->x + x, win->y + y)) {
y >= dlg_y + 35 && y < dlg_y + 55) { state->dialog_input_cursor = (win->x + x - state->dialog_textbox.x - 5) / 8;
state->dialog_input_cursor = (x - dlg_x - 15) / 8;
if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) { if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) {
state->dialog_input_cursor = explorer_strlen(state->dialog_input); state->dialog_input_cursor = explorer_strlen(state->dialog_input);
} }
if (state->dialog_input_cursor < 0) state->dialog_input_cursor = 0;
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_DELETE_CONFIRM) { } else if (state->dialog_state == DIALOG_DELETE_CONFIRM) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 50 && x < dlg_x + 130 &&
y >= dlg_y + 65 && y < dlg_y + 90) {
dialog_confirm_delete(win); dialog_confirm_delete(win);
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y)) {
y >= dlg_y + 65 && y < dlg_y + 90) { state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) { } else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
dialog_confirm_replace(win); dialog_confirm_replace(win);
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) {
state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) { } else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
dialog_confirm_replace_move(win); dialog_confirm_replace_move(win);
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) {
state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) { } else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
dialog_force_create_file(win); dialog_force_create_file(win);
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) {
state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_ERROR) { } else if (state->dialog_state == DIALOG_ERROR) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 110 && x < dlg_x + 190 && y >= dlg_y + 70 && y < dlg_y + 95) {
dialog_close(win); dialog_close(win);
return; return;
} }
return; return;
} else if (state->dialog_state == DIALOG_RENAME) { } else if (state->dialog_state == DIALOG_RENAME) {
int dlg_x = win->w / 2 - 150; if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) {
int dlg_y = win->h / 2 - 80; state->btn_primary.pressed = false;
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 65 && y < dlg_y + 90) {
char new_path[FAT32_MAX_PATH]; char new_path[FAT32_MAX_PATH];
explorer_strcpy(new_path, state->current_path); explorer_strcpy(new_path, state->current_path);
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input); explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh_all(); if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh_all();
dialog_close(win); dialog_close(win);
return; return;
} }
if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 65 && y < dlg_y + 90) { if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) {
state->btn_secondary.pressed = false;
dialog_close(win); dialog_close(win);
return; return;
} }
if (TEXTBOX_CLICKED(&state->dialog_textbox, win->x + x, win->y + y + 20)) {
if (x >= dlg_x + 10 && x < dlg_x + 290 && y >= dlg_y + 35 && y < dlg_y + 55) { state->dialog_input_cursor = (win->x + x - state->dialog_textbox.x - 5) / 8;
state->dialog_input_cursor = (x - dlg_x - 15) / 8;
if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) { if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) {
state->dialog_input_cursor = explorer_strlen(state->dialog_input); state->dialog_input_cursor = explorer_strlen(state->dialog_input);
} }
if (state->dialog_input_cursor < 0) state->dialog_input_cursor = 0;
return; return;
} }
return; return;
} }
if (state->drive_menu_visible) {
int menu_x = 4;
int menu_y = 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
if (x >= menu_x && x < menu_x + menu_w && y >= menu_y && y < menu_y + menu_h) {
int idx = (y - menu_y) / 25;
Disk *d = disk_get_by_index(idx);
if (d) {
char path[4];
path[0] = d->letter;
path[1] = ':';
path[2] = '/';
path[3] = 0;
explorer_load_directory(win, path);
}
state->drive_menu_visible = false;
return;
}
state->drive_menu_visible = false;
return;
}
if (state->dropdown_menu_visible) { if (state->dropdown_menu_visible) {
int dropdown_btn_x = win->w - 90; int dropdown_btn_x = win->w - 90;
@@ -1413,34 +1308,28 @@ static void explorer_handle_click(Window *win, int x, int y) {
} }
int button_y = 3;
if (x >= 4 && x < 64 && y >= button_y && y < button_y + 22) {
state->drive_menu_visible = !state->drive_menu_visible;
state->dropdown_menu_visible = false;
return;
}
if (x >= win->w - 90 && x < win->w - 55 &&
y >= button_y && y < button_y + 22) { if (WIDGET_CLICKED(&state->btn_dropdown, win->x + x, win->y + y + 20)) {
state->btn_dropdown.pressed = false;
dropdown_menu_toggle(win); dropdown_menu_toggle(win);
state->drive_menu_visible = false;
return; return;
} }
if (x >= win->w - 40 && x < win->w - 10 && if (WIDGET_CLICKED(&state->btn_back, win->x + x, win->y + y + 20)) {
y >= button_y && y < button_y + 22) { state->btn_back.pressed = false;
explorer_navigate_to(win, ".."); explorer_navigate_to(win, "..");
return; return;
} }
if (x >= win->w - 160 && x < win->w - 130 && if (WIDGET_CLICKED(&state->btn_up, win->x + x, win->y + y + 20)) {
y >= button_y && y < button_y + 22) { state->btn_up.pressed = false;
if (state->explorer_scroll_row > 0) state->explorer_scroll_row--; if (state->explorer_scroll_row > 0) state->explorer_scroll_row--;
return; return;
} }
if (x >= win->w - 125 && x < win->w - 95 && if (WIDGET_CLICKED(&state->btn_fwd, win->x + x, win->y + y + 20)) {
y >= button_y && y < button_y + 22) { state->btn_fwd.pressed = false;
int total_rows = (state->item_count + EXPLORER_COLS - 1) / EXPLORER_COLS; int total_rows = (state->item_count + EXPLORER_COLS - 1) / EXPLORER_COLS;
if (total_rows == 0) total_rows = 1; if (total_rows == 0) total_rows = 1;
if (state->explorer_scroll_row < total_rows - (EXPLORER_ROWS - 1)) state->explorer_scroll_row++; if (state->explorer_scroll_row < total_rows - (EXPLORER_ROWS - 1)) state->explorer_scroll_row++;
@@ -1494,7 +1383,7 @@ static void explorer_handle_key(Window *win, char c, bool pressed) {
explorer_strcpy(new_path, state->current_path); explorer_strcpy(new_path, state->current_path);
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input); explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh(win); if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh(win);
dialog_close(win); dialog_close(win);
} }
} else if (c == 19) { } else if (c == 19) {
@@ -1716,7 +1605,7 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
state->dialog_input_cursor = explorer_strlen(state->dialog_input); state->dialog_input_cursor = explorer_strlen(state->dialog_input);
explorer_strcpy(state->dialog_target_path, full_path); explorer_strcpy(state->dialog_target_path, full_path);
} else if (clicked_action == 110) { // Open with Text Editor } else if (clicked_action == 110) { // Open with Text Editor
process_create_elf("A:/bin/txtedit.elf", full_path); process_create_elf("/bin/txtedit.elf", full_path);
} else if (clicked_action == ACTION_RESTORE) { } else if (clicked_action == ACTION_RESTORE) {
explorer_restore_file(win, state->file_context_menu_item); explorer_restore_file(win, state->file_context_menu_item);
} else if (clicked_action == ACTION_CREATE_SHORTCUT) { } else if (clicked_action == ACTION_CREATE_SHORTCUT) {
@@ -1810,10 +1699,10 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path); explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w"); vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) { if (fh) {
fat32_write(fh, source_path, explorer_strlen(source_path)); vfs_write(fh, source_path, explorer_strlen(source_path));
fat32_close(fh); vfs_close(fh);
} }
} }
@@ -1821,7 +1710,7 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, source_path); explorer_strcpy(origin_path, source_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
fat32_delete(origin_path); vfs_delete(origin_path);
} }
if (explorer_copy_recursive(source_path, dest_path)) { if (explorer_copy_recursive(source_path, dest_path)) {
@@ -1855,7 +1744,7 @@ void explorer_import_file_to(Window *win, const char *source_path, const char *d
if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/"); if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/");
explorer_strcat(dest_path, filename); explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) { if (vfs_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) {
explorer_strcpy(state->dialog_move_src, source_path); explorer_strcpy(state->dialog_move_src, source_path);
explorer_strcpy(state->dialog_dest_dir, dest_dir); explorer_strcpy(state->dialog_dest_dir, dest_dir);
state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM; state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM;
@@ -1900,12 +1789,12 @@ Window* explorer_create_window(const char *path) {
state->explorer_scroll_row = 0; state->explorer_scroll_row = 0;
state->dialog_state = DIALOG_NONE; state->dialog_state = DIALOG_NONE;
if (explorer_strcmp(path, "/") == 0) explorer_load_directory(win, "A:/"); if (explorer_strcmp(path, "/") == 0) explorer_load_directory(win, "/");
else explorer_load_directory(win, path); else explorer_load_directory(win, path);
explorer_wins[explorer_win_count++] = win; explorer_wins[explorer_win_count++] = win;
wm_add_window(win); wm_add_window_locked(win);
wm_bring_to_front(win); // wm_add_window_locked already calls wm_bring_to_front_locked!
return win; return win;
} }
@@ -1937,11 +1826,11 @@ void explorer_init(void) {
state->dialog_state = DIALOG_NONE; state->dialog_state = DIALOG_NONE;
explorer_wins[explorer_win_count++] = &win_explorer; explorer_wins[explorer_win_count++] = &win_explorer;
explorer_load_directory(&win_explorer, "A:/"); explorer_load_directory(&win_explorer, "/");
} }
void explorer_reset(void) { void explorer_reset(void) {
ExplorerState *state = (ExplorerState*)win_explorer.data; ExplorerState *state = (ExplorerState*)win_explorer.data;
explorer_load_directory(&win_explorer, "A:/"); explorer_load_directory(&win_explorer, "/");
state->explorer_scroll_row = 0; state->explorer_scroll_row = 0;
win_explorer.focused = false; win_explorer.focused = false;
} }

View File

@@ -7,6 +7,7 @@
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.h"
#include <stddef.h> #include <stddef.h>
#include "libwidget.h"
// External windows references (for opening other apps) // External windows references (for opening other apps)
extern Window win_explorer; extern Window win_explorer;
@@ -55,6 +56,16 @@ typedef struct {
int file_context_menu_y; int file_context_menu_y;
int file_context_menu_item; int file_context_menu_item;
// GUI widgets
widget_button_t btn_primary;
widget_button_t btn_secondary;
widget_button_t btn_dropdown;
widget_button_t btn_back;
widget_button_t btn_up;
widget_button_t btn_fwd;
widget_textbox_t dialog_textbox;
} ExplorerState; } ExplorerState;
void explorer_init(void); void explorer_init(void);

View File

@@ -71,12 +71,13 @@ static int loaded_font_count = 0;
#define FONT_CACHE_SIZE 2048 #define FONT_CACHE_SIZE 2048
typedef struct { typedef struct {
char c; uint32_t codepoint;
float pixel_height; float scale;
void *font;
int w, h, xoff, yoff; int w, h, xoff, yoff;
unsigned char *bitmap; unsigned char *bitmap;
} font_cache_entry_t; } font_cache_entry_t;
static font_cache_entry_t font_cache[FONT_CACHE_SIZE] = {0};
bool font_manager_init(void) { bool font_manager_init(void) {
return true; return true;
@@ -200,11 +201,17 @@ void font_manager_render_char_scaled(ttf_font_t *font, int x, int y, uint32_t co
if (!font) font = default_font; if (!font) font = default_font;
if (!font) return; if (!font) return;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info; uint32_t hash = (codepoint * 31 + (uint32_t)scale * 73) % FONT_CACHE_SIZE;
font_cache_entry_t *entry = &font_cache[hash];
unsigned char *bitmap = NULL; unsigned char *bitmap = NULL;
int w, h, xoff, yoff; int w, h, xoff, yoff;
if (entry->bitmap && entry->codepoint == codepoint && entry->scale == scale && entry->font == font) {
bitmap = entry->bitmap;
w = entry->w; h = entry->h; xoff = entry->xoff; yoff = entry->yoff;
} else {
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) { if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
@@ -214,6 +221,14 @@ void font_manager_render_char_scaled(ttf_font_t *font, int x, int y, uint32_t co
bitmap = stbtt_GetCodepointBitmap(info, 0, real_scale, codepoint, &w, &h, &xoff, &yoff); bitmap = stbtt_GetCodepointBitmap(info, 0, real_scale, codepoint, &w, &h, &xoff, &yoff);
if (entry->bitmap) stbtt_FreeBitmap(entry->bitmap, NULL);
entry->codepoint = codepoint;
entry->scale = scale;
entry->font = font;
entry->w = w; entry->h = h; entry->xoff = xoff; entry->yoff = yoff;
entry->bitmap = bitmap;
}
if (bitmap) { if (bitmap) {
for (int row = 0; row < h; row++) { for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) { for (int col = 0; col < w; col++) {
@@ -226,7 +241,6 @@ void font_manager_render_char_scaled(ttf_font_t *font, int x, int y, uint32_t co
} }
} }
} }
stbtt_FreeBitmap(bitmap, NULL);
} }
} }
@@ -234,11 +248,17 @@ void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t co
if (!font) font = default_font; if (!font) font = default_font;
if (!font) return; if (!font) return;
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info; uint32_t hash = (codepoint * 31 + (uint32_t)scale * 73) % FONT_CACHE_SIZE;
font_cache_entry_t *entry = &font_cache[hash];
unsigned char *bitmap = NULL; unsigned char *bitmap = NULL;
int w, h, xoff, yoff; int w, h, xoff, yoff;
if (entry->bitmap && entry->codepoint == codepoint && entry->scale == scale && entry->font == font) {
bitmap = entry->bitmap;
w = entry->w; h = entry->h; xoff = entry->xoff; yoff = entry->yoff;
} else {
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) { if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
@@ -248,6 +268,14 @@ void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t co
bitmap = stbtt_GetCodepointBitmap(info, 0, real_scale, codepoint, &w, &h, &xoff, &yoff); bitmap = stbtt_GetCodepointBitmap(info, 0, real_scale, codepoint, &w, &h, &xoff, &yoff);
if (entry->bitmap) stbtt_FreeBitmap(entry->bitmap, NULL);
entry->codepoint = codepoint;
entry->scale = scale;
entry->font = font;
entry->w = w; entry->h = h; entry->xoff = xoff; entry->yoff = yoff;
entry->bitmap = bitmap;
}
if (bitmap) { if (bitmap) {
for (int row = 0; row < h; row++) { for (int row = 0; row < h; row++) {
int slant_offset = (int)((h - row) * slope); int slant_offset = (int)((h - row) * slope);
@@ -262,7 +290,6 @@ void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t co
} }
} }
} }
stbtt_FreeBitmap(bitmap, NULL);
} }
} }
@@ -303,6 +330,13 @@ int font_manager_get_string_width_scaled(ttf_font_t *font, const char *s, float
while (*s) { while (*s) {
int advance, lsb; int advance, lsb;
uint32_t codepoint = utf8_decode(&s); 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; stbtt_fontinfo *current_info = info;
float current_scale = real_scale; float current_scale = real_scale;
@@ -323,6 +357,12 @@ int font_manager_get_codepoint_width_scaled(ttf_font_t *font, uint32_t codepoint
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info; stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); 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) { if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
info = (stbtt_fontinfo *)fallback_font->info; info = (stbtt_fontinfo *)fallback_font->info;
real_scale = stbtt_ScaleForPixelHeight(info, scale); real_scale = stbtt_ScaleForPixelHeight(info, scale);

View File

@@ -31,12 +31,19 @@ static DirtyRect g_dirty = {0, 0, 0, 0, false};
#define MAX_FB_HEIGHT 2048 #define MAX_FB_HEIGHT 2048
static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096))); static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096)));
static int g_clip_x = 0, g_clip_y = 0, g_clip_w = 0, g_clip_h = 0; #define MAX_RENDER_CPUS 32
static bool g_clip_enabled = false; #define CLIP_STACK_DEPTH 8
static int g_clip_stack_x[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_y[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_w[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_h[MAX_RENDER_CPUS][CLIP_STACK_DEPTH];
static int g_clip_stack_ptr[MAX_RENDER_CPUS] = {0};
static bool g_clip_enabled[MAX_RENDER_CPUS] = {false};
static uint32_t *g_render_target = NULL; extern uint32_t smp_this_cpu_id(void);
static int g_rt_width = 0; static uint32_t *g_render_target[MAX_RENDER_CPUS] = {0};
static int g_rt_height = 0; static int g_rt_width[MAX_RENDER_CPUS] = {0};
static int g_rt_height[MAX_RENDER_CPUS] = {0};
static ttf_font_t *g_current_ttf = NULL; static ttf_font_t *g_current_ttf = NULL;
@@ -101,6 +108,14 @@ int get_screen_height(void) {
return g_fb ? g_fb->height : 0; return g_fb ? g_fb->height : 0;
} }
uint64_t graphics_get_fb_addr(void) {
return g_fb ? (uint64_t)g_fb->address : 0;
}
int graphics_get_fb_bpp(void) {
return g_fb ? g_fb->bpp : 0;
}
// Merge new dirty rect with existing one // Merge new dirty rect with existing one
static void merge_dirty_rect(int x, int y, int w, int h) { static void merge_dirty_rect(int x, int y, int w, int h) {
if (!g_dirty.active) { if (!g_dirty.active) {
@@ -176,15 +191,19 @@ void graphics_clear_dirty_no_lock(void) {
} }
void graphics_set_render_target(uint32_t *buffer, int w, int h) { void graphics_set_render_target(uint32_t *buffer, int w, int h) {
g_render_target = buffer; uint32_t cpu = smp_this_cpu_id();
g_rt_width = w; if (cpu < MAX_RENDER_CPUS) {
g_rt_height = h; g_render_target[cpu] = buffer;
g_rt_width[cpu] = w;
g_rt_height[cpu] = h;
}
} }
void put_pixel(int x, int y, uint32_t color) { void put_pixel(int x, int y, uint32_t color) {
if (g_render_target) { uint32_t cpu = smp_this_cpu_id();
if (x >= 0 && x < g_rt_width && y >= 0 && y < g_rt_height) { if (cpu < MAX_RENDER_CPUS && g_render_target[cpu]) {
g_render_target[y * g_rt_width + x] = color; if (x >= 0 && x < g_rt_width[cpu] && y >= 0 && y < g_rt_height[cpu]) {
g_render_target[cpu][y * g_rt_width[cpu] + x] = color;
} }
return; return;
} }
@@ -192,9 +211,10 @@ void put_pixel(int x, int y, uint32_t color) {
if (!g_fb) return; if (!g_fb) return;
if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return; if (x < 0 || x >= (int)g_fb->width || y < 0 || y >= (int)g_fb->height) return;
if (g_clip_enabled) { if (g_clip_enabled[cpu]) {
if (x < g_clip_x || x >= g_clip_x + g_clip_w || int ptr = g_clip_stack_ptr[cpu];
y < g_clip_y || y >= g_clip_y + g_clip_h) { if (x < g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y < g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return; return;
} }
} }
@@ -204,9 +224,10 @@ void put_pixel(int x, int y, uint32_t color) {
} }
uint32_t graphics_get_pixel(int x, int y) { uint32_t graphics_get_pixel(int x, int y) {
if (g_render_target) { uint32_t cpu = smp_this_cpu_id();
if (x >= 0 && x < g_rt_width && y >= 0 && y < g_rt_height) { if (cpu < MAX_RENDER_CPUS && g_render_target[cpu]) {
return g_render_target[y * g_rt_width + x]; if (x >= 0 && x < g_rt_width[cpu] && y >= 0 && y < g_rt_height[cpu]) {
return g_render_target[cpu][y * g_rt_width[cpu] + x];
} }
return 0; return 0;
} }
@@ -220,15 +241,16 @@ uint32_t graphics_get_pixel(int x, int y) {
void draw_rect(int x, int y, int w, int h, uint32_t color) { void draw_rect(int x, int y, int w, int h, uint32_t color) {
int x1 = x, y1 = y, x2 = x + w, y2 = y + h; int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
if (g_render_target) { uint32_t cpu = smp_this_cpu_id();
if (cpu < MAX_RENDER_CPUS && g_render_target[cpu]) {
if (x1 < 0) x1 = 0; if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0; if (y1 < 0) y1 = 0;
if (x2 > g_rt_width) x2 = g_rt_width; if (x2 > g_rt_width[cpu]) x2 = g_rt_width[cpu];
if (y2 > g_rt_height) y2 = g_rt_height; if (y2 > g_rt_height[cpu]) y2 = g_rt_height[cpu];
if (x1 >= x2 || y1 >= y2) return; if (x1 >= x2 || y1 >= y2) return;
for (int i = y1; i < y2; i++) { for (int i = y1; i < y2; i++) {
uint32_t *row = &g_render_target[i * g_rt_width + x1]; uint32_t *row = &g_render_target[cpu][i * g_rt_width[cpu] + x1];
int len = x2 - x1; int len = x2 - x1;
for (int j = 0; j < len; j++) { for (int j = 0; j < len; j++) {
row[j] = color; row[j] = color;
@@ -239,11 +261,12 @@ void draw_rect(int x, int y, int w, int h, uint32_t color) {
if (!g_fb) return; if (!g_fb) return;
if (g_clip_enabled) { if (g_clip_enabled[cpu]) {
if (x1 < g_clip_x) x1 = g_clip_x; int ptr = g_clip_stack_ptr[cpu];
if (y1 < g_clip_y) y1 = g_clip_y; if (x1 < g_clip_stack_x[cpu][ptr]) x1 = g_clip_stack_x[cpu][ptr];
if (x2 > g_clip_x + g_clip_w) x2 = g_clip_x + g_clip_w; if (y1 < g_clip_stack_y[cpu][ptr]) y1 = g_clip_stack_y[cpu][ptr];
if (y2 > g_clip_y + g_clip_h) y2 = g_clip_y + g_clip_h; if (x2 > g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]) x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr];
if (y2 > g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
} }
if (x1 < 0) x1 = 0; if (x1 < 0) x1 = 0;
@@ -279,28 +302,39 @@ static int isqrt(int n) {
void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) { void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) {
if (radius > w / 2) radius = w / 2; if (radius > w / 2) radius = w / 2;
if (radius > h / 2) radius = h / 2; if (radius > h / 2) radius = h / 2;
if (radius < 1) radius = 1; if (radius < 1) {
// Draw a simple rect outline if no radius
draw_rect(x, y, w, 1, color);
draw_rect(x, y + h - 1, w, 1, color);
draw_rect(x, y + 1, 1, h - 2, color);
draw_rect(x + w - 1, y + 1, 1, h - 2, color);
return;
}
// Draw top and bottom edges // Draw top and bottom straight edges
draw_rect(x + radius, y, w - 2*radius, 1, color); draw_rect(x + radius, y, w - 2*radius, 1, color);
draw_rect(x + radius, y + h - 1, w - 2*radius, 1, color); draw_rect(x + radius, y + h - 1, w - 2*radius, 1, color);
// Draw left and right edges // Draw left and right straight edges
draw_rect(x, y + radius, 1, h - 2*radius, color); draw_rect(x, y + radius, 1, h - 2*radius, color);
draw_rect(x + w - 1, y + radius, 1, h - 2*radius, color); draw_rect(x + w - 1, y + radius, 1, h - 2*radius, color);
// Draw corner circles using integer approximation // Draw four corner arcs
for (int i = 0; i < radius; i++) { for (int dy = 0; dy < radius; dy++) {
int j = isqrt(radius*radius - i*i); int y_dist = radius - 1 - dy;
int dx = isqrt(radius*radius - y_dist*y_dist);
int next_dx = (dy < radius - 1) ? isqrt(radius*radius - (y_dist - 1)*(y_dist - 1)) : radius;
// Top-left corner for (int i = dx; i < next_dx && i <= radius; i++) {
put_pixel(x + radius - i - 1, y + radius - j, color); // Top-left
// Top-right corner put_pixel(x + radius - 1 - i, y + dy, color);
put_pixel(x + w - radius + i, y + radius - j, color); // Top-right
// Bottom-left corner put_pixel(x + w - radius + i, y + dy, color);
put_pixel(x + radius - i - 1, y + h - radius + j - 1, color); // Bottom-left
// Bottom-right corner put_pixel(x + radius - 1 - i, y + h - 1 - dy, color);
put_pixel(x + w - radius + i, y + h - radius + j - 1, color); // Bottom-right
put_pixel(x + w - radius + i, y + h - 1 - dy, color);
}
} }
} }
@@ -308,25 +342,21 @@ void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) {
void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color) { void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color) {
if (radius > w / 2) radius = w / 2; if (radius > w / 2) radius = w / 2;
if (radius > h / 2) radius = h / 2; if (radius > h / 2) radius = h / 2;
if (radius < 1) radius = 1; if (radius < 1) {
draw_rect(x, y, w, h, color);
return;
}
// Draw main rectangle body (center part without corners) // Draw main rectangle body
draw_rect(x + radius, y, w - 2*radius, h, color); draw_rect(x, y + radius, w, h - 2*radius, color);
draw_rect(x, y + radius, radius, h - 2*radius, color);
draw_rect(x + w - radius, y + radius, radius, h - 2*radius, color);
// Draw rounded top and bottom caps
for (int dy = 0; dy < radius; dy++) { for (int dy = 0; dy < radius; dy++) {
int dx_top = isqrt(radius*radius - (radius - dy) * (radius - dy)); int y_dist = radius - 1 - dy;
int dx = isqrt(radius*radius - y_dist*y_dist);
int dx_bottom = isqrt(radius*radius - dy*dy); draw_rect(x + radius - dx, y + dy, w - 2*radius + 2*dx, 1, color);
draw_rect(x + radius - dx, y + h - 1 - dy, w - 2*radius + 2*dx, 1, color);
draw_rect(x + radius - dx_top, y + dy, dx_top, 1, color);
draw_rect(x + w - radius, y + dy, dx_top, 1, color);
draw_rect(x + radius - dx_bottom, y + h - radius + dy, dx_bottom, 1, color);
draw_rect(x + w - radius, y + h - radius + dy, dx_bottom, 1, color);
} }
} }
@@ -394,16 +424,23 @@ void draw_rounded_rect_blurred(int x, int y, int w, int h, int radius, uint32_t
} }
} }
for (int c = 0; c < w; c++) {
for (int r = 0; r < h; r++) { for (int r = 0; r < h; r++) {
int g_y = y + r; int g_y = y + r;
int g_x = x + c; if (g_y < 0 || g_y >= sh) continue;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
if (g_x < g_clip_x || g_x >= g_clip_x + g_clip_w || if (g_clip_enabled[cpu]) {
g_y < g_clip_y || g_y >= g_clip_y + g_clip_h) { int ptr = g_clip_stack_ptr[cpu];
continue; if (g_y < g_clip_stack_y[cpu][ptr] || g_y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) continue;
} }
for (int c = 0; c < w; c++) {
int g_x = x + c;
if (g_x < 0 || g_x >= sw) continue;
if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
if (g_x < g_clip_stack_x[cpu][ptr] || g_x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]) continue;
} }
bool in_corner = false; bool in_corner = false;
@@ -461,9 +498,12 @@ void draw_char(int x, int y, char c, uint32_t color) {
unsigned char uc = (unsigned char)c; unsigned char uc = (unsigned char)c;
if (uc > 127) return; if (uc > 127) return;
if (g_clip_enabled && !g_render_target) { uint32_t cpu = smp_this_cpu_id();
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) { if (g_clip_enabled[cpu] && !has_rt) {
int ptr = g_clip_stack_ptr[cpu];
if (x + 8 <= g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y + 8 <= g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return; return;
} }
} }
@@ -479,14 +519,16 @@ void draw_char(int x, int y, char c, uint32_t color) {
} }
} }
// Bitmap-only version for terminal — always uses 8x8 bitmap font regardless of TTF
void draw_char_bitmap(int x, int y, char c, uint32_t color) { void draw_char_bitmap(int x, int y, char c, uint32_t color) {
unsigned char uc = (unsigned char)c; unsigned char uc = (unsigned char)c;
if (uc > 127) return; if (uc > 127) return;
if (g_clip_enabled && !g_render_target) { uint32_t cpu = smp_this_cpu_id();
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || bool has_rt = (cpu < MAX_RENDER_CPUS && g_render_target[cpu]);
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) { if (g_clip_enabled[cpu] && !has_rt) {
int ptr = g_clip_stack_ptr[cpu];
if (x + 8 <= g_clip_stack_x[cpu][ptr] || x >= g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr] ||
y + 8 <= g_clip_stack_y[cpu][ptr] || y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) {
return; return;
} }
} }
@@ -513,6 +555,8 @@ void draw_string_bitmap(int x, int y, const char *str, uint32_t color) {
if (*s == '\n') { if (*s == '\n') {
cur_x = x; cur_x = x;
cur_y += 10; cur_y += 10;
} else if (*s == '\t') {
cur_x += 8 * 4;
} else { } else {
draw_char_bitmap(cur_x, cur_y, *s, color); draw_char_bitmap(cur_x, cur_y, *s, color);
cur_x += 8; cur_x += 8;
@@ -541,8 +585,9 @@ int graphics_get_string_width_scaled(const char *s, float scale) {
} }
int len = 0; int len = 0;
while (s && *s) { while (s && *s) {
utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
len++; if (codepoint == '\t') len += 4;
else len++;
} }
return len * 8; // Fallback bitmap width return len * 8; // Fallback bitmap width
} }
@@ -557,7 +602,6 @@ void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale
int cur_x = x; int cur_x = x;
if (g_current_ttf) { if (g_current_ttf) {
// We let the font manager handle the stbtt scale internally to avoid bringing stb_truetype into graphics.c
int baseline = y + font_manager_get_font_ascent_scaled(g_current_ttf, scale) - 2; int baseline = y + font_manager_get_font_ascent_scaled(g_current_ttf, scale) - 2;
int line_height = font_manager_get_font_line_height_scaled(g_current_ttf, scale); int line_height = font_manager_get_font_line_height_scaled(g_current_ttf, scale);
@@ -566,6 +610,8 @@ void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale
if (codepoint == '\n') { if (codepoint == '\n') {
cur_x = x; cur_x = x;
baseline += line_height; baseline += line_height;
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, ' ', scale) * 4;
} else { } else {
font_manager_render_char_scaled(g_current_ttf, cur_x, baseline, codepoint, color, scale, put_pixel); 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); cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, codepoint, scale);
@@ -580,6 +626,8 @@ void draw_string_scaled(int x, int y, const char *s, uint32_t color, float scale
if (codepoint == '\n') { if (codepoint == '\n') {
cur_x = x; cur_x = x;
cur_y += 10; cur_y += 10;
} else if (codepoint == '\t') {
cur_x += 8 * 4;
} else { } else {
draw_char(cur_x, cur_y, (codepoint < 128) ? (char)codepoint : '?', color); draw_char(cur_x, cur_y, (codepoint < 128) ? (char)codepoint : '?', color);
cur_x += 8; cur_x += 8;
@@ -605,6 +653,8 @@ void draw_string_scaled_sloped(int x, int y, const char *s, uint32_t color, floa
if (codepoint == '\n') { if (codepoint == '\n') {
cur_x = x; cur_x = x;
baseline += line_height; baseline += line_height;
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, ' ', scale) * 4;
} else { } else {
font_manager_render_char_sloped(g_current_ttf, cur_x, baseline, codepoint, color, scale, slope, put_pixel); 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); cur_x += font_manager_get_codepoint_width_scaled(g_current_ttf, codepoint, scale);
@@ -623,9 +673,11 @@ void draw_desktop_background(void) {
if (g_use_image && g_bg_image) { if (g_use_image && g_bg_image) {
// Draw wallpaper image (stretch/scale to screen) // Draw wallpaper image (stretch/scale to screen)
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height; int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
x1 = g_clip_x; y1 = g_clip_y; if (g_clip_enabled[cpu]) {
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h; int ptr = g_clip_stack_ptr[cpu];
x1 = g_clip_stack_x[cpu][ptr]; y1 = g_clip_stack_y[cpu][ptr];
x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]; y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
} }
for (int y = y1; y < y2; y++) { for (int y = y1; y < y2; y++) {
int src_y = y * g_bg_image_h / (int)g_fb->height; int src_y = y * g_bg_image_h / (int)g_fb->height;
@@ -640,9 +692,11 @@ void draw_desktop_background(void) {
} else if (g_use_pattern) { } else if (g_use_pattern) {
// Optimized tiled pattern: only draw within the clipping/dirty rect // Optimized tiled pattern: only draw within the clipping/dirty rect
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height; int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) { uint32_t cpu = smp_this_cpu_id();
x1 = g_clip_x; y1 = g_clip_y; if (g_clip_enabled[cpu]) {
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h; int ptr = g_clip_stack_ptr[cpu];
x1 = g_clip_stack_x[cpu][ptr]; y1 = g_clip_stack_y[cpu][ptr];
x2 = g_clip_stack_x[cpu][ptr] + g_clip_stack_w[cpu][ptr]; y2 = g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr];
} }
for (int y = y1; y < y2; y++) { for (int y = y1; y < y2; y++) {
@@ -758,10 +812,9 @@ void graphics_flip_buffer(void) {
uint32_t *src_row = &g_back_buffer[curr_y * g_fb->width + x]; uint32_t *src_row = &g_back_buffer[curr_y * g_fb->width + x];
if (g_fb->bpp == 32) { if (g_fb->bpp == 32) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
for (int j = 0; j < w; j++) { mem_memcpy(dst_row, src_row, w * 4);
dst_row[j] = src_row[j];
}
} else if (g_fb->bpp == 16) { } else if (g_fb->bpp == 16) {
uint16_t *dst_row = (uint16_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; uint16_t *dst_row = (uint16_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
for (int j = 0; j < w; j++) { for (int j = 0; j < w; j++) {
@@ -794,12 +847,6 @@ void graphics_flip_buffer(void) {
int gray = (r * 77 + g * 150 + b * 29) >> 8; int gray = (r * 77 + g * 150 + b * 29) >> 8;
// Boost contrast by 2x to separate the dark UI colors:
// Background (~30) -> 60
// Panel (~40) -> 80
// With thresholds {0, 64, 128, 192}:
// BG > 0 (1/4 white), Panel > 64 (2/4 white - checkerboard)
// Text (~170) -> 255 (solid white)
gray = gray * 2; gray = gray * 2;
if (gray > 255) gray = 255; if (gray > 255) gray = 255;
@@ -824,19 +871,49 @@ void graphics_flip_buffer(void) {
void graphics_copy_screenbuffer(uint32_t *dest) { void graphics_copy_screenbuffer(uint32_t *dest) {
if (!g_fb || !dest) return; if (!g_fb || !dest) return;
uint64_t rflags; extern uint64_t wm_lock_acquire(void);
asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); extern void wm_lock_release(uint64_t);
int sw = g_fb->width; uint64_t rflags = wm_lock_acquire();
int sh = g_fb->height;
// Copy the internal back object to the dest directly int sw = (int)g_fb->width;
int sh = (int)g_fb->height;
// Copy from the composition back buffer, applying color mode transformations if necessary
for (int y = 0; y < sh; y++) { for (int y = 0; y < sh; y++) {
uint32_t *src_row = &g_back_buffer[y * sw]; uint32_t *src_row = &g_back_buffer[y * sw];
for (int x = 0; x < sw; x++) { for (int x = 0; x < sw; x++) {
dest[y * sw + x] = src_row[x]; uint32_t px = src_row[x];
if (g_color_mode == 1) { // 8-bit Grayscale
uint8_t r = (px >> 16) & 0xFF;
uint8_t g = (px >> 8) & 0xFF;
uint8_t b = px & 0xFF;
uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8);
dest[y * sw + x] = 0xFF000000 | (gray << 16) | (gray << 8) | gray;
} else if (g_color_mode == 2) { // 1-bit Monochrome (Dithered)
static const uint8_t bayer2[2][2] = {
{ 0, 128 },
{192, 64 }
};
uint8_t r = (px >> 16) & 0xFF;
uint8_t g = (px >> 8) & 0xFF;
uint8_t b = px & 0xFF;
int gray = (r * 77 + g * 150 + b * 29) >> 8;
// Boost contrast (matches graphics_flip_buffer logic)
gray = gray * 2;
if (gray > 255) gray = 255;
uint8_t threshold = bayer2[y & 1][x & 1];
dest[y * sw + x] = (gray > threshold) ? 0xFFFFFFFF : 0xFF000000;
} else {
// 32-bit (Standard)
dest[y * sw + x] = px;
} }
} }
asm volatile("push %0; popfq" : : "r"(rflags)); }
wm_lock_release(rflags);
} }
void graphics_set_clipping(int x, int y, int w, int h) { void graphics_set_clipping(int x, int y, int w, int h) {
@@ -849,33 +926,94 @@ void graphics_set_clipping(int x, int y, int w, int h) {
if (w < 0) w = 0; if (w < 0) w = 0;
if (h < 0) h = 0; if (h < 0) h = 0;
g_clip_x = x; uint32_t cpu = smp_this_cpu_id();
g_clip_y = y; g_clip_stack_x[cpu][0] = x;
g_clip_w = w; g_clip_stack_y[cpu][0] = y;
g_clip_h = h; g_clip_stack_w[cpu][0] = w;
g_clip_enabled = true; g_clip_stack_h[cpu][0] = h;
g_clip_stack_ptr[cpu] = 0; // Reset to base
g_clip_enabled[cpu] = true;
}
void graphics_push_clipping(int x, int y, int w, int h) {
uint32_t cpu = smp_this_cpu_id();
int cur_ptr = g_clip_stack_ptr[cpu];
if (cur_ptr + 1 >= CLIP_STACK_DEPTH) return; // Stack overflow
// Intersect with current top
int cx1 = g_clip_stack_x[cpu][cur_ptr];
int cy1 = g_clip_stack_y[cpu][cur_ptr];
int cx2 = cx1 + g_clip_stack_w[cpu][cur_ptr];
int cy2 = cy1 + g_clip_stack_h[cpu][cur_ptr];
int nx1 = x;
int ny1 = y;
int nx2 = x + w;
int ny2 = y + h;
if (nx1 < cx1) nx1 = cx1;
if (ny1 < cy1) ny1 = cy1;
if (nx2 > cx2) nx2 = cx2;
if (ny2 > cy2) ny2 = cy2;
int nw = nx2 - nx1;
int nh = ny2 - ny1;
if (nw < 0) nw = 0;
if (nh < 0) nh = 0;
g_clip_stack_ptr[cpu]++;
g_clip_stack_x[cpu][cur_ptr + 1] = nx1;
g_clip_stack_y[cpu][cur_ptr + 1] = ny1;
g_clip_stack_w[cpu][cur_ptr + 1] = nw;
g_clip_stack_h[cpu][cur_ptr + 1] = nh;
g_clip_enabled[cpu] = true;
}
void graphics_pop_clipping(void) {
uint32_t cpu = smp_this_cpu_id();
if (g_clip_stack_ptr[cpu] > 0) {
g_clip_stack_ptr[cpu]--;
} else {
g_clip_enabled[cpu] = false;
}
} }
void graphics_clear_clipping(void) { void graphics_clear_clipping(void) {
g_clip_enabled = false; uint32_t cpu = smp_this_cpu_id();
g_clip_stack_ptr[cpu] = 0;
g_clip_enabled[cpu] = false;
} }
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) { void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
if (!g_fb || !src) return; if (!g_fb || !src) return;
int sw = get_screen_width(); int sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
for (int y = 0; y < h; y++) { uint32_t cpu = smp_this_cpu_id();
int vy = dst_y + y; int cx1 = 0, cy1 = 0, cx2 = sw, cy2 = sh;
if (vy < 0 || vy >= sh) continue; if (g_clip_enabled[cpu]) {
int ptr = g_clip_stack_ptr[cpu];
cx1 = g_clip_stack_x[cpu][ptr];
cy1 = g_clip_stack_y[cpu][ptr];
cx2 = cx1 + g_clip_stack_w[cpu][ptr];
cy2 = cy1 + g_clip_stack_h[cpu][ptr];
}
for (int x = 0; x < w; x++) { int x1 = dst_x, y1 = dst_y, x2 = dst_x + w, y2 = dst_y + h;
int vx = dst_x + x; if (x1 < cx1) x1 = cx1;
if (vx < 0 || vx >= sw) continue; if (y1 < cy1) y1 = cy1;
if (x2 > cx2) x2 = cx2;
if (y2 > cy2) y2 = cy2;
uint32_t pcol = src[y * w + x]; if (x1 >= x2 || y1 >= y2) return;
for (int y = y1; y < y2; y++) {
uint32_t *dst_row = &g_back_buffer[y * sw + x1];
uint32_t *src_row = &src[(y - dst_y) * w + (x1 - dst_x)];
int len = x2 - x1;
for (int x = 0; x < len; x++) {
uint32_t pcol = src_row[x];
if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) { if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) {
g_back_buffer[vy * sw + vx] = pcol; dst_row[x] = pcol;
} }
} }
} }

View File

@@ -42,6 +42,8 @@ void draw_boredos_logo(int x, int y, int scale);
// Get screen dimensions // Get screen dimensions
int get_screen_width(void); int get_screen_width(void);
int get_screen_height(void); int get_screen_height(void);
uint64_t graphics_get_fb_addr(void);
int graphics_get_fb_bpp(void);
void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode); void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode);
// Dirty rectangle management // Dirty rectangle management
@@ -57,6 +59,8 @@ void graphics_clear_back_buffer(uint32_t color);
// Clipping // Clipping
void graphics_set_clipping(int x, int y, int w, int h); void graphics_set_clipping(int x, int y, int w, int h);
void graphics_push_clipping(int x, int y, int w, int h);
void graphics_pop_clipping(void);
void graphics_clear_clipping(void); void graphics_clear_clipping(void);
// Font access (requires font_manager.h for ttf_font_t) // Font access (requires font_manager.h for ttf_font_t)

View File

@@ -14,10 +14,11 @@
#define GUI_CMD_GET_STRING_WIDTH 8 #define GUI_CMD_GET_STRING_WIDTH 8
#define GUI_CMD_GET_FONT_HEIGHT 9 #define GUI_CMD_GET_FONT_HEIGHT 9
#define GUI_CMD_WINDOW_SET_RESIZABLE 14 #define GUI_CMD_WINDOW_SET_RESIZABLE 14
#define GUI_CMD_GET_SCREEN_SIZE 17 // Remapped Screenshot API Commands to avoid collisions (Originals 17, 18, 19 conflicted with magic numbers)
#define GUI_CMD_GET_SCREENBUFFER 18 #define GUI_CMD_GET_SCREEN_SIZE 50
#define GUI_CMD_SHOW_NOTIFICATION 19 #define GUI_CMD_GET_SCREENBUFFER 51
#define GUI_CMD_GET_DATETIME 20 #define GUI_CMD_SHOW_NOTIFICATION 52
#define GUI_CMD_GET_DATETIME 53
#define GUI_EVENT_NONE 0 #define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1 #define GUI_EVENT_PAINT 1

455
src/wm/libwidget.c Normal file
View File

@@ -0,0 +1,455 @@
#include "libwidget.h"
#include <stddef.h>
#define COLOR_GRAY 0xFFC0C0C0
#define COLOR_LTGRAY 0xFFDFDFDF
#define COLOR_DKGRAY 0xFF808080
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_BLACK 0xFF000000
#define COLOR_SCROLLBAR_BG 0xFF2A2A2A
#define COLOR_SCROLLBAR_THUMB 0xFF505050
#define COLOR_SCROLLBAR_THUMB_HOVER 0xFF707070
#define COLOR_SCROLLBAR_THUMB_DRAG 0xFF909090
static size_t string_len(const char *str) {
size_t l = 0;
while(str && str[l]) l++;
return l;
}
#define MAC_BTN_BORDER 0xFF4A4A4C
#define MAC_BTN_BG_NORMAL 0xFF353537
#define MAC_BTN_BG_HOVER 0xFF454547
#define MAC_BTN_BG_PRESSED 0xFF555557
// --- Button Implementation ---
void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text) {
btn->x = x;
btn->y = y;
btn->w = w;
btn->h = h;
btn->text = text;
btn->pressed = false;
btn->hovered = false;
btn->on_click = NULL;
}
void widget_button_draw(widget_context_t *ctx, widget_button_t *btn) {
uint32_t border_color = MAC_BTN_BORDER;
uint32_t normal_bg = MAC_BTN_BG_NORMAL;
uint32_t hover_bg = MAC_BTN_BG_HOVER;
uint32_t pressed_bg = MAC_BTN_BG_PRESSED;
uint32_t text_color = COLOR_WHITE;
if (ctx->use_light_theme) {
border_color = 0xFFB0B0B0;
normal_bg = 0xFFEAEAEA;
hover_bg = 0xFFD0D0D0;
pressed_bg = 0xFFB0B0B0;
text_color = COLOR_BLACK;
}
uint32_t bg_color = normal_bg;
if (btn->pressed) {
bg_color = pressed_bg;
} else if (btn->hovered) {
bg_color = hover_bg;
}
if (ctx->draw_rounded_rect_filled) {
ctx->draw_rounded_rect_filled(ctx->user_data, btn->x, btn->y, btn->w, btn->h, 6, border_color);
ctx->draw_rounded_rect_filled(ctx->user_data, btn->x + 1, btn->y + 1, btn->w - 2, btn->h - 2, 5, bg_color);
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, btn->x, btn->y, btn->w, btn->h, border_color);
ctx->draw_rect(ctx->user_data, btn->x + 1, btn->y + 1, btn->w - 2, btn->h - 2, bg_color);
}
if (btn->text && ctx->draw_string) {
int len = string_len(btn->text);
int tx = btn->x + (btn->w - (len * 8)) / 2;
int ty = btn->y + (btn->h - 8) / 2;
ctx->draw_string(ctx->user_data, tx, ty, btn->text, text_color);
}
}
bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data) {
bool in_bounds = (mx >= btn->x && mx < btn->x + btn->w && my >= btn->y && my < btn->y + btn->h);
btn->hovered = in_bounds;
if (mouse_clicked && in_bounds) {
btn->pressed = true;
return true;
}
if (!mouse_down && btn->pressed) {
btn->pressed = false;
if (in_bounds && btn->on_click) {
btn->on_click(user_data);
}
return true;
}
return in_bounds;
}
// --- Scrollbar Implementation ---
void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h) {
sb->x = x;
sb->y = y;
sb->w = w;
sb->h = h;
sb->content_height = h;
sb->scroll_y = 0;
sb->is_dragging = false;
sb->on_scroll = NULL;
}
void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y) {
sb->content_height = content_height;
sb->scroll_y = scroll_y;
}
void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb) {
// Only draw scrollbar if content is larger than view
if (sb->content_height > sb->h) {
// Draw the track background
uint32_t track_color = ctx->use_light_theme ? 0xFFE0E0E0 : 0xFF2A2A2A;
if (ctx->draw_rounded_rect_filled) {
ctx->draw_rounded_rect_filled(ctx->user_data, sb->x, sb->y, sb->w, sb->h, 4, track_color);
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, sb->x, sb->y, sb->w, sb->h, track_color);
}
int thumb_h = (sb->h * sb->h) / sb->content_height;
if (thumb_h < 20) thumb_h = 20;
int max_scroll = sb->content_height - sb->h;
if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll;
if (sb->scroll_y < 0) sb->scroll_y = 0;
int thumb_y = sb->y + (sb->scroll_y * (sb->h - thumb_h)) / max_scroll;
uint32_t color = 0xFF888888; // Subtle gray thumb for mac style
if (sb->is_dragging) color = 0xFF666666;
if (ctx->draw_rounded_rect_filled) {
// Pill shaped thumb with margin
int margin = 2;
int radius = (sb->w - margin*2) / 2;
ctx->draw_rounded_rect_filled(ctx->user_data, sb->x + margin, thumb_y + margin, sb->w - margin*2, thumb_h - margin*2, radius, color);
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, sb->x, thumb_y, sb->w, thumb_h, color);
}
}
}
bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data) {
if (sb->content_height <= sb->h) return false;
int thumb_h = (sb->h * sb->h) / sb->content_height;
if (thumb_h < 20) thumb_h = 20;
int max_scroll = sb->content_height - sb->h;
if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll;
if (sb->scroll_y < 0) sb->scroll_y = 0;
int thumb_y = sb->y + (sb->scroll_y * (sb->h - thumb_h)) / max_scroll;
bool in_thumb = (mx >= sb->x && mx < sb->x + sb->w && my >= thumb_y && my < thumb_y + thumb_h);
bool in_track = (mx >= sb->x && mx < sb->x + sb->w && my >= sb->y && my < sb->y + sb->h);
if (mouse_down && !sb->is_dragging) {
if (in_thumb) {
sb->is_dragging = true;
sb->drag_start_my = my;
sb->drag_start_scroll_y = sb->scroll_y;
return true;
} else if (in_track) {
// Page scroll
if (my < thumb_y) {
sb->scroll_y -= sb->h;
} else {
sb->scroll_y += sb->h;
}
if (sb->scroll_y < 0) sb->scroll_y = 0;
if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll;
if (sb->on_scroll) sb->on_scroll(user_data, sb->scroll_y);
return true;
}
} else if (!mouse_down) {
sb->is_dragging = false;
}
if (sb->is_dragging && mouse_down) {
int dy = my - sb->drag_start_my;
int track_h = sb->h - thumb_h;
if (track_h > 0) {
float ratio = (float)max_scroll / (float)track_h;
int new_scroll = sb->drag_start_scroll_y + (int)(dy * ratio);
if (new_scroll < 0) new_scroll = 0;
if (new_scroll > max_scroll) new_scroll = max_scroll;
if (new_scroll != sb->scroll_y) {
sb->scroll_y = new_scroll;
if (sb->on_scroll) sb->on_scroll(user_data, sb->scroll_y);
}
}
return true;
}
return in_track || sb->is_dragging;
}
// --- TextBox Implementation ---
void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len) {
tb->x = x; tb->y = y; tb->w = w; tb->h = h;
tb->text = buffer;
tb->max_len = max_len;
tb->cursor_pos = string_len(buffer);
tb->focused = false;
tb->on_change = NULL;
}
void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb) {
uint32_t border_color = MAC_BTN_BORDER;
uint32_t bg_color = COLOR_BLACK;
uint32_t text_color = COLOR_WHITE;
if (ctx->use_light_theme) {
border_color = 0xFFA0A0A0;
bg_color = 0xFFFFFFFF;
text_color = COLOR_BLACK;
}
// Background and border
if (ctx->draw_rounded_rect_filled) {
ctx->draw_rounded_rect_filled(ctx->user_data, tb->x, tb->y, tb->w, tb->h, 4, border_color);
ctx->draw_rounded_rect_filled(ctx->user_data, tb->x + 1, tb->y + 1, tb->w - 2, tb->h - 2, 3, bg_color); // background
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, tb->x, tb->y, tb->w, tb->h, border_color);
ctx->draw_rect(ctx->user_data, tb->x + 1, tb->y + 1, tb->w - 2, tb->h - 2, bg_color);
}
if (ctx->draw_string && tb->text) {
int max_w = tb->w - 15;
int scroll_x = 0;
int text_w = 0;
if (ctx->measure_string_width) {
text_w = ctx->measure_string_width(ctx->user_data, tb->text);
} else {
text_w = string_len(tb->text) * 8;
}
if (text_w > max_w) scroll_x = text_w - max_w;
// Very basic simple drawing, without proper clipping since context lacks it
ctx->draw_string(ctx->user_data, tb->x + 5, tb->y + (tb->h - 8) / 2, tb->text, text_color);
if (tb->focused && ctx->draw_rect) {
int cx = 0;
if (ctx->measure_string_width) {
// measure up to cursor
char tmp[256];
int k = 0;
for (k = 0; k < tb->cursor_pos && tb->text[k]; k++) {
tmp[k] = tb->text[k];
}
tmp[k] = 0;
cx = ctx->measure_string_width(ctx->user_data, tmp);
} else {
cx = tb->cursor_pos * 8;
}
if (cx > max_w) cx = max_w; // clamped to visible end
ctx->draw_rect(ctx->user_data, tb->x + 5 + cx, tb->y + (tb->h - 12) / 2, 2, 12, text_color);
}
}
}
bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data) {
bool in_bounds = (mx >= tb->x && mx < tb->x + tb->w && my >= tb->y && my < tb->y + tb->h);
if (mouse_clicked) {
tb->focused = in_bounds;
if (in_bounds && tb->text) {
int rel_x = mx - (tb->x + 5);
if (rel_x < 0) rel_x = 0;
// Rough estimation for fixed-width font 8px chars
int new_pos = rel_x / 8;
int len = string_len(tb->text);
if (new_pos > len) new_pos = len;
tb->cursor_pos = new_pos;
}
}
return in_bounds;
}
bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data) {
if (!tb->focused || !tb->text) return false;
int len = string_len(tb->text);
if (c == 19) { // LEFT
if (tb->cursor_pos > 0) tb->cursor_pos--;
return true;
} else if (c == 20) { // RIGHT
if (tb->cursor_pos < len) tb->cursor_pos++;
return true;
}
if (c == '\b') { // backspace
if (tb->cursor_pos > 0) {
for (int i = tb->cursor_pos - 1; i < len; i++) {
tb->text[i] = tb->text[i + 1];
}
tb->cursor_pos--;
if (tb->on_change) tb->on_change(user_data);
}
} else if (c >= 32 && c < 127) {
if (len < tb->max_len - 1) {
for (int i = len; i >= tb->cursor_pos; i--) {
tb->text[i + 1] = tb->text[i];
}
tb->text[tb->cursor_pos] = c;
tb->cursor_pos++;
if (tb->on_change) tb->on_change(user_data);
}
}
return true;
}
// --- Dropdown Implementation ---
void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count) {
dd->x = x; dd->y = y; dd->w = w; dd->h = h;
dd->items = items;
dd->item_count = count;
dd->selected_idx = 0;
dd->is_open = false;
dd->on_select = NULL;
}
void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd) {
uint32_t border_color = MAC_BTN_BORDER;
uint32_t bg_color = MAC_BTN_BG_NORMAL;
uint32_t text_color = COLOR_WHITE;
if (ctx->use_light_theme) {
border_color = 0xFFB0B0B0;
bg_color = 0xFFE0E0E0;
text_color = COLOR_BLACK;
}
if (ctx->draw_rounded_rect_filled) {
ctx->draw_rounded_rect_filled(ctx->user_data, dd->x, dd->y, dd->w, dd->h, 4, border_color);
ctx->draw_rounded_rect_filled(ctx->user_data, dd->x + 1, dd->y + 1, dd->w - 2, dd->h - 2, 3, bg_color);
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, dd->x, dd->y, dd->w, dd->h, border_color);
ctx->draw_rect(ctx->user_data, dd->x + 1, dd->y + 1, dd->w - 2, dd->h - 2, bg_color);
}
if (ctx->draw_string && dd->items && dd->item_count > 0 && dd->selected_idx >= 0 && dd->selected_idx < dd->item_count) {
ctx->draw_string(ctx->user_data, dd->x + 5, dd->y + (dd->h - 8) / 2, dd->items[dd->selected_idx], text_color);
ctx->draw_string(ctx->user_data, dd->x + dd->w - 12, dd->y + (dd->h - 8) / 2, "v", text_color);
}
if (dd->is_open && ctx->draw_rect && dd->items) {
int menu_h = dd->item_count * dd->h;
ctx->draw_rect(ctx->user_data, dd->x, dd->y + dd->h, dd->w, menu_h, border_color);
ctx->draw_rect(ctx->user_data, dd->x + 1, dd->y + dd->h + 1, dd->w - 2, menu_h - 2, bg_color);
for (int i = 0; i < dd->item_count; i++) {
if (ctx->draw_string) {
ctx->draw_string(ctx->user_data, dd->x + 5, dd->y + dd->h + i * dd->h + (dd->h - 8)/2, dd->items[i], text_color);
}
}
}
}
bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data) {
if (!mouse_clicked) return false;
if (dd->is_open) {
int menu_h = dd->item_count * dd->h;
if (mx >= dd->x && mx < dd->x + dd->w && my >= dd->y + dd->h && my < dd->y + dd->h + menu_h) {
int clicked_idx = (my - (dd->y + dd->h)) / dd->h;
if (clicked_idx >= 0 && clicked_idx < dd->item_count) {
dd->selected_idx = clicked_idx;
dd->is_open = false;
if (dd->on_select) dd->on_select(user_data, clicked_idx);
return true;
}
}
dd->is_open = false;
}
if (mx >= dd->x && mx < dd->x + dd->w && my >= dd->y && my < dd->y + dd->h) {
dd->is_open = !dd->is_open;
return true;
}
return false;
}
// --- Checkbox / Radio Implementation ---
void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio) {
cb->x = x; cb->y = y; cb->w = w; cb->h = h;
cb->text = text;
cb->checked = false;
cb->is_radio = is_radio;
cb->on_toggle = NULL;
}
void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb) {
int box_size = 14;
int box_y = cb->y + (cb->h - box_size) / 2;
uint32_t border_color = MAC_BTN_BORDER;
uint32_t bg_color = MAC_BTN_BG_NORMAL;
uint32_t inner_color = COLOR_WHITE;
uint32_t text_color = COLOR_WHITE;
if (ctx->use_light_theme) {
border_color = 0xFF909090;
bg_color = 0xFFFFFFFF;
inner_color = 0xFF404040;
text_color = COLOR_BLACK;
}
if (ctx->draw_rounded_rect_filled) {
int radius = cb->is_radio ? box_size / 2 : 3;
ctx->draw_rounded_rect_filled(ctx->user_data, cb->x, box_y, box_size, box_size, radius, border_color);
ctx->draw_rounded_rect_filled(ctx->user_data, cb->x + 1, box_y + 1, box_size - 2, box_size - 2, radius - 1, bg_color);
if (cb->checked) {
int inner = box_size - 6;
int inner_rad = cb->is_radio ? inner / 2 : 2;
ctx->draw_rounded_rect_filled(ctx->user_data, cb->x + 3, box_y + 3, inner, inner, inner_rad, inner_color);
}
} else if (ctx->draw_rect) {
ctx->draw_rect(ctx->user_data, cb->x, box_y, box_size, box_size, border_color);
ctx->draw_rect(ctx->user_data, cb->x + 1, box_y + 1, box_size - 2, box_size - 2, bg_color);
if (cb->checked) {
int inner = box_size - 6;
ctx->draw_rect(ctx->user_data, cb->x + 3, box_y + 3, inner, inner, inner_color);
}
}
if (ctx->draw_string && cb->text) {
ctx->draw_string(ctx->user_data, cb->x + box_size + 8, cb->y + (cb->h - 8) / 2, cb->text, text_color);
}
}
bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data) {
if (!mouse_clicked) return false;
if (mx >= cb->x && mx < cb->x + cb->w && my >= cb->y && my < cb->y + cb->h) {
cb->checked = !cb->checked;
if (cb->on_toggle) cb->on_toggle(user_data, cb->checked);
return true;
}
return false;
}

93
src/wm/libwidget.h Normal file
View File

@@ -0,0 +1,93 @@
#ifndef LIBWIDGET_H
#define LIBWIDGET_H
#include <stdint.h>
#include <stdbool.h>
// Widget Context for abstract drawing backend
typedef struct {
void *user_data;
void (*draw_rect)(void *user_data, int x, int y, int w, int h, uint32_t color);
void (*draw_rounded_rect_filled)(void *user_data, int x, int y, int w, int h, int r, uint32_t color);
void (*draw_string)(void *user_data, int x, int y, const char *str, uint32_t color);
int (*measure_string_width)(void *user_data, const char *str);
void (*mark_dirty)(void *user_data, int x, int y, int w, int h);
bool use_light_theme;
} widget_context_t;
// --- Button ---
typedef struct {
int x, y, w, h;
const char *text;
bool pressed;
bool hovered;
void (*on_click)(void *user_data);
} widget_button_t;
void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text);
void widget_button_draw(widget_context_t *ctx, widget_button_t *btn);
// Returns true if event was consumed
bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data);
// --- Scrollbar ---
typedef struct {
int x, y, w, h;
int content_height;
int scroll_y;
bool is_dragging;
int drag_start_my;
int drag_start_scroll_y;
void (*on_scroll)(void *user_data, int new_scroll_y);
} widget_scrollbar_t;
void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h);
void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y);
void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb);
// Returns true if event was consumed
bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data);
// --- TextBox ---
typedef struct {
int x, y, w, h;
char *text;
int max_len;
int cursor_pos;
bool focused;
void (*on_change)(void *user_data);
} widget_textbox_t;
void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len);
void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb);
bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data);
bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data);
// --- Dropdown ---
typedef struct {
int x, y, w, h;
const char **items;
int item_count;
int selected_idx;
bool is_open;
void (*on_select)(void *user_data, int new_idx);
} widget_dropdown_t;
void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count);
void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd);
bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data);
// --- Checkbox / Radio ---
typedef struct {
int x, y, w, h;
const char *text;
bool checked;
bool is_radio;
void (*on_toggle)(void *user_data, bool new_state);
} widget_checkbox_t;
void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio);
void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb);
bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data);
#endif

View File

@@ -151,8 +151,8 @@ int wallpaper_get_height(void) { return wp_height; }
void wallpaper_init(void) { void wallpaper_init(void) {
// We expect Limine modules to have been copied to /Library/images/Wallpapers/ by main.c // We expect Limine modules to have been copied to /Library/images/Wallpapers/ by main.c
// Set a default wallpaper if one exists // Set a default wallpaper if one exists
if (fat32_exists("/Library/images/Wallpapers/mountain.jpg")) { if (fat32_exists("/Library/images/Wallpapers/bored.jpg")) {
wallpaper_request_set_from_file("/Library/images/Wallpapers/mountain.jpg"); wallpaper_request_set_from_file("/Library/images/Wallpapers/bored.jpg");
} else if (fat32_exists("/Library/images/Wallpapers/moon.jpg")) { } else if (fat32_exists("/Library/images/Wallpapers/moon.jpg")) {
wallpaper_request_set_from_file("/Library/images/Wallpapers/moon.jpg"); wallpaper_request_set_from_file("/Library/images/Wallpapers/moon.jpg");
} }

View File

@@ -17,6 +17,8 @@
#include "userland/stb_image.h" #include "userland/stb_image.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "disk.h" #include "disk.h"
#include "../sys/work_queue.h"
#include "../sys/smp.h"
// Hello developer, // Hello developer,
@@ -81,7 +83,7 @@ void (*wm_custom_paint_hook)(void) = NULL;
// Notification state // Notification state
static char notif_text[256] = {0}; static char notif_text[256] = {0};
static int notif_timer = 0; static int notif_timer = 0;
static int notif_x_offset = 300; // Starts offscreen static int notif_x_offset = 420; // Starts offscreen
static bool notif_active = false; static bool notif_active = false;
extern bool ps2_ctrl_pressed; extern bool ps2_ctrl_pressed;
@@ -98,6 +100,14 @@ static int drag_offset_y = 0;
bool is_dragging_file = false; bool is_dragging_file = false;
static char drag_file_path[FAT32_MAX_PATH]; static char drag_file_path[FAT32_MAX_PATH];
static int drag_icon_type = 0; static int drag_icon_type = 0;
typedef struct {
int y_start;
int y_end;
DirtyRect dirty;
volatile int *completion_counter;
int pass;
} wm_strip_job_t;
static int drag_start_x = 0; static int drag_start_x = 0;
static int drag_start_y = 0; static int drag_start_y = 0;
static int drag_icon_orig_x = 0; static int drag_icon_orig_x = 0;
@@ -111,10 +121,8 @@ static int window_count = 0;
// Redraw system // Redraw system
static bool force_redraw = true; static bool force_redraw = true;
static uint32_t timer_ticks = 0; static uint32_t timer_ticks = 0;
static int desktop_refresh_timer = 0;
// Cursor state // Cursor state
static bool cursor_visible = true;
static int last_cursor_x = 400; static int last_cursor_x = 400;
static int last_cursor_y = 300; static int last_cursor_y = 300;
@@ -180,7 +188,7 @@ static void refresh_desktop_icons(void) {
FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(MAX_DESKTOP_ICONS * sizeof(FAT32_FileInfo)); FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(MAX_DESKTOP_ICONS * sizeof(FAT32_FileInfo));
if (!files) return; if (!files) return;
int file_count = fat32_list_directory("/Desktop", files, MAX_DESKTOP_ICONS); int file_count = fat32_list_directory("/root/Desktop", files, MAX_DESKTOP_ICONS);
// Temp array to hold new state // Temp array to hold new state
DesktopIcon new_icons[MAX_DESKTOP_ICONS]; DesktopIcon new_icons[MAX_DESKTOP_ICONS];
@@ -302,7 +310,7 @@ void wm_refresh_desktop(void) {
} }
static void create_desktop_shortcut(const char *app_name) { static void create_desktop_shortcut(const char *app_name) {
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p = 9; int p = 9;
int n = 0; while(app_name[n]) path[p++] = app_name[n++]; int n = 0; while(app_name[n]) path[p++] = app_name[n++];
const char *ext = ".shortcut"; const char *ext = ".shortcut";
@@ -425,12 +433,14 @@ static void draw_dock_files(int x, int y);
static void draw_dock_settings(int x, int y); static void draw_dock_settings(int x, int y);
static void draw_dock_notepad(int x, int y); static void draw_dock_notepad(int x, int y);
static void draw_dock_calculator(int x, int y); static void draw_dock_calculator(int x, int y);
static void draw_dock_grapher(int x, int y);
static void draw_dock_terminal(int x, int y); static void draw_dock_terminal(int x, int y);
static void draw_dock_minesweeper(int x, int y); static void draw_dock_minesweeper(int x, int y);
static void draw_dock_paint(int x, int y); static void draw_dock_paint(int x, int y);
static void draw_dock_clock(int x, int y); static void draw_dock_clock(int x, int y);
static void draw_dock_taskman(int x, int y); static void draw_dock_taskman(int x, int y);
static void draw_dock_editor(int x, int y); static void draw_dock_word(int x, int y);
static void draw_dock_browser(int x, int y);
static void draw_dock_editor(int x, int y); static void draw_dock_editor(int x, int y);
static void draw_filled_circle(int cx, int cy, int r, uint32_t color); static void draw_filled_circle(int cx, int cy, int r, uint32_t color);
@@ -768,6 +778,11 @@ void draw_calculator_icon(int x, int y, const char *label) {
draw_icon_label(x, y, label); draw_icon_label(x, y, label);
} }
void draw_grapher_icon(int x, int y, const char *label) {
draw_scaled_icon(x, y, draw_dock_grapher);
draw_icon_label(x, y, label);
}
void draw_terminal_icon(int x, int y, const char *label) { void draw_terminal_icon(int x, int y, const char *label) {
draw_scaled_icon(x, y, draw_dock_terminal); draw_scaled_icon(x, y, draw_dock_terminal);
draw_icon_label(x, y, label); draw_icon_label(x, y, label);
@@ -986,6 +1001,42 @@ static void draw_dock_notepad(int x, int y) {
draw_rect(x + 33, y + 24, 1, 2, 0xFF555555); draw_rect(x + 33, y + 24, 1, 2, 0xFF555555);
} }
static void draw_dock_grapher(int x, int y) {
// Dark background with a panel look
draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF121212);
draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF1E1E1E);
draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF161616);
// Subtle grid (matches Grapher's theme)
uint32_t grid_color = 0xFF2A2A2A;
for (int i = 8; i < 40; i += 8) {
draw_rect(x + i, y + 6, 1, 36, grid_color);
draw_rect(x + 6, y + i + 6, 36, 1, grid_color);
}
// Axis line
draw_rect(x + 24, y + 10, 1, 28, 0xFF444444);
draw_rect(x + 10, y + 24, 28, 1, 0xFF444444);
// Vibrant Sine Wave (Neon Cyan)
uint32_t curve_color = 0xFF00E5FF;
int curve_y[] = {24, 23, 21, 19, 17, 16, 15, 15, 16, 17, 19, 21, 23, 24, 26, 28, 30, 32, 33, 33, 32, 30, 28, 26, 24, 23, 21, 19, 17, 16, 15, 15, 16, 17, 19, 21};
for (int i = 0; i < 35; i++) {
int x1 = x + 6 + i;
int y1 = y + curve_y[i];
int y2 = y + curve_y[i+1];
// Anti-aliased look with multi-point vertical connector
if (y1 < y2) for (int j = y1; j <= y2; j++) put_pixel(x1, j, curve_color);
else for (int j = y2; j <= y1; j++) put_pixel(x1, j, curve_color);
}
// Add white indicator "nodes" at the peaks
draw_filled_circle(x + 6 + 7, y + 15, 2, 0xFFFFFFFF);
draw_filled_circle(x + 6 + 18, y + 33, 2, 0xFFFFFFFF);
draw_filled_circle(x + 6 + 30, y + 15, 2, 0xFFFFFFFF);
}
static void draw_dock_calculator(int x, int y) { static void draw_dock_calculator(int x, int y) {
draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF111111); draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF111111);
draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF222222); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF222222);
@@ -1110,27 +1161,6 @@ static void draw_dock_clock(int x, int y) {
draw_rect(cx, cy - 1, 10, 2, 0xFF333333); draw_rect(cx, cy - 1, 10, 2, 0xFF333333);
} }
static void draw_dock_editor(int x, int y) {
draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF0A1628);
draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF1565C0);
draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF0D47A1);
draw_rect(x + 5, y + 8, 9, 32, 0xFF1A237E);
draw_filled_circle(x + 10, y + 14, 2, 0xFF7986CB);
draw_filled_circle(x + 10, y + 22, 2, 0xFF7986CB);
draw_filled_circle(x + 10, y + 30, 2, 0xFF7986CB);
draw_rect(x + 15, y + 8, 28, 32, 0xFF1B2B3C);
draw_rect(x + 15, y + 8, 14, 5, 0xFF1B2B3C);
draw_rect(x + 15, y + 8, 14, 1, 0xFF569CD6);
draw_rect(x + 18, y + 13, 9, 2, 0xFF569CD6);
draw_rect(x + 29, y + 13, 8, 2, 0xFF4EC9B0);
draw_rect(x + 18, y + 18, 5, 2, 0xFFCE9178);
draw_rect(x + 25, y + 18, 7, 2, 0xFFCE9178);
draw_rect(x + 21, y + 23, 7, 2, 0xFF9CDCFE);
draw_rect(x + 30, y + 23, 5, 2, 0xFFD4D4D4);
draw_rect(x + 18, y + 28, 16, 2, 0xFF6A9955);
draw_rect(x + 18, y + 33, 10, 2, 0xFFD4D4D4);
draw_rect(x + 30, y + 33, 6, 2, 0xFF569CD6);
}
void draw_window(Window *win) { void draw_window(Window *win) {
if (!win->visible) return; if (!win->visible) return;
@@ -1240,6 +1270,7 @@ static void erase_cursor(int x, int y) {
} }
// --- Clock --- // --- Clock ---
static uint8_t rtc_read(uint8_t reg) { static uint8_t rtc_read(uint8_t reg) {
outb(0x70, reg); outb(0x70, reg);
return inb(0x71); return inb(0x71);
@@ -1274,60 +1305,43 @@ static void draw_clock(int x, int y) {
} }
// --- Main Paint Function --- // --- Main Paint Function ---
void wm_paint(void) { 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 Window *sorted_windows_cache[32];
static int sorted_window_count_cache = 0;
static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
int sw = get_screen_width(); int sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
uint64_t rflags; int cx = 0, cy = y_start, cw = sw, ch = y_end - y_start;
rflags = wm_lock_acquire();
wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12);
wm_mark_dirty(mx, my, 12, 12);
DirtyRect dirty = graphics_get_dirty_rect();
if (dirty.active) { if (dirty.active) {
int d_h = 60; if (cx < dirty.x) { cw -= (dirty.x - cx); cx = dirty.x; }
int d_y = sh - d_h - 6; if (cy < dirty.y) { ch -= (dirty.y - cy); cy = dirty.y; }
int d_item_sz = 48; if (cx + cw > dirty.x + dirty.w) cw = dirty.x + dirty.w - cx;
int d_space = 10; if (cy + ch > dirty.y + dirty.h) ch = dirty.y + dirty.h - cy;
int d_tw = 10 * (d_item_sz + d_space);
int d_bg_x = (sw - d_tw) / 2 - 12;
int d_bg_w = d_tw + 24;
if (!(dirty.x >= d_bg_x + d_bg_w || dirty.x + dirty.w <= d_bg_x ||
dirty.y >= d_y + d_h || dirty.y + dirty.h <= d_y)) {
graphics_mark_dirty(d_bg_x - 10, d_y - 10, d_bg_w + 20, d_h + 20);
dirty = graphics_get_dirty_rect();
}
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h);
} else {
graphics_clear_clipping();
} }
// 1. Desktop Background (respects wallpaper color/pattern) if (cw <= 0 || ch <= 0) return;
graphics_set_clipping(cx, cy, cw, ch);
if (pass == 1) {
draw_desktop_background(); draw_desktop_background();
// Draw Desktop Icons
for (int i = 0; i < desktop_icon_count; i++) { for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i]; DesktopIcon *icon = &desktop_icons[i];
if (dirty.active) { if (icon->y + 85 <= cy || icon->y >= cy + ch) continue;
if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w || if (dirty.active && (icon->x + 85 <= dirty.x || icon->x >= dirty.x + dirty.w)) continue;
icon->y + 80 <= dirty.y || icon->y >= dirty.y + dirty.h) {
continue;
}
}
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name); if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) { else if (icon->type == 2) {
// App icon - strip .shortcut for display char label[64]; int len = 0;
char label[64];
int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; } while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0; label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) { if (len > 9 && str_ends_with(label, ".shortcut")) label[len-9] = 0;
label[len-9] = 0;
}
if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label); if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Calculator")) draw_calculator_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Calculator")) draw_calculator_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Terminal")) draw_terminal_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
@@ -1338,13 +1352,13 @@ void wm_paint(void) {
else if (str_starts_with(icon->name, "Recycle Bin")) draw_recycle_bin_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Recycle Bin")) draw_recycle_bin_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Files")) draw_folder_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Files")) draw_folder_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Grapher")) draw_grapher_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label); else draw_icon(icon->x, icon->y, label);
} else { } else {
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name); if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name); else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (is_image_file(icon->name)) { else if (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/"; char full_path[128] = "/root/Desktop/"; int p=14; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
int p=9; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
draw_image_icon(icon->x, icon->y, full_path); draw_image_icon(icon->x, icon->y, full_path);
draw_icon_label(icon->x, icon->y, icon->name); draw_icon_label(icon->x, icon->y, icon->name);
} }
@@ -1353,196 +1367,203 @@ void wm_paint(void) {
} }
} }
// 3. Windows - sort by z-index and draw for (int i = 0; i < sorted_window_count_cache; i++) {
int local_window_count = window_count; Window *win = sorted_windows_cache[i];
Window *sorted_windows[32];
for (int i = 0; i < local_window_count; i++) {
sorted_windows[i] = all_windows[i];
}
for (int i = 0; i < local_window_count - 1; i++) {
for (int j = 0; j < local_window_count - i - 1; j++) {
if (sorted_windows[j] && sorted_windows[j + 1] &&
sorted_windows[j]->z_index > sorted_windows[j + 1]->z_index) {
Window *temp = sorted_windows[j];
sorted_windows[j] = sorted_windows[j + 1];
sorted_windows[j + 1] = temp;
}
}
}
for (int i = 0; i < local_window_count; i++) {
Window *win = sorted_windows[i];
if (!win || !win->visible) continue; if (!win || !win->visible) continue;
if (win->y + win->h <= cy || win->y >= cy + ch) continue;
if (dirty.active && !win->focused) { if (dirty.active && !win->focused && (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w)) continue;
if (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w ||
win->y + win->h <= dirty.y || win->y >= dirty.y + dirty.h) {
continue;
}
}
draw_window(win); draw_window(win);
} }
} 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_TOPBAR_BG);
draw_boredos_logo(8, 8, 1); draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12); draw_clock(sw - 80, 12);
}
if (start_menu_open) { if (start_menu_open && 40 < cy + ch && 125 > cy) {
int menu_h = 85; draw_rounded_rect_filled(8, 40, 160, 85, 8, COLOR_DARK_PANEL);
draw_rounded_rect_filled(8, 40, 160, menu_h, 8, COLOR_DARK_PANEL);
draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT); draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT);
draw_string(20, 68, "Settings", COLOR_DARK_TEXT); draw_string(20, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT); draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT); draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
} }
int dock_h = 60; int dock_h = 60, dock_y = sh - dock_h - 6;
int dock_y = sh - dock_h - 6; if (dock_y < cy + ch && dock_y + dock_h > cy) {
int dock_item_size = 48; int d_item_sz = 48, d_space = 10, d_total_w = 12 * (d_item_sz + d_space);
int dock_spacing = 10; int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
int total_dock_width = 11 * (dock_item_size + dock_spacing); draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
int dock_bg_w = total_dock_width + 24; draw_dock_files(dx, dy); dx += d_item_sz+d_space;
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
draw_dock_notepad(dx, dy); dx += d_item_sz+d_space;
draw_dock_calculator(dx, dy); dx += d_item_sz+d_space;
draw_dock_grapher(dx, dy); dx += d_item_sz+d_space;
draw_dock_terminal(dx, dy); dx += d_item_sz+d_space;
draw_dock_minesweeper(dx, dy); dx += d_item_sz+d_space;
draw_dock_paint(dx, dy); dx += d_item_sz+d_space;
draw_dock_browser(dx, dy); dx += d_item_sz+d_space;
draw_dock_taskman(dx, dy); dx += d_item_sz+d_space;
draw_dock_clock(dx, dy); dx += d_item_sz+d_space;
draw_dock_word(dx, dy);
}
// Draw blurred dock background with reduced radius and tint
draw_rounded_rect_blurred(dock_bg_x, dock_y, dock_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dock_x = (sw - total_dock_width) / 2;
int dock_item_y = dock_y + 6;
draw_dock_files(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_settings(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_notepad(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_calculator(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_terminal(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_minesweeper(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_paint(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_browser(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_taskman(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_clock(dock_x, dock_item_y);
dock_x += dock_item_size + dock_spacing;
draw_dock_word(dock_x, dock_item_y);
// Desktop Context Menu (with rounded corners)
if (desktop_menu_visible) { if (desktop_menu_visible) {
int menu_w = 140; int d_mw = 140, d_mh = (desktop_menu_target_icon != -1) ? 125 : 75;
if (desktop_menu_y < cy + ch && desktop_menu_y + d_mh > cy) {
draw_rounded_rect_filled(desktop_menu_x, desktop_menu_y, d_mw, d_mh, 8, COLOR_DARK_PANEL);
int item_h = 25; int item_h = 25;
int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75;
draw_rounded_rect_filled(desktop_menu_x, desktop_menu_y, menu_w, menu_h, 8, COLOR_DARK_PANEL);
if (desktop_menu_target_icon != -1) { if (desktop_menu_target_icon != -1) {
bool can_paste = explorer_clipboard_has_content(); bool cp = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "Cut", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "Cut", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "Copy", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "Copy", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_TRAFFIC_RED); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_TRAFFIC_RED);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE);
} else { } else {
bool can_paste = explorer_clipboard_has_content(); bool cp = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "New File", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "New File", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "New Folder", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "New Folder", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
}
} }
} }
// Desktop Dialogs (dark mode)
if (desktop_dialog_state != 0) { if (desktop_dialog_state != 0) {
int dlg_w = 300; int dlg_h = 110; int dlg_w = 300, dlg_h = 110, dg_x = (sw - dlg_w)/2, dg_y = (sh - dlg_h)/2;
int dlg_x = (sw - dlg_w) / 2; if (dg_y < cy + ch && dg_y + dlg_h > cy) {
int dlg_y = (sh - dlg_h) / 2; draw_rounded_rect_filled(dg_x, dg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL);
const char *title = (desktop_dialog_state == 1) ? "Create New File" : (desktop_dialog_state == 2 ? "Create New Folder" : "Rename");
draw_rounded_rect_filled(dlg_x, dlg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL); const char *btn = (desktop_dialog_state == 0) ? "Rename" : "Create";
draw_string(dg_x + 10, dg_y + 10, title, COLOR_WHITE);
const char *title = "Rename"; draw_rounded_rect_filled(dg_x + 10, dg_y + 35, 280, 20, 4, COLOR_DARK_BG);
const char *btn_text = "Rename"; draw_string(dg_x + 15, dg_y + 40, desktop_dialog_input, COLOR_WHITE);
if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; } char temp_sub[64]; int k; for (k=0; k<desktop_dialog_cursor&&desktop_dialog_input[k]; k++) temp_sub[k]=desktop_dialog_input[k]; temp_sub[k]=0;
else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; } draw_rect(dg_x + 15 + font_manager_get_string_width(graphics_get_current_ttf(), temp_sub), dg_y + 39, 2, 12, COLOR_WHITE);
draw_rounded_rect_filled(dg_x + 50, dg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dg_x + 70, dg_y + 72, btn, COLOR_WHITE);
draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_WHITE); draw_rounded_rect_filled(dg_x + 170, dg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dg_x + 185, dg_y + 72, "Cancel", COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); }
draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_WHITE);
// Cursor
char sub[64];
int k;
for (k = 0; k < desktop_dialog_cursor && desktop_dialog_input[k]; k++) sub[k] = desktop_dialog_input[k];
sub[k] = 0;
int cx = font_manager_get_string_width(graphics_get_current_ttf(), sub);
draw_rect(dlg_x + 15 + cx, dlg_y + 39, 2, 12, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER);
draw_string(dlg_x + 70, dlg_y + 72, btn_text, COLOR_WHITE);
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER);
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE);
} }
// Message Box (dark mode)
if (msg_box_visible) { if (msg_box_visible) {
int mw = 320; int mw = 320, mh = 100, m_x = (sw - mw)/2, m_y = (sh - mh)/2;
int mh = 100; if (m_y < cy + ch && m_y + mh > cy) {
int mx = (sw - mw) / 2; draw_rounded_rect_filled(m_x, m_y, mw, mh, 8, COLOR_DARK_PANEL);
int my = (sh - mh) / 2; draw_string(m_x + 15, m_y + 10, msg_box_title, COLOR_DARK_TEXT);
draw_string(m_x + 10, m_y + 40, msg_box_text, COLOR_DARK_TEXT);
draw_rounded_rect_filled(mx, my, mw, mh, 8, COLOR_DARK_PANEL); draw_rounded_rect_filled(m_x + mw/2 - 30, m_y + 70, 60, 20, 4, COLOR_DARK_BORDER);
draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT); draw_string(m_x + mw/2 - 10, m_y + 75, "OK", COLOR_WHITE);
draw_string(mx + 10, my + 40, msg_box_text, COLOR_DARK_TEXT); }
draw_rounded_rect_filled(mx + mw/2 - 30, my + 70, 60, 20, 4, COLOR_DARK_BORDER);
} }
// Notification (dark mode)
if (notif_active) { if (notif_active) {
int nx = sw - 280 + notif_x_offset; int nx = sw - 400 + notif_x_offset, ny = 40, nw = 380, nh = 50;
int ny = 40; if (ny < cy + ch && ny + nh > cy) {
int nw = 260;
int nh = 50;
draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL); draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL);
draw_string(nx + 15, ny + 10, "Screenshot", COLOR_DARK_TEXT); draw_string(nx + 15, ny + 10, "Screenshot", COLOR_DARK_TEXT);
draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY); draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY);
} }
// Custom Overlay (VM Graphics)
if (wm_custom_paint_hook) {
wm_custom_paint_hook();
} }
// Draw Dragged Icon if (wm_custom_paint_hook) wm_custom_paint_hook();
if (is_dragging_file) { if (is_dragging_file) {
if (mx - 20 < cx + cw && mx + 20 > cx && my - 20 < cy + ch && my + 20 > cy) {
if (drag_icon_type == 1) draw_folder_icon(mx - 20, my - 20, "Moving..."); if (drag_icon_type == 1) draw_folder_icon(mx - 20, my - 20, "Moving...");
else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving..."); else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving...");
else draw_document_icon(mx - 20, my - 20, "Moving..."); else draw_document_icon(mx - 20, my - 20, "Moving...");
} }
}
}
}
// 7. Mouse cursor (draw last so it's on top) static void wm_strip_worker_job(void *arg) {
wm_strip_job_t *job = (wm_strip_job_t *)arg;
wm_paint_region(job->y_start, job->y_end, job->dirty, job->pass);
__atomic_sub_fetch(job->completion_counter, 1, __ATOMIC_SEQ_CST);
}
void wm_paint(void) {
int sw = get_screen_width();
int sh = get_screen_height();
uint64_t rflags;
rflags = wm_lock_acquire();
wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12);
wm_mark_dirty(mx, my, 12, 12);
DirtyRect dirty = graphics_get_dirty_rect();
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;
if (!(dirty.x >= d_bg_x + d_bg_w || dirty.x + dirty.w <= d_bg_x ||
dirty.y >= d_y + d_h || dirty.y + dirty.h <= d_y)) {
graphics_mark_dirty(d_bg_x - 10, d_y - 10, d_bg_w + 20, d_h + 20);
dirty = graphics_get_dirty_rect();
}
}
sorted_window_count_cache = window_count;
if (sorted_window_count_cache > 32) sorted_window_count_cache = 32;
for (int i = 0; i < sorted_window_count_cache; i++) sorted_windows_cache[i] = all_windows[i];
for (int i = 0; i < sorted_window_count_cache - 1; i++) {
for (int j = 0; j < sorted_window_count_cache - i - 1; j++) {
if (sorted_windows_cache[j] && sorted_windows_cache[j+1] &&
sorted_windows_cache[j]->z_index > sorted_windows_cache[j+1]->z_index) {
Window *tmp = sorted_windows_cache[j];
sorted_windows_cache[j] = sorted_windows_cache[j+1];
sorted_windows_cache[j+1] = tmp;
}
}
}
// Memory barrier to ensure APs see the sorted window list correctly
asm volatile("" ::: "memory");
uint32_t cpu_count = smp_cpu_count();
if (cpu_count > 32) cpu_count = 32;
if (cpu_count < 1) cpu_count = 1;
volatile int completion_counter = (int)cpu_count;
wm_strip_job_t jobs[32];
int rows_per_strip = sh / cpu_count;
// PASS 1: BACKGROUND & WINDOWS
for (uint32_t i = 0; i < cpu_count; i++) {
jobs[i].y_start = i * rows_per_strip;
jobs[i].y_end = (i == cpu_count - 1) ? sh : (i + 1) * rows_per_strip;
jobs[i].dirty = dirty;
jobs[i].completion_counter = &completion_counter;
jobs[i].pass = 1;
if (i < cpu_count - 1) work_queue_submit(wm_strip_worker_job, &jobs[i]);
}
wm_paint_region(jobs[cpu_count-1].y_start, jobs[cpu_count-1].y_end, dirty, 1);
__atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
while (completion_counter > 0) {
if (!work_queue_drain_one()) asm volatile("pause");
}
// PASS 2: UI OVERLAY (Dock, start menu, menus etc)
completion_counter = (int)cpu_count;
for (uint32_t i = 0; i < cpu_count; i++) {
jobs[i].pass = 2;
if (i < cpu_count - 1) work_queue_submit(wm_strip_worker_job, &jobs[i]);
}
wm_paint_region(jobs[cpu_count-1].y_start, jobs[cpu_count-1].y_end, dirty, 2);
__atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
while (completion_counter > 0) {
if (!work_queue_drain_one()) asm volatile("pause");
}
graphics_clear_clipping();
draw_cursor(mx, my); draw_cursor(mx, my);
last_cursor_x = mx; last_cursor_x = mx;
last_cursor_y = my; last_cursor_y = my;
// Flip the buffer - display the rendered frame atomically
graphics_flip_buffer(); graphics_flip_buffer();
graphics_clear_dirty_no_lock(); graphics_clear_dirty_no_lock();
// Restore IRQs
wm_lock_release(rflags); wm_lock_release(rflags);
} }
// --- Input Handling ---
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;
}
void wm_bring_to_front_locked(Window *win) { void wm_bring_to_front_locked(Window *win) {
for (int i = 0; i < window_count; i++) { for (int i = 0; i < window_count; i++) {
all_windows[i]->focused = false; all_windows[i]->focused = false;
@@ -1566,28 +1587,36 @@ void wm_bring_to_front(Window *win) {
wm_lock_release(rflags); wm_lock_release(rflags);
} }
void wm_add_window(Window *win) { void wm_add_window_locked(Window *win) {
uint64_t rflags;
rflags = wm_lock_acquire();
if (window_count < 32) { if (window_count < 32) {
all_windows[window_count++] = win; all_windows[window_count++] = win;
wm_bring_to_front_locked(win); // Ensure newly added windows are on top wm_bring_to_front_locked(win); // Ensure newly added windows are on top
} }
}
void wm_add_window(Window *win) {
uint64_t rflags;
rflags = wm_lock_acquire();
wm_add_window_locked(win);
wm_lock_release(rflags); wm_lock_release(rflags);
} }
Window* wm_find_window_by_title_locked(const char *title) {
if (!title) return NULL;
for (int i = 0; i < window_count; i++) {
if (all_windows[i] && all_windows[i]->title && str_eq(all_windows[i]->title, title)) {
return all_windows[i];
}
}
return NULL;
}
Window* wm_find_window_by_title(const char *title) { Window* wm_find_window_by_title(const char *title) {
if (!title) return NULL; if (!title) return NULL;
uint64_t rflags; uint64_t rflags = wm_lock_acquire();
rflags = wm_lock_acquire(); Window *win = wm_find_window_by_title_locked(title);
for (int i = 0; i < window_count; i++) {
if (all_windows[i] && all_windows[i]->title && str_eq(all_windows[i]->title, title)) {
wm_lock_release(rflags); wm_lock_release(rflags);
return all_windows[i]; return win;
}
}
wm_lock_release(rflags);
return NULL;
} }
void wm_remove_window(Window *win) { void wm_remove_window(Window *win) {
@@ -1673,13 +1702,13 @@ void wm_handle_click(int x, int y) {
if (item == 0 && desktop_menu_target_icon != -1) { // Cut if (item == 0 && desktop_menu_target_icon != -1) { // Cut
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_cut(path); explorer_clipboard_cut(path);
} else if (item == 1 && desktop_menu_target_icon != -1) { // Copy } else if (item == 1 && desktop_menu_target_icon != -1) { // Copy
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_copy(path); explorer_clipboard_copy(path);
} else if (item == 0 && desktop_menu_target_icon == -1) { // New File } else if (item == 0 && desktop_menu_target_icon == -1) { // New File
desktop_dialog_state = 1; desktop_dialog_state = 1;
@@ -1700,13 +1729,13 @@ void wm_handle_click(int x, int y) {
int old_count = desktop_icon_count; int old_count = desktop_icon_count;
if (desktop_menu_target_icon != -1 && desktop_icons[desktop_menu_target_icon].type == 1) { if (desktop_menu_target_icon != -1 && desktop_icons[desktop_menu_target_icon].type == 1) {
// Paste into folder // Paste into folder
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_paste(&win_explorer, path); explorer_clipboard_paste(&win_explorer, path);
} else { } else {
// Paste to desktop // Paste to desktop
explorer_clipboard_paste(&win_explorer, "/Desktop"); explorer_clipboard_paste(&win_explorer, "/root/Desktop");
} }
refresh_desktop_icons(); refresh_desktop_icons();
@@ -1717,7 +1746,8 @@ void wm_handle_click(int x, int y) {
if (desktop_snap_to_grid) { if (desktop_snap_to_grid) {
int col = (desktop_icons[new_idx].x - 20 + 40) / 80; int col = (desktop_icons[new_idx].x - 20 + 40) / 80;
int row = (desktop_icons[new_idx].y - 20 + 40) / 80; int row = (desktop_icons[new_idx].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0; if (col < 0) col = 0;
if (row < 0) row = 0;
desktop_icons[new_idx].x = 20 + col * 80; desktop_icons[new_idx].x = 20 + col * 80;
desktop_icons[new_idx].y = 20 + row * 80; desktop_icons[new_idx].y = 20 + row * 80;
} }
@@ -1726,8 +1756,8 @@ void wm_handle_click(int x, int y) {
} }
else if (item == 3 && desktop_menu_target_icon != -1) { // Delete else if (item == 3 && desktop_menu_target_icon != -1) { // Delete
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_delete_recursive(path); explorer_delete_recursive(path);
refresh_desktop_icons(); refresh_desktop_icons();
} }
@@ -1752,10 +1782,10 @@ void wm_handle_click(int x, int y) {
int dlg_x = (sw - 300) / 2; int dlg_y = (sh - 110) / 2; int dlg_x = (sw - 300) / 2; int dlg_y = (sh - 110) / 2;
if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Confirm if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Confirm
if (desktop_dialog_state == 8) { // Rename if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/"; char old_path[128] = "/root/Desktop/";
char new_path[128] = "/Desktop/"; char new_path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0; int p=14; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0; p=14; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) { if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons(); refresh_desktop_icons();
@@ -1765,8 +1795,8 @@ void wm_handle_click(int x, int y) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!"); wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) { } else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0; int p=14; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) { if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w"); FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh); if (fh) fat32_close(fh);
@@ -1811,7 +1841,7 @@ void wm_handle_click(int x, int y) {
if (item == 0) { // About if (item == 0) { // About
process_create_elf("/bin/about.elf", NULL); process_create_elf("/bin/about.elf", NULL);
} else if (item == 1) { // Settings } else if (item == 1) { // Settings
Window *existing = wm_find_window_by_title("Settings"); Window *existing = wm_find_window_by_title_locked("Settings");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/settings.elf", NULL); else process_create_elf("/bin/settings.elf", NULL);
} else if (item == 2) { // Shutdown } else if (item == 2) { // Shutdown
@@ -1946,7 +1976,9 @@ void wm_handle_right_click(int x, int y) {
} }
force_redraw = true; force_redraw = true;
}void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz) { }
static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
int sw = get_screen_width(); int sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
@@ -2005,7 +2037,7 @@ void wm_handle_right_click(int x, int y) {
int dock_y = sh - dock_h - 6; int dock_y = sh - dock_h - 6;
int dock_item_size = 48; int dock_item_size = 48;
int dock_spacing = 10; int dock_spacing = 10;
int total_dock_width = 11 * (dock_item_size + dock_spacing); int total_dock_width = 12 * (dock_item_size + dock_spacing);
int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dock_bg_x = (sw - total_dock_width) / 2 - 12;
int dock_bg_w = total_dock_width + 24; int dock_bg_w = total_dock_width + 24;
@@ -2020,13 +2052,14 @@ void wm_handle_right_click(int x, int y) {
else if (item == 1) start_menu_pending_app = "Settings"; else if (item == 1) start_menu_pending_app = "Settings";
else if (item == 2) start_menu_pending_app = "Notepad"; else if (item == 2) start_menu_pending_app = "Notepad";
else if (item == 3) start_menu_pending_app = "Calculator"; else if (item == 3) start_menu_pending_app = "Calculator";
else if (item == 4) start_menu_pending_app = "Terminal"; else if (item == 4) start_menu_pending_app = "Grapher";
else if (item == 5) start_menu_pending_app = "Minesweeper"; else if (item == 5) start_menu_pending_app = "Terminal";
else if (item == 6) start_menu_pending_app = "Paint"; else if (item == 6) start_menu_pending_app = "Minesweeper";
else if (item == 7) start_menu_pending_app = "Browser"; else if (item == 7) start_menu_pending_app = "Paint";
else if (item == 8) start_menu_pending_app = "Task Manager"; else if (item == 8) start_menu_pending_app = "Browser";
else if (item == 9) start_menu_pending_app = "Clock"; else if (item == 9) start_menu_pending_app = "Task Manager";
else if (item == 10) start_menu_pending_app = "Word Processor"; else if (item == 10) start_menu_pending_app = "Clock";
else if (item == 11) start_menu_pending_app = "Word Processor";
} }
} else { } else {
wm_handle_click(mx, my); wm_handle_click(mx, my);
@@ -2097,8 +2130,8 @@ void wm_handle_right_click(int x, int y) {
drag_icon_orig_x = icon->x; drag_icon_orig_x = icon->x;
drag_icon_orig_y = icon->y; drag_icon_orig_y = icon->y;
// Construct path // Construct path
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
int k=0; while(path[k]) { drag_file_path[k] = path[k]; k++; } drag_file_path[k]=0; int k=0; while(path[k]) { drag_file_path[k] = path[k]; k++; } drag_file_path[k]=0;
} }
// 2. Check Explorer Items // 2. Check Explorer Items
@@ -2151,55 +2184,59 @@ void wm_handle_right_click(int x, int y) {
if (start_menu_pending_app) { if (start_menu_pending_app) {
// Launch App // Launch App
if (str_starts_with(start_menu_pending_app, "Files")) { if (str_starts_with(start_menu_pending_app, "Files")) {
explorer_open_directory("/"); explorer_open_directory("/root");
} else if (str_starts_with(start_menu_pending_app, "Notepad")) { } else if (str_starts_with(start_menu_pending_app, "Notepad")) {
Window *existing = wm_find_window_by_title("Notepad"); Window *existing = wm_find_window_by_title_locked("Notepad");
if (existing) { if (existing) {
wm_bring_to_front_locked(existing); wm_bring_to_front_locked(existing);
} else { } else {
process_create_elf("/bin/notepad.elf", NULL); process_create_elf("/bin/notepad.elf", NULL);
} }
} else if (str_starts_with(start_menu_pending_app, "Editor")) { } else if (str_starts_with(start_menu_pending_app, "Editor")) {
Window *existing = wm_find_window_by_title("Txtedit"); Window *existing = wm_find_window_by_title_locked("Txtedit");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/txtedit.elf", NULL); else process_create_elf("/bin/txtedit.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Word Processor")) { } else if (str_starts_with(start_menu_pending_app, "Word Processor")) {
Window *existing = wm_find_window_by_title("Word Processor"); Window *existing = wm_find_window_by_title_locked("Word Processor");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/boredword.elf", NULL); else process_create_elf("/bin/boredword.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Terminal")) { } else if (str_starts_with(start_menu_pending_app, "Terminal")) {
cmd_reset(); wm_bring_to_front_locked(&win_cmd); cmd_reset(); wm_bring_to_front_locked(&win_cmd);
} 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 if (str_starts_with(start_menu_pending_app, "Calculator")) { } else if (str_starts_with(start_menu_pending_app, "Calculator")) {
Window *existing = wm_find_window_by_title("Calculator"); Window *existing = wm_find_window_by_title_locked("Calculator");
if (existing) { if (existing) {
wm_bring_to_front_locked(existing); wm_bring_to_front_locked(existing);
} else { } else {
process_create_elf("/bin/calculator.elf", NULL); process_create_elf("/bin/calculator.elf", NULL);
} }
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) { } else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
Window *existing = wm_find_window_by_title("Minesweeper"); Window *existing = wm_find_window_by_title_locked("Minesweeper");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/minesweeper.elf", NULL); else process_create_elf("/bin/minesweeper.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Settings")) { } else if (str_starts_with(start_menu_pending_app, "Settings")) {
Window *existing = wm_find_window_by_title("Settings"); Window *existing = wm_find_window_by_title_locked("Settings");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/settings.elf", NULL); else process_create_elf("/bin/settings.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Paint")) { } else if (str_starts_with(start_menu_pending_app, "Paint")) {
Window *existing = wm_find_window_by_title("Paint"); Window *existing = wm_find_window_by_title_locked("Paint");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/paint.elf", NULL); else process_create_elf("/bin/paint.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Clock")) { } else if (str_starts_with(start_menu_pending_app, "Clock")) {
Window *existing = wm_find_window_by_title("Clock"); Window *existing = wm_find_window_by_title_locked("Clock");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/clock.elf", NULL); else process_create_elf("/bin/clock.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Browser")) { } else if (str_starts_with(start_menu_pending_app, "Browser")) {
Window *existing = wm_find_window_by_title("Web Browser"); Window *existing = wm_find_window_by_title_locked("Web Browser");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/browser.elf", NULL); else process_create_elf("/bin/browser.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "About")) { } else if (str_starts_with(start_menu_pending_app, "About")) {
process_create_elf("/bin/about.elf", NULL); process_create_elf("/bin/about.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Task Manager")) { } else if (str_starts_with(start_menu_pending_app, "Task Manager")) {
Window *existing = wm_find_window_by_title("Task Manager"); Window *existing = wm_find_window_by_title_locked("Task Manager");
if (existing) wm_bring_to_front_locked(existing); if (existing) wm_bring_to_front_locked(existing);
else process_create_elf("/bin/taskman.elf", NULL); else process_create_elf("/bin/taskman.elf", NULL);
} else if (str_starts_with(start_menu_pending_app, "Shutdown")) { } else if (str_starts_with(start_menu_pending_app, "Shutdown")) {
@@ -2242,8 +2279,8 @@ void wm_handle_right_click(int x, int y) {
if (!handled) { if (!handled) {
// Generic Shortcut Handling // Generic Shortcut Handling
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) { if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) {
FAT32_FileHandle *fh = fat32_open(path, "r"); FAT32_FileHandle *fh = fat32_open(path, "r");
@@ -2266,12 +2303,12 @@ void wm_handle_right_click(int x, int y) {
} }
} }
} else if (icon->type == 1) { // Folder } else if (icon->type == 1) { // Folder
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_open_directory(path); explorer_open_directory(path);
} else { // File } else { // File
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; 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")) { if (str_ends_with(icon->name, ".elf")) {
process_create_elf(path, NULL); process_create_elf(path, NULL);
@@ -2317,7 +2354,7 @@ void wm_handle_right_click(int x, int y) {
explorer_import_file(drop_win, drag_file_path); explorer_import_file(drop_win, drag_file_path);
} }
if (str_starts_with(drag_file_path, "/Desktop/")) { if (str_starts_with(drag_file_path, "/root/Desktop/")) {
refresh_desktop_icons(); refresh_desktop_icons();
} }
} else { } else {
@@ -2329,15 +2366,15 @@ void wm_handle_right_click(int x, int y) {
bool dropped_on_target = false; bool dropped_on_target = false;
for (int i = 0; i < desktop_icon_count; i++) { for (int i = 0; i < desktop_icon_count; i++) {
if (from_desktop) { if (from_desktop) {
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0; int p=14; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
if (str_eq(path, drag_file_path) != 0) continue; if (str_eq(path, drag_file_path) != 0) continue;
} }
if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) { if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) {
if (desktop_icons[i].type == 1) { if (desktop_icons[i].type == 1) {
char target_path[256] = "/Desktop/"; char target_path[256] = "/root/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0; int p=14; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0;
explorer_import_file_to(&win_explorer, drag_file_path, target_path); explorer_import_file_to(&win_explorer, drag_file_path, target_path);
refresh_desktop_icons(); refresh_desktop_icons();
dropped_on_target = true; dropped_on_target = true;
@@ -2360,7 +2397,7 @@ void wm_handle_right_click(int x, int y) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!"); wm_show_message("Error", "Desktop is full!");
} else { } else {
explorer_import_file_to(&win_explorer, drag_file_path, "/Desktop"); explorer_import_file_to(&win_explorer, drag_file_path, "/root/Desktop");
} }
// Handle insertion at specific position // Handle insertion at specific position
@@ -2404,7 +2441,8 @@ void wm_handle_right_click(int x, int y) {
if (desktop_snap_to_grid) { if (desktop_snap_to_grid) {
int col = (desktop_icons[i].x - 20 + 40) / 80; int col = (desktop_icons[i].x - 20 + 40) / 80;
int row = (desktop_icons[i].y - 20 + 40) / 80; int row = (desktop_icons[i].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0; if (col < 0) col = 0;
if (row < 0) row = 0;
desktop_icons[i].x = 20 + col * 80; desktop_icons[i].x = 20 + col * 80;
desktop_icons[i].y = 20 + row * 80; desktop_icons[i].y = 20 + row * 80;
} }
@@ -2415,8 +2453,8 @@ void wm_handle_right_click(int x, int y) {
} else if (!dropped_on_target) { } else if (!dropped_on_target) {
int dragged_idx = -1; int dragged_idx = -1;
for(int i=0; i<desktop_icon_count; i++) { for(int i=0; i<desktop_icon_count; i++) {
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0; int p=14; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
if (str_eq(path, drag_file_path) != 0) { if (str_eq(path, drag_file_path) != 0) {
dragged_idx = i; dragged_idx = i;
break; break;
@@ -2575,6 +2613,12 @@ void wm_handle_right_click(int x, int y) {
prev_left = left; prev_left = left;
} }
void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz) {
uint64_t rflags = wm_lock_acquire();
wm_handle_mouse_internal(dx, dy, buttons, dz);
wm_lock_release(rflags);
}
// Input Queue // Input Queue
#define INPUT_QUEUE_SIZE 128 #define INPUT_QUEUE_SIZE 128
typedef struct { typedef struct {
@@ -2590,10 +2634,10 @@ static void wm_dispatch_key(char c, bool pressed) {
int len = 0; while(desktop_dialog_input[len]) len++; int len = 0; while(desktop_dialog_input[len]) len++;
if (c == '\n') { if (c == '\n') {
if (desktop_dialog_state == 8) { // Rename if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/"; char old_path[128] = "/root/Desktop/";
char new_path[128] = "/Desktop/"; char new_path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0; int p=14; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0; p=14; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) { if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons(); refresh_desktop_icons();
explorer_refresh_all(); explorer_refresh_all();
@@ -2602,8 +2646,8 @@ static void wm_dispatch_key(char c, bool pressed) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!"); wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) { } else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/"; char path[128] = "/root/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0; int p=14; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) { if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w"); FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh); if (fh) fat32_close(fh);
@@ -2663,7 +2707,7 @@ void wm_show_notification(const char *msg) {
notif_text[i] = 0; notif_text[i] = 0;
notif_timer = 180; // ~3 seconds at 60Hz notif_timer = 180; // ~3 seconds at 60Hz
notif_x_offset = 300; notif_x_offset = 420;
notif_active = true; notif_active = true;
force_redraw = true; force_redraw = true;
} }
@@ -2795,19 +2839,19 @@ void wm_timer_tick(void) {
notif_timer--; notif_timer--;
// Slide in // Slide in
if (notif_timer > 165 && notif_x_offset > 0) { // First 15 ticks (1/4 sec) slide in if (notif_timer > 165 && notif_x_offset > 0) { // First 15 ticks (1/4 sec) slide in
notif_x_offset -= 20; notif_x_offset -= 28; // Slightly faster slide for larger distance
if (notif_x_offset < 0) notif_x_offset = 0; if (notif_x_offset < 0) notif_x_offset = 0;
} }
// Slide out // Slide out
else if (notif_timer < 15 && notif_x_offset < 300) { // Last 15 ticks slide out else if (notif_timer < 15 && notif_x_offset < 420) { // Last 15 ticks slide out
notif_x_offset += 20; notif_x_offset += 28;
} }
} else { } else {
notif_active = false; notif_active = false;
} }
int sw = get_screen_width(); int sw = get_screen_width();
wm_mark_dirty(sw - 280, 40, 275, 60); wm_mark_dirty(sw - 420, 40, 415, 60);
} }
} }

View File

@@ -6,6 +6,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "../sys/spinlock.h"
uint64_t wm_lock_acquire(void); uint64_t wm_lock_acquire(void);
void wm_lock_release(uint64_t flags); void wm_lock_release(uint64_t flags);
@@ -18,14 +19,13 @@ void wm_lock_release(uint64_t flags);
#define COLOR_BLUE 0xFF000080 #define COLOR_BLUE 0xFF000080
#define COLOR_LTGRAY 0xFFDFDFDF #define COLOR_LTGRAY 0xFFDFDFDF
#define COLOR_DKGRAY 0xFF808080 #define COLOR_DKGRAY 0xFF808080
#define COLOR_RED 0xFFFF0000
#define COLOR_PURPLE 0xFF800080 #define COLOR_PURPLE 0xFF800080
#define COLOR_COFFEE 0xFF6B4423 #define COLOR_COFFEE 0xFF6B4423
#define COLOR_APPLE_RED 0xFFFF0000 #define COLOR_RED 0xFFFF0000
#define COLOR_APPLE_ORANGE 0xFFFF7F00 #define COLOR_ORANGE 0xFFFF7F00
#define COLOR_APPLE_YELLOW 0xFFFFFF00 #define COLOR_YELLOW 0xFFFFFF00
#define COLOR_APPLE_GREEN 0xFF00FF00 #define COLOR_GREEN 0xFF00FF00
#define COLOR_APPLE_BLUE 0xFF0000FF #define COLOR_LIGHTBLUE 0xFF0000FF
#define COLOR_APPLE_INDIGO 0xFF4B0082 #define COLOR_APPLE_INDIGO 0xFF4B0082
#define COLOR_APPLE_VIOLET 0xFF9400D3 #define COLOR_APPLE_VIOLET 0xFF9400D3
@@ -56,6 +56,7 @@ struct Window {
uint32_t *pixels; uint32_t *pixels;
uint32_t *comp_pixels; uint32_t *comp_pixels;
void *font; void *font;
spinlock_t lock;
// Callbacks // Callbacks
void (*paint)(Window *win); void (*paint)(Window *win);
@@ -74,8 +75,10 @@ void wm_handle_click(int x, int y);
void wm_handle_right_click(int x, int y); void wm_handle_right_click(int x, int y);
void wm_process_input(void); void wm_process_input(void);
void wm_process_deferred_thumbs(void); void wm_process_deferred_thumbs(void);
void wm_add_window_locked(Window *win);
void wm_add_window(Window *win); void wm_add_window(Window *win);
void wm_remove_window(Window *win); void wm_remove_window(Window *win);
void wm_bring_to_front_locked(Window *win);
void wm_bring_to_front(Window *win); void wm_bring_to_front(Window *win);
Window* wm_find_window_by_title(const char *title); Window* wm_find_window_by_title(const char *title);