33 Commits

Author SHA1 Message Date
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
42 changed files with 4363 additions and 2570 deletions

2
.gitattributes vendored
View File

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

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]

View File

@@ -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,7 @@ isr%2_wrapper:
push r14 push r14
push r15 push r15
; Save SSE/FPU state ; Save SSE/FPU state (fxsave requires 16-byte alignment)
sub rsp, 512 sub rsp, 512
fxsave [rsp] fxsave [rsp]
@@ -164,7 +164,7 @@ exception_common:
push r14 push r14
push r15 push r15
; Save SSE/FPU state ; Save SSE/FPU state (fxsave requires 16-byte alignment)
sub rsp, 512 sub rsp, 512
fxsave [rsp] fxsave [rsp]

View File

@@ -16,6 +16,7 @@
#include "wm.h" #include "wm.h"
#include "io.h" #include "io.h"
#include "fat32.h" #include "fat32.h"
#include "tar.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "platform.h" #include "platform.h"
#include "wallpaper.h" #include "wallpaper.h"
@@ -210,21 +211,31 @@ 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;
char dir_path[256]; int len = 0;
int last_slash = -1; while(clean_path[len]) len++;
for (int j = 0; clean_path[j]; j++) {
if (clean_path[j] == '/') last_slash = j;
}
if (last_slash > 0) {
for (int j = 0; j < last_slash; j++) dir_path[j] = clean_path[j];
dir_path[last_slash] = '\0';
fat32_mkdir_recursive(dir_path);
}
FAT32_FileHandle *fh = fat32_open(clean_path, "w"); if (len >= 4 && clean_path[len-4] == '.' && clean_path[len-3] == 't' && clean_path[len-2] == 'a' && clean_path[len-1] == 'r') {
if (fh && fh->valid) { serial_write("[DEBUG] Parsing TAR initrd: ");
fat32_write(fh, mod->address, mod->size); serial_write(clean_path);
fat32_close(fh); serial_write("\n");
tar_parse(mod->address, mod->size);
} else {
char dir_path[256];
int last_slash = -1;
for (int j = 0; clean_path[j]; j++) {
if (clean_path[j] == '/') last_slash = j;
}
if (last_slash > 0) {
for (int j = 0; j < last_slash; j++) dir_path[j] = clean_path[j];
dir_path[last_slash] = '\0';
fat32_mkdir_recursive(dir_path);
}
FAT32_FileHandle *fh = fat32_open(clean_path, "w");
if (fh && fh->valid) {
fat32_write(fh, mod->address, mod->size);
fat32_close(fh);
}
} }
} }
} }

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 = "Geometry";
const char *kernel_name = "Boredkernel"; const char *kernel_name = "Boredkernel";
const char *kernel_version = "3.1.2"; const char *kernel_version = "3.2.3";
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";

View File

@@ -232,6 +232,16 @@ static uint32_t ramfs_allocate_cluster(void) {
return cluster; return cluster;
} }
static int ramfs_count_files_in_dir(const char *normalized_path) {
int count = 0;
for (int i = 0; i < MAX_FILES; i++) {
if (files[i].used && fs_strcmp(files[i].parent_path, normalized_path) == 0) {
count++;
}
}
return count;
}
static bool check_desktop_limit(const char *normalized_path) { static bool check_desktop_limit(const char *normalized_path) {
if (desktop_file_limit < 0) return true; if (desktop_file_limit < 0) return true;
if (fs_strlen(normalized_path) > 9 && if (fs_strlen(normalized_path) > 9 &&
@@ -245,10 +255,8 @@ static bool check_desktop_limit(const char *normalized_path) {
if (*p == '/') return true; if (*p == '/') return true;
p++; p++;
} }
FAT32_FileInfo *info = (FAT32_FileInfo*)kmalloc(256 * sizeof(FAT32_FileInfo));
if (!info) return true; int count = ramfs_count_files_in_dir("/Desktop");
int count = fat32_list_directory("/Desktop", info, 256);
kfree(info);
if (count >= desktop_file_limit) return false; if (count >= desktop_file_limit) return false;
} }
return true; return true;

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

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

@@ -24,6 +24,7 @@ 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 = NULL;
static uint64_t free_pml4_later = 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; // Round-robin CPU assignment (start from CPU 1)
@@ -379,6 +380,11 @@ uint64_t process_schedule(uint64_t current_rsp) {
kfree(free_kernel_stack_later); kfree(free_kernel_stack_later);
free_kernel_stack_later = NULL; free_kernel_stack_later = NULL;
} }
if (free_pml4_later) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(free_pml4_later);
free_pml4_later = 0;
}
uint32_t my_cpu = smp_this_cpu_id(); uint32_t my_cpu = smp_this_cpu_id();
process_t *cur = current_process[my_cpu]; process_t *cur = current_process[my_cpu];
@@ -469,6 +475,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 +530,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,6 +594,8 @@ 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) {

View File

@@ -9,17 +9,16 @@
#include "platform.h" #include "platform.h"
#include "paging.h" #include "paging.h"
#include "process.h" #include "process.h"
#include "work_queue.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;
// 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));
@@ -44,49 +43,37 @@ 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)
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].online = true; cpu_states[my_id].online = true;
serial_write("[SMP] AP "); serial_write("[SMP] AP ");
@@ -95,25 +82,17 @@ 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); process_set_current_for_cpu(my_id, ap_idle);
// 10. Enable interrupts and enter idle halt loop.
// 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"); }
} }
// --- 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 +114,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 +123,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,7 +132,6 @@ 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].online = true; cpu_states[i].online = true;
bsp_index = i; bsp_index = i;
serial_write("[SMP] BSP CPU "); serial_write("[SMP] BSP CPU ");
@@ -165,7 +140,6 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
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 +149,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

@@ -9,6 +9,8 @@
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.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 +21,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 +38,90 @@ 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)
} }
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 +129,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 +158,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 +257,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 +312,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 +334,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 +349,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 +369,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 +393,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();
@@ -380,7 +431,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 == 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 +455,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 +469,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 +497,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();
@@ -478,7 +535,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 == 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 +573,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();
@@ -553,7 +613,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_IMAGE) { } else if (cmd == GUI_CMD_DRAW_IMAGE) {
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
@@ -564,31 +625,32 @@ 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;
if (ry + rh > (win->h - 20)) rh = (win->h - 20) - ry; if (ry + rh > (win->h - 20)) rh = (win->h - 20) - ry;
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 +661,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 +674,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]);
} }
@@ -1251,6 +1316,36 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void get_os_info(os_info_t *info); extern void get_os_info(os_info_t *info);
get_os_info(info); get_os_info(info);
return 0; return 0;
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3;
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 -1; return -1;
} }

View File

@@ -58,6 +58,7 @@ typedef struct {
#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_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);

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

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

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; needs_repaint = 1;
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;
}
} 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

@@ -113,6 +113,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 +138,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 +256,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 +293,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 (sys_dns_lookup(hostname, &ip) != 0) return 0; if (str_iequals(hostname, dns_cache_host)) {
ip = dns_cache_ip;
} else {
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 +326,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 +461,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 (fit_w > win_w - 60) { fit_h = fit_h * (win_w - 60) / fit_w; fit_w = win_w - 60; } if (el->attr_w > 0) {
if (fit_h > 400) { fit_w = fit_w * 400 / fit_h; fit_h = 400; } 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_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 +612,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 +655,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 +665,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 +684,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 +728,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 +886,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 +930,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 +975,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 +995,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 +1109,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 +1174,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 +1188,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 +1340,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 +1357,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 +1403,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 +1438,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) {
@@ -1253,9 +1456,19 @@ static void parse_html_incremental(const char *html, int safe_len) {
ui_window_set_title(win_browser, page_title); ui_window_set_title(win_browser, page_title);
skip_content = true; skip_content = true;
} }
} 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 +1527,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 +1546,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 +1638,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 +1649,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);
widget_textbox_t tb;
if (focused_element == i) { widget_textbox_init(&tb, el->x, draw_y, el->w, el->h, visible, max_v);
int cursor_pos = el->input_cursor - el->input_scroll; tb.cursor_pos = el->input_cursor - el->input_scroll;
if (cursor_pos >= 0 && cursor_pos < max_v) { if (tb.cursor_pos < 0) tb.cursor_pos = 0;
char sub[64]; tb.focused = (focused_element == i);
int k; widget_textbox_draw(&browser_ctx, &tb);
for (k = 0; k < cursor_pos && visible[k]; k++) sub[k] = visible[k]; browser_ctx.use_light_theme = false;
sub[k] = 0;
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 +1723,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 +1802,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;
needs_repaint = true; 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;
continue;
}
if (widget_button_handle_mouse(&btn_back, mx, my, is_down, is_click, NULL)) {
if (is_click && history_count > 0) {
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;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
}
needs_repaint = true; continue;
}
if (widget_button_handle_mouse(&btn_home, mx, my, is_down, is_click, NULL)) {
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++; }
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;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
}
needs_repaint = true; continue;
}
if (is_click) {
focused_element = -1; needs_repaint = true;
}
continue; continue;
} }
if (ev.arg2 < URL_BAR_H) { my = ev.arg2 - URL_BAR_H + scroll_y;
// Check back button
if (mx >= BACK_BTN_X && mx < BACK_BTN_X + BTN_W && history_count > 0) {
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;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
needs_repaint = true; continue;
}
// Check home button
if (mx >= HOME_BTN_X && mx < HOME_BTN_X + BTN_W) {
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";
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;
needs_repaint = true; continue;
}
focused_element = -1; needs_repaint = true; continue;
}
int 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 +1865,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)) {
elements[k].checked = false; if (elements[k].checked) {
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 +2010,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,105 +144,93 @@ 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; "C", "s", "r", "/",
int gap = 5; "7", "8", "9", "*",
int start_x = 10; "4", "5", "6", "-",
int start_y = 35; // Matches the hitboxes "1", "2", "3", "+",
"0", ".", "B", "="
};
char lbl = labels_map[idx][0];
for (int i = 0; i < 20; i++) { if (lbl >= '0' && lbl <= '9') {
int r = i / 4; if (calc_new_entry || calc_error) {
int c = i % 4; calc_curr = (lbl - '0') * SCALE;
int bx = start_x + c*(bw+gap); calc_new_entry = false;
int by = start_y + r*(bh+gap); calc_decimal_mode = false;
} else {
if (x >= bx && x < bx + bw && y >= by && y < by + bh) { if (calc_decimal_mode) {
const char *labels[] = { if (calc_decimal_divisor <= SCALE) {
"C", "s", "r", "/", long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor;
"7", "8", "9", "*", if (calc_curr >= 0) calc_curr += digit_val;
"4", "5", "6", "-", else calc_curr -= digit_val;
"1", "2", "3", "+", calc_decimal_divisor *= 10;
"0", ".", "B", "=" }
};
char lbl = labels[i][0];
if (lbl >= '0' && lbl <= '9') {
if (calc_new_entry || calc_error) {
calc_curr = (lbl - '0') * SCALE;
calc_new_entry = false;
calc_decimal_mode = false;
} else {
if (calc_decimal_mode) {
if (calc_decimal_divisor <= SCALE) {
long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor;
if (calc_curr >= 0) calc_curr += digit_val;
else calc_curr -= digit_val;
calc_decimal_divisor *= 10;
}
} else {
if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE;
else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE;
}
}
calc_error = false;
} else if (lbl == '.') {
if (calc_new_entry) {
calc_curr = 0;
calc_new_entry = false;
}
if (!calc_decimal_mode) {
calc_decimal_mode = true;
calc_decimal_divisor = 10;
}
} else if (lbl == 'C') {
calc_curr = 0; calc_acc = 0; calc_op = 0;
calc_new_entry = true; calc_error = false; calc_decimal_mode = false;
} else if (lbl == 'B') {
if (!calc_new_entry && !calc_error) {
if (calc_decimal_mode) {
if (calc_decimal_divisor > 10) {
calc_decimal_divisor /= 10;
long long unit = SCALE / calc_decimal_divisor;
calc_curr = (calc_curr / (unit * 10)) * (unit * 10);
} else {
calc_decimal_mode = false;
calc_decimal_divisor = 10;
calc_curr = (calc_curr / SCALE) * SCALE;
}
} else {
calc_curr = (calc_curr / SCALE / 10) * SCALE;
}
}
} else if (lbl == 's') { // sqr
calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true;
} else if (lbl == 'r') { // rt
long long s = isqrt(calc_curr);
if (s == -1) calc_error = true;
else calc_curr = s * 1000;
calc_new_entry = true;
} else if (lbl == '=') {
do_op();
calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false;
} else { } else {
if (!calc_new_entry) { if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE;
if (calc_op) do_op(); else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE;
else calc_acc = calc_curr;
}
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;
} }
calc_error = false;
} else if (lbl == '.') {
if (calc_new_entry) {
calc_curr = 0;
calc_new_entry = false;
}
if (!calc_decimal_mode) {
calc_decimal_mode = true;
calc_decimal_divisor = 10;
}
} else if (lbl == 'C') {
calc_curr = 0; calc_acc = 0; calc_op = 0;
calc_new_entry = true; calc_error = false; calc_decimal_mode = false;
} else if (lbl == 'B') {
if (!calc_new_entry && !calc_error) {
if (calc_decimal_mode) {
if (calc_decimal_divisor > 10) {
calc_decimal_divisor /= 10;
long long unit = SCALE / calc_decimal_divisor;
calc_curr = (calc_curr / (unit * 10)) * (unit * 10);
} else {
calc_decimal_mode = false;
calc_decimal_divisor = 10;
calc_curr = (calc_curr / SCALE) * SCALE;
}
} else {
calc_curr = (calc_curr / SCALE / 10) * SCALE;
}
}
} else if (lbl == 's') { // sqr
calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true;
} else if (lbl == 'r') { // rt
long long s = isqrt(calc_curr);
if (s == -1) calc_error = true;
else calc_curr = s * 1000;
calc_new_entry = true;
} else if (lbl == '=') {
do_op();
calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false;
} else {
if (!calc_new_entry) {
if (calc_op) do_op();
else calc_acc = calc_curr;
}
calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false;
} }
} }
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;
calc_op = 0; calc_op = 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);
} }

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

File diff suppressed because it is too large Load Diff

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
@@ -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]);
@@ -202,7 +202,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, "/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++; append_num((int)dt[3], 2); // Hour
if (digits == 4) { append_num((int)dt[4], 2); // Min
filename[len++] = '0' + (num / 1000) % 10; append_num((int)dt[5], 2); // Sec
filename[len++] = '0' + (num / 100) % 10; strcat(g_filename, ".png");
// 6. Generate PNG in memory
png_output_idx = 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) {
sys_write_fs(fd, png_output_buf, png_output_idx);
sys_close(fd);
// Show notification
char notif[256] = "Saved ";
strcat(notif, g_filename + 9); // Skip "/Desktop/"
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)notif);
} else {
res = 0;
} }
filename[len++] = '0' + (num / 10) % 10;
filename[len++] = '0' + (num % 10);
filename[len] = '\0';
} }
append_num((int)dt[0], 4); if (!res) {
append_num((int)dt[1], 2); syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)"Failed to save screenshot");
append_num((int)dt[2], 2);
int len = 0; while (filename[len]) len++;
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
int fd = sys_open(filename, "w"); // Open file
int res = 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_close(fd); // Close file
} }
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

@@ -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,46 +98,42 @@ 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) { // Fixed-point 16.16
// Fast path: 1:1 uint32_t step_x = (viewer_img_w << 16) / disp_w;
for (int i = 0; i < disp_w * disp_h; i++) temp_buf[i] = pixels[i]; uint32_t step_y = (viewer_img_h << 16) / disp_h;
} else { uint32_t curr_y = 0;
// Fixed-point 16.16
uint32_t step_x = (viewer_img_w << 16) / disp_w;
uint32_t step_y = (viewer_img_h << 16) / disp_h;
uint32_t curr_y = 0;
for (int y = 0; y < disp_h; y++) { for (int y = 0; y < disp_h; y++) {
uint32_t src_y = curr_y >> 16; uint32_t src_y = curr_y >> 16;
if (src_y >= (uint32_t)viewer_img_h) src_y = viewer_img_h - 1; if (src_y >= (uint32_t)viewer_img_h) src_y = viewer_img_h - 1;
uint32_t curr_x = 0; uint32_t curr_x = 0;
uint32_t src_row_off = src_y * viewer_img_w; uint32_t src_row_off = src_y * viewer_img_w;
uint32_t dst_row_off = y * disp_w; uint32_t dst_row_off = y * disp_w;
for (int x = 0; x < disp_w; x++) { for (int x = 0; x < disp_w; x++) {
uint32_t src_x = curr_x >> 16; uint32_t src_x = curr_x >> 16;
if (src_x >= (uint32_t)viewer_img_w) src_x = viewer_img_w - 1; if (src_x >= (uint32_t)viewer_img_w) src_x = viewer_img_w - 1;
temp_buf[dst_row_off + x] = pixels[src_row_off + src_x]; temp_buf[dst_row_off + x] = pixels[src_row_off + src_x];
curr_x += step_x; curr_x += step_x;
}
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) {

View File

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

View File

@@ -76,6 +76,7 @@
#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_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);

View File

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

View File

@@ -42,6 +42,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
@@ -386,7 +408,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];
} }
@@ -715,7 +737,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++;
} }
@@ -788,15 +810,15 @@ static void explorer_open_target(const char *path) {
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);
} }
} }
} }
@@ -898,7 +920,7 @@ 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);
@@ -950,20 +972,19 @@ static void explorer_paint(Window *win) {
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,11 +1010,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) { if (state->drive_menu_visible) {
int menu_x = win->x + 4; int menu_x = win->x + 4;
@@ -1043,25 +1061,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 +1078,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 +1105,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 +1124,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 +1154,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 +1167,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 +1215,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,106 +1226,90 @@ 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, "/");
@@ -1338,16 +1320,17 @@ static void explorer_handle_click(Window *win, int x, int y) {
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;
@@ -1420,27 +1403,27 @@ static void explorer_handle_click(Window *win, int x, int y) {
return; return;
} }
if (x >= win->w - 90 && x < win->w - 55 && if (WIDGET_CLICKED(&state->btn_dropdown, win->x + x, win->y + y + 20)) {
y >= button_y && y < button_y + 22) { state->btn_dropdown.pressed = false;
dropdown_menu_toggle(win); dropdown_menu_toggle(win);
state->drive_menu_visible = false; 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++;
@@ -1716,7 +1699,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) {
@@ -1900,12 +1883,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 +1920,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,19 +201,33 @@ 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;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); if (entry->bitmap && entry->codepoint == codepoint && entry->scale == scale && entry->font == font) {
bitmap = entry->bitmap;
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) { w = entry->w; h = entry->h; xoff = entry->xoff; yoff = entry->yoff;
info = (stbtt_fontinfo *)fallback_font->info; } else {
real_scale = stbtt_ScaleForPixelHeight(info, scale); stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
} float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
info = (stbtt_fontinfo *)fallback_font->info;
real_scale = stbtt_ScaleForPixelHeight(info, scale);
}
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++) {
@@ -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,19 +248,33 @@ 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;
float real_scale = stbtt_ScaleForPixelHeight(info, scale); if (entry->bitmap && entry->codepoint == codepoint && entry->scale == scale && entry->font == font) {
bitmap = entry->bitmap;
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) { w = entry->w; h = entry->h; xoff = entry->xoff; yoff = entry->yoff;
info = (stbtt_fontinfo *)fallback_font->info; } else {
real_scale = stbtt_ScaleForPixelHeight(info, scale); stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
} float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
info = (stbtt_fontinfo *)fallback_font->info;
real_scale = stbtt_ScaleForPixelHeight(info, scale);
}
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++) {
@@ -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);
} }
} }

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;
@@ -176,15 +183,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 +203,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 +216,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 +233,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 +253,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 +294,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 +334,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,18 +416,25 @@ 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; if (g_y < 0 || g_y >= sh) continue;
int g_x = x + c;
uint32_t cpu = smp_this_cpu_id();
if (g_clip_enabled) { if (g_clip_enabled[cpu]) {
if (g_x < g_clip_x || g_x >= g_clip_x + g_clip_w || int ptr = g_clip_stack_ptr[cpu];
g_y < g_clip_y || g_y >= g_clip_y + g_clip_h) { if (g_y < g_clip_stack_y[cpu][ptr] || g_y >= g_clip_stack_y[cpu][ptr] + g_clip_stack_h[cpu][ptr]) continue;
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;
int dx = 0, dy = 0; int dx = 0, dy = 0;
if (c < radius && r < radius) { if (c < radius && r < radius) {
@@ -461,9 +490,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 +511,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;
} }
} }
@@ -557,7 +591,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);
@@ -623,9 +656,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 +675,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 +795,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 +830,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 +854,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;
int sw = (int)g_fb->width;
int sh = (int)g_fb->height;
// Copy the internal back object to the dest directly // 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 +909,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];
for (int x = 0; x < w; x++) { cx1 = g_clip_stack_x[cpu][ptr];
int vx = dst_x + x; cy1 = g_clip_stack_y[cpu][ptr];
if (vx < 0 || vx >= sw) continue; cx2 = cx1 + g_clip_stack_w[cpu][ptr];
cy2 = cy1 + g_clip_stack_h[cpu][ptr];
uint32_t pcol = src[y * w + x]; }
int x1 = dst_x, y1 = dst_y, x2 = dst_x + w, y2 = dst_y + h;
if (x1 < cx1) x1 = cx1;
if (y1 < cy1) y1 = cy1;
if (x2 > cx2) x2 = cx2;
if (y2 > cy2) y2 = cy2;
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

@@ -57,6 +57,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

@@ -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;
@@ -1110,27 +1118,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 +1227,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,275 +1262,263 @@ 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();
int cx = 0, cy = y_start, cw = sw, ch = y_end - y_start;
if (dirty.active) {
if (cx < dirty.x) { cw -= (dirty.x - cx); cx = dirty.x; }
if (cy < dirty.y) { ch -= (dirty.y - cy); cy = dirty.y; }
if (cx + cw > dirty.x + dirty.w) cw = dirty.x + dirty.w - cx;
if (cy + ch > dirty.y + dirty.h) ch = dirty.y + dirty.h - cy;
}
if (cw <= 0 || ch <= 0) return;
graphics_set_clipping(cx, cy, cw, ch);
if (pass == 1) {
draw_desktop_background();
for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i];
if (icon->y + 85 <= cy || icon->y >= cy + ch) continue;
if (dirty.active && (icon->x + 85 <= dirty.x || icon->x >= dirty.x + dirty.w)) continue;
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) {
char label[64]; int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) label[len-9] = 0;
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, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Minesweeper")) draw_minesweeper_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Settings")) draw_control_panel_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Clock")) draw_clock_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "About")) draw_about_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, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label);
} else {
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 (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/"; 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_icon_label(icon->x, icon->y, icon->name);
}
else if (str_ends_with(icon->name, ".pdf")) draw_pdf_icon(icon->x, icon->y, icon->name);
else draw_document_icon(icon->x, icon->y, icon->name);
}
}
for (int i = 0; i < sorted_window_count_cache; i++) {
Window *win = sorted_windows_cache[i];
if (!win || !win->visible) continue;
if (win->y + win->h <= cy || win->y >= cy + ch) continue;
if (dirty.active && !win->focused && (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w)) continue;
draw_window(win);
}
} else if (pass == 2) {
if (0 < cy + ch && 30 > cy) {
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG);
draw_boredos_logo(8, 8, 1);
draw_clock(sw - 80, 12);
}
if (start_menu_open && 40 < cy + ch && 125 > cy) {
draw_rounded_rect_filled(8, 40, 160, 85, 8, COLOR_DARK_PANEL);
draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT);
draw_string(20, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
}
int dock_h = 60, dock_y = sh - dock_h - 6;
if (dock_y < cy + ch && dock_y + dock_h > cy) {
int d_item_sz = 48, d_space = 10, d_total_w = 11 * (d_item_sz + d_space);
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
draw_dock_files(dx, dy); dx += d_item_sz+d_space;
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
draw_dock_notepad(dx, dy); dx += d_item_sz+d_space;
draw_dock_calculator(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);
}
if (desktop_menu_visible) {
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;
if (desktop_menu_target_icon != -1) {
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 + item_h, "Copy", COLOR_WHITE);
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 * 4, "Rename", COLOR_WHITE);
} else {
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 + item_h, "New Folder", COLOR_WHITE);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", cp ? COLOR_WHITE : COLOR_DKGRAY);
}
}
}
if (desktop_dialog_state != 0) {
int dlg_w = 300, dlg_h = 110, dg_x = (sw - dlg_w)/2, dg_y = (sh - dlg_h)/2;
if (dg_y < cy + ch && dg_y + dlg_h > cy) {
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");
const char *btn = (desktop_dialog_state == 0) ? "Rename" : "Create";
draw_string(dg_x + 10, dg_y + 10, title, COLOR_WHITE);
draw_rounded_rect_filled(dg_x + 10, dg_y + 35, 280, 20, 4, COLOR_DARK_BG);
draw_string(dg_x + 15, dg_y + 40, desktop_dialog_input, COLOR_WHITE);
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;
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_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);
}
}
if (msg_box_visible) {
int mw = 320, mh = 100, m_x = (sw - mw)/2, m_y = (sh - mh)/2;
if (m_y < cy + ch && m_y + mh > cy) {
draw_rounded_rect_filled(m_x, m_y, mw, mh, 8, COLOR_DARK_PANEL);
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(m_x + mw/2 - 30, m_y + 70, 60, 20, 4, COLOR_DARK_BORDER);
draw_string(m_x + mw/2 - 10, m_y + 75, "OK", COLOR_WHITE);
}
}
if (notif_active) {
int nx = sw - 400 + notif_x_offset, ny = 40, nw = 380, nh = 50;
if (ny < cy + ch && ny + nh > cy) {
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 + 30, notif_text, COLOR_DKGRAY);
}
}
if (wm_custom_paint_hook) wm_custom_paint_hook();
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...");
else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving...");
else draw_document_icon(mx - 20, my - 20, "Moving...");
}
}
}
}
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; uint64_t rflags;
rflags = wm_lock_acquire(); rflags = wm_lock_acquire();
wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12); wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12);
wm_mark_dirty(mx, my, 12, 12); wm_mark_dirty(mx, my, 12, 12);
DirtyRect dirty = graphics_get_dirty_rect(); DirtyRect dirty = graphics_get_dirty_rect();
if (dirty.active) { if (dirty.active) {
int d_h = 60; int d_h = 60, d_y = sh - d_h - 6, d_total_w = 11 * (48 + 10);
int d_y = sh - d_h - 6; int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
int d_item_sz = 48;
int d_space = 10;
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 || 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)) { 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); graphics_mark_dirty(d_bg_x - 10, d_y - 10, d_bg_w + 20, d_h + 20);
dirty = graphics_get_dirty_rect(); 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) sorted_window_count_cache = window_count;
draw_desktop_background(); 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];
// Draw Desktop Icons for (int i = 0; i < sorted_window_count_cache - 1; i++) {
for (int i = 0; i < desktop_icon_count; i++) { for (int j = 0; j < sorted_window_count_cache - i - 1; j++) {
DesktopIcon *icon = &desktop_icons[i]; if (sorted_windows_cache[j] && sorted_windows_cache[j+1] &&
if (dirty.active) { sorted_windows_cache[j]->z_index > sorted_windows_cache[j+1]->z_index) {
if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w || Window *tmp = sorted_windows_cache[j];
icon->y + 80 <= dirty.y || icon->y >= dirty.y + dirty.h) { sorted_windows_cache[j] = sorted_windows_cache[j+1];
continue; sorted_windows_cache[j+1] = tmp;
}
}
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) {
// App icon - strip .shortcut for display
char label[64];
int len = 0;
while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; }
label[len] = 0;
if (len > 9 && str_ends_with(label, ".shortcut")) {
label[len-9] = 0;
}
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, "Terminal")) draw_terminal_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Minesweeper")) draw_minesweeper_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Settings")) draw_control_panel_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "Clock")) draw_clock_icon(icon->x, icon->y, label);
else if (str_starts_with(icon->name, "About")) draw_about_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, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label);
} else {
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 (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/";
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_icon_label(icon->x, icon->y, icon->name);
}
else if (str_ends_with(icon->name, ".pdf")) draw_pdf_icon(icon->x, icon->y, icon->name);
else draw_document_icon(icon->x, icon->y, icon->name);
}
}
// 3. Windows - sort by z-index and draw
int local_window_count = window_count;
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++) { // Memory barrier to ensure APs see the sorted window list correctly
Window *win = sorted_windows[i]; asm volatile("" ::: "memory");
if (!win || !win->visible) continue;
if (dirty.active && !win->focused) { uint32_t cpu_count = smp_cpu_count();
if (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w || if (cpu_count > 32) cpu_count = 32;
win->y + win->h <= dirty.y || win->y >= dirty.y + dirty.h) { if (cpu_count < 1) cpu_count = 1;
continue;
} volatile int completion_counter = (int)cpu_count;
} wm_strip_job_t jobs[32];
draw_window(win); 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);
draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG); __atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
draw_boredos_logo(8, 8, 1); while (completion_counter > 0) {
draw_clock(sw - 80, 12); if (!work_queue_drain_one()) asm volatile("pause");
if (start_menu_open) {
int menu_h = 85;
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, 68, "Settings", COLOR_DARK_TEXT);
draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT);
draw_string(20, 108, "Restart", COLOR_DARK_TEXT);
}
int dock_h = 60;
int dock_y = sh - dock_h - 6;
int dock_item_size = 48;
int dock_spacing = 10;
int total_dock_width = 11 * (dock_item_size + dock_spacing);
int dock_bg_x = (sw - total_dock_width) / 2 - 12;
int dock_bg_w = total_dock_width + 24;
// 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) {
int menu_w = 140;
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) {
bool can_paste = 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 + 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 * 3, "Delete", COLOR_TRAFFIC_RED);
draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE);
} else {
bool can_paste = 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 + 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);
}
} }
// Desktop Dialogs (dark mode) // PASS 2: UI OVERLAY (Dock, start menu, menus etc)
if (desktop_dialog_state != 0) { completion_counter = (int)cpu_count;
int dlg_w = 300; int dlg_h = 110; for (uint32_t i = 0; i < cpu_count; i++) {
int dlg_x = (sw - dlg_w) / 2; jobs[i].pass = 2;
int dlg_y = (sh - dlg_h) / 2; if (i < cpu_count - 1) work_queue_submit(wm_strip_worker_job, &jobs[i]);
draw_rounded_rect_filled(dlg_x, dlg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL);
const char *title = "Rename";
const char *btn_text = "Rename";
if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; }
else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; }
draw_string(dlg_x + 10, dlg_y + 10, title, 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);
} }
wm_paint_region(jobs[cpu_count-1].y_start, jobs[cpu_count-1].y_end, dirty, 2);
// Message Box (dark mode) __atomic_sub_fetch(&completion_counter, 1, __ATOMIC_SEQ_CST);
if (msg_box_visible) { while (completion_counter > 0) {
int mw = 320; if (!work_queue_drain_one()) asm volatile("pause");
int mh = 100;
int mx = (sw - mw) / 2;
int my = (sh - mh) / 2;
draw_rounded_rect_filled(mx, my, mw, mh, 8, COLOR_DARK_PANEL);
draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT);
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) graphics_clear_clipping();
if (notif_active) {
int nx = sw - 280 + notif_x_offset;
int ny = 40;
int nw = 260;
int nh = 50;
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 + 30, notif_text, COLOR_DKGRAY);
}
// Custom Overlay (VM Graphics)
if (wm_custom_paint_hook) {
wm_custom_paint_hook();
}
// Draw Dragged Icon
if (is_dragging_file) {
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 draw_document_icon(mx - 20, my - 20, "Moving...");
}
// 7. Mouse cursor (draw last so it's on top)
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 +1542,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);
return all_windows[i];
}
}
wm_lock_release(rflags); wm_lock_release(rflags);
return NULL; return win;
} }
void wm_remove_window(Window *win) { void wm_remove_window(Window *win) {
@@ -1717,7 +1701,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;
} }
@@ -1811,7 +1796,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 +1931,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();
@@ -2153,53 +2140,53 @@ void wm_handle_right_click(int x, int y) {
if (str_starts_with(start_menu_pending_app, "Files")) { if (str_starts_with(start_menu_pending_app, "Files")) {
explorer_open_directory("/"); explorer_open_directory("/");
} 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, "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")) {
@@ -2404,7 +2391,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;
} }
@@ -2575,6 +2563,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 {
@@ -2663,7 +2657,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 +2789,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_BLUE 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);