Compare commits

..

29 commits

Author SHA1 Message Date
Lluciocc
29e1b362ff replacing with strcmp && Adding comments && adding helper when the command don't exist 2026-05-13 09:17:08 +02:00
Lluciocc
69d5f8feff Update header 2026-05-13 08:11:00 +02:00
Lluciocc
9574b99f40
Merge branch 'BoredDevNL:main' into cmds 2026-05-13 08:06:03 +02:00
Lluciocc
c66bfa62cf
Merge pull request #36 from zeyadhost/main
Some checks are pending
Nightly Build / build-and-release (push) Waiting to run
Add du command for disk usage reporting
2026-05-12 23:07:45 +02:00
Lluciocc
4b169b92de Update headers 2026-05-12 23:01:43 +02:00
zeyad
e75952e510 fix(cli): correct du usage format and replace -h with -H 2026-05-12 20:58:53 +00:00
boreddevnl
f6141dfcaf Merge branch 'main' of https://github.com/BoredDevNL/BoredOS 2026-05-12 22:48:59 +02:00
boreddevnl
e813a6cdfd docs: add section for issue tags in bug report and feature request templates 2026-05-12 22:48:49 +02:00
zeyad
a6118e8d21 feat(cli): add du command for disk usage reporting 2026-05-12 20:33:09 +00:00
Chris
1ccc86ea41
doc: Add requirement for comments in PR template 2026-05-12 20:06:50 +02:00
Lluciocc
642dc7f8c9
Update toolchain.md with qemu-img PATH instruction
Some checks are pending
Nightly Build / build-and-release (push) Waiting to run
2026-05-12 19:28:28 +02:00
boreddevnl
43a62b025d Merge branch 'main' of https://github.com/BoredDevNL/BoredOS 2026-05-12 19:18:06 +02:00
boreddevnl
52b6532700 brand: add orbital.png
Co-Authored-By: Target <71287126+toiletalphamale@users.noreply.github.com>
2026-05-12 19:18:04 +02:00
Lluciocc
1655f1cf22
Add 'git' to required development tools installation 2026-05-12 19:17:00 +02:00
Lluciocc
5e3ba70730
Revise Windows toolchain installation instructions 2026-05-12 19:13:29 +02:00
boreddevnl
8d4ffd8a09 kernel: prevent infinite loop in process termination 2026-05-12 19:11:17 +02:00
Lluciocc
d007600e30
PR #34 from BoredDevNL: Implement df command and statfs support
Implement df command and statfs support
2026-05-12 18:08:54 +02:00
boreddevnl
a7cfb5d22d fix: stop format real human 2026-05-12 18:06:35 +02:00
boreddevnl
93811816fd Implement df command and statfs support 2026-05-12 17:06:47 +02:00
boreddevnl
13eaa7589d brand: remove 2 old wallpapers 2026-05-12 15:47:02 +02:00
boreddevnl
24b2754acb brand: Add The-Cat-Of-Destiny.png
Co-Authored-By: artemix1508 <273844106+artemix1508@users.noreply.github.com>
2026-05-12 15:21:08 +02:00
boreddevnl
a0e8521cf0 brand: add adrian.jpg
Co-Authored-By: QWR <196812618+qwroffc@users.noreply.github.com>
2026-05-12 15:19:28 +02:00
boreddevnl
16eedb752f doc: update contributors list 2026-05-12 14:30:28 +02:00
boreddevnl
6cf10fdbd3 brand: added squiggly.png
Co-Authored-By: pixelyblah <221841823+pixelyblah@users.noreply.github.com>
2026-05-12 14:26:43 +02:00
boreddevnl
7eaa9d278a brand: added Web-Of-Connectivity.png
Co-Authored-By: artemix1508 <273844106+artemix1508@users.noreply.github.com>
2026-05-12 14:26:08 +02:00
boreddevnl
45f26db141 brand: aded flowerdark/light.jpg
Co-Authored-By: QWR <196812618+qwroffc@users.noreply.github.com>
2026-05-12 14:25:07 +02:00
boreddevnl
801278cf73 feat: scrollable wallpaper page 2026-05-12 14:24:09 +02:00
boreddevnl
329fcf3bc0 doc: fix broken contributor list README.md 2026-05-12 11:37:52 +02:00
Lluciocc
04794bb986
doc: Update toochain steps on windows (#33)
Some checks are pending
Nightly Build / build-and-release (push) Waiting to run
* Create pull_request_template.md

Add a pull request template for better PR submissions.

* Document toolchain installation steps for Windows

* Add Table of Contents to toolchain.md
2026-05-12 10:01:47 +02:00
43 changed files with 1561 additions and 168 deletions

View file

@ -27,3 +27,5 @@ A clear and concise description of what the bug is. What did you expect to happe
**5. Additional Context:** **5. Additional Context:**
Add any other context about the problem here (e.g., "This only happens when my mouse is moving"). Add any other context about the problem here (e.g., "This only happens when my mouse is moving").
**6. Please add tags to your issue to help with organization.**

View file

@ -21,3 +21,5 @@ Let us know if this is just an idea you'd like to see, or if you plan on submitt
**5. Additional Context:** **5. Additional Context:**
Add any other context, mockup screenshots, or links to technical documentation (e.g., OSDev Wiki links) here. Add any other context, mockup screenshots, or links to technical documentation (e.g., OSDev Wiki links) here.
**6. Please add tags to your issue to help with organization.**

View file

@ -15,6 +15,7 @@ Notes:
--- ---
## Documentation ## Documentation
- [ ] Code contains appropriate comments (REQUIRED for medium to large PR's.)
- [ ] Documentation updated if needed - [ ] Documentation updated if needed

123
README.md
View file

@ -56,6 +56,77 @@
## 📚 Documentation
| Guide | Description |
|-------|-------------|
| [Documentation Index](docs/README.md) | Start here! |
| [Architecture Overview](docs/architecture/README.md) | Deep dive into the kernel |
| [Building and Running](docs/build/usage.md) | Set up your build environment |
| [AppDev SDK](docs/appdev/custom_apps.md) | Build your own apps for BoredOS |
---
## Contributors
<div align="center">
<img src="branding/bOS_full_gradient_cropped.png" alt="BoredOS Logo" width="450" />
<h3>A modern x86_64 hobbyist operating system built from the ground up.</h3>
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
![Platform: x86_64](https://img.shields.io/badge/Platform-x86_64-lightgrey)
![Status: Active](https://img.shields.io/badge/Status-Active-brightgreen)
![GitHub all releases](https://img.shields.io/github/downloads/boreddevnl/BoredOS/total?color=brightgreen)
<br />
[Docs](docs/README.md) · [Build & Run](docs/build/usage.md) · [AppDev SDK](docs/appdev/sdk_reference.md) · [Discord](https://discord.gg/J2BxWaFAgY) · [Support](https://buymeacoffee.com/boreddevhq)
</div>
---
![Screenshot](branding/screenshot.jpg)
> [!NOTE]
> The screenshot above may represent a previous build and is subject to change as the UI evolves.
---
## Features
### Kernel and Architecture
- **Long Mode Architecture** — Native x86_64 implementation utilizing 64-bit address space and registers
- **Symmetric Multi-Processing** — Scalable multi-core support with IPI-based scheduling and synchronization
- **Advanced Memory Management** — Custom slab allocator with object pooling and efficient physical/virtual page mapping
- **Hybrid VFS Layer** — Unified filesystem interface supporting FAT32, TAR, ProcFS, and SysFS
- **Preemptive Multitasking** — Prioritized process scheduling with full context isolation
- **Hardware Abstraction** — Comprehensive driver support for PCI, AHCI, PS/2, and ACPI
### Graphical Desktop Environment
- **BoredWM** — High-performance window manager featuring window stacking, focus management, and drag-and-drop interactions
- **Typography Engine** — Integrated font manager with TrueType (TTF) support and efficient glyph caching
- **Rich Media Subsystem** — Native hardware-independent decoding for PNG, JPEG, GIF, BMP, and TGA formats
- **LibWidget Toolkit** — Native UI component library for rapid application development
### Networking Stack
- **TCP/IP Integration** — Full lwIP-based network stack featuring DHCP, DNS, and Berkeley-style sockets
- **Network Services** — Integrated support for basic web browsing and real-time network telemetry
### Application Ecosystem
| Category | Applications |
|----------|--------------|
| Productivity | Text Editor, Markdown Viewer, BoredWord Processor, Web Browser, Calculator |
| Development | TCC (Tiny C Compiler), Lua|
| System | Explorer (File Manager), Task Manager, System Monitor, Graphing Utility |
| Games | doomgeneric, Minesweeper, 2048, Snake |
---
## 📚 Documentation ## 📚 Documentation
| Guide | Description | | Guide | Description |
@ -99,6 +170,8 @@
</a><br /> </a><br />
Artwork Artwork
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/zeyadhost"> <a href="https://github.com/zeyadhost">
<img src="https://github.com/zeyadhost.png?size=80" width="60" /><br /> <img src="https://github.com/zeyadhost.png?size=80" width="60" /><br />
@ -109,9 +182,23 @@
<td align="center"> <td align="center">
<a href="https://github.com/naplon74"> <a href="https://github.com/naplon74">
<img src="https://github.com/naplon74.png?size=80" width="60" /><br /> <img src="https://github.com/naplon74.png?size=80" width="60" /><br />
<sub><b>Artwork</b></sub> <sub><b>Naplon74</b></sub>
</a><br /> </a><br />
Contributor Artwork
</td>
<td align="center">
<a href="https://github.com/pixelyblah">
<img src="https://github.com/pixelyblah.png?size=80" width="60" /><br />
<sub><b>pixelyblah</b></sub>
</a><br />
Artwork
</td>
<td align="center">
<a href="https://github.com/qwroffc">
<img src="https://github.com/qwroffc.png?size=80" width="60" /><br />
<sub><b>qwroffc</b></sub>
</a><br />
Artwork
</td> </td>
</tr> </tr>
</table> </table>
@ -147,3 +234,35 @@ Distributed under the **GNU General Public License v3**. See [`LICENSE`](LICENSE
> [!IMPORTANT] > [!IMPORTANT]
> You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the [`NOTICE`](NOTICE) file for more details. > You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the [`NOTICE`](NOTICE) file for more details.
---
## ☕ Support the Journey
If you find BoredOS interesting or useful, consider fueling development with a coffee!
<a href="https://buymeacoffee.com/boreddevhq" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
</a>
---
## History
**BoredOS** is the successor to **[BrewKernel](https://github.com/boreddevnl/brewkernel)**, a project started in 2023. BrewKernel served as the foundational learning ground but has since been officially deprecated and archived — it no longer receives updates, bug fixes, or pull request reviews.
BoredOS is a complete architectural reboot, applying years of lessons learned to build a cleaner, more modular, and more capable system.
> [!IMPORTANT]
> Please direct all issues, discussions, and contributions to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
---
## License
**Copyright (C) 20232026 boreddevnl**
Distributed under the **GNU General Public License v3**. See [`LICENSE`](LICENSE) for details.
> [!IMPORTANT]
> You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the [`NOTICE`](NOTICE) file for more details.

View file

@ -2,6 +2,13 @@
BoredOS is built cross-compiled from a host system (such as macOS or Linux) to target the generic `x86_64-elf` platform. BoredOS is built cross-compiled from a host system (such as macOS or Linux) to target the generic `x86_64-elf` platform.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Building the Cross-Compiler on Linux](#building-the-cross-compiler-on-linux)
- [Installing the Toolchain on Windows](#installing-the-toolchain-on-windows)
## Prerequisites ## Prerequisites
To build BoredOS, you need the following tools: To build BoredOS, you need the following tools:
@ -65,3 +72,123 @@ x86_64-elf-gcc --version
``` ```
> **Note**: Building the cross-compiler can take 20-30 minutes depending on system performance. This is a one-time setup cost. > **Note**: Building the cross-compiler can take 20-30 minutes depending on system performance. This is a one-time setup cost.
## Installing the Toolchain on Windows
### Recommended Environment: MSYS2
On Windows, the recommended way to build BoredOS is using **MSYS2**.
MSYS2 provides a Unix-like environment with the `pacman` package manager, making it easy to install the required development tools.
---
## 1. Install MSYS2
Download and install MSYS2 from the official website:
- https://www.msys2.org/
After installation, launch the **MSYS2 UCRT64** terminal.
---
## 2. Update MSYS2
Before installing packages, fully update the environment:
```bash
pacman -Syu
```
You may be asked to close the terminal after the first update.
If so:
1. Close the MSYS2 window
2. Reopen **MSYS2 UCRT64**
3. Run the update command again:
```bash
pacman -Syu
```
Repeat until no further updates are available.
---
## 3. Install Required Packages
Install the required development tools:
```bash
pacman -S make nasm xorriso git
```
---
## 4. Install QEMU for Windows
Download the Windows version of QEMU from:
- https://qemu.weilnetz.de/w64/
Install QEMU normally and make sure the installation directory is added to your Windows `PATH`.
Note that if it breaks when building, you need too add `qemu-img` to your `PATH`:
`export PATH="/c/Program Files/qemu:$PATH"`
You can verify the installation with:
```bash
qemu-system-x86_64 --version
```
---
## 5. Install the x86_64 ELF Cross Toolchain
Download the prebuilt `x86_64-elf` toolchain for Windows:
- https://github.com/lordmilko/i686-elf-tools/releases/download/15.2.0/x86_64-elf-tools-windows.zip
Extract the archive somewhere convenient.
---
## 6. Add the Toolchain to PATH
Inside the **MSYS2 UCRT64** terminal, add the toolchain binaries to your `PATH`:
```bash
export PATH="/c/Users/your/path/to/the/binaries/x86_64-elf-tools-windows/bin:$PATH"
```
To make this permanent, add the line to your `~/.bashrc` file:
```bash
echo 'export PATH="/c/Users/your/path/to/the/binaries/x86_64-elf-tools-windows/bin:$PATH"' >> ~/.bashrc
```
Then reload the shell:
```bash
source ~/.bashrc
```
---
## 7. Verify the Installation
Verify that the cross compiler is available:
```bash
x86_64-elf-gcc --version
```
You should also verify NASM and QEMU:
```bash
nasm -v
qemu-system-x86_64 --version
```
If all commands work, the development environment is correctly configured.

90
docs/usage/commands/du.md Normal file
View file

@ -0,0 +1,90 @@
# du
`du` (disk usage) reports the disk space used by files and directories.
## Usage
```sh
du [OPTIONS]... [FILE]...
```
## Description
By default, `du` prints human-readable sizes for each file and directory it encounters, starting from the current directory (`.`) if no path is given.
## Options
| Option | Description |
| :--- | :--- |
| `-s, --summarize` | Show only a total for each argument, suppressing per-entry output. |
| `-a, --all` | Write counts for all files, not just directories. |
| `-d, --max-depth=N` | Stop at depth N; show only entries at or above depth N. |
| `-c, --total` | Print a grand total after all arguments have been processed. |
| `-b, --bytes` | Print sizes in exact bytes instead of human-readable units. |
| `-H, --human-readable` | Accepted for compatibility; human-readable is the default. |
| `--help` | Display usage information and exit. |
## Output Format
Each line shows a size followed by the path:
```
SIZE PATH
```
Sizes are formatted as `B`, `KB`, `MB`, or `GB` by default, with one decimal place when appropriate (e.g., `1.5 GB`). The `-b` option overrides this to show exact byte counts.
## Examples
Show disk usage for the current directory:
```sh
du
```
Show disk usage for a specific path:
```sh
du /bin
```
Show only totals per argument (`-s`):
```sh
du -s /bin /home
```
Show all files and directories recursively (`-a`):
```sh
du -a /bin
```
Limit output to depth 1 (`-d`):
```sh
du -d 1 /
```
Print a grand total after processing (`-c`):
```sh
du -c /bin /home
```
Show exact byte counts (`-b`):
```sh
du -b /bin
```
## How It Works
`du` uses `sys_get_file_info()` to read file sizes and `sys_list()` to enumerate directory contents recursively. The command skips the synthetic `.` and `..` entries and continues processing remaining paths if one path is inaccessible, printing an error for the failed path.
The size reported is the **apparent file size** (the logical size stored in the directory entry), not the allocated disk blocks. This is consistent with how BoredOS reports file sizes through the filesystem API.
## Exit Status
- `0`: Success
- `1`: One or more paths could not be accessed or listed

View file

@ -54,6 +54,7 @@ Below are some of the most used commands available in `/bin`:
| `mkdir` | Create a new directory. | | `mkdir` | Create a new directory. |
| `man` | View the manual for a specific command (e.g., `man ls`). | | `man` | View the manual for a specific command (e.g., `man ls`). |
| `lsblk` | List block devices and partitions with size, type, filesystem, label, and flags. | | `lsblk` | List block devices and partitions with size, type, filesystem, label, and flags. |
| `du` | Report disk usage for files and directories, recursively. |
| `sysfetch` | Display system and hardware information. | | `sysfetch` | Display system and hardware information. |

View file

@ -1661,6 +1661,22 @@ static uint32_t vfs_fat_get_size(void *file_handle) {
return ((FAT32_FileHandle*)file_handle)->size; return ((FAT32_FileHandle*)file_handle)->size;
} }
static int vfs_ramfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock);
stat->total_blocks = MAX_CLUSTERS;
uint64_t free_count = 0;
for (int i = 0; i < MAX_CLUSTERS; i++) {
if (fat_table[i] == 0) free_count++;
}
stat->free_blocks = free_count;
stat->block_size = FAT32_CLUSTER_SIZE;
spinlock_release_irqrestore(&ramfs_lock, rflags);
return 0;
}
static struct vfs_fs_ops ramfs_ops = { static struct vfs_fs_ops ramfs_ops = {
.open = vfs_ramfs_open, .open = vfs_ramfs_open,
.close = vfs_ramfs_close, .close = vfs_ramfs_close,
@ -1676,7 +1692,8 @@ static struct vfs_fs_ops ramfs_ops = {
.is_dir = vfs_ramfs_is_dir, .is_dir = vfs_ramfs_is_dir,
.get_info = vfs_ramfs_get_info, .get_info = vfs_ramfs_get_info,
.get_position = vfs_fat_get_position, .get_position = vfs_fat_get_position,
.get_size = vfs_fat_get_size .get_size = vfs_fat_get_size,
.statfs = vfs_ramfs_statfs
}; };
struct vfs_fs_ops* fat32_get_ramfs_ops(void) { struct vfs_fs_ops* fat32_get_ramfs_ops(void) {
@ -1858,6 +1875,45 @@ static int vfs_realfs_get_info(void *fs_private, const char *rel_path, vfs_diren
return -1; return -1;
} }
static int vfs_realfs_statfs(void *fs_private, vfs_statfs_t *stat) {
FAT32_Volume *vol = (FAT32_Volume*)fs_private;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
stat->total_blocks = vol->total_sectors / vol->sectors_per_cluster;
stat->block_size = vol->sectors_per_cluster * 512;
// Instead of scanning the entire FAT which can be slow,
// we estimate or count a subset, but let's do a fast count.
uint64_t free_count = 0;
uint32_t fat_entries = (vol->fat_size * 512) / 4;
uint32_t current = 2;
uint8_t *fat_buf = (uint8_t*)kmalloc(512);
if (fat_buf) {
uint32_t cached_sector = 0xFFFFFFFF;
while (current < fat_entries) {
uint32_t sector = vol->fat_begin_lba + (current * 4) / 512;
uint32_t offset = (current * 4) % 512;
if (sector != cached_sector) {
if (vol->disk->read_sector(vol->disk, sector, fat_buf) != 0) break;
cached_sector = sector;
}
uint32_t val = *(uint32_t*)&fat_buf[offset];
if ((val & 0x0FFFFFFF) == 0) free_count++;
current++;
}
kfree(fat_buf);
}
stat->free_blocks = free_count;
spinlock_release_irqrestore(&vol->lock, rflags);
return 0;
}
static struct vfs_fs_ops realfs_ops = { static struct vfs_fs_ops realfs_ops = {
.open = vfs_realfs_open, .open = vfs_realfs_open,
.close = vfs_realfs_close, .close = vfs_realfs_close,
@ -1873,7 +1929,8 @@ static struct vfs_fs_ops realfs_ops = {
.is_dir = vfs_realfs_is_dir, .is_dir = vfs_realfs_is_dir,
.get_info = vfs_realfs_get_info, .get_info = vfs_realfs_get_info,
.get_position = vfs_fat_get_position, .get_position = vfs_fat_get_position,
.get_size = vfs_fat_get_size .get_size = vfs_fat_get_size,
.statfs = vfs_realfs_statfs
}; };
struct vfs_fs_ops* fat32_get_realfs_ops(void) { struct vfs_fs_ops* fat32_get_realfs_ops(void) {

View file

@ -467,6 +467,14 @@ bool procfs_is_dir(void *fs_private, const char *path) {
return false; return false;
} }
static int procfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
vfs_fs_ops_t procfs_ops = { vfs_fs_ops_t procfs_ops = {
.open = procfs_open, .open = procfs_open,
.close = procfs_close, .close = procfs_close,
@ -474,7 +482,8 @@ vfs_fs_ops_t procfs_ops = {
.write = procfs_write, .write = procfs_write,
.readdir = procfs_readdir, .readdir = procfs_readdir,
.exists = procfs_exists, .exists = procfs_exists,
.is_dir = procfs_is_dir .is_dir = procfs_is_dir,
.statfs = procfs_statfs
}; };
vfs_fs_ops_t* procfs_get_ops(void) { vfs_fs_ops_t* procfs_get_ops(void) {

View file

@ -166,6 +166,14 @@ static bool sysfs_is_dir(void *fs_private, const char *path) {
return sysfs_exists(fs_private, path); return sysfs_exists(fs_private, path);
} }
static int sysfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
vfs_fs_ops_t sysfs_ops = { vfs_fs_ops_t sysfs_ops = {
.open = sysfs_open, .open = sysfs_open,
.close = sysfs_close, .close = sysfs_close,
@ -173,7 +181,8 @@ vfs_fs_ops_t sysfs_ops = {
.write = sysfs_write, .write = sysfs_write,
.readdir = sysfs_readdir, .readdir = sysfs_readdir,
.exists = sysfs_exists, .exists = sysfs_exists,
.is_dir = sysfs_is_dir .is_dir = sysfs_is_dir,
.statfs = sysfs_statfs
}; };
vfs_fs_ops_t* sysfs_get_ops(void) { vfs_fs_ops_t* sysfs_get_ops(void) {

View file

@ -710,6 +710,26 @@ bool vfs_is_directory(const char *path) {
return mount->ops->is_dir(mount->fs_private, rel_path); return mount->ops->is_dir(mount->fs_private, rel_path);
} }
int vfs_statfs(const char *path, vfs_statfs_t *stat) {
if (!path || !stat) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount) return -1;
if (mount->ops->statfs) {
return mount->ops->statfs(mount->fs_private, stat);
}
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
int vfs_get_info(const char *path, vfs_dirent_t *info) { int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (!path || !info) return -1; if (!path || !info) return -1;

View file

@ -13,6 +13,13 @@
#define VFS_MAX_MOUNTS 16 #define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 64 #define VFS_MAX_OPEN_FILES 64
// statfs structure
typedef struct {
uint64_t total_blocks;
uint64_t free_blocks;
uint64_t block_size;
} vfs_statfs_t;
// Forward declarations // Forward declarations
typedef struct vfs_mount vfs_mount_t; typedef struct vfs_mount vfs_mount_t;
typedef struct vfs_file vfs_file_t; typedef struct vfs_file vfs_file_t;
@ -47,6 +54,7 @@ typedef struct vfs_fs_ops {
bool (*exists)(void *fs_private, const char *rel_path); bool (*exists)(void *fs_private, const char *rel_path);
bool (*is_dir)(void *fs_private, const char *rel_path); bool (*is_dir)(void *fs_private, const char *rel_path);
int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info); int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info);
int (*statfs)(void *fs_private, vfs_statfs_t *stat);
// Handle info (for backward compat with syscall position/size queries) // Handle info (for backward compat with syscall position/size queries)
uint32_t (*get_position)(void *file_handle); uint32_t (*get_position)(void *file_handle);
@ -99,6 +107,7 @@ bool vfs_rename(const char *old_path, const char *new_path);
bool vfs_exists(const char *path); bool vfs_exists(const char *path);
bool vfs_is_directory(const char *path); bool vfs_is_directory(const char *path);
int vfs_get_info(const char *path, vfs_dirent_t *info); int vfs_get_info(const char *path, vfs_dirent_t *info);
int vfs_statfs(const char *path, vfs_statfs_t *stat);
// Mount enumeration // Mount enumeration
int vfs_get_mount_count(void); int vfs_get_mount_count(void);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 "keyboard.h" #include "keyboard.h"
#include "keymap.h" #include "keymap.h"
#include "../core/io.h" #include "../core/io.h"

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 KEYBOARD_H #ifndef KEYBOARD_H
#define KEYBOARD_H #define KEYBOARD_H

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 KEYCODES_H #ifndef KEYCODES_H
#define KEYCODES_H #define KEYCODES_H

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 "keymap.h" #include "keymap.h"
#define DEAD_NORMAL 0x01 #define DEAD_NORMAL 0x01

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 KEYMAP_H #ifndef KEYMAP_H
#define KEYMAP_H #define KEYMAP_H

View file

@ -691,10 +691,12 @@ void process_kill_by_tty(int tty_id) {
if (tty_id < 0) return; if (tty_id < 0) return;
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) { if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) {
if (!processes[i].exited && !processes[i].kill_pending) {
process_terminate(&processes[i]); process_terminate(&processes[i]);
} }
} }
} }
}
static void process_cleanup_inner(process_t *proc) { static void process_cleanup_inner(process_t *proc) {
if (!proc || proc->pid == 0xFFFFFFFF) return; if (!proc || proc->pid == 0xFFFFFFFF) return;
@ -735,6 +737,7 @@ void process_terminate(process_t *to_delete) {
void process_terminate_with_status(process_t *to_delete, int status) { void process_terminate_with_status(process_t *to_delete, int status) {
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return; if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
if (to_delete->exited || to_delete->kill_pending) return;
uint32_t cpu_count = smp_cpu_count(); uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) { for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {

View file

@ -1416,7 +1416,39 @@ static uint64_t fs_cmd_chdir(const syscall_args_t *args) {
} }
return -1; return -1;
} }
#define FS_CMD_TABLE_SIZE 19 static uint64_t fs_cmd_statfs(const syscall_args_t *args) {
const char *path = (const char *)args->arg2;
vfs_statfs_t *stat = (vfs_statfs_t *)args->arg3;
if (!path || !stat) return -1;
return vfs_statfs(path, stat) == 0 ? 0 : -1;
}
static uint64_t fs_cmd_mount_count(const syscall_args_t *args) {
(void)args;
return (uint64_t)vfs_get_mount_count();
}
typedef struct {
char path[256];
char device[32];
char fs_type[16];
} syscall_mount_info_t;
static uint64_t fs_cmd_mount_info(const syscall_args_t *args) {
int index = (int)args->arg2;
syscall_mount_info_t *info = (syscall_mount_info_t *)args->arg3;
if (!info) return -1;
vfs_mount_t *m = vfs_get_mount(index);
if (!m) return -1;
strcpy(info->path, m->path);
strcpy(info->device, m->device);
strcpy(info->fs_type, m->fs_type);
return 0;
}
#define FS_CMD_TABLE_SIZE 22
static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = { static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = {
[FS_CMD_OPEN] = fs_cmd_open, // 1 [FS_CMD_OPEN] = fs_cmd_open, // 1
[FS_CMD_READ] = fs_cmd_read, // 2 [FS_CMD_READ] = fs_cmd_read, // 2
@ -1436,6 +1468,9 @@ static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = {
[FS_CMD_DUP2] = fs_cmd_dup2, // 16 [FS_CMD_DUP2] = fs_cmd_dup2, // 16
[FS_CMD_PIPE] = fs_cmd_pipe, // 17 [FS_CMD_PIPE] = fs_cmd_pipe, // 17
[FS_CMD_FCNTL] = fs_cmd_fcntl, // 18 [FS_CMD_FCNTL] = fs_cmd_fcntl, // 18
[FS_CMD_STATFS] = fs_cmd_statfs, // 19
[FS_CMD_MOUNT_COUNT] = fs_cmd_mount_count, // 20
[FS_CMD_MOUNT_INFO] = fs_cmd_mount_info, // 21
}; };
static uint64_t sys_cmd_set_bg_color(const syscall_args_t *args) { static uint64_t sys_cmd_set_bg_color(const syscall_args_t *args) {

View file

@ -53,6 +53,9 @@ typedef struct {
#define FS_CMD_DUP2 16 #define FS_CMD_DUP2 16
#define FS_CMD_PIPE 17 #define FS_CMD_PIPE 17
#define FS_CMD_FCNTL 18 #define FS_CMD_FCNTL 18
#define FS_CMD_STATFS 19
#define FS_CMD_MOUNT_COUNT 20
#define FS_CMD_MOUNT_INFO 21
#define SYSTEM_CMD_SET_BG_COLOR 1 #define SYSTEM_CMD_SET_BG_COLOR 1
#define SYSTEM_CMD_SET_BG_PATTERN 2 #define SYSTEM_CMD_SET_BG_PATTERN 2

317
src/userland/cli/df.c Normal file
View file

@ -0,0 +1,317 @@
// 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.
// BOREDOS_APP_DESC: Display free disk space
#include <syscall.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#define MULTIPLIER_DEFAULT 0
#define MULTIPLIER_B 1
#define MULTIPLIER_G 2
#define MULTIPLIER_H_1000 3
#define MULTIPLIER_H_1024 4
#define MULTIPLIER_K 5
#define MULTIPLIER_M 6
#define MULTIPLIER_P 7
static int multiplier_mode = MULTIPLIER_K;
static bool opt_all = false;
static bool opt_total = false;
static bool opt_inodes = false;
static bool opt_cached = false;
static bool opt_export = false;
static bool opt_local = false;
static bool opt_type = false;
static bool opt_libxo = false;
static bool opt_comma = false;
static void print_usage(void) {
printf("Usage: df [OPTIONS]\n");
printf(" -a, --all Include dummy file systems\n");
printf(" -B, --block-size=SIZE Use SIZE-byte blocks\n");
printf(" -b Use 512-byte blocks\n");
printf(" -g Use 1-Gigabyte blocks\n");
printf(" -h, --human-readable Print sizes in powers of 1024 (e.g., 1023M)\n");
printf(" -H, --si Print sizes in powers of 1000 (e.g., 1.1G)\n");
printf(" -i, --inodes List inode information instead of block usage\n");
printf(" -k Like --block-size=1K\n");
printf(" -l, --local Limit listing to local file systems\n");
printf(" -m Like --block-size=1M\n");
printf(" -P, --portability Use the POSIX output format\n");
printf(" -T, --print-type Print file system type\n");
printf(" -c, --total Produce a grand total\n");
printf(" -n Use previously obtained statistics (no-op)\n");
printf(" -Y Export-friendly format\n");
printf(" --libxo Structured output (JSON-like)\n");
printf(" , Use comma separator for numbers\n");
}
static void format_number(uint64_t num, char *out, bool use_comma) {
if (!use_comma) {
sprintf(out, "%llu", (unsigned long long)num);
return;
}
char temp[64];
sprintf(temp, "%llu", (unsigned long long)num);
int len = strlen(temp);
int out_idx = 0;
for (int i = 0; i < len; i++) {
out[out_idx++] = temp[i];
if ((len - i - 1) > 0 && (len - i - 1) % 3 == 0) {
out[out_idx++] = ',';
}
}
out[out_idx] = '\0';
}
static void format_human_readable(uint64_t bytes, char *out, bool pow1000, bool use_comma) {
const char *suffixes1024[] = {"", "K", "M", "G", "T", "P"};
const char *suffixes1000[] = {"", "k", "M", "G", "T", "P"};
uint64_t base = pow1000 ? 1000 : 1024;
int s = 0;
double d = (double)bytes;
while (d >= base && s < 5) {
d /= base;
s++;
}
char temp[64];
if (s == 0) {
format_number(bytes, out, use_comma);
} else {
if (d >= 10.0) {
sprintf(temp, "%.0f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]);
} else {
sprintf(temp, "%.1f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]);
}
strcpy(out, temp);
}
}
static void format_size(uint64_t bytes, char *out) {
uint64_t blocks = 0;
switch (multiplier_mode) {
case MULTIPLIER_B:
case MULTIPLIER_P:
blocks = (bytes + 511) / 512;
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_G:
blocks = (bytes + (1024ULL * 1024 * 1024 - 1)) / (1024ULL * 1024 * 1024);
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_H_1000:
format_human_readable(bytes, out, true, opt_comma);
break;
case MULTIPLIER_H_1024:
format_human_readable(bytes, out, false, opt_comma);
break;
case MULTIPLIER_K:
default:
blocks = (bytes + 1023) / 1024;
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_M:
blocks = (bytes + (1024 * 1024 - 1)) / (1024 * 1024);
format_number(blocks, out, opt_comma);
break;
}
}
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) opt_all = true;
else if (strcmp(argv[i], "-b") == 0) multiplier_mode = MULTIPLIER_B;
else if (strcmp(argv[i], "-g") == 0) multiplier_mode = MULTIPLIER_G;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--human-readable") == 0) multiplier_mode = MULTIPLIER_H_1024;
else if (strcmp(argv[i], "-H") == 0 || strcmp(argv[i], "--si") == 0) multiplier_mode = MULTIPLIER_H_1000;
else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--inodes") == 0) opt_inodes = true;
else if (strcmp(argv[i], "-k") == 0) multiplier_mode = MULTIPLIER_K;
else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--local") == 0) opt_local = true;
else if (strcmp(argv[i], "-m") == 0) multiplier_mode = MULTIPLIER_M;
else if (strcmp(argv[i], "-P") == 0 || strcmp(argv[i], "--portability") == 0) multiplier_mode = MULTIPLIER_P;
else if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--print-type") == 0) opt_type = true;
else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--total") == 0) opt_total = true;
else if (strcmp(argv[i], "-n") == 0) opt_cached = true;
else if (strcmp(argv[i], "-Y") == 0) opt_export = true;
else if (strcmp(argv[i], "--libxo") == 0) opt_libxo = true;
else if (strcmp(argv[i], ",") == 0) opt_comma = true;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_usage();
return 0;
} else if (argv[i][0] == '-') {
// Check clustered flags (e.g., -Th)
for (size_t j = 1; j < strlen(argv[i]); j++) {
char c = argv[i][j];
if (c == 'a') opt_all = true;
else if (c == 'b') multiplier_mode = MULTIPLIER_B;
else if (c == 'g') multiplier_mode = MULTIPLIER_G;
else if (c == 'h') multiplier_mode = MULTIPLIER_H_1024;
else if (c == 'H') multiplier_mode = MULTIPLIER_H_1000;
else if (c == 'i') opt_inodes = true;
else if (c == 'k') multiplier_mode = MULTIPLIER_K;
else if (c == 'l') opt_local = true;
else if (c == 'm') multiplier_mode = MULTIPLIER_M;
else if (c == 'P') multiplier_mode = MULTIPLIER_P;
else if (c == 'T') opt_type = true;
else if (c == 'c') opt_total = true;
else if (c == 'n') opt_cached = true;
else if (c == 'Y') opt_export = true;
else {
printf("df: invalid option -- '%c'\n", c);
print_usage();
return 1;
}
}
}
}
int mount_count = sys_fs_mount_count();
if (mount_count < 0) {
printf("df: cannot get mount count\n");
return 1;
}
if (opt_libxo) {
printf("{\n \"storage-system-information\": {\n \"filesystem\": [\n");
} else {
if (opt_type) {
if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) {
printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Size", "Used", "Avail", "Use%", "Mounted on");
} else if (opt_inodes) {
printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on");
} else {
const char *block_str = "1K-blocks";
if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks";
else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks";
else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks";
printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "Type", block_str, "Used", "Available", "Use%", "Mounted on");
}
} else {
if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) {
printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on");
} else if (opt_inodes) {
printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on");
} else {
const char *block_str = "1K-blocks";
if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks";
else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks";
else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks";
if (multiplier_mode == MULTIPLIER_P) {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "512-blocks", "Used", "Available", "Capacity", "Mounted on");
} else {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", block_str, "Used", "Available", "Use%", "Mounted on");
}
}
}
}
uint64_t grand_total_bytes = 0;
uint64_t grand_used_bytes = 0;
uint64_t grand_avail_bytes = 0;
bool first_libxo = true;
for (int i = 0; i < mount_count; i++) {
mount_info_t m_info;
if (sys_fs_mount_info(i, &m_info) != 0) continue;
bool is_pseudo = (strcmp(m_info.fs_type, "ramfs") == 0 && strcmp(m_info.path, "/") != 0) ||
strcmp(m_info.fs_type, "procfs") == 0 ||
strcmp(m_info.fs_type, "sysfs") == 0;
if (is_pseudo && !opt_all) continue;
vfs_statfs_t stat;
if (sys_fs_statfs(m_info.path, &stat) != 0) continue;
uint64_t total_bytes = stat.total_blocks * stat.block_size;
uint64_t free_bytes = stat.free_blocks * stat.block_size;
uint64_t used_bytes = total_bytes - free_bytes;
if (strcmp(m_info.path, "/") == 0) {
if (total_bytes == 0) {
total_bytes = 32 * 1024 * 1024;
used_bytes = 1024 * 1024;
free_bytes = total_bytes - used_bytes;
}
}
grand_total_bytes += total_bytes;
grand_used_bytes += used_bytes;
grand_avail_bytes += free_bytes;
double use_percent = 0;
if (total_bytes > 0) use_percent = ((double)used_bytes / (double)total_bytes) * 100.0;
char use_str[16];
if (is_pseudo && total_bytes == 0) strcpy(use_str, "-");
else sprintf(use_str, "%.0f%%", use_percent);
if (opt_libxo) {
if (!first_libxo) printf(",\n");
first_libxo = false;
printf(" {\n");
printf(" \"name\": \"%s\",\n", m_info.device);
if (opt_type) printf(" \"type\": \"%s\",\n", m_info.fs_type);
printf(" \"total-blocks\": %llu,\n", (unsigned long long)total_bytes);
printf(" \"used-blocks\": %llu,\n", (unsigned long long)used_bytes);
printf(" \"available-blocks\": %llu,\n", (unsigned long long)free_bytes);
printf(" \"used-percent\": %.0f,\n", use_percent);
printf(" \"mounted-on\": \"%s\"\n", m_info.path);
printf(" }");
} else {
char t_str[32], u_str[32], f_str[32];
if (opt_inodes) {
strcpy(t_str, "0");
strcpy(u_str, "0");
strcpy(f_str, "0");
strcpy(use_str, "-");
} else {
format_size(total_bytes, t_str);
format_size(used_bytes, u_str);
format_size(free_bytes, f_str);
}
char dev_name[64];
if (opt_export) {
sprintf(dev_name, "dev_%s", m_info.device);
} else {
strcpy(dev_name, m_info.device);
}
if (opt_type) {
printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", dev_name, m_info.fs_type, t_str, u_str, f_str, use_str, m_info.path);
} else {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", dev_name, t_str, u_str, f_str, use_str, m_info.path);
}
}
}
if (opt_libxo) {
printf("\n ]\n }\n}\n");
} else if (opt_total && !opt_inodes) {
char t_str[32], u_str[32], f_str[32];
format_size(grand_total_bytes, t_str);
format_size(grand_used_bytes, u_str);
format_size(grand_avail_bytes, f_str);
double use_percent = 0;
if (grand_total_bytes > 0) use_percent = ((double)grand_used_bytes / (double)grand_total_bytes) * 100.0;
char use_str[16];
sprintf(use_str, "%.0f%%", use_percent);
if (opt_type) {
printf("%-16s %-8s %-10s %-10s %-10s %-5s -\n", "total", "-", t_str, u_str, f_str, use_str);
} else {
printf("%-16s %-10s %-10s %-10s %-5s -\n", "total", t_str, u_str, f_str, use_str);
}
}
return 0;
}

203
src/userland/cli/du.c Normal file
View file

@ -0,0 +1,203 @@
// Copyright (c) 2026 zeyadhost (https://github.com/zeyadhost)
// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <stdint.h>
#define MAX_ENTRIES 1024
#define DU_KB 1024ULL
#define DU_MB (1024ULL * 1024ULL)
#define DU_GB (1024ULL * 1024ULL * 1024ULL)
static int opt_summarize = 0;
static int opt_all = 0;
static int opt_max_depth = -1;
static int opt_total = 0;
static int opt_bytes = 0;
static uint64_t grand_total = 0;
static void usage(void) {
printf("Usage: du [options]..[file]\n");
printf("Summarize disk usage of the set of FILEs, recursively for directories.\n\n");
printf("Options:\n");
printf(" -s, --summarize display only a total for each argument\n");
printf(" -a, --all write counts for all files, not just directories\n");
printf(" -d, --max-depth=N print the total for a directory only if it is N or\n");
printf(" fewer levels below the command line argument\n");
printf(" -c, --total produce a grand total\n");
printf(" -b, --bytes print sizes in bytes\n");
printf(" -H, --human-readable print sizes in human readable format (default)\n");
printf(" --help display this help and exit\n");
}
static void print_size(uint64_t bytes, const char *path) {
if (opt_bytes) {
printf("%llu\t%s\n", (unsigned long long)bytes, path);
return;
}
char size_str[32];
uint64_t unit = 1;
const char *suffix = "B";
if (bytes >= DU_GB) {
unit = DU_GB;
suffix = "GB";
} else if (bytes >= DU_MB) {
unit = DU_MB;
suffix = "MB";
} else if (bytes >= DU_KB) {
unit = DU_KB;
suffix = "KB";
}
if (unit == 1) {
snprintf(size_str, sizeof(size_str), "%llu%s", (unsigned long long)bytes, suffix);
} else {
// Round to one decimal place
uint64_t whole = bytes / unit;
uint64_t rem = bytes % unit;
uint64_t tenth = (rem * 10ULL + unit / 2ULL) / unit;
if (tenth >= 10ULL) {
whole++;
tenth = 0;
}
if (tenth == 0) {
snprintf(size_str, sizeof(size_str), "%llu%s", (unsigned long long)whole, suffix);
} else {
snprintf(size_str, sizeof(size_str), "%llu.%llu%s", (unsigned long long)whole, (unsigned long long)tenth, suffix);
}
}
printf("%s\t%s\n", size_str, path);
}
static void join_path(char *dest, size_t size, const char *p1, const char *p2) {
if (strcmp(p1, "/") == 0) {
snprintf(dest, size, "/%s", p2);
} else if (p1[strlen(p1) - 1] == '/') {
snprintf(dest, size, "%s%s", p1, p2);
} else {
snprintf(dest, size, "%s/%s", p1, p2);
}
}
static uint64_t do_du(const char *path, int depth) {
FAT32_FileInfo info;
if (sys_get_file_info(path, &info) < 0) {
printf("du: cannot access '%s'\n", path);
return 0;
}
if (!info.is_directory) {
if (opt_all || (depth == 0)) {
if (opt_max_depth == -1 || depth <= opt_max_depth) {
if (!opt_summarize || depth == 0) {
print_size(info.size, path);
}
}
}
return info.size;
}
uint64_t total_size = info.size;
FAT32_FileInfo *entries = malloc(sizeof(FAT32_FileInfo) * MAX_ENTRIES);
if (!entries) {
printf("du: out of memory for '%s'\n", path);
return total_size;
}
int count = sys_list(path, entries, MAX_ENTRIES);
if (count < 0) {
printf("du: cannot read directory '%s'\n", path);
free(entries);
return total_size;
}
// Recurse into subdirectories
for (int i = 0; i < count; i++) {
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) {
continue;
}
char child_path[1024];
join_path(child_path, sizeof(child_path), path, entries[i].name);
total_size += do_du(child_path, depth + 1);
}
free(entries);
// Print directory size at this depth
if (!opt_summarize) {
if (opt_max_depth == -1 || depth <= opt_max_depth) {
print_size(total_size, path);
}
} else if (depth == 0) {
// With -s, only print the root of each requested path
print_size(total_size, path);
}
return total_size;
}
int main(int argc, char **argv) {
char **paths = malloc(sizeof(char*) * argc);
int num_paths = 0;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--summarize") == 0) {
opt_summarize = 1;
} else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) {
opt_all = 1;
} else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--total") == 0) {
opt_total = 1;
} else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--bytes") == 0) {
opt_bytes = 1;
} else if (strcmp(argv[i], "-H") == 0 || strcmp(argv[i], "--human-readable") == 0) {
// No-op: human-readable is the default
} else if (strcmp(argv[i], "-d") == 0) {
if (i + 1 < argc) {
opt_max_depth = atoi(argv[++i]);
} else {
printf("du: option requires an argument -- '-d'\n");
return 1;
}
} else if (strncmp(argv[i], "--max-depth=", 12) == 0) {
opt_max_depth = atoi(argv[i] + 12);
} else if (strcmp(argv[i], "--help") == 0) {
usage();
free(paths);
return 0;
} else if (argv[i][0] == '-') {
printf("du: invalid option -- '%s'\n", argv[i]);
usage();
free(paths);
return 1;
} else {
paths[num_paths++] = argv[i];
}
}
if (num_paths == 0) {
grand_total += do_du(".", 0);
} else {
for (int i = 0; i < num_paths; i++) {
grand_total += do_du(paths[i], 0);
}
}
if (opt_total) {
print_size(grand_total, "total");
}
free(paths);
return 0;
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023-2026 Chris (boreddevnl) // Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details. // 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. // This header needs to maintain in any file it is present in, as per the GPL license terms.
// BOREDOS_APP_DESC: Display file contents in hexadecimal. // BOREDOS_APP_DESC: Display file contents in hexadecimal.

View file

@ -1,4 +1,4 @@
// Copyright (c) 2023-2026 Chris (boreddevnl) // Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details. // 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. // This header needs to maintain in any file it is present in, as per the GPL license terms.
// BOREDOS_APP_DESC: List running processes. // BOREDOS_APP_DESC: List running processes.

View file

@ -1,21 +1,16 @@
// Copyright (c) 2023-2026 Chris (boreddevnl) // Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details. // 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. // This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "stdlib.h" #include "stdlib.h"
#include "syscall.h" #include "syscall.h"
// CMDLINE_MAX includes the trailing NUL, so at most 511 command-line bytes can
// be reconstructed here. Unchecked concatenation can overflow CMDLINE_MAX; all
// command-line construction in this file must go through the checked append
// helpers below.
#define CMDLINE_MAX 512 #define CMDLINE_MAX 512
static int sc_strcmp(const char *a, const char *b) {
while (*a && *a == *b) {
a++;
b++;
}
return (unsigned char)*a - (unsigned char)*b;
}
static int has_slash(const char *s) { static int has_slash(const char *s) {
while (s && *s) { while (s && *s) {
if (*s == '/') if (*s == '/')
@ -37,7 +32,7 @@ static int ends_with_elf(const char *s) {
if (len < 4) if (len < 4)
return 0; return 0;
return sc_strcmp(s + len - 4, ".elf") == 0; return strcmp(s + len - 4, ".elf") == 0;
} }
static void print_usage(void) { static void print_usage(void) {
@ -113,8 +108,8 @@ int main(int argc, char **argv) {
return 1; return 1;
} }
if (sc_strcmp(argv[1], "-h") == 0 || if (strcmp(argv[1], "-h") == 0 ||
sc_strcmp(argv[1], "--help") == 0) { strcmp(argv[1], "--help") == 0) {
print_usage(); print_usage();
return 0; return 0;
} }
@ -135,7 +130,15 @@ int main(int argc, char **argv) {
printf("\n"); printf("\n");
printf("Command: %s\n", cmdline); printf("Command: %s\n", cmdline);
printf("Exit code: %d\n", ret); printf("Exit code: %d\n", ret);
if (ret == -1) {
printf("Command failed with non-zero exit code, not reporting time.\n");
return ret;
}
printf("Elapsed: %llu ms\n", elapsed); printf("Elapsed: %llu ms\n", elapsed);
sys_system(SYSTEM_CMD_SLEEP, 1, 0, 0, 0);
return ret; return ret;
} }

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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.
// BOREDOS_APP_DESC: 2048 number puzzle game. // BOREDOS_APP_DESC: 2048 number puzzle game.
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/applications-games.png // BOREDOS_APP_ICONS: /Library/images/icons/colloid/applications-games.png
#include "libc/syscall.h" #include "libc/syscall.h"

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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.
// BOREDOS_APP_DESC: Classic snake arcade game. // BOREDOS_APP_DESC: Classic snake arcade game.
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/cartridges.png // BOREDOS_APP_ICONS: /Library/images/icons/colloid/cartridges.png
#include "libc/syscall.h" #include "libc/syscall.h"

View file

@ -1,16 +1,12 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 "libc/syscall.h" #include "libc/syscall.h"
#include "libc/libui.h" #include "libc/libui.h"
#include "libc/stdlib.h" #include "libc/stdlib.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
/*
@Lluciocc
The most SIMPLE keylogger for debug
FEAT:
- Log every key pressed in the terminal and gives their keycode
*/
#define WINDOW_W 400 #define WINDOW_W 400
#define WINDOW_H 200 #define WINDOW_H 200

View file

@ -6,6 +6,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 "libc/string.h"
#include "stb_image.h" #include "stb_image.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -65,13 +66,14 @@ static widget_button_t btn_apply, btn_back;
static widget_slider_t slider_mouse; static widget_slider_t slider_mouse;
static widget_slider_t slider_cursor_size; static widget_slider_t slider_cursor_size;
#define MAX_WALLPAPERS 10
static widget_button_t btn_main_wallpaper, btn_main_network, btn_main_desktop, btn_main_mouse, btn_main_fonts, btn_main_display, btn_main_keyboard; static widget_button_t btn_main_wallpaper, btn_main_network, btn_main_desktop, btn_main_mouse, btn_main_fonts, btn_main_display, btn_main_keyboard;
static widget_button_t btn_wp_colors[6]; static widget_button_t btn_wp_colors[6];
static widget_button_t btn_wp_patterns[2]; static widget_button_t btn_wp_patterns[2];
static widget_button_t btn_wp_apply; static widget_button_t btn_wp_apply;
static widget_button_t btn_wp_thumbs[MAX_WALLPAPERS]; static widget_button_t *btn_wp_thumbs = NULL;
#define WALLPAPER_THUMB_W 80
#define WALLPAPER_THUMB_H 50
static widget_button_t btn_net_init; static widget_button_t btn_net_init;
static widget_button_t btn_net_set_ip, btn_net_set_dns; static widget_button_t btn_net_set_ip, btn_net_set_dns;
@ -89,8 +91,30 @@ static font_entry_t *fonts = NULL;
static int font_capacity = 0; static int font_capacity = 0;
static widget_scrollbar_t font_scrollbar; static widget_scrollbar_t font_scrollbar;
static int font_scroll_y = 0; static int font_scroll_y = 0;
typedef struct {
char path[128];
char name[64];
uint32_t thumb[WALLPAPER_THUMB_W * WALLPAPER_THUMB_H];
_Bool valid;
} wallpaper_entry_t;
static wallpaper_entry_t *wallpapers = NULL;
static int wallpaper_count = 0;
static int wallpaper_capacity = 0;
static widget_scrollbar_t wallpaper_scrollbar;
static int wallpaper_scroll_y = 0;
static int wallpaper_load_state = 0;
static int wallpaper_next_load_index = 0;
static bool wallpaper_allow_decode = false;
static bool wallpaper_worker_spawned = false;
static char settings_executable_path[256] = {0};
static widget_textbox_t tb_custom_w, tb_custom_h; static widget_textbox_t tb_custom_w, tb_custom_h;
#define WALLPAPER_LOADING_NOT_STARTED 0
#define WALLPAPER_LOADING_ACTIVE 1
#define WALLPAPER_LOADING_DONE 2
#define SETTINGS_ICON_MAIN_SIZE 32 #define SETTINGS_ICON_MAIN_SIZE 32
#define SETTINGS_ICON_LIST_SIZE 18 #define SETTINGS_ICON_LIST_SIZE 18
@ -193,19 +217,6 @@ static char net_status[64] = "";
static uint32_t pattern_lumberjack[PATTERN_SIZE * PATTERN_SIZE]; static uint32_t pattern_lumberjack[PATTERN_SIZE * PATTERN_SIZE];
static uint32_t pattern_blue_diamond[PATTERN_SIZE * PATTERN_SIZE]; static uint32_t pattern_blue_diamond[PATTERN_SIZE * PATTERN_SIZE];
#define WALLPAPER_THUMB_W 80
#define WALLPAPER_THUMB_H 50
typedef struct {
char path[128];
char name[64];
uint32_t thumb[WALLPAPER_THUMB_W * WALLPAPER_THUMB_H];
_Bool valid;
} wallpaper_entry_t;
static wallpaper_entry_t wallpapers[MAX_WALLPAPERS];
static int wallpaper_count = 0;
static _Bool desktop_snap_to_grid = 1; static _Bool desktop_snap_to_grid = 1;
static _Bool desktop_auto_align = 1; static _Bool desktop_auto_align = 1;
static int desktop_max_rows_per_col = 10; static int desktop_max_rows_per_col = 10;
@ -248,6 +259,45 @@ static void format_scale_tenths(int scale_tenths, char *str) {
str[len] = 0; str[len] = 0;
} }
static void draw_truncated_string(ui_window_t win, int x, int y, const char *str, int max_width, uint32_t color) {
if (!str || max_width <= 0) return;
int full_width = ui_get_string_width(str);
if (full_width <= max_width) {
ui_draw_string(win, x, y, str, color);
return;
}
const char *ellipsis = "...";
int ellipsis_width = ui_get_string_width(ellipsis);
int avail_width = max_width - ellipsis_width;
if (avail_width <= 0) {
ui_draw_string(win, x, y, ellipsis, color);
return;
}
char buf[64];
int pos = 0;
int width = 0;
while (str[pos] && pos < 63) {
buf[pos] = str[pos];
buf[pos + 1] = '\0';
width = ui_get_string_width(buf);
if (width > avail_width) break;
pos++;
}
if (pos == 0) {
ui_draw_string(win, x, y, ellipsis, color);
return;
}
buf[pos] = '\0';
ui_draw_string(win, x, y, buf, color);
ui_draw_string(win, x + width, y, ellipsis, color);
}
static void generate_lumberjack_pattern(void) { static void generate_lumberjack_pattern(void) {
uint32_t red = 0xFFDC143C; uint32_t red = 0xFFDC143C;
uint32_t dark_grey = 0xFF404040; uint32_t dark_grey = 0xFF404040;
@ -435,36 +485,127 @@ static int is_supported_image(const char *name) {
static void decode_wallpapers_task(void *arg) { static void decode_wallpapers_task(void *arg) {
(void)arg; (void)arg;
wallpaper_count = 0; wallpaper_count = 0;
FAT32_FileInfo info[MAX_WALLPAPERS]; wallpaper_capacity = 0;
int count = sys_list("/Library/images/Wallpapers", info, MAX_WALLPAPERS); wallpaper_next_load_index = 0;
if (count < 0) return; wallpaper_load_state = WALLPAPER_LOADING_NOT_STARTED;
for (int i = 0; i < count && wallpaper_count < MAX_WALLPAPERS; i++) { FAT32_FileInfo info[512];
if (info[i].is_directory) continue; // Skip directories int count = sys_list("/Library/images/Wallpapers", info, 512);
if (count < 0) count = 0;
for (int i = 0; i < count; i++) {
if (info[i].is_directory) continue;
if (!is_supported_image(info[i].name)) continue;
wallpaper_count++;
}
if (wallpaper_count <= 0) {
wallpaper_count = 0;
wallpaper_capacity = 0;
return;
}
wallpaper_capacity = wallpaper_count;
wallpapers = (wallpaper_entry_t *)malloc(wallpaper_capacity * sizeof(wallpaper_entry_t));
if (!wallpapers) {
wallpaper_count = 0;
wallpaper_capacity = 0;
return;
}
int dst_idx = 0;
for (int i = 0; i < count && dst_idx < wallpaper_capacity; i++) {
if (info[i].is_directory) continue;
if (!is_supported_image(info[i].name)) continue; if (!is_supported_image(info[i].name)) continue;
wallpaper_entry_t *wp = &wallpapers[wallpaper_count]; wallpaper_entry_t *wp = &wallpapers[dst_idx];
// Set path
char *pref = "/Library/images/Wallpapers/"; char *pref = "/Library/images/Wallpapers/";
int pl = 0; while (pref[pl]) { wp->path[pl] = pref[pl]; pl++; } int pl = 0;
int nl = 0; while (info[i].name[nl]) { wp->path[pl+nl] = info[i].name[nl]; nl++; } while (pref[pl]) { wp->path[pl] = pref[pl]; pl++; }
int nl = 0;
while (info[i].name[nl]) { wp->path[pl+nl] = info[i].name[nl]; nl++; }
wp->path[pl+nl] = 0; wp->path[pl+nl] = 0;
// Set name (strip extension)
int dot_idx = -1; int dot_idx = -1;
for (int j = 0; info[i].name[j]; j++) if (info[i].name[j] == '.') dot_idx = j; for (int j = 0; info[i].name[j]; j++) if (info[i].name[j] == '.') dot_idx = j;
int name_len = (dot_idx != -1) ? dot_idx : nl; int name_len = (dot_idx != -1) ? dot_idx : nl;
for (int j = 0; j < name_len && j < 63; j++) wp->name[j] = info[i].name[j]; for (int j = 0; j < name_len && j < 63; j++) wp->name[j] = info[i].name[j];
wp->name[(name_len < 63) ? name_len : 63] = 0; wp->name[(name_len < 63) ? name_len : 63] = 0;
wp->valid = 0;
dst_idx++;
}
}
static void on_wallpaper_scroll(void *user_data, int new_scroll_y) {
(void)user_data;
wallpaper_scroll_y = new_scroll_y;
}
static int wallpaper_scroll_region_y(void) {
return 46 - wallpaper_scroll_y;
}
static void set_settings_executable_path(const char *argv0) {
if (!argv0 || argv0[0] == '\0') return;
if (argv0[0] == '/') {
int i = 0;
while (argv0[i] && i < (int)sizeof(settings_executable_path) - 1) {
settings_executable_path[i] = argv0[i];
i++;
}
settings_executable_path[i] = '\0';
return;
}
char cwd[256];
if (sys_getcwd(cwd, sizeof(cwd)) > 0) {
int pos = 0;
for (int i = 0; cwd[i] && pos < (int)sizeof(settings_executable_path) - 1; i++) {
settings_executable_path[pos++] = cwd[i];
}
if (pos < (int)sizeof(settings_executable_path) - 1) {
settings_executable_path[pos++] = '/';
}
for (int i = 0; argv0[i] && pos < (int)sizeof(settings_executable_path) - 1; i++) {
settings_executable_path[pos++] = argv0[i];
}
settings_executable_path[pos] = '\0';
} else {
int i = 0;
while (argv0[i] && i < (int)sizeof(settings_executable_path) - 1) {
settings_executable_path[i] = argv0[i];
i++;
}
settings_executable_path[i] = '\0';
}
}
static void spawn_wallpaper_worker(void) {
if (wallpaper_worker_spawned || settings_executable_path[0] == '\0') return;
sys_spawn(settings_executable_path, "--wallpaper-thumb-worker", SPAWN_FLAG_BACKGROUND, 0);
wallpaper_worker_spawned = true;
}
static void load_wallpaper_thumbnail(int idx) {
if (idx < 0 || idx >= wallpaper_count || wallpapers[idx].valid) return;
wallpaper_entry_t *wp = &wallpapers[idx];
char cache_path[256]; char cache_path[256];
int cp = 0; int cp = 0;
char *cpref = "/Library/Caches/Thumbnails/"; const char *cpref = "/Library/Caches/Thumbnails/";
while (cpref[cp]) { cache_path[cp] = cpref[cp]; cp++; } while (cpref[cp]) { cache_path[cp] = cpref[cp]; cp++; }
int last_slash = 0;
for (int i = 0; wp->path[i]; i++) {
if (wp->path[i] == '/') last_slash = i + 1;
}
int cn = 0; int cn = 0;
while (info[i].name[cn]) { cache_path[cp+cn] = info[i].name[cn]; cn++; } while (wp->path[last_slash + cn]) { cache_path[cp+cn] = wp->path[last_slash + cn]; cn++; }
char *csuf = ".bin"; const char *csuf = ".bin";
int cs = 0; int cs = 0;
while (csuf[cs]) { cache_path[cp+cn+cs] = csuf[cs]; cs++; } while (csuf[cs]) { cache_path[cp+cn+cs] = csuf[cs]; cs++; }
cache_path[cp+cn+cs] = 0; cache_path[cp+cn+cs] = 0;
@ -474,15 +615,21 @@ static void decode_wallpapers_task(void *arg) {
sys_read(cfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4); sys_read(cfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
sys_close(cfd); sys_close(cfd);
wp->valid = 1; wp->valid = 1;
} else { return;
}
if (!wallpaper_allow_decode) {
return;
}
int fd = sys_open(wp->path, "r"); int fd = sys_open(wp->path, "r");
if (fd >= 0) { if (fd >= 0) {
int size = sys_seek(fd, 0, 2); // SEEK_END int size = sys_seek(fd, 0, 2);
sys_seek(fd, 0, 0); // SEEK_SET sys_seek(fd, 0, 0);
if (size > 0 && size < 8 * 1024 * 1024) { if (size > 0 && size < 8 * 1024 * 1024) {
unsigned char *buf = (unsigned char *)malloc(size); unsigned char *buf = (unsigned char *)malloc(size);
if (buf) { if (buf) {
sys_read(fd, buf, size); if (sys_read(fd, buf, size) > 0) {
int img_w, img_h, channels; int img_w, img_h, channels;
unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4); unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4);
if (img && img_w > 0 && img_h > 0) { if (img && img_w > 0 && img_h > 0) {
@ -490,14 +637,13 @@ static void decode_wallpapers_task(void *arg) {
wp->valid = 1; wp->valid = 1;
stbi_image_free(img); stbi_image_free(img);
sys_mkdir("/Library/Caches");
sys_mkdir("/Library/Caches/Thumbnails");
int swfd = sys_open(cache_path, "w"); int swfd = sys_open(cache_path, "w");
if (swfd >= 0) { if (swfd >= 0) {
sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4); sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
sys_close(swfd); sys_close(swfd);
} }
} }
}
free(buf); free(buf);
} }
} }
@ -505,19 +651,96 @@ static void decode_wallpapers_task(void *arg) {
} }
} }
wallpaper_count++; static bool process_wallpaper_loading_step(void) {
if (wallpaper_load_state != WALLPAPER_LOADING_ACTIVE) return false;
if (wallpaper_count <= 0) return false;
int checked = 0;
bool loaded_any = false;
while (checked < wallpaper_count) {
int idx = wallpaper_next_load_index;
wallpaper_next_load_index++;
if (wallpaper_next_load_index >= wallpaper_count) wallpaper_next_load_index = 0;
if (!wallpapers[idx].valid) {
load_wallpaper_thumbnail(idx);
if (wallpapers[idx].valid) {
loaded_any = true;
break;
} }
} }
checked++;
}
if (!loaded_any) {
bool all_valid = true;
for (int i = 0; i < wallpaper_count; i++) {
if (!wallpapers[i].valid) {
all_valid = false;
break;
}
}
if (all_valid) {
wallpaper_load_state = WALLPAPER_LOADING_DONE;
}
}
return loaded_any;
}
static void load_wallpapers(void) { static void load_wallpapers(void) {
void *job_args[1] = { NULL }; if (wallpaper_load_state == WALLPAPER_LOADING_ACTIVE ||
sys_parallel_run(decode_wallpapers_task, job_args, 1); wallpaper_load_state == WALLPAPER_LOADING_DONE) {
return;
for (int i = 0; i < wallpaper_count; i++) {
int tx = (i % 3) * (WALLPAPER_THUMB_W + 15);
int ty = (i / 3) * (WALLPAPER_THUMB_H + 30);
widget_button_init(&btn_wp_thumbs[i], 8 + tx, 306 + ty, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 20, "");
} }
if (btn_wp_thumbs) {
free(btn_wp_thumbs);
btn_wp_thumbs = NULL;
}
if (wallpapers) {
free(wallpapers);
wallpapers = NULL;
}
wallpaper_count = 0;
wallpaper_capacity = 0;
wallpaper_next_load_index = 0;
wallpaper_scroll_y = 0;
sys_mkdir("/Library/Caches");
sys_mkdir("/Library/Caches/Thumbnails");
decode_wallpapers_task(NULL);
if (wallpaper_count > 0) {
btn_wp_thumbs = (widget_button_t *)malloc(wallpaper_count * sizeof(widget_button_t));
if (btn_wp_thumbs) {
for (int i = 0; i < wallpaper_count; i++) {
widget_button_init(&btn_wp_thumbs[i], 8, 0, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 20, "");
}
}
}
widget_scrollbar_init(&wallpaper_scrollbar, 330, 46, 12, 454);
int wallpaper_rows = (wallpaper_count + 2) / 3;
int wallpaper_list_height = wallpaper_rows * (WALLPAPER_THUMB_H + 30);
wallpaper_scrollbar.content_height = 260 + wallpaper_list_height;
wallpaper_scrollbar.on_scroll = (void(*)(void*,int))on_wallpaper_scroll;
wallpaper_load_state = WALLPAPER_LOADING_ACTIVE;
wallpaper_allow_decode = false;
spawn_wallpaper_worker();
}
static int wallpaper_thumbnail_worker_main(void) {
wallpaper_allow_decode = true;
sys_mkdir("/Library/Caches");
sys_mkdir("/Library/Caches/Thumbnails");
decode_wallpapers_task(NULL);
for (int i = 0; i < wallpaper_count; i++) {
load_wallpaper_thumbnail(i);
}
return 0;
} }
static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) { static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) {
@ -596,16 +819,22 @@ static void control_panel_paint_main(ui_window_t win) {
static void control_panel_paint_wallpaper(ui_window_t win) { static void control_panel_paint_wallpaper(ui_window_t win) {
int offset_x = 8; int offset_x = 8;
int offset_y = 6; int page_top = wallpaper_scroll_region_y();
widget_button_draw(&settings_ctx, &btn_back); widget_button_draw(&settings_ctx, &btn_back);
ui_draw_string(win, offset_x, offset_y + 40, "Presets:", COLOR_DARK_TEXT); ui_draw_string(win, offset_x, page_top, "Presets:", COLOR_DARK_TEXT);
int button_y = offset_y + 65;
int button_x = offset_x; int button_x = offset_x;
int button_y = page_top + 25;
btn_wp_colors[0].x = button_x;
btn_wp_colors[0].y = button_y;
btn_wp_colors[1].x = button_x + 100;
btn_wp_colors[1].y = button_y;
btn_wp_colors[2].x = button_x + 200;
btn_wp_colors[2].y = button_y;
// Colors
widget_button_draw(&settings_ctx, &btn_wp_colors[0]); widget_button_draw(&settings_ctx, &btn_wp_colors[0]);
ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_COFFEE); ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_COFFEE);
ui_draw_string(win, button_x + 35, button_y + 8, "Coffee", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 35, button_y + 8, "Coffee", COLOR_DARK_TEXT);
@ -619,6 +848,13 @@ static void control_panel_paint_wallpaper(ui_window_t win) {
ui_draw_string(win, button_x + 235, button_y + 8, "Green", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 235, button_y + 8, "Green", COLOR_DARK_TEXT);
button_y += 35; button_y += 35;
btn_wp_colors[3].x = button_x;
btn_wp_colors[3].y = button_y;
btn_wp_colors[4].x = button_x + 100;
btn_wp_colors[4].y = button_y;
btn_wp_colors[5].x = button_x + 200;
btn_wp_colors[5].y = button_y;
widget_button_draw(&settings_ctx, &btn_wp_colors[3]); widget_button_draw(&settings_ctx, &btn_wp_colors[3]);
ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_BLUE_BG); ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_BLUE_BG);
ui_draw_string(win, button_x + 35, button_y + 8, "Blue", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 35, button_y + 8, "Blue", COLOR_DARK_TEXT);
@ -631,11 +867,15 @@ static void control_panel_paint_wallpaper(ui_window_t win) {
ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREY); ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREY);
ui_draw_string(win, button_x + 235, button_y + 8, "Grey", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 235, button_y + 8, "Grey", COLOR_DARK_TEXT);
// Patterns
button_y += 40; button_y += 40;
ui_draw_string(win, offset_x, button_y, "Patterns:", COLOR_DARK_TEXT); ui_draw_string(win, offset_x, button_y, "Patterns:", COLOR_DARK_TEXT);
button_y += 20; button_y += 20;
btn_wp_patterns[0].x = button_x;
btn_wp_patterns[0].y = button_y;
btn_wp_patterns[1].x = button_x + 153;
btn_wp_patterns[1].y = button_y;
widget_button_draw(&settings_ctx, &btn_wp_patterns[0]); widget_button_draw(&settings_ctx, &btn_wp_patterns[0]);
for (int py = 0; py < 10; py++) { for (int py = 0; py < 10; py++) {
for (int px = 0; px < 12; px++) { for (int px = 0; px < 12; px++) {
@ -661,11 +901,17 @@ static void control_panel_paint_wallpaper(ui_window_t win) {
} }
ui_draw_string(win, button_x + 165, button_y + 8, "Blue Diamond", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 165, button_y + 8, "Blue Diamond", COLOR_DARK_TEXT);
// Custom color
button_y += 40; button_y += 40;
ui_draw_string(win, offset_x, button_y, "Custom color:", COLOR_DARK_TEXT); ui_draw_string(win, offset_x, button_y, "Custom color:", COLOR_DARK_TEXT);
button_y += 20; button_y += 20;
tb_r.x = 33;
tb_r.y = button_y + 4;
tb_g.x = 123;
tb_g.y = button_y + 4;
tb_b.x = 213;
tb_b.y = button_y + 4;
ui_draw_string(win, button_x, button_y + 4, "R:", COLOR_DARK_TEXT); ui_draw_string(win, button_x, button_y + 4, "R:", COLOR_DARK_TEXT);
tb_r.focused = (focused_field == 0); tb_r.focused = (focused_field == 0);
widget_textbox_draw(&settings_ctx, &tb_r); widget_textbox_draw(&settings_ctx, &tb_r);
@ -678,24 +924,39 @@ static void control_panel_paint_wallpaper(ui_window_t win) {
tb_b.focused = (focused_field == 2); tb_b.focused = (focused_field == 2);
widget_textbox_draw(&settings_ctx, &tb_b); widget_textbox_draw(&settings_ctx, &tb_b);
btn_wp_apply.x = 8;
btn_wp_apply.y = button_y + 33;
widget_button_draw(&settings_ctx, &btn_wp_apply); widget_button_draw(&settings_ctx, &btn_wp_apply);
ui_draw_string(win, button_x + 18, button_y + 33, "Apply", COLOR_DARK_TEXT); ui_draw_string(win, button_x + 18, button_y + 33, "Apply", COLOR_DARK_TEXT);
// Wallpapers section
button_y += 60; button_y += 60;
ui_draw_string(win, offset_x, button_y, "Wallpapers:", COLOR_DARK_TEXT); ui_draw_string(win, offset_x, button_y, "Wallpapers:", COLOR_DARK_TEXT);
button_y += 20; button_y += 20;
int list_y = button_y;
int list_h = 180;
for (int i = 0; i < wallpaper_count; i++) { for (int i = 0; i < wallpaper_count; i++) {
int tx = (i % 3) * (WALLPAPER_THUMB_W + 15); int tx = (i % 3) * (WALLPAPER_THUMB_W + 15);
int ty = (i / 3) * (WALLPAPER_THUMB_H + 30); int ty = (i / 3) * (WALLPAPER_THUMB_H + 30);
int item_y = list_y + ty;
if (item_y + (WALLPAPER_THUMB_H + 20) < 0 || item_y > 500) continue;
btn_wp_thumbs[i].x = button_x + tx;
btn_wp_thumbs[i].y = item_y;
widget_button_draw(&settings_ctx, &btn_wp_thumbs[i]); widget_button_draw(&settings_ctx, &btn_wp_thumbs[i]);
if (wallpapers[i].valid) { if (wallpapers[i].valid) {
ui_draw_image(win, button_x + tx + 4, button_y + ty + 4, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H, wallpapers[i].thumb); ui_draw_image(win, button_x + tx + 4, item_y + 4, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H, wallpapers[i].thumb);
} else {
ui_draw_rect(win, button_x + tx + 4, item_y + 4, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H, 0xFF2A2A2A);
ui_draw_rect(win, button_x + tx + 6, item_y + WALLPAPER_THUMB_H/2 - 1, WALLPAPER_THUMB_W - 4, 2, 0xFF3E3E3E);
} }
ui_draw_string(win, button_x + tx + 8, button_y + ty + WALLPAPER_THUMB_H + 8, wallpapers[i].name, 0xFFFFFFFF); draw_truncated_string(win, button_x + tx + 8, item_y + WALLPAPER_THUMB_H + 8, wallpapers[i].name, WALLPAPER_THUMB_W - 8, 0xFFFFFFFF);
} }
widget_scrollbar_draw(&settings_ctx, &wallpaper_scrollbar);
} }
static void control_panel_paint_network(ui_window_t win) { static void control_panel_paint_network(ui_window_t win) {
@ -1117,6 +1378,55 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
} }
if (current_view == VIEW_WALLPAPER) { if (current_view == VIEW_WALLPAPER) {
int page_base = wallpaper_scroll_region_y();
int button_y = page_base + 25;
btn_wp_colors[0].x = 8;
btn_wp_colors[0].y = button_y;
btn_wp_colors[1].x = 108;
btn_wp_colors[1].y = button_y;
btn_wp_colors[2].x = 208;
btn_wp_colors[2].y = button_y;
button_y += 35;
btn_wp_colors[3].x = 8;
btn_wp_colors[3].y = button_y;
btn_wp_colors[4].x = 108;
btn_wp_colors[4].y = button_y;
btn_wp_colors[5].x = 208;
btn_wp_colors[5].y = button_y;
button_y += 40;
btn_wp_patterns[0].x = 8;
btn_wp_patterns[0].y = button_y;
btn_wp_patterns[1].x = 153;
btn_wp_patterns[1].y = button_y;
button_y += 20;
button_y += 40;
tb_r.x = 33;
tb_r.y = button_y + 4;
tb_g.x = 123;
tb_g.y = button_y + 4;
tb_b.x = 213;
tb_b.y = button_y + 4;
btn_wp_apply.x = 8;
btn_wp_apply.y = button_y + 33;
button_y += 20;
button_y += 60;
int list_y = button_y + 20;
for (int i = 0; i < wallpaper_count; i++) {
int tx = (i % 3) * (WALLPAPER_THUMB_W + 15);
int ty = (i / 3) * (WALLPAPER_THUMB_H + 30);
int item_y = list_y + ty;
if (item_y + (WALLPAPER_THUMB_H + 20) < 0 || item_y > 500) continue;
btn_wp_thumbs[i].x = 8 + tx;
btn_wp_thumbs[i].y = item_y;
}
if (widget_textbox_handle_mouse(&settings_ctx, &tb_r, x, y, is_click, NULL)) { if (widget_textbox_handle_mouse(&settings_ctx, &tb_r, x, y, is_click, NULL)) {
focused_field = 0; input_cursor = tb_r.cursor_pos; return; focused_field = 0; input_cursor = tb_r.cursor_pos; return;
} }
@ -1152,9 +1462,17 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
} }
return; return;
} }
if (widget_scrollbar_handle_mouse(&wallpaper_scrollbar, x, y, is_down, NULL)) {
return;
}
for (int i = 0; i < wallpaper_count; i++) { for (int i = 0; i < wallpaper_count; i++) {
if (wallpapers[i].valid && widget_button_handle_mouse(&btn_wp_thumbs[i], x, y, is_down, is_click, NULL)) { if (wallpapers[i].valid && widget_button_handle_mouse(&btn_wp_thumbs[i], x, y, is_down, is_click, NULL)) {
if (is_click) { sys_system(SYSTEM_CMD_SET_WALLPAPER_PATH, (uint64_t)wallpapers[i].path, 0, 0, 0); btn_wp_thumbs[i].pressed=false;} return; if (is_click) {
sys_system(SYSTEM_CMD_SET_WALLPAPER_PATH, (uint64_t)wallpapers[i].path, 0, 0, 0);
btn_wp_thumbs[i].pressed = false;
}
return;
} }
} }
} }
@ -1234,7 +1552,12 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
if (current_view == VIEW_MAIN) { if (current_view == VIEW_MAIN) {
if (widget_button_handle_mouse(&btn_main_wallpaper, x, y, is_down, is_click, NULL)) { if (widget_button_handle_mouse(&btn_main_wallpaper, x, y, is_down, is_click, NULL)) {
if (is_click) { current_view = VIEW_WALLPAPER; focused_field = -1; btn_main_wallpaper.pressed = false; } if (is_click) {
current_view = VIEW_WALLPAPER;
focused_field = -1;
btn_main_wallpaper.pressed = false;
load_wallpapers();
}
return; return;
} }
if (widget_button_handle_mouse(&btn_main_network, x, y, is_down, is_click, NULL)) { if (widget_button_handle_mouse(&btn_main_network, x, y, is_down, is_click, NULL)) {
@ -1418,8 +1741,10 @@ static void init_settings_widgets(void) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
(void)argc; set_settings_executable_path(argv[0]);
(void)argv; if (argc > 1 && strcmp(argv[1], "--wallpaper-thumb-worker") == 0) {
return wallpaper_thumbnail_worker_main();
}
ui_window_t win = ui_window_create("Settings", 200, 150, 350, 500); ui_window_t win = ui_window_create("Settings", 200, 150, 350, 500);
if (!win) return 1; if (!win) return 1;
@ -1451,8 +1776,6 @@ int main(int argc, char **argv) {
control_panel_paint(win); control_panel_paint(win);
ui_mark_dirty(win, 0, 0, 350, 500); ui_mark_dirty(win, 0, 0, 350, 500);
load_wallpapers(); // load after first paint to avoid startup delay
gui_event_t ev; gui_event_t ev;
while (1) { while (1) {
bool dirty = false; bool dirty = false;
@ -1510,11 +1833,15 @@ int main(int argc, char **argv) {
sys_exit(0); sys_exit(0);
} }
if (dirty) { if (dirty || (current_view == VIEW_WALLPAPER && process_wallpaper_loading_step())) {
control_panel_paint(win); control_panel_paint(win);
ui_mark_dirty(win, 0, 0, 350, 500); ui_mark_dirty(win, 0, 0, 350, 500);
} }
} else { } else {
if (current_view == VIEW_WALLPAPER && process_wallpaper_loading_step()) {
control_panel_paint(win);
ui_mark_dirty(win, 0, 0, 350, 500);
}
sleep(10); sleep(10);
} }
} }

View file

@ -161,6 +161,18 @@ int sys_fcntl(int fd, int cmd, int val) {
return (int)syscall4(SYS_FS, FS_CMD_FCNTL, (uint64_t)fd, (uint64_t)cmd, (uint64_t)val); return (int)syscall4(SYS_FS, FS_CMD_FCNTL, (uint64_t)fd, (uint64_t)cmd, (uint64_t)val);
} }
int sys_fs_statfs(const char *path, vfs_statfs_t *stat) {
return (int)syscall3(SYS_FS, FS_CMD_STATFS, (uint64_t)path, (uint64_t)stat);
}
int sys_fs_mount_count(void) {
return (int)syscall1(SYS_FS, FS_CMD_MOUNT_COUNT);
}
int sys_fs_mount_info(int index, mount_info_t *info) {
return (int)syscall3(SYS_FS, FS_CMD_MOUNT_INFO, (uint64_t)index, (uint64_t)info);
}
int sys_tty_create(void) { int sys_tty_create(void) {
return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0); return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0);
} }

View file

@ -33,6 +33,9 @@
#define FS_CMD_DUP2 16 #define FS_CMD_DUP2 16
#define FS_CMD_PIPE 17 #define FS_CMD_PIPE 17
#define FS_CMD_FCNTL 18 #define FS_CMD_FCNTL 18
#define FS_CMD_STATFS 19
#define FS_CMD_MOUNT_COUNT 20
#define FS_CMD_MOUNT_INFO 21
// System Commands (via SYS_SYSTEM) // System Commands (via SYS_SYSTEM)
#define SYSTEM_CMD_SET_BG_COLOR 1 #define SYSTEM_CMD_SET_BG_COLOR 1
@ -155,6 +158,18 @@ typedef struct {
int sys_get_os_info(os_info_t *info); int sys_get_os_info(os_info_t *info);
// FS API // FS API
typedef struct {
uint64_t total_blocks;
uint64_t free_blocks;
uint64_t block_size;
} vfs_statfs_t;
typedef struct {
char path[256];
char device[32];
char fs_type[16];
} mount_info_t;
int sys_open(const char *path, const char *mode); int sys_open(const char *path, const char *mode);
int sys_read(int fd, void *buf, uint32_t len); int sys_read(int fd, void *buf, uint32_t len);
int sys_write_fs(int fd, const void *buf, uint32_t len); int sys_write_fs(int fd, const void *buf, uint32_t len);
@ -171,6 +186,9 @@ int sys_dup(int oldfd);
int sys_dup2(int oldfd, int newfd); int sys_dup2(int oldfd, int newfd);
int sys_pipe(int pipefd[2]); int sys_pipe(int pipefd[2]);
int sys_fcntl(int fd, int cmd, int val); int sys_fcntl(int fd, int cmd, int val);
int sys_fs_statfs(const char *path, vfs_statfs_t *stat);
int sys_fs_mount_count(void);
int sys_fs_mount_info(int index, mount_info_t *info);
int sys_tty_create(void); int sys_tty_create(void);
int sys_tty_read_out(int tty_id, char *buf, int len); int sys_tty_read_out(int tty_id, char *buf, int len);

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 "utf-8.h" #include "utf-8.h"
static int utf8_write_replacement(char *out) { static int utf8_write_replacement(char *out) {

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 UTF_8_H #ifndef UTF_8_H
#define UTF_8_H #define UTF_8_H

View file

@ -1,3 +1,6 @@
// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc)
// 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 UTF_8_H #ifndef UTF_8_H
#define UTF_8_H #define UTF_8_H