Merge branch 'BoredDevNL:main' into cmds
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -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.**
|
||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -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.**
|
||||||
|
|
|
||||||
1
.github/pull_request_template.md
vendored
|
|
@ -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
|
|
@ -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>
|
||||||
|
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|

|
||||||
|

|
||||||
|

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

|
||||||
|
|
||||||
|
> [!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,10 +182,24 @@
|
||||||
<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>
|
||||||
|
<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>
|
||||||
</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) 2023–2026 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.
|
||||||
|
|
|
||||||
127
docs/build/toolchain.md
vendored
|
|
@ -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
|
|
@ -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
|
||||||
|
|
@ -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. |
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
20
src/fs/vfs.c
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
BIN
src/images/wallpapers/The-Cat-Of-Destiny.png
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
src/images/wallpapers/Web-Of-Connectivity.png
Normal file
|
After Width: | Height: | Size: 817 KiB |
BIN
src/images/wallpapers/adrian.jpg
Normal file
|
After Width: | Height: | Size: 434 KiB |
BIN
src/images/wallpapers/flowerdark.jpg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
src/images/wallpapers/flowerlight.jpg
Normal file
|
After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 574 KiB |
BIN
src/images/wallpapers/orbital.png
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
src/images/wallpapers/squiggly.png
Normal file
|
After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 370 KiB |
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,7 +691,9 @@ 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) {
|
||||||
process_terminate(&processes[i]);
|
if (!processes[i].exited && !processes[i].kill_pending) {
|
||||||
|
process_terminate(&processes[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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++) {
|
||||||
|
|
|
||||||
|
|
@ -1416,26 +1416,61 @@ 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
|
||||||
[FS_CMD_WRITE] = fs_cmd_write, // 3
|
[FS_CMD_WRITE] = fs_cmd_write, // 3
|
||||||
[FS_CMD_CLOSE] = fs_cmd_close, // 4
|
[FS_CMD_CLOSE] = fs_cmd_close, // 4
|
||||||
[FS_CMD_SEEK] = fs_cmd_seek, // 5
|
[FS_CMD_SEEK] = fs_cmd_seek, // 5
|
||||||
[FS_CMD_TELL] = fs_cmd_tell, // 6
|
[FS_CMD_TELL] = fs_cmd_tell, // 6
|
||||||
[FS_CMD_LIST] = fs_cmd_list, // 7
|
[FS_CMD_LIST] = fs_cmd_list, // 7
|
||||||
[FS_CMD_DELETE] = fs_cmd_delete, // 8
|
[FS_CMD_DELETE] = fs_cmd_delete, // 8
|
||||||
[FS_CMD_SIZE] = fs_cmd_size, // 9
|
[FS_CMD_SIZE] = fs_cmd_size, // 9
|
||||||
[FS_CMD_MKDIR] = fs_cmd_mkdir, // 10
|
[FS_CMD_MKDIR] = fs_cmd_mkdir, // 10
|
||||||
[FS_CMD_EXISTS] = fs_cmd_exists, // 11
|
[FS_CMD_EXISTS] = fs_cmd_exists, // 11
|
||||||
[FS_CMD_GETCWD] = fs_cmd_getcwd, // 12
|
[FS_CMD_GETCWD] = fs_cmd_getcwd, // 12
|
||||||
[FS_CMD_CHDIR] = fs_cmd_chdir, // 13
|
[FS_CMD_CHDIR] = fs_cmd_chdir, // 13
|
||||||
[FS_CMD_GET_INFO] = fs_cmd_get_info, // 14
|
[FS_CMD_GET_INFO] = fs_cmd_get_info, // 14
|
||||||
[FS_CMD_DUP] = fs_cmd_dup, // 15
|
[FS_CMD_DUP] = fs_cmd_dup, // 15
|
||||||
[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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,89 +485,262 @@ 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;
|
||||||
|
|
||||||
char cache_path[256];
|
wp->valid = 0;
|
||||||
int cp = 0;
|
dst_idx++;
|
||||||
char *cpref = "/Library/Caches/Thumbnails/";
|
|
||||||
while (cpref[cp]) { cache_path[cp] = cpref[cp]; cp++; }
|
|
||||||
int cn = 0;
|
|
||||||
while (info[i].name[cn]) { cache_path[cp+cn] = info[i].name[cn]; cn++; }
|
|
||||||
char *csuf = ".bin";
|
|
||||||
int cs = 0;
|
|
||||||
while (csuf[cs]) { cache_path[cp+cn+cs] = csuf[cs]; cs++; }
|
|
||||||
cache_path[cp+cn+cs] = 0;
|
|
||||||
|
|
||||||
int cfd = sys_open(cache_path, "r");
|
|
||||||
if (cfd >= 0) {
|
|
||||||
sys_read(cfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
|
|
||||||
sys_close(cfd);
|
|
||||||
wp->valid = 1;
|
|
||||||
} else {
|
|
||||||
int fd = sys_open(wp->path, "r");
|
|
||||||
if (fd >= 0) {
|
|
||||||
int size = sys_seek(fd, 0, 2); // SEEK_END
|
|
||||||
sys_seek(fd, 0, 0); // SEEK_SET
|
|
||||||
if (size > 0 && size < 8 * 1024 * 1024) {
|
|
||||||
unsigned char *buf = (unsigned char *)malloc(size);
|
|
||||||
if (buf) {
|
|
||||||
sys_read(fd, buf, size);
|
|
||||||
int img_w, img_h, channels;
|
|
||||||
unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4);
|
|
||||||
if (img && img_w > 0 && img_h > 0) {
|
|
||||||
scale_rgba_to_argb(img, img_w, img_h, wp->thumb, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H);
|
|
||||||
wp->valid = 1;
|
|
||||||
stbi_image_free(img);
|
|
||||||
|
|
||||||
sys_mkdir("/Library/Caches");
|
|
||||||
sys_mkdir("/Library/Caches/Thumbnails");
|
|
||||||
int swfd = sys_open(cache_path, "w");
|
|
||||||
if (swfd >= 0) {
|
|
||||||
sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
|
|
||||||
sys_close(swfd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sys_close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wallpaper_count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_wallpapers(void) {
|
static void on_wallpaper_scroll(void *user_data, int new_scroll_y) {
|
||||||
void *job_args[1] = { NULL };
|
(void)user_data;
|
||||||
sys_parallel_run(decode_wallpapers_task, job_args, 1);
|
wallpaper_scroll_y = new_scroll_y;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < wallpaper_count; i++) {
|
static int wallpaper_scroll_region_y(void) {
|
||||||
int tx = (i % 3) * (WALLPAPER_THUMB_W + 15);
|
return 46 - wallpaper_scroll_y;
|
||||||
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, "");
|
|
||||||
|
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];
|
||||||
|
int cp = 0;
|
||||||
|
const char *cpref = "/Library/Caches/Thumbnails/";
|
||||||
|
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;
|
||||||
|
while (wp->path[last_slash + cn]) { cache_path[cp+cn] = wp->path[last_slash + cn]; cn++; }
|
||||||
|
const char *csuf = ".bin";
|
||||||
|
int cs = 0;
|
||||||
|
while (csuf[cs]) { cache_path[cp+cn+cs] = csuf[cs]; cs++; }
|
||||||
|
cache_path[cp+cn+cs] = 0;
|
||||||
|
|
||||||
|
int cfd = sys_open(cache_path, "r");
|
||||||
|
if (cfd >= 0) {
|
||||||
|
sys_read(cfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
|
||||||
|
sys_close(cfd);
|
||||||
|
wp->valid = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wallpaper_allow_decode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = sys_open(wp->path, "r");
|
||||||
|
if (fd >= 0) {
|
||||||
|
int size = sys_seek(fd, 0, 2);
|
||||||
|
sys_seek(fd, 0, 0);
|
||||||
|
if (size > 0 && size < 8 * 1024 * 1024) {
|
||||||
|
unsigned char *buf = (unsigned char *)malloc(size);
|
||||||
|
if (buf) {
|
||||||
|
if (sys_read(fd, buf, size) > 0) {
|
||||||
|
int img_w, img_h, channels;
|
||||||
|
unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4);
|
||||||
|
if (img && img_w > 0 && img_h > 0) {
|
||||||
|
scale_rgba_to_argb(img, img_w, img_h, wp->thumb, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H);
|
||||||
|
wp->valid = 1;
|
||||||
|
stbi_image_free(img);
|
||||||
|
|
||||||
|
int swfd = sys_open(cache_path, "w");
|
||||||
|
if (swfd >= 0) {
|
||||||
|
sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4);
|
||||||
|
sys_close(swfd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (wallpaper_load_state == WALLPAPER_LOADING_ACTIVE ||
|
||||||
|
wallpaper_load_state == WALLPAPER_LOADING_DONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,46 +819,63 @@ 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;
|
||||||
// Colors
|
|
||||||
|
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;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
widget_button_draw(&settings_ctx, &btn_wp_colors[1]);
|
widget_button_draw(&settings_ctx, &btn_wp_colors[1]);
|
||||||
ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_TEAL);
|
ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_TEAL);
|
||||||
ui_draw_string(win, button_x + 135, button_y + 8, "Teal", COLOR_DARK_TEXT);
|
ui_draw_string(win, button_x + 135, button_y + 8, "Teal", COLOR_DARK_TEXT);
|
||||||
|
|
||||||
widget_button_draw(&settings_ctx, &btn_wp_colors[2]);
|
widget_button_draw(&settings_ctx, &btn_wp_colors[2]);
|
||||||
ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREEN);
|
ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREEN);
|
||||||
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);
|
||||||
|
|
||||||
widget_button_draw(&settings_ctx, &btn_wp_colors[4]);
|
widget_button_draw(&settings_ctx, &btn_wp_colors[4]);
|
||||||
ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_PURPLE);
|
ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_PURPLE);
|
||||||
ui_draw_string(win, button_x + 132, button_y + 8, "Purple", COLOR_DARK_TEXT);
|
ui_draw_string(win, button_x + 132, button_y + 8, "Purple", COLOR_DARK_TEXT);
|
||||||
|
|
||||||
widget_button_draw(&settings_ctx, &btn_wp_colors[5]);
|
widget_button_draw(&settings_ctx, &btn_wp_colors[5]);
|
||||||
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++) {
|
||||||
|
|
@ -647,7 +887,7 @@ static void control_panel_paint_wallpaper(ui_window_t win) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui_draw_string(win, button_x + 28, button_y + 8, "Lumberjack", COLOR_DARK_TEXT);
|
ui_draw_string(win, button_x + 28, button_y + 8, "Lumberjack", COLOR_DARK_TEXT);
|
||||||
|
|
||||||
widget_button_draw(&settings_ctx, &btn_wp_patterns[1]);
|
widget_button_draw(&settings_ctx, &btn_wp_patterns[1]);
|
||||||
for (int py = 0; py < 8; py++) {
|
for (int py = 0; py < 8; py++) {
|
||||||
for (int px = 0; px < 10; px++) {
|
for (int px = 0; px < 10; px++) {
|
||||||
|
|
@ -660,42 +900,63 @@ 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);
|
||||||
|
|
||||||
ui_draw_string(win, button_x + 90, button_y + 4, "G:", COLOR_DARK_TEXT);
|
ui_draw_string(win, button_x + 90, button_y + 4, "G:", COLOR_DARK_TEXT);
|
||||||
tb_g.focused = (focused_field == 1);
|
tb_g.focused = (focused_field == 1);
|
||||||
widget_textbox_draw(&settings_ctx, &tb_g);
|
widget_textbox_draw(&settings_ctx, &tb_g);
|
||||||
|
|
||||||
ui_draw_string(win, button_x + 180, button_y + 4, "B:", COLOR_DARK_TEXT);
|
ui_draw_string(win, button_x + 180, button_y + 4, "B:", COLOR_DARK_TEXT);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -1126,7 +1436,7 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
|
||||||
if (widget_textbox_handle_mouse(&settings_ctx, &tb_b, x, y, is_click, NULL)) {
|
if (widget_textbox_handle_mouse(&settings_ctx, &tb_b, x, y, is_click, NULL)) {
|
||||||
focused_field = 2; input_cursor = tb_b.cursor_pos; return;
|
focused_field = 2; input_cursor = tb_b.cursor_pos; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<6; i++) {
|
for (int i=0; i<6; i++) {
|
||||||
if (widget_button_handle_mouse(&btn_wp_colors[i], x, y, is_down, is_click, NULL)) {
|
if (widget_button_handle_mouse(&btn_wp_colors[i], x, y, is_down, is_click, NULL)) {
|
||||||
if (is_click) {
|
if (is_click) {
|
||||||
|
|
@ -1152,9 +1462,17 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i=0; i<wallpaper_count; i++) {
|
if (widget_scrollbar_handle_mouse(&wallpaper_scrollbar, x, y, is_down, NULL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||