添加状态栏

This commit is contained in:
2026-02-05 05:23:22 +08:00
parent 3b7bd55519
commit 194c2b000d
7 changed files with 335 additions and 1 deletions

View File

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

View File

@@ -3,6 +3,7 @@
#include <cairo-xcb.h>
#include <cairo.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_icccm.h>
@@ -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);
}

240
src/status.c Normal file
View File

@@ -0,0 +1,240 @@
#include "status.h"
#include <glib.h>
#include <glibconfig.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
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(&current_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;
}

40
src/status.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <glib.h>
#include <stdint.h>
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);

View File

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

View File

@@ -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);

View File

@@ -6,6 +6,7 @@
#include <xkbcommon/xkbcommon.h>
#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;