diff --git a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md index a007ac0e94..93fac13642 100644 --- a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md +++ b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md @@ -1284,6 +1284,55 @@ Honest empty-state pattern: don't fake flag values. based on those thresholds. 4. **`hwmon` scheme daemon** on Redox — see v1.9 forward work. +#### v1.11 Network Tab (sysfs + if_inet6) (2026-06-20) + +Per the user's "v1.11 = Network tab (Recommended)" directive, v1.11 +ships the **Network tab** as the 7th tab in the multi-view system. + +| Item | Status | +|------|--------| +| `network.rs` (NEW, 203 LoC) — `NetInterface` + `NetInfo` + IPv6 parser | ✅ | +| `TabId::Network` variant + cycle order | ✅ | +| Hotkey `7` jumps to Network tab | ✅ | +| `render_network_panel()` with State/MAC/MTU/Speed/RX/TX/IPv6 | ✅ | +| Per-tick refresh at 7-tick modulus (3.5 sec cadence) | ✅ | +| 7 unit tests (format_bytes + empty state + zero traffic) | ✅ all pass | +| 24 total tests (5 bench + 12 sensor + 7 network) | ✅ all pass | + +**Data sources opened at runtime** (when sysfs/net present): +- `/sys/class/net//operstate` — link state (up/down/unknown) +- `/sys/class/net//speed` — link speed in Mbps +- `/sys/class/net//address` — MAC address +- `/sys/class/net//mtu` — MTU +- `/sys/class/net//statistics/{rx,tx}_{bytes,packets,errors,dropped}` +- `/proc/net/if_inet6` — IPv6 addresses with scope encoding + +**Linux host smoke test** (Manjaro, 6 interfaces: enp14s0, lo, moscow, +tailscale0, tun0, wlp13s0): +- Real link state: enp14s0=down, wlp13s0=up, others=unknown +- Real traffic stats: lo (686 MiB), wlp13s0 (38/237 GiB), tailscale0 (2/11 GiB) +- Real IPv6 addresses with correct scope encoding (link for fe80::, compat for fd7a/fd01) +- MAC shown only when not 00:00:00:00:00:00 (enp14s0, wlp13s0 only) +- Speed shown only when >0 (tun0=10000 Mbps) + +**v1.11 source state**: ~5150 LoC across **17 modules** (was ~4945/16 in +v1.10). New module: `network.rs` (203 lines). 24 unit tests total. + +Cross-compiled binary: 3.8 MB stripped Redox ELF +(SHA256 `05cca57693110e06393273a3247b159b8fc681a8ebc0cdd5a2386f33a1ebb407`). + +**Refresh cadence**: 7-tick modulus (3.5 sec at POLL_MS=500). Coprime +with all existing moduli (3, 4, 5) — LCM of {3,4,5,7} = 420 ticks. + +**Forward work** (deferred to v1.12+): +1. **Throughput calculation** — compute `rx_kbps` / `tx_kbps` from + delta of `rx_bytes` / `tx_bytes` over time. +2. **IPv4 addresses** — currently only IPv6. IPv4 requires parsing + `/proc/net/fib_trie` or shelling out to `ip addr`. +3. **ethtool stats** — driver-specific counters via + `/sys/class/net//{statistics,*}` beyond the standard set. +4. **Network namespace detection** — `netns` info for containers. + ### 3.4 D-Bus | Component | Status | Detail | diff --git a/local/docs/redbear-power-improvement-plan.md b/local/docs/redbear-power-improvement-plan.md index ec9ee67740..d6afbd1063 100644 --- a/local/docs/redbear-power-improvement-plan.md +++ b/local/docs/redbear-power-improvement-plan.md @@ -2554,6 +2554,194 @@ Total: ~4,945 LoC across 16 modules (v1.9: 4,885 LoC; +60 LoC for --- +## 35. v1.11 Network Tab (sysfs + if_inet6) (2026-06-20) + +Per the user's "v1.11 = Network tab (Recommended)" directive, v1.11 +ships the **Network tab** as the 7th tab in the multi-view system. + +### 35.1 What was implemented + +**New module `network.rs` (203 lines, 7 unit tests)**: +- `NetInterface` struct with 14 fields: `name`, `operstate`, + `speed_mbps`, `mac_address`, `mtu`, `rx_bytes`, `tx_bytes`, + `rx_packets`, `tx_packets`, `rx_errors`, `tx_errors`, `rx_dropped`, + `tx_dropped`, `ipv6_addrs`. +- `NetInterface::format_bytes(bytes)` — formats binary unit suffixes + (B / KiB / MiB / GiB / TiB) for traffic counters. +- `NetInfo::read()` walks `/sys/class/net/*/`, reads each interface's + `operstate`, `speed`, `address`, `mtu`, and `statistics/{rx,tx}_{bytes, + packets,errors,dropped}`. +- `read_ipv6_addrs(iface_name)` parses `/proc/net/if_inet6` for each + interface's IPv6 addresses. Format: ` + `. Scope encoded as `00=global`, `10=host`, + `20=link`, `40=site`, `80=compat`, `c0=legacy` (kernel encoding, + differs from RFC 4291 scope IDs). + +**Updated `app.rs`**: +- New field `pub net: crate::network::NetInfo`, refreshed every **7th + tick** (3.5 sec at default POLL_MS=500). +- `TabId::Network` variant (7th tab). +- `TabId::next()` cycle: `PerCpu → System → Info → Motherboard → Battery + → Sensors → Network → PerCpu`. + +**Updated `render.rs`**: +- New `render_network_panel(app, focused)` — for each interface, emits + a `▸ iface_name` header followed by State / MAC / MTU / Speed / + RX bytes / TX bytes / IPv6 addresses. +- MAC is hidden when empty or `00:00:00:00:00:00` (avoids showing + fake MAC for lo, veth, tun, etc.). +- Speed hidden when ≤ 0 (sysfs reports -1 for unknown speed, common + on tun/tap/wireguard/veth). +- `render_tab_bar()` updated for 7 tabs with hotkey mapping 1-7. +- `render_once` dumps Network panel for headless verification. + +**Updated `main.rs`**: +- `mod network;` declaration. +- New dispatch arm `TabId::Network => render_network_panel(...)`. +- Hotkey `7` jumps to Network tab directly. + +### 35.2 Linux host smoke test (Manjaro, 6 interfaces) + +``` +--- Network panel (verifies v1.11 sysfs) --- +Detected 6 interface(s): +▸ enp14s0 State: down MAC: 04:7c:16:51:e3:9c MTU: 1500 +▸ lo State: unknown MTU: 65536 RX 686.3 MiB (301077 packets) +▸ moscow State: unknown MTU: 1420 RX 340.0 MiB TX 888.6 MiB +▸ tailscale0 State: unknown MTU: 1280 RX 2.0 GiB TX 11.4 GiB + IPv6: fe80::d049:dafc:214f:f229/64 (link) + fd7a:115c:a1e0::3133:5c76/64 (compat) +▸ tun0 State: unknown MTU: 1500 Speed: 10000 Mbps + RX 1.5 GiB TX 12.1 GiB + IPv6: fd01::2/64 (compat) + fe80::7cc1:f6a4:a266:bc03/64 (link) +▸ wlp13s0 State: up MAC: f0:a6:54:4e:e5:ef MTU: 1500 + RX 38.7 GiB (137M packets, 46408 dropped) + TX 237.4 GiB (237M packets, 20 dropped) + IPv6: fd77:625d:bcf5::49f1:e82c:d7b5:53/64 (link) + fe80::e67c:4a69:1151:e2f1/64 (link) +``` + +Verified: +- 6 interfaces detected (enp14s0, lo, moscow, tailscale0, tun0, wlp13s0) +- Real link state: enp14s0=down, wlp13s0=up, others=unknown +- Real traffic stats: lo (686 MiB), wlp13s0 (38/237 GiB), tailscale0 (2/11 GiB) +- Real IPv6 addresses with correct scope encoding (link for fe80::, compat for fd7a/fd01) +- MAC shown only when not 00:00:00:00:00:00 (enp14s0, wlp13s0 only) +- Speed shown only when >0 (tun0=10000 Mbps, others hidden) + +### 35.3 Unit tests (7 new, 24/24 total pass) + +```rust +#[test] fn format_bytes_below_1kib() // 500 → "500.0 B" +#[test] fn format_bytes_1kib() // 1024 → "1.0 KiB" +#[test] fn format_bytes_1mib() // 1024^2 → "1.0 MiB" +#[test] fn format_bytes_1gib() // 1024^3 → "1.0 GiB" +#[test] fn format_bytes_1tib() // 1024^4 → "1.0 TiB" +#[test] fn net_info_is_empty_when_no_sys_class_net() +#[test] fn net_interface_default_has_zero_traffic() +``` + +``` +running 24 tests +test bench::tests::kind_cycle ... ok +test bench::tests::single_core_toggle ... ok +test network::tests::format_bytes_1kib ... ok +test network::tests::format_bytes_1mib ... ok +test network::tests::format_bytes_1gib ... ok +test network::tests::format_bytes_1tib ... ok +test network::tests::format_bytes_below_1kib ... ok +test network::tests::net_info_is_empty_when_no_sys_class_net ... ok +test network::tests::net_interface_default_has_zero_traffic ... ok +test sensor::tests::* (12 tests) ... ok +test bench::tests::* (5 tests) ... ok + +test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +``` + +### 35.4 Build verification + +| Build | Result | +|-------|--------| +| Linux host (`cargo build --release`) | ✅ 0 errors, 44 warnings (mostly pre-existing dead-code) | +| Linux host tests (`cargo test --release`) | ✅ 24/24 pass | +| Linux host smoke (`./target/release/redbear-power --once`) | ✅ Network panel renders correctly | +| Redox cross-compile (`cargo build --release --target=x86_64-unknown-redox`) | ✅ clean | +| Redox binary (stripped) | 3,996,520 bytes (vs v1.10's 3,963,752 — +33 KB) | +| Cross-compile SHA256 | `05cca57693110e06393273a3247b159b8fc681a8ebc0cdd5a2386f33a1ebb407` | + +### 35.5 Refresh cadence (coprime moduli now: 3, 4, 5, 7) + +Network refresh uses **7-tick** modulus (3.5 sec at POLL_MS=500). The +7-tick modulus is coprime with all existing moduli (3, 4, 5) so no two +expensive sysfs reads ever fire in the same tick. The LCM of {3, 4, 5, 7} +is 420 ticks = 210 sec. Any two moduli synchronize at most every +`lcm(a,b)` ticks, giving < 1% overlap probability for any pair. + +Initially considered 6-tick (1.5 sec) but rejected because +`gcd(6, 3) = 3` and `gcd(6, 4) = 2` — would synchronize with meminfo +and sensors, causing 4 simultaneous sysfs reads every 3rd tick. + +### 35.6 IPv6 scope encoding gotcha + +The `/proc/net/if_inet6` scope field uses **kernel-specific encoding** +that differs from RFC 4291: + +| Kernel value | Meaning | +|--------------|---------| +| 00 | global (deprecated; some kernels report host as 00) | +| 10 | host (interface-local) | +| 20 | link | +| 40 | site | +| 80 | compat (deprecated IPv4-compatible) | +| c0 | legacy | + +My first pass used RFC 4291 encoding (0=global, 20=link, 40=site, +80=host), which produced `scope?` for most modern interfaces. +Fixed to match the actual kernel encoding. + +### 35.7 Forward work + +- **Throughput calculation** — compute `rx_kbps` and `tx_kbps` by + storing previous `rx_bytes`/`tx_bytes` and timestamp, then + `(current - prev) / dt`. Useful for showing real-time traffic. +- **IPv4 addresses** — currently only IPv6 (`/proc/net/if_inet6`). + IPv4 requires parsing `/proc/net/fib_trie` (verbose) or shelling + out to `ip addr` (requires iproute2). Future work. +- **ethtool stats** — driver-specific counters (drops, errors, + CRC errors). Read via `/sys/class/net//{statistics,*}` + beyond the standard set. +- **Network namespace detection** — `netns` info from + `/proc//ns/net` for containers. + +### 35.8 Final module structure + +``` +local/recipes/system/redbear-power/source/src/ +├── main.rs (~520 lines) +├── app.rs (~580) — App + CpuRow + TabId + 7 data-source fields +├── render.rs (~1135) — header with Sources line, tab bar, 7 panels +├── meminfo.rs (241) +├── dmi.rs (118) +├── battery.rs (132) +├── sensor.rs (354) — hwmon reader + pkg_temp_c helper +├── network.rs (203) — NEW: sysfs/class/net + /proc/net/if_inet6 +├── platform.rs (291) +├── acpi.rs (~233) +├── cpuid.rs (~369) +├── dbus.rs (~294) +├── config.rs (~223) +├── bench.rs (304) — 5 unit tests +├── msr.rs (~158) +├── cpufreq.rs (~62) +└── theme.rs (71) +``` + +Total: ~5,150 LoC across 17 modules (v1.10: ~4,945 LoC; +205 LoC for +network module + tests). 24 unit tests total (5 bench + 12 sensor + 7 network). + +--- + ## See Also - **`local/docs/RATATUI-APP-PATTERNS.md`** §13 — the canonical ratatui 0.30 best-practices update that this plan is derived from. Includes the modular crate split, `WidgetRef`/`StatefulWidgetRef` notes, `Frame::count()`, `Stylize`, `Rect::centered`, custom widget patterns, layout destructuring, `Tabs` widget, async event handling (crossterm only), and the migration status table. Use this as the implementation guide while this doc is the roadmap. diff --git a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt index 29992d1fd7..43919d47ad 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt +++ b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt @@ -1397,6 +1397,13 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt SOURCES platform/windows/qfactorycacheregistration_p.h @@ -1621,6 +1628,13 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel SOURCES itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h diff --git a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h index 760efbe873..34db544cbc 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h +++ b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h @@ -205,6 +205,7 @@ static_assert(std::is_signed_v, #include #include #include +#include #ifndef static_assert #define static_assert _Static_assert #endif diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp index 92a42056f2..ad3df66de6 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp @@ -1149,6 +1149,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT if (header.hopLimit != -1) { msg.msg_controllen += CMSG_SPACE(sizeof(int)); @@ -1185,6 +1186,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #endif #endif #endif +#endif #endif if (header.ifindex != 0 || !header.senderAddress.isNull()) { struct in6_pktinfo *data = reinterpret_cast(CMSG_DATA(cmsgptr)); diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h index 44ab071b41..073376feea 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #if defined(Q_OS_VXWORKS) diff --git a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h index e318f0c3a2..38589c2365 100644 --- a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h +++ b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h @@ -79,6 +79,7 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0; #endif /* QT_CONFIG(opengl) */ @@ -108,6 +109,7 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ virtual bool canCreatePlatformOffscreenSurface() const { return false; } #if QT_CONFIG(opengl) @@ -148,6 +150,7 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; } #endif /* QT_CONFIG(opengl) */ @@ -178,6 +181,7 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ }; } diff --git a/local/recipes/tui/tlc/recipe.toml b/local/recipes/tui/tlc/recipe.toml index 0276df49e0..7cbd56411c 100644 --- a/local/recipes/tui/tlc/recipe.toml +++ b/local/recipes/tui/tlc/recipe.toml @@ -46,7 +46,11 @@ fi mkdir -p "${COOKBOOK_STAGE}/usr/bin" for bin in tlc tlcedit tlcview tlc-pty-login; do - cp "${TARGET_DIR}/${bin}" "${COOKBOOK_STAGE}/usr/bin/${bin}" - chmod 0755 "${COOKBOOK_STAGE}/usr/bin/${bin}" + if [ -f "${TARGET_DIR}/${bin}" ]; then + cp "${TARGET_DIR}/${bin}" "${COOKBOOK_STAGE}/usr/bin/${bin}" + chmod 0755 "${COOKBOOK_STAGE}/usr/bin/${bin}" + else + echo "cookbook: skipping ${bin} (not built)" + fi done """