diff --git a/Makefile b/Makefile index c51352a..5f5047a 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,18 @@ ISO_DIR = iso_root KERNEL_ELF = $(BUILD_DIR)/boredos.elf ISO_IMAGE = boredos.iso +BLUE = \033[1;34m +GREEN = \033[1;32m +YELLOW= \033[1;33m +RESET = \033[0m + +define PRINT_STEP + @printf "" + @printf "\n$(BLUE)============================================================$(RESET)\n" + @printf "$(BLUE)== %s$(RESET)\n" "$(1)" + @printf "$(BLUE)============================================================$(RESET)\n" +endef + DOCK_COLLOID_ICONS = $(shell sed -n 's/^[[:space:]]*{"\([^"]*\.png\)",[[:space:]]*DOCK_ICON_UNTRIED.*/\1/p' $(SRC_DIR)/wm/wm.c) USERLAND_COLLOID_ICONS = $(shell { \ find $(SRC_DIR)/userland -type f -name '*.c' ! -path '*/third_party/*' -exec grep -hoE '"[^"]+\.png"' {} + 2>/dev/null; \ @@ -67,89 +79,123 @@ NASMFLAGS = -f elf64 LIMINE_VERSION = 10.8.2 LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERSION) -.PHONY: all clean run limine-setup +.PHONY: all clean run limine-setup run-windows run-mac run-linux -all: $(ISO_IMAGE) +all: + $(call PRINT_STEP,STARTING BOREDOS BUILD) + $(MAKE) $(ISO_IMAGE) + $(call PRINT_STEP,BUILD COMPLETE) $(BUILD_DIR): + $(call PRINT_STEP,CREATING BUILD DIRECTORY) mkdir -p $(BUILD_DIR) mkdir -p $(BUILD_DIR) limine-setup: + $(call PRINT_STEP,SETTING UP LIMINE) @if [ ! -f limine/limine-bios.sys ]; then \ - echo "Limine binaries missing or invalid. Cloning v$(LIMINE_VERSION)-binary..."; \ + printf "$(YELLOW)[LIMINE] Limine binaries missing or invalid. Cloning v$(LIMINE_VERSION)-binary...$(RESET)"; \ rm -rf limine; \ git clone https://github.com/limine-bootloader/limine.git --branch=v$(LIMINE_VERSION)-binary --depth=1 limine; \ + else \ + printf "$(YELLOW)[LIMINE] Existing Limine binaries found.$(RESET)"; \ fi @if [ ! -f $(SRC_DIR)/core/limine.h ]; then \ - echo "Copying limine.h..."; \ + printf "$(YELLOW)[LIMINE] Copying limine.h...$(RESET)"; \ cp limine/limine.h $(SRC_DIR)/core/limine.h; \ + else \ + printf "$(YELLOW)[LIMINE] limine.h already present.$(RESET)"; \ fi - @echo "Building Limine host utility..."; \ + @printf "$(YELLOW)[LIMINE] Building Limine host utility...$(RESET)" $(MAKE) -C limine + @printf "$(GREEN)[OK] Limine setup complete.$(RESET)" $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET) $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/core/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[core] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/sys/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[sys] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/mem/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[mem] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/dev/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[dev] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/input/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[input] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/net/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[net] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/net/nic/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[net/nic] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/fs/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[fs] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/wm/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[wm] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/lwip/%.o: $(SRC_DIR)/net/third_party/lwip/%.c | $(BUILD_DIR) limine-setup + @printf "$(YELLOW)[CC]$(RESET)[lwIP] $< -> $@" mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/arch/%.asm | $(BUILD_DIR) + @printf "$(YELLOW)[ASM]$(RESET) $< -> $@" $(NASM) $(NASMFLAGS) $< -o $@ $(BUILD_DIR)/test_syscall.o: $(SRC_DIR)/arch/test_syscall.asm | $(BUILD_DIR) + @printf "$(YELLOW)[ASM][test_syscall]$(RESET) $< -> $@" $(NASM) $(NASMFLAGS) $< -o $@ $(BUILD_DIR)/user_test.o: $(SRC_DIR)/arch/user_test.asm | $(BUILD_DIR) + @printf "$(YELLOW)[ASM][user_test]$(RESET) $< -> $@" $(NASM) $(NASMFLAGS) $< -o $@ $(BUILD_DIR)/process_asm.o: $(SRC_DIR)/arch/process_asm.asm | $(BUILD_DIR) + @printf "$(YELLOW)[ASM][process]$(RESET) $< -> $@" $(NASM) $(NASMFLAGS) $< -o $@ $(KERNEL_ELF): $(OBJ_FILES) + $(call PRINT_STEP,LINKING KERNEL) + @printf "$(YELLOW)[LD]$(RESET) Linking kernel ELF: $@" $(LD) $(LDFLAGS) -o $@ $(OBJ_FILES) + @printf "$(GREEN)[OK]$(RESET) Kernel ELF built: $@" + $(call PRINT_STEP,BUILDING USERLAND) $(MAKE) -C $(SRC_DIR)/userland + @printf "$(GREEN)[OK]$(RESET) Userland build complete." $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF) + $(call PRINT_STEP,BUILDING INITRD) + @printf "$(YELLOW)[INITRD]$(RESET) Cleaning previous initrd directory..." rm -rf $(BUILD_DIR)/initrd + + @printf "$(YELLOW)[INITRD]$(RESET) Creating directory structure..." mkdir -p $(BUILD_DIR)/initrd/bin mkdir -p $(BUILD_DIR)/initrd/Library/images/Wallpapers mkdir -p $(BUILD_DIR)/initrd/Library/images/gif @@ -159,81 +205,142 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF) mkdir -p $(BUILD_DIR)/initrd/Library/bsh mkdir -p $(BUILD_DIR)/initrd/docs + @printf "$(YELLOW)[COPY]$(RESET) Userland binaries..." @for f in $(SRC_DIR)/userland/bin/*.elf; do \ - if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/bin/; fi \ + if [ -f "$$f" ]; then \ + printf " -> $$f"; \ + cp "$$f" $(BUILD_DIR)/initrd/bin/; \ + fi \ done + + @printf "$(YELLOW)[COPY]$(RESET) Wallpapers..." @for f in $(SRC_DIR)/images/wallpapers/*; do \ - if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; fi \ + if [ -f "$$f" ]; then \ + printf " -> $$f"; \ + cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; \ + fi \ done + + @printf "$(YELLOW)[COPY]$(RESET) GIF assets..." @for f in $(SRC_DIR)/images/gif/*.gif; do \ - if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; fi \ + if [ -f "$$f" ]; then \ + printf " -> $$f"; \ + cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; \ + fi \ done + + @printf "$(YELLOW)[COPY]$(RESET) Colloid icons..." @for f in $(COLLOID_ICONS); do \ src="$(SRC_DIR)/images/icons/colloid/$$f"; \ - if [ -f "$$src" ]; then cp "$$src" $(BUILD_DIR)/initrd/Library/images/icons/colloid/; fi \ + if [ -f "$$src" ]; then \ + printf " -> $$src"; \ + cp "$$src" $(BUILD_DIR)/initrd/Library/images/icons/colloid/; \ + fi \ done + + @printf "$(YELLOW)[COPY]$(RESET) Fonts..." @for f in $(SRC_DIR)/fonts/*.ttf; do \ - if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; fi \ + if [ -f "$$f" ]; then \ + printf " -> $$f"; \ + cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; \ + fi \ done + + @printf "$(YELLOW)[COPY]$(RESET) Emoji fonts..." @for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \ - if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \ + if [ -f "$$f" ]; then \ + printf " -> $$f"; \ + cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; \ + fi \ done - @if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi - @if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi - @if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi - @if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi + + @printf "$(YELLOW)[COPY]$(RESET) bsh configuration..." + @if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then printf " -> bshrc"; cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi + @if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then printf " -> startup.bsh"; cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi + @if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then printf " -> boot.bsh"; cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi + + @printf "$(YELLOW)[COPY]$(RESET) DOOM assets..." + @if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then printf " -> doom1.wad"; cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi + + @printf "$(YELLOW)[COPY]$(RESET) Documentation..." @for f in $$(find docs -name '*.md' 2>/dev/null); do \ if [ -f "$$f" ]; then \ + printf " -> $$f"; \ 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 - @if [ -f limine.conf ]; then cp limine.conf $(BUILD_DIR)/initrd/; fi + + @printf "$(YELLOW)[COPY]$(RESET) Root files..." + @if [ -f README.md ]; then printf " -> README.md"; cp README.md $(BUILD_DIR)/initrd/; fi + @if [ -f LICENSE ]; then printf " -> LICENSE"; cp LICENSE $(BUILD_DIR)/initrd/; fi + @if [ -f limine.conf ]; then printf " -> limine.conf"; cp limine.conf $(BUILD_DIR)/initrd/; fi + @printf "$(YELLOW)[TAR]$(RESET) Creating initrd.tar..." cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar * + @printf "$(GREEN)[OK]$(RESET) Initrd created: $(BUILD_DIR)/initrd.tar" $(ISO_IMAGE): $(KERNEL_ELF) $(BUILD_DIR)/initrd.tar limine.conf limine-setup + $(call PRINT_STEP,CREATING ISO IMAGE) + @printf "$(YELLOW)[ISO]$(RESET) Cleaning previous ISO root..." rm -rf $(ISO_DIR) + + @printf "$(YELLOW)[ISO]$(RESET) Creating ISO directory structure..." mkdir -p $(ISO_DIR) mkdir -p $(ISO_DIR)/EFI/BOOT + @printf "$(YELLOW)[COPY]$(RESET) Kernel ELF..." cp $(KERNEL_ELF) $(ISO_DIR)/ + + @printf "$(YELLOW)[COPY]$(RESET) Limine config..." cp limine.conf $(ISO_DIR)/ + @printf "$(YELLOW)[COPY]$(RESET) Initrd..." cp $(BUILD_DIR)/initrd.tar $(ISO_DIR)/ - echo " module_path: boot():/initrd.tar" >> $(ISO_DIR)/limine.conf + + @printf "$(YELLOW)[CONFIG]$(RESET) Adding initrd module path..." + printf " module_path: boot():/initrd.tar" >> $(ISO_DIR)/limine.conf - @if [ -f splash.jpg ]; then cp splash.jpg $(ISO_DIR)/; fi + @printf "$(YELLOW)[COPY]$(RESET) Optional splash image..." + @if [ -f splash.jpg ]; then printf " -> splash.jpg"; cp splash.jpg $(ISO_DIR)/; else printf " -> no splash.jpg found"; fi + @printf "$(YELLOW)[COPY]$(RESET) Limine boot files..." cp limine/limine-bios.sys $(ISO_DIR)/ cp limine/limine-bios-cd.bin $(ISO_DIR)/ cp limine/limine-uefi-cd.bin $(ISO_DIR)/ + @printf "$(YELLOW)[COPY]$(RESET) EFI bootloaders..." cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/ cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/ + $(call PRINT_STEP,GENERATING BOOTABLE ISO) $(XORRISO) -as mkisofs -R -J -b limine-bios-cd.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table \ --efi-boot limine-uefi-cd.bin \ -efi-boot-part --efi-boot-image --protective-msdos-label \ $(ISO_DIR) -o $(ISO_IMAGE) + @printf "$(YELLOW)[LIMINE]$(RESET) Installing BIOS bootloader..." ./limine/limine bios-install $(ISO_IMAGE) + @printf "$(GREEN)[OK]$(RESET) ISO image ready: $(ISO_IMAGE)" clean: + $(call PRINT_STEP,CLEANING BUILD OUTPUT) rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE) $(MAKE) -C $(SRC_DIR)/userland clean + @printf "$(GREEN)[OK]$(RESET) Clean complete." run-windows: $(ISO_IMAGE) + $(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON WINDOWS) qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ -smp 4 \ -audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -drive file=disk.img,format=raw,file.locking=off + run-mac: $(ISO_IMAGE) + $(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON MACOS) qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ -smp 4 \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ @@ -241,7 +348,9 @@ run-mac: $(ISO_IMAGE) -display cocoa,show-cursor=off \ -drive file=disk.img,format=raw,file.locking=off \ -cpu max + run-linux: $(ISO_IMAGE) + $(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON LINUX) qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ -smp 4 \ -audiodev pa,id=audio0 -machine pcspk-audiodev=audio0 \ @@ -249,4 +358,3 @@ run-linux: $(ISO_IMAGE) -display gtk,show-cursor=off \ -drive file=disk.img,format=raw,file.locking=off \ -cpu max - diff --git a/docs/appdev/inputs_api_(utf8).md b/docs/appdev/inputs_api_(utf8).md new file mode 100644 index 0000000..0c267a7 --- /dev/null +++ b/docs/appdev/inputs_api_(utf8).md @@ -0,0 +1,262 @@ +# UTF-8 Library — Application Development Guide + +## Overview + +The userland libc provides a lightweight UTF-8 utility module located in: + +- src/userland/libc/utf-8.c +- src/userland/libc/utf-8.h + +This module is designed for **direct use in applications** requiring UTF-8 handling. It provides basic primitives for decoding, encoding, and traversing UTF-8 strings safely. + +It is intended for: + +- text rendering +- terminal input/output +- cursor movement +- string processing at the character level + +--- + +## Synopsis + +```c +#include "utf-8.h" + +uint32_t text_decode_utf8(const char *s, int *advance); +int text_encode_utf8(uint32_t cp, char *out); + +const char* text_next_utf8(const char *s); +const char* text_prev_utf8(const char *start, const char *s); + +int text_strlen_utf8(const char *s); +``` + +--- + +## API Reference + +### text_decode_utf8 + +```c +uint32_t text_decode_utf8(const char *s, int *advance); +``` + +Decodes a UTF-8 sequence into a Unicode code point. + +- `s`: pointer to current position in a UTF-8 string +- `advance`: receives number of bytes consumed + +Returns: + +- decoded Unicode code point (`uint32_t`) +- `0` if input is null or empty +- `0xFFFD` for invalid sequences + +--- + +### text_encode_utf8 + +```c +int text_encode_utf8(uint32_t cp, char *out); +``` + +Encodes a Unicode code point into UTF-8. + +- `cp`: Unicode code point +- `out`: buffer receiving encoded bytes + +Returns: + +- number of bytes written (1–4) +- writes replacement character if `cp` is invalid + +--- + +### text_next_utf8 + +```c +const char* text_next_utf8(const char *s); +``` + +Advances to the next UTF-8 character. + +Returns a pointer to the next character boundary. + +--- + +### text_prev_utf8 + +```c +const char* text_prev_utf8(const char *start, const char *s); +``` + +Moves backward to the previous UTF-8 character. + +- `start`: beginning of the buffer +- `s`: current position + +Used for reverse traversal and cursor movement. + +--- + +### text_strlen_utf8 + +```c +int text_strlen_utf8(const char *s); +``` + +Counts UTF-8 characters (code points), not bytes. + +--- + +## Usage Examples + +### Iterating over UTF-8 characters + +```c +const char *p = text; + +while (*p) { + int adv; + uint32_t cp = text_decode_utf8(p, &adv); + + /* process cp */ + + p += adv; +} +``` + +--- + +### Cursor movement + +```c +cursor = text_next_utf8(cursor); +cursor = text_prev_utf8(buffer_start, cursor); +``` + +--- + +### Encoding a character + +```c +char out[4]; +int len = text_encode_utf8(0x20AC, out); +``` + +--- + +### Backspace handling + +```c +char *prev = (char*)text_prev_utf8(buffer, cursor); +cursor = prev; +``` + +--- + +## Implementation Notes + +### UTF-8 Encoding + +The implementation supports: + +- 1 byte: `0x00 – 0x7F` +- 2 bytes: `0x80 – 0x7FF` +- 3 bytes: `0x800 – 0xFFFF` +- 4 bytes: `0x10000 – 0x10FFFF` + +--- + +### Replacement Character + +Invalid sequences are replaced with: + +- code point: `0xFFFD` +- UTF-8 encoding: `0xEF 0xBF 0xBD` + +--- + +### Control Signals + +Some decoded code points correspond to control signals instead of printable characters. + +ASCII control range: + +- `0x00 – 0x1F` + +Examples: + +- `0x08` → Backspace +- `0x09` → Tab +- `0x0A` → Line Feed +- `0x0D` → Carriage Return +- `0x1B` → Escape + +These are typically interpreted by: + +- terminal logic +- shell input handling +- system interfaces + +--- + +### Non-ASCII Characters + +Characters outside the ASCII range (`0x00 – 0x7F`) are encoded using multi-byte UTF-8 sequences. + +Examples: + +- 'é' → `0xC3 0xA9` +- '€' → `0xE2 0x82 0xAC` + +Decoded values: + +- 'é' → `U+00E9` +- '€' → `U+20AC` + +--- + +### Modifiers and Layout + +Character output depends on: + +- keyboard layout +- modifier keys (Shift, Ctrl, AltGr) + +Example: + +- `KEY_E` → 'e' +- `KEY_E + SHIFT` → 'E' +- `KEY_E + AltGr` → '€' + +--- + +## Limitations + +- No full UTF-8 validation (overlong, surrogates not fully rejected) +- No grapheme cluster handling +- No Unicode normalization + +--- + +## Best Practices + +- Never iterate UTF-8 strings byte-by-byte +- Always use provided helpers for navigation +- Separate byte length from character count +- Handle invalid sequences safely + +--- + +## Summary + +This module provides essential UTF-8 primitives for userland applications. + +It should be used whenever an application needs to safely: + +- decode UTF-8 +- encode Unicode +- traverse text +- handle user input correctly