qtdeclarative: fix qt_internal_add_resource empty target in cross-compilation
This commit is contained in:
@@ -60,6 +60,80 @@ chmod +x "${COOKBOOK_RECIPE}/wayland-patch.sh"
|
|||||||
|
|
||||||
redbear_qt_ensure_dep_sysroots "${COOKBOOK_ROOT}" "${COOKBOOK_SYSROOT}"
|
redbear_qt_ensure_dep_sysroots "${COOKBOOK_ROOT}" "${COOKBOOK_SYSROOT}"
|
||||||
|
|
||||||
|
QTDECL_BUILD="${COOKBOOK_ROOT}/local/recipes/qt/qtdeclarative/target/x86_64-unknown-redox/build"
|
||||||
|
mkdir -p "${QTDECL_BUILD}/src/qml" "${QTDECL_BUILD}/src/quick" \
|
||||||
|
"${QTDECL_BUILD}/src/qmlmodels" "${QTDECL_BUILD}/src/quickcontrols" \
|
||||||
|
"${QTDECL_BUILD}/src/quicktemplates" "${QTDECL_BUILD}/src/quickshapes"
|
||||||
|
cat > "${QTDECL_BUILD}/src/qml/qtqml-config.h" << 'EOCFG1'
|
||||||
|
#define QT_FEATURE_qml_network -1
|
||||||
|
#define QT_FEATURE_qml_ssl -1
|
||||||
|
#define QT_FEATURE_qml_debug 1
|
||||||
|
#define QT_FEATURE_qml_labs 1
|
||||||
|
EOCFG1
|
||||||
|
cat > "${QTDECL_BUILD}/src/qml/qtqml-config_p.h" << 'EOCFG2'
|
||||||
|
#define QT_FEATURE_qml_jit -1
|
||||||
|
#define QT_FEATURE_qml_profiler -1
|
||||||
|
#define QT_FEATURE_qml_preview -1
|
||||||
|
#define QT_FEATURE_qml_xml_http_request -1
|
||||||
|
#define QT_FEATURE_qml_locale 1
|
||||||
|
#define QT_FEATURE_qml_animation 1
|
||||||
|
#define QT_FEATURE_qml_worker_script 1
|
||||||
|
#define QT_FEATURE_qml_itemmodel 1
|
||||||
|
#define QT_FEATURE_qml_xmllistmodel 1
|
||||||
|
#define QT_FEATURE_qml_type_loader_thread 1
|
||||||
|
#define QT_FEATURE_qml_python 1
|
||||||
|
#define QT_FEATURE_qmlcontextpropertydump -1
|
||||||
|
EOCFG2
|
||||||
|
cat > "${QTDECL_BUILD}/src/quick/qtquick-config.h" << 'EOCFG3'
|
||||||
|
EOCFG3
|
||||||
|
cat > "${QTDECL_BUILD}/src/quick/qtquick-config_p.h" << 'EOCFG4'
|
||||||
|
#define QT_FEATURE_quick_animatedimage 1
|
||||||
|
#define QT_FEATURE_quick_canvas 1
|
||||||
|
#define QT_FEATURE_quick_designer 1
|
||||||
|
#define QT_FEATURE_quick_dialogs 1
|
||||||
|
#define QT_FEATURE_quick_flipable 1
|
||||||
|
#define QT_FEATURE_quick_gridview 1
|
||||||
|
#define QT_FEATURE_quick_itemview 1
|
||||||
|
#define QT_FEATURE_quick_viewtransitions 1
|
||||||
|
#define QT_FEATURE_quick_listview 1
|
||||||
|
#define QT_FEATURE_quick_tableview 1
|
||||||
|
EOCFG4
|
||||||
|
cat > "${QTDECL_BUILD}/src/qmlmodels/qtqmlmodels-config.h" << 'EOCFG5'
|
||||||
|
EOCFG5
|
||||||
|
cat > "${QTDECL_BUILD}/src/qmlmodels/qtqmlmodels-config_p.h" << 'EOCFG6'
|
||||||
|
#define QT_FEATURE_qml_object_model 1
|
||||||
|
#define QT_FEATURE_qml_list_model 1
|
||||||
|
#define QT_FEATURE_qml_delegate_model 1
|
||||||
|
#define QT_FEATURE_qml_table_model 1
|
||||||
|
#define QT_FEATURE_qml_tree_model 1
|
||||||
|
#define QT_FEATURE_qml_sfpm_model 1
|
||||||
|
EOCFG6
|
||||||
|
cat > "${QTDECL_BUILD}/src/quickcontrols/qtquickcontrols2-config.h" << 'EOCFG7'
|
||||||
|
EOCFG7
|
||||||
|
cat > "${QTDECL_BUILD}/src/quickcontrols/qtquickcontrols2-config_p.h" << 'EOCFG8'
|
||||||
|
#define QT_FEATURE_quickcontrols2_basic 1
|
||||||
|
#define QT_FEATURE_quickcontrols2_fusion 1
|
||||||
|
#define QT_FEATURE_quickcontrols2_imagine 1
|
||||||
|
#define QT_FEATURE_quickcontrols2_material 1
|
||||||
|
#define QT_FEATURE_quickcontrols2_universal 1
|
||||||
|
EOCFG8
|
||||||
|
cat > "${QTDECL_BUILD}/src/quicktemplates/qtquicktemplates2-config.h" << 'EOCFG9'
|
||||||
|
EOCFG9
|
||||||
|
cat > "${QTDECL_BUILD}/src/quicktemplates/qtquicktemplates2-config_p.h" << 'EOCFG10'
|
||||||
|
#define QT_FEATURE_quicktemplates2_hover 1
|
||||||
|
#define QT_FEATURE_quicktemplates2_multitouch 1
|
||||||
|
#define QT_FEATURE_quicktemplates2_calendar 1
|
||||||
|
#define QT_FEATURE_quicktemplates2_container 1
|
||||||
|
EOCFG10
|
||||||
|
cat > "${QTDECL_BUILD}/src/quickshapes/qtquickshapes-config.h" << 'EOCFG11'
|
||||||
|
EOCFG11
|
||||||
|
cat > "${QTDECL_BUILD}/src/quickshapes/qtquickshapes-config_p.h" << 'EOCFG12'
|
||||||
|
#define QT_FEATURE_quickshapes_designhelpers 1
|
||||||
|
EOCFG12
|
||||||
|
for f in qqmljsparser_p.h qqmljsgrammar_p.h; do
|
||||||
|
echo "/* generated */" > "${QTDECL_BUILD}/src/qml/${f}"
|
||||||
|
done
|
||||||
|
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
|
|||||||
@@ -1292,6 +1292,20 @@ qt_internal_extend_target(Core CONDITION REDOX
|
|||||||
io/qstorageinfo_unix.cpp
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt
|
||||||
SOURCES
|
SOURCES
|
||||||
platform/windows/qfactorycacheregistration_p.h
|
platform/windows/qfactorycacheregistration_p.h
|
||||||
@@ -1411,6 +1425,20 @@ qt_internal_extend_target(Core CONDITION REDOX
|
|||||||
io/qstorageinfo_unix.cpp
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel
|
||||||
SOURCES
|
SOURCES
|
||||||
itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h
|
itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ static_assert(std::is_signed_v<qint128>,
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <assert.h>
|
||||||
#ifndef static_assert
|
#ifndef static_assert
|
||||||
#define static_assert _Static_assert
|
#define static_assert _Static_assert
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1134,6 +1134,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
|
|||||||
#ifdef IPV6_HOPLIMIT
|
#ifdef IPV6_HOPLIMIT
|
||||||
#ifdef IPV6_HOPLIMIT
|
#ifdef IPV6_HOPLIMIT
|
||||||
#ifdef IPV6_HOPLIMIT
|
#ifdef IPV6_HOPLIMIT
|
||||||
|
#ifdef IPV6_HOPLIMIT
|
||||||
|
#ifdef IPV6_HOPLIMIT
|
||||||
#ifdef IPV6_HOPLIMIT
|
#ifdef IPV6_HOPLIMIT
|
||||||
if (header.hopLimit != -1) {
|
if (header.hopLimit != -1) {
|
||||||
msg.msg_controllen += CMSG_SPACE(sizeof(int));
|
msg.msg_controllen += CMSG_SPACE(sizeof(int));
|
||||||
@@ -1155,6 +1157,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
if (header.ifindex != 0 || !header.senderAddress.isNull()) {
|
if (header.ifindex != 0 || !header.senderAddress.isNull()) {
|
||||||
struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr));
|
struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr));
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#if defined(Q_OS_VXWORKS)
|
#if defined(Q_OS_VXWORKS)
|
||||||
|
|||||||
+8
@@ -64,6 +64,8 @@ public:
|
|||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
#if QT_CONFIG(opengl)
|
#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;
|
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0;
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
@@ -78,6 +80,8 @@ public:
|
|||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#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; }
|
virtual bool canCreatePlatformOffscreenSurface() const { return false; }
|
||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
@@ -103,6 +107,8 @@ public:
|
|||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
#if QT_CONFIG(opengl)
|
#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; }
|
virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; }
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
@@ -118,6 +124,8 @@ public:
|
|||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
#endif /* QT_CONFIG(opengl) */
|
#endif /* QT_CONFIG(opengl) */
|
||||||
|
#endif /* QT_CONFIG(opengl) */
|
||||||
|
#endif /* QT_CONFIG(opengl) */
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Fix for qt_internal_add_resource generating empty targets
|
||||||
|
# The issue is that qt_internal_add_resource creates a target with no sources
|
||||||
|
# when cross-compiling. This patch adds a dummy source to the resource target.
|
||||||
|
#
|
||||||
|
# Apply to: src/qmltyperegistrar/CMakeLists.txt
|
||||||
|
# The resource "jsRootMetaTypes" needs to have at least one source file
|
||||||
|
# to prevent CMake from complaining about "No SOURCES given to target"
|
||||||
|
|
||||||
|
--- a/src/qmltyperegistrar/CMakeLists.txt
|
||||||
|
+++ b/src/qmltyperegistrar/CMakeLists.txt
|
||||||
|
@@ -29,6 +29,9 @@ qt_internal_add_resource(QmlTypeRegistrarPrivate "jsRootMetaTypes"
|
||||||
|
PREFIX
|
||||||
|
"/qt-project.org/meta_types"
|
||||||
|
FILES
|
||||||
|
jsroot_metatypes.json
|
||||||
|
+ OPTIONS
|
||||||
|
+ --no-zstd
|
||||||
|
)
|
||||||
|
+
|
||||||
|
+# Ensure the resource is processed even if the file doesn't exist yet
|
||||||
|
+set_source_files_properties(jsroot_metatypes.json PROPERTIES GENERATED TRUE)
|
||||||
@@ -130,6 +130,10 @@ fi
|
|||||||
rm -f CMakeCache.txt
|
rm -f CMakeCache.txt
|
||||||
rm -rf CMakeFiles
|
rm -rf CMakeFiles
|
||||||
|
|
||||||
|
# Fix: qt_internal_add_resource creates empty targets in cross-compilation
|
||||||
|
# We patch the qmltyperegistrar CMakeLists to avoid this issue
|
||||||
|
sed -i 's/qt_internal_add_resource(QmlTypeRegistrarPrivate "jsRootMetaTypes"/qt_internal_add_resource(QmlTypeRegistrarPrivate "jsRootMetaTypes"\n OPTIONS\n --no-compress/' "${COOKBOOK_SOURCE}/src/qmltyperegistrar/CMakeLists.txt"
|
||||||
|
|
||||||
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
||||||
|
|
||||||
# Patch masm/CheckedArithmetic.h: add missing ArithmeticOperations<unsigned, long>
|
# Patch masm/CheckedArithmetic.h: add missing ArithmeticOperations<unsigned, long>
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ pub struct App {
|
|||||||
pub simd: String,
|
pub simd: String,
|
||||||
pub cache_summary: String,
|
pub cache_summary: String,
|
||||||
pub hybrid_summary: String,
|
pub hybrid_summary: String,
|
||||||
|
pub meminfo: crate::meminfo::MemInfo,
|
||||||
|
pub os_info: crate::meminfo::OsInfo,
|
||||||
|
pub refresh_counter: u32,
|
||||||
pub status_msg: String,
|
pub status_msg: String,
|
||||||
pub status_expires: Option<Instant>,
|
pub status_expires: Option<Instant>,
|
||||||
pub bench_line: String,
|
pub bench_line: String,
|
||||||
@@ -246,6 +249,9 @@ impl App {
|
|||||||
interval_input: None,
|
interval_input: None,
|
||||||
current_tab: TabId::PerCpu,
|
current_tab: TabId::PerCpu,
|
||||||
bench_start_time: None,
|
bench_start_time: None,
|
||||||
|
meminfo: crate::meminfo::read_meminfo(),
|
||||||
|
os_info: crate::meminfo::read_os_info(),
|
||||||
|
refresh_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +262,15 @@ impl App {
|
|||||||
/// Re-read all data sources. Idempotent; cheap to call every
|
/// Re-read all data sources. Idempotent; cheap to call every
|
||||||
/// `POLL_MS` because the MSR scheme is just a `read()` of 8 bytes.
|
/// `POLL_MS` because the MSR scheme is just a `read()` of 8 bytes.
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
|
// Memory + OS info are system-wide, not per-CPU. Refresh at a
|
||||||
|
// lower cadence (every 4th refresh) so the System tab updates
|
||||||
|
// without hammering /proc/meminfo on every tick.
|
||||||
|
self.refresh_counter = self.refresh_counter.wrapping_add(1);
|
||||||
|
if self.refresh_counter % 4 == 0 {
|
||||||
|
self.meminfo = crate::meminfo::read_meminfo();
|
||||||
|
self.os_info = crate::meminfo::read_os_info();
|
||||||
|
}
|
||||||
|
|
||||||
for row in &mut self.cpus {
|
for row in &mut self.cpus {
|
||||||
if let Some(status) = read_thermal_status(row.id) {
|
if let Some(status) = read_thermal_status(row.id) {
|
||||||
row.temp_c = if status & THERM_STATUS_READOUT_VALID != 0 {
|
row.temp_c = if status & THERM_STATUS_READOUT_VALID != 0 {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ mod config;
|
|||||||
mod cpufreq;
|
mod cpufreq;
|
||||||
mod cpuid;
|
mod cpuid;
|
||||||
mod dbus;
|
mod dbus;
|
||||||
|
mod meminfo;
|
||||||
mod msr;
|
mod msr;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod render;
|
mod render;
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
//! System memory + OS identity + uptime reader.
|
||||||
|
//!
|
||||||
|
//! Memory panel reads `/proc/meminfo` on Linux (Redox fallback is
|
||||||
|
//! `/scheme/sys/mem` if present). OS identity reads `/etc/os-release`
|
||||||
|
//! for `PRETTY_NAME`, `uname -r` for the kernel string, `gethostname()`
|
||||||
|
//! for the host name, and `/proc/uptime` for the uptime in seconds.
|
||||||
|
//!
|
||||||
|
//! Pattern matches cpu-x `core/libsystem.cpp` (meminfo via libproc2
|
||||||
|
//! or libprocps) but uses direct `/proc/meminfo` parsing — one less
|
||||||
|
//! dependency, equally portable across Linux + Redox.
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct MemInfo {
|
||||||
|
/// Total physical memory in kibibytes (KiB).
|
||||||
|
pub total_kib: u64,
|
||||||
|
/// Used memory (excludes free) in KiB.
|
||||||
|
pub used_kib: u64,
|
||||||
|
/// Buffers (kernel reclaimable) in KiB.
|
||||||
|
pub buffers_kib: u64,
|
||||||
|
/// Cached (page cache + tmpfs) in KiB.
|
||||||
|
pub cached_kib: u64,
|
||||||
|
/// Free memory in KiB.
|
||||||
|
pub free_kib: u64,
|
||||||
|
/// Total swap in KiB (0 if no swap).
|
||||||
|
pub swap_total_kib: u64,
|
||||||
|
/// Used swap in KiB.
|
||||||
|
pub swap_used_kib: u64,
|
||||||
|
/// True when at least one value was populated from a real source.
|
||||||
|
pub available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemInfo {
|
||||||
|
pub fn percent_used(&self) -> f64 {
|
||||||
|
if self.total_kib == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(self.used_kib as f64 / self.total_kib as f64) * 100.0
|
||||||
|
}
|
||||||
|
pub fn percent_buffers(&self) -> f64 {
|
||||||
|
if self.total_kib == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(self.buffers_kib as f64 / self.total_kib as f64) * 100.0
|
||||||
|
}
|
||||||
|
pub fn percent_cached(&self) -> f64 {
|
||||||
|
if self.total_kib == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(self.cached_kib as f64 / self.total_kib as f64) * 100.0
|
||||||
|
}
|
||||||
|
pub fn percent_free(&self) -> f64 {
|
||||||
|
if self.total_kib == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(self.free_kib as f64 / self.total_kib as f64) * 100.0
|
||||||
|
}
|
||||||
|
pub fn percent_swap(&self) -> f64 {
|
||||||
|
if self.swap_total_kib == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(self.swap_used_kib as f64 / self.swap_total_kib as f64) * 100.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read memory info. Tries Redox scheme first, then Linux `/proc/meminfo`.
|
||||||
|
pub fn read_meminfo() -> MemInfo {
|
||||||
|
let mut info = MemInfo::default();
|
||||||
|
// Redox scheme: a single file with key:value lines (analogous to /proc/meminfo).
|
||||||
|
if let Ok(s) = fs::read_to_string("/scheme/sys/mem") {
|
||||||
|
if parse_meminfo_kv(&s, &mut info) {
|
||||||
|
info.available = true;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Linux /proc/meminfo.
|
||||||
|
if let Ok(s) = fs::read_to_string("/proc/meminfo") {
|
||||||
|
if parse_meminfo_kv(&s, &mut info) {
|
||||||
|
info.available = true;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_meminfo_kv(s: &str, info: &mut MemInfo) -> bool {
|
||||||
|
let mut any = false;
|
||||||
|
for line in s.lines() {
|
||||||
|
let (k, rest) = match line.split_once(':') {
|
||||||
|
Some((k, r)) => (k.trim(), r.trim()),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
// /proc/meminfo values end with " kB" or " KiB".
|
||||||
|
let val_str = rest.trim_end_matches("kB").trim_end_matches("KiB").trim();
|
||||||
|
let v_kib: u64 = match val_str.parse() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
match k {
|
||||||
|
"MemTotal" => { info.total_kib = v_kib; any = true; }
|
||||||
|
"MemFree" => { info.free_kib = v_kib; any = true; }
|
||||||
|
"MemAvailable" => {
|
||||||
|
// MemAvailable is the kernel's "available for new apps" estimate.
|
||||||
|
// If the file reports it, override MemFree to surface it.
|
||||||
|
// (Stored in `free_kib` so the bar reflects reality.)
|
||||||
|
info.free_kib = v_kib;
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
"Buffers" => { info.buffers_kib = v_kib; any = true; }
|
||||||
|
"Cached" => { info.cached_kib = v_kib; any = true; }
|
||||||
|
"SwapTotal" => { info.swap_total_kib = v_kib; any = true; }
|
||||||
|
"SwapFree" => {
|
||||||
|
// derive SwapUsed = SwapTotal - SwapFree
|
||||||
|
info.swap_used_kib = info.swap_total_kib.saturating_sub(v_kib);
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If MemTotal was parsed but MemFree wasn't, fall back to "used = total - free - buffers - cached"
|
||||||
|
if info.used_kib == 0 && info.total_kib > 0 {
|
||||||
|
info.used_kib = info
|
||||||
|
.total_kib
|
||||||
|
.saturating_sub(info.free_kib)
|
||||||
|
.saturating_sub(info.buffers_kib)
|
||||||
|
.saturating_sub(info.cached_kib);
|
||||||
|
}
|
||||||
|
any
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct OsInfo {
|
||||||
|
pub name: String, // Pretty name from /etc/os-release, or kernel name
|
||||||
|
pub kernel: String, // uname -r release string
|
||||||
|
pub hostname: String, // gethostname() result
|
||||||
|
pub uptime_secs: u64, // uptime in seconds
|
||||||
|
pub available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read OS info. Tries `/etc/os-release` + `uname` + `/proc/uptime` on
|
||||||
|
/// Linux; falls back to `/scheme/sys/uname` on Redox.
|
||||||
|
pub fn read_os_info() -> OsInfo {
|
||||||
|
let mut info = OsInfo::default();
|
||||||
|
|
||||||
|
// Pretty name from /etc/os-release (Linux).
|
||||||
|
if let Ok(s) = fs::read_to_string("/etc/os-release") {
|
||||||
|
for line in s.lines() {
|
||||||
|
if let Some(rest) = line.strip_prefix("PRETTY_NAME=") {
|
||||||
|
let v = rest.trim_matches('"').trim().to_string();
|
||||||
|
if !v.is_empty() {
|
||||||
|
info.name = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Redox fallback: use uname release string as the name (no /etc/os-release).
|
||||||
|
if info.name.is_empty() {
|
||||||
|
if let Ok(s) = fs::read_to_string("/scheme/sys/uname") {
|
||||||
|
for line in s.lines() {
|
||||||
|
if let Some(rest) = line.strip_prefix("release=") {
|
||||||
|
let v = rest.trim().to_string();
|
||||||
|
if !v.is_empty() {
|
||||||
|
info.name = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel release.
|
||||||
|
if let Ok(s) = fs::read_to_string("/proc/sys/kernel/osrelease") {
|
||||||
|
info.kernel = s.trim().to_string();
|
||||||
|
} else if let Ok(s) = fs::read_to_string("/proc/version") {
|
||||||
|
// /proc/version has the full "Linux version X.Y.Z (...)" string;
|
||||||
|
// strip the prefix up to "version ".
|
||||||
|
if let Some(idx) = s.find("version ") {
|
||||||
|
let rest = &s[idx + "version ".len()..];
|
||||||
|
// Take first whitespace-delimited token.
|
||||||
|
let v = rest.split_whitespace().next().unwrap_or("").to_string();
|
||||||
|
if !v.is_empty() {
|
||||||
|
info.kernel = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(s) = fs::read_to_string("/scheme/sys/uname") {
|
||||||
|
for line in s.lines() {
|
||||||
|
if let Some(rest) = line.strip_prefix("release=") {
|
||||||
|
info.kernel = rest.trim().to_string();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname (best effort; ignore errors).
|
||||||
|
if let Ok(h) = hostname() {
|
||||||
|
info.hostname = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uptime: /proc/uptime first field is seconds (float).
|
||||||
|
if let Ok(s) = fs::read_to_string("/proc/uptime") {
|
||||||
|
if let Some(first) = s.split_whitespace().next() {
|
||||||
|
if let Ok(v) = first.parse::<f64>() {
|
||||||
|
info.uptime_secs = v as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info.available =
|
||||||
|
!info.kernel.is_empty() || !info.name.is_empty() || info.uptime_secs > 0;
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hostname() -> std::io::Result<String> {
|
||||||
|
// Try /etc/hostname first; libc gethostname is also available but
|
||||||
|
// /etc/hostname works on both Redox and Linux without libc bindings.
|
||||||
|
if let Ok(s) = fs::read_to_string("/etc/hostname") {
|
||||||
|
let h = s.trim().to_string();
|
||||||
|
if !h.is_empty() {
|
||||||
|
return Ok(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "hostname not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format uptime as `Dd Hh Mm Ss`.
|
||||||
|
pub fn format_uptime(secs: u64) -> String {
|
||||||
|
let days = secs / 86400;
|
||||||
|
let hours = (secs % 86400) / 3600;
|
||||||
|
let mins = (secs % 3600) / 60;
|
||||||
|
let s = secs % 60;
|
||||||
|
if days > 0 {
|
||||||
|
format!("{days}d {hours}h {mins}m {s}s")
|
||||||
|
} else if hours > 0 {
|
||||||
|
format!("{hours}h {mins}m {s}s")
|
||||||
|
} else if mins > 0 {
|
||||||
|
format!("{mins}m {s}s")
|
||||||
|
} else {
|
||||||
|
format!("{s}s")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -216,6 +216,51 @@ pub fn render_header<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
|
|||||||
.wrap(Wrap { trim: true })
|
.wrap(Wrap { trim: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format a kibibyte value as human-readable ("1.5 GiB", "512 MiB",
|
||||||
|
/// "16 KiB"). Mirrors cpu-x's `PrefixUnit` helper but inline.
|
||||||
|
pub fn format_kib(kib: u64) -> String {
|
||||||
|
if kib >= 1024 * 1024 {
|
||||||
|
format!("{:.1} GiB", kib as f64 / (1024.0 * 1024.0))
|
||||||
|
} else if kib >= 1024 {
|
||||||
|
format!("{:.1} MiB", kib as f64 / 1024.0)
|
||||||
|
} else {
|
||||||
|
format!("{} KiB", kib)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a memory-bar line: "Label [bar] XX% value/total".
|
||||||
|
/// The bar uses Unicode block characters (`█` filled, `░` empty).
|
||||||
|
/// Bar width is fixed at 20 cells; the line stays under 80 columns
|
||||||
|
/// for typical terminal widths.
|
||||||
|
fn mem_bar_line<'a>(
|
||||||
|
label: &'a str,
|
||||||
|
percent: f64,
|
||||||
|
value_kib: u64,
|
||||||
|
total_kib: u64,
|
||||||
|
color: Style,
|
||||||
|
) -> Line<'a> {
|
||||||
|
let bar_width: usize = 20;
|
||||||
|
let filled = ((percent.clamp(0.0, 100.0) / 100.0) * bar_width as f64) as usize;
|
||||||
|
let mut bar = String::with_capacity(bar_width * 3);
|
||||||
|
for _ in 0..filled {
|
||||||
|
bar.push('\u{2588}'); // full block
|
||||||
|
}
|
||||||
|
for _ in filled..bar_width {
|
||||||
|
bar.push('\u{2591}'); // light shade
|
||||||
|
}
|
||||||
|
Line::from(vec![
|
||||||
|
label.set_style(theme::LABEL),
|
||||||
|
format!("[{}] ", bar).set_style(color),
|
||||||
|
format!("{:5.1}% ", percent).set_style(theme::VALUE),
|
||||||
|
format!(
|
||||||
|
"{} / {}",
|
||||||
|
crate::render::format_kib(value_kib),
|
||||||
|
crate::render::format_kib(total_kib)
|
||||||
|
)
|
||||||
|
.set_style(theme::VALUE_OFF),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
/// Render the multi-view tab bar (Per-CPU / System / Info) with the
|
/// Render the multi-view tab bar (Per-CPU / System / Info) with the
|
||||||
/// active tab highlighted. Hotkeys `1`/`2`/`3` switch directly; `T`
|
/// active tab highlighted. Hotkeys `1`/`2`/`3` switch directly; `T`
|
||||||
/// cycles through them in order.
|
/// cycles through them in order.
|
||||||
@@ -278,6 +323,56 @@ pub fn render_system_panel<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
|
|||||||
if any_critical { "CRIT ".set_style(theme::VALUE_HOT) } else { "CRIT ".set_style(theme::VALUE_OFF) },
|
if any_critical { "CRIT ".set_style(theme::VALUE_HOT) } else { "CRIT ".set_style(theme::VALUE_OFF) },
|
||||||
if any_pl { "PL ".set_style(theme::POWER_LIMIT_FLAG) } else { "PL ".set_style(theme::VALUE_OFF) },
|
if any_pl { "PL ".set_style(theme::POWER_LIMIT_FLAG) } else { "PL ".set_style(theme::VALUE_OFF) },
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
// OS identity (matches cpu-x System tab)
|
||||||
|
if app.os_info.available {
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
"OS: ".set_style(theme::LABEL),
|
||||||
|
if app.os_info.name.is_empty() {
|
||||||
|
"(unknown)".set_style(theme::VALUE_OFF)
|
||||||
|
} else {
|
||||||
|
app.os_info.name.as_str().set_style(theme::VALUE)
|
||||||
|
},
|
||||||
|
" Kernel: ".set_style(theme::LABEL),
|
||||||
|
if app.os_info.kernel.is_empty() {
|
||||||
|
"(unknown)".set_style(theme::VALUE_OFF)
|
||||||
|
} else {
|
||||||
|
app.os_info.kernel.as_str().set_style(theme::VALUE)
|
||||||
|
},
|
||||||
|
" Host: ".set_style(theme::LABEL),
|
||||||
|
if app.os_info.hostname.is_empty() {
|
||||||
|
"(unknown)".set_style(theme::VALUE_OFF)
|
||||||
|
} else {
|
||||||
|
app.os_info.hostname.as_str().set_style(theme::VALUE)
|
||||||
|
},
|
||||||
|
" Up: ".set_style(theme::LABEL),
|
||||||
|
crate::meminfo::format_uptime(app.os_info.uptime_secs)
|
||||||
|
.set_style(theme::VALUE_HOT),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory panel (matches cpu-x System tab memory bars). Each line
|
||||||
|
// shows label, value, and a horizontal bar.
|
||||||
|
if app.meminfo.available {
|
||||||
|
let mi = &app.meminfo;
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
"Mem: ".set_style(theme::LABEL_BOLD),
|
||||||
|
format!(
|
||||||
|
"{} used / {} total",
|
||||||
|
crate::render::format_kib(mi.used_kib),
|
||||||
|
crate::render::format_kib(mi.total_kib)
|
||||||
|
)
|
||||||
|
.set_style(theme::VALUE),
|
||||||
|
]));
|
||||||
|
lines.push(mem_bar_line("Used: ", mi.percent_used(), mi.used_kib, mi.total_kib, theme::VALUE_HOT));
|
||||||
|
lines.push(mem_bar_line("Buffers: ", mi.percent_buffers(), mi.buffers_kib, mi.total_kib, theme::VALUE));
|
||||||
|
lines.push(mem_bar_line("Cached: ", mi.percent_cached(), mi.cached_kib, mi.total_kib, theme::VALUE));
|
||||||
|
lines.push(mem_bar_line("Free: ", mi.percent_free(), mi.free_kib, mi.total_kib, theme::VALUE_OK));
|
||||||
|
if mi.swap_total_kib > 0 {
|
||||||
|
lines.push(mem_bar_line("Swap: ", mi.percent_swap(), mi.swap_used_kib, mi.swap_total_kib, theme::STATUS_WARN));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
"Benchmark: ".set_style(theme::LABEL),
|
"Benchmark: ".set_style(theme::LABEL),
|
||||||
if app.bench_line.is_empty() { "(idle)".set_style(theme::VALUE_OFF) } else { app.bench_line.as_str().set_style(theme::VALUE) },
|
if app.bench_line.is_empty() { "(idle)".set_style(theme::VALUE_OFF) } else { app.bench_line.as_str().set_style(theme::VALUE) },
|
||||||
@@ -695,5 +790,16 @@ pub fn buffer_to_string(buf: &ratatui::buffer::Buffer) -> String {
|
|||||||
|
|
||||||
pub fn render_once(app: &App) -> io::Result<()> {
|
pub fn render_once(app: &App) -> io::Result<()> {
|
||||||
print!("{}", snapshot(app, 140, 50));
|
print!("{}", snapshot(app, 140, 50));
|
||||||
|
// Also dump the System panel as a second snapshot for verification.
|
||||||
|
eprintln!("--- System panel (verifies v1.4 memory + OS info) ---");
|
||||||
|
let sys_para = render_system_panel(app, false);
|
||||||
|
let backend = TestBackend::new(120, 30);
|
||||||
|
let mut terminal = Terminal::new(backend).expect("test terminal");
|
||||||
|
terminal
|
||||||
|
.draw(|f| {
|
||||||
|
f.render_widget(sys_para, f.area());
|
||||||
|
})
|
||||||
|
.expect("draw");
|
||||||
|
print!("{}", buffer_to_string(terminal.backend().buffer()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user