From 194c2b000d0d6eb31b84fd1032ed44be1a5ff40b Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Thu, 5 Feb 2026 05:23:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=8A=B6=E6=80=81=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + src/monitor.c | 42 ++++++++- src/status.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++ src/status.h | 40 +++++++++ src/types.h | 1 + src/wm.c | 10 +++ src/wm.h | 2 + 7 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 src/status.c create mode 100644 src/status.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fc362f..26b807a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ add_executable(${APP_NAME} ${SOURCE_DIR}/atoms.c ${SOURCE_DIR}/layout.c ${SOURCE_DIR}/xres.c + ${SOURCE_DIR}/status.c ) find_package(PkgConfig REQUIRED) diff --git a/src/monitor.c b/src/monitor.c index c43793e..98cc91e 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include "base.h" #include "client.h" #include "color.h" +#include "status.h" #include "text.h" #include "types.h" #include "utils.h" @@ -234,6 +236,41 @@ static void monitor_draw_layout_symbol(monitor_t *monitor) { } } +static void monitor_draw_status(monitor_t *monitor, status_t *status) { + monitor->status_extent.end = monitor->geometry.width; + monitor->status_extent.start = monitor->geometry.width; + int16_t end = monitor->layout_symbol_extent.end; + + if (status == nullptr) return; + + char cpu[16], mem[64]; + snprintf(cpu, sizeof(cpu), "%.1lf", status->cpu_usage_percent); + snprintf(mem, sizeof(mem), "%s(%.1f%%)", status->mem_usage.mem_used_text, + status->mem_usage.mem_percent); + char *status_items[] = { + status->net_speed.down, status->net_speed.up, mem, cpu, status->time, + }; + int16_t start = monitor->status_extent.start; + for (int i = countof(status_items) - 1; i >= 0; i--) { + const char *text = status_items[i]; + if (!text || !strlen(text)) continue; + + int width = 0; + text_get_size(text, &width, nullptr); + if (!width) continue; + + start -= width; + if (start <= end) return; + color_t *color = &wm.color_set.tag_color; + area_t rect = {.x = start, .y = 0, .width = width, .height = wm.bar_height}; + draw_text(monitor->bar_cr, text, color, rect, true); + monitor->status_extent.start = start; + + start -= wm.padding.tag_x; + if (start < end) return; + } +} + static void monitor_draw_tasks(monitor_t *monitor) { task_in_tag_t *task_list = monitor->selected_tag->task_list; if (!task_list) return; @@ -243,7 +280,9 @@ static void monitor_draw_tasks(monitor_t *monitor) { if (task_count == 0) return; int16_t x = monitor->layout_symbol_extent.end; - uint16_t task_width = (monitor->geometry.width - x) / task_count; + uint16_t task_width = (monitor->status_extent.start - x) / task_count; + if (task_width < wm.font_size) return; + for (task_in_tag_t *task = task_list; task; task = task->next) { task->bar_extent.start = x; task->bar_extent.end = x + task_width; @@ -275,6 +314,7 @@ void monitor_draw_bar(monitor_t *monitor) { monitor_draw_tags(monitor); monitor_draw_layout_symbol(monitor); + monitor_draw_status(monitor, wm.status); monitor_draw_tasks(monitor); } diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..473d873 --- /dev/null +++ b/src/status.c @@ -0,0 +1,240 @@ +#include "status.h" + +#include +#include +#include +#include +#include +#include +#include + +static constexpr char CPU_FILE[] = "/proc/stat"; +static constexpr char MEM_FILE[] = "/proc/meminfo"; +static constexpr char NET_FILE[] = "/proc/net/dev"; + +static constexpr uint32_t INTERVAL = 1; +static constexpr char TIME_FORMAT[] = "%A %m-%d %H:%M:%S"; + +static status_t status; +static guint timer = 0; +static status_changed_notify listener = nullptr; + +typedef struct cpu_stat_t { + uint64_t user; + uint64_t nice; + uint64_t system; + uint64_t idle; + uint64_t iowait; + uint64_t irq; + uint64_t softirq; + uint64_t steal; + uint64_t guest; + uint64_t guest_nice; +} cpu_stat_t; + +static double calc_cpu_usage(cpu_stat_t curr, cpu_stat_t prev) { + uint64_t curr_idle = curr.idle + curr.iowait; + uint64_t curr_total = curr.user + curr.nice + curr.system + curr.idle + + curr.iowait + curr.irq + curr.softirq + curr.steal + + curr.guest + curr.guest_nice; + + uint64_t prev_idle = prev.idle + prev.iowait; + uint64_t prev_total = prev.user + prev.nice + prev.system + prev.idle + + prev.iowait + prev.irq + prev.softirq + prev.steal + + prev.guest + prev.guest_nice; + + uint64_t total = curr_total - prev_total; + uint64_t idle = curr_idle - prev_idle; + uint64_t active = total - idle; + if (total == 0) return 0.0; + + double usage = ((double)active) / total * 100; + return usage; +} + +static bool get_cpu_usage(double *usage, GError *error) { + static cpu_stat_t prev_cpu_stat = {0}; + static bool inited = false; + + gchar *contents = nullptr; + gsize length = 0; + + if (!g_file_get_contents(CPU_FILE, &contents, &length, &error)) return false; + + gchar **lines = g_strsplit(contents, "\n", 0); + g_free(contents); + + char cpu_line[256]; + for (gchar **line = lines; *line != nullptr; ++line) { + if (strncmp(cpu_line, "cpu", 4) == 0) { + strncpy(cpu_line, *line, sizeof(cpu_line)); + break; + } + } + + cpu_stat_t stat = {0}; + sscanf(cpu_line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &stat.user, + &stat.nice, &stat.system, &stat.idle, &stat.iowait, &stat.irq, + &stat.softirq, &stat.steal, &stat.guest, &stat.guest_nice); + + if (!inited) { + prev_cpu_stat = stat; + inited = true; + } + + double percent = calc_cpu_usage(stat, prev_cpu_stat); + prev_cpu_stat = stat; + *usage = percent; + + g_strfreev(lines); + return true; +} + +static void bytes_to_readable_size(uint64_t bytes, char *str) { + static char *uints[] = {"B", "K", "M", "G", "T"}; + + double size = (double)bytes; + int i = 0; + while (size > 1024.0) { + size /= 1024.0; + ++i; + } + + sprintf(str, "%.1lf%s", size, uints[i]); +} + +static void calc_mem_usage(memory_usage_t *usage) { + usage->mem_used = usage->mem_total - usage->mem_free - usage->buffers - + usage->cached - usage->s_reclaimable; + usage->swap_used = usage->swap_total - usage->swap_free; + + usage->mem_percent = + ((float)usage->mem_used) / ((float)usage->mem_total) * 100; + if (usage->swap_total == 0) { + usage->swap_percent = 0; + } else { + usage->swap_percent = + ((float)usage->swap_used) / ((float)usage->swap_total) * 100; + } + + bytes_to_readable_size(usage->mem_used * 1024, usage->mem_used_text); + bytes_to_readable_size(usage->swap_used * 1024, usage->swap_used_text); +} + +static bool get_mem_usage(memory_usage_t *usage, GError *error) { + gchar *contents = nullptr; + gsize length = 0; + + if (!g_file_get_contents(MEM_FILE, &contents, &length, &error)) return false; + + gchar **lines = g_strsplit(contents, "\n", 0); + g_free(contents); + + for (gchar **line = lines; *line != nullptr; ++line) { + char name[32]; + uint64_t kb = 0; + sscanf(*line, "%[^:]: %lu", name, &kb); + if (g_str_equal(name, "MemTotal")) { + usage->mem_total = kb; + } else if (g_str_equal(name, "MemFree")) { + usage->mem_free = kb; + } else if (g_str_equal(name, "Buffers")) { + usage->buffers = kb; + } else if (g_str_equal(name, "Cached")) { + usage->cached = kb; + } else if (g_str_equal(name, "SwapTotal")) { + usage->swap_total = kb; + } else if (g_str_equal(name, "SwapFree")) { + usage->swap_free = kb; + } else if (g_str_equal(name, "SReclaimable")) { + usage->s_reclaimable = kb; + } + } + calc_mem_usage(usage); + g_strfreev(lines); + return true; +} + +typedef struct net_stat_t { + uint64_t rx_bytes; + uint64_t tx_bytes; +} net_stat_t; + +static void calc_speed(const uint32_t interval, const net_stat_t *current, + const net_stat_t *previous, net_speed_t *speed) { + speed->rx_bytes = current->rx_bytes - previous->rx_bytes; + speed->tx_bytes = current->tx_bytes - previous->tx_bytes; + bytes_to_readable_size(speed->rx_bytes / interval, speed->down); + bytes_to_readable_size(speed->tx_bytes / interval, speed->up); +} + +static bool get_net_speed(const uint32_t interval, net_speed_t *speed, + GError *error) { + static net_stat_t prev = {0}; + static bool inited = false; + + gchar *contents = nullptr; + gsize length = 0; + + if (!g_file_get_contents(NET_FILE, &contents, &length, &error)) return false; + + gchar **lines = g_strsplit(contents, "\n", 0); + g_free(contents); + + net_stat_t stat = {0}; + for (gchar **line = lines; *line != nullptr; ++line) { + if (strchr(*line, ':') == nullptr) continue; + + char name[16]; + uint64_t rx_bytes, tx_bytes; + sscanf(*line, " %[^:]: %lu %*u %*u %*u %*u %*u %*u %*u %lu", name, + &rx_bytes, &tx_bytes); + if (g_str_equal(name, "lo")) continue; + + stat.rx_bytes += rx_bytes; + stat.tx_bytes += tx_bytes; + } + + if (!inited) { + inited = true; + prev = stat; + } + + calc_speed(interval, &stat, &prev, speed); + prev = stat; + + g_strfreev(lines); + return true; +} + +static void get_date_time(char *time_text, size_t length) { + time_t current_time = time(nullptr); + struct tm *time_info = localtime(¤t_time); + strftime(time_text, length, TIME_FORMAT, time_info); +} + +static inline void notify_status_change(void) { + if (listener) listener(&status); +} + +static gboolean update_status(gpointer data) { + GError *error = nullptr; + get_net_speed(INTERVAL, &status.net_speed, error); + get_mem_usage(&status.mem_usage, error); + get_cpu_usage(&status.cpu_usage_percent, error); + get_date_time(status.time, sizeof(status.time)); + notify_status_change(); + + return true; +} + +void init_status(GMainContext *context, status_changed_notify callback) { + listener = callback; + update_status(nullptr); + timer = g_timeout_add_seconds(INTERVAL, update_status, nullptr); +} + +void clean_status(void) { + g_source_remove(timer); + listener = nullptr; +} diff --git a/src/status.h b/src/status.h new file mode 100644 index 0000000..422357c --- /dev/null +++ b/src/status.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +typedef struct memory_usage_t { + char mem_used_text[32]; + char swap_used_text[32]; + + float mem_percent; + float swap_percent; + uint64_t mem_used; + uint64_t swap_used; + + uint64_t mem_total; + uint64_t mem_free; + uint64_t buffers; + uint64_t cached; + uint64_t swap_total; + uint64_t swap_free; + uint64_t s_reclaimable; +} memory_usage_t; + +typedef struct net_speed_t { + uint64_t rx_bytes; + uint64_t tx_bytes; + char up[64]; + char down[64]; +} net_speed_t; + +typedef struct status_t { + net_speed_t net_speed; + memory_usage_t mem_usage; + double cpu_usage_percent; + char time[64]; +} status_t; + +typedef void (*status_changed_notify)(status_t *status); +void init_status(GMainContext *context, status_changed_notify callback); +void clean_status(void); diff --git a/src/types.h b/src/types.h index 3938f88..9694d12 100644 --- a/src/types.h +++ b/src/types.h @@ -62,6 +62,7 @@ struct monitor_t { extent_in_bar_t tag_extent; extent_in_bar_t layout_symbol_extent; + extent_in_bar_t status_extent; xcb_window_t bar_window; diff --git a/src/wm.c b/src/wm.c index 6fe42e3..8c47380 100644 --- a/src/wm.c +++ b/src/wm.c @@ -29,6 +29,7 @@ #include "default_config.h" #include "event.h" #include "monitor.h" +#include "status.h" #include "text.h" #include "types.h" #include "utils.h" @@ -47,6 +48,7 @@ static void wm_setup(void); static void wm_get_xres_config(void); static void wm_init_color_set(void); static void wm_setup_keybindings(void); +static void wm_update_status(status_t *status); static void wm_run_autostart(const char *const commands[]); static void run_once(const char *command); static bool command_already_running(const char *command); @@ -310,6 +312,7 @@ void wm_scan_clients(void) { } void wm_clean(void) { + clean_status(); text_clean_pango_layout(); monitor_clean(wm.monitor_list); @@ -436,6 +439,12 @@ void wm_setup_keybindings(void) { xwindow_grab_keys(wm.screen->root, key_list, countof(key_list)); } +void wm_update_status(status_t *status) { + wm.status = status; + for (monitor_t *m = wm.monitor_list; m; m = m->next) monitor_draw_bar(m); + xcb_flush(wm.xcb_conn); +} + /** * @brief 允许自启动程序 * @param commands 命令字符串数组(以 nullptr 结尾) @@ -593,6 +602,7 @@ int main(int argc, char *argv[]) { if (wm.loop == nullptr) { wm.loop = g_main_loop_new(nullptr, FALSE); + init_status(g_main_loop_get_context(wm.loop), wm_update_status); g_main_loop_run(wm.loop); } g_main_loop_unref(wm.loop); diff --git a/src/wm.h b/src/wm.h index 6fdaaec..7e76cee 100644 --- a/src/wm.h +++ b/src/wm.h @@ -6,6 +6,7 @@ #include #include "base.h" +#include "status.h" #include "types.h" typedef struct wm_t { @@ -16,6 +17,7 @@ typedef struct wm_t { bool need_restart; GMainLoop *loop; + status_t *status; client_t *client_list; client_t *client_stack_list;