Compare commits
5 Commits
a08fbcbb9c
...
194c2b000d
| Author | SHA1 | Date | |
|---|---|---|---|
|
194c2b000d
|
|||
|
3b7bd55519
|
|||
|
8337ab9519
|
|||
|
0e07ba135b
|
|||
|
4b6df52b41
|
@@ -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)
|
||||
|
||||
@@ -87,3 +87,9 @@ void toggle_client_fullscreen(const user_action_arg_t *arg) {
|
||||
|
||||
client_set_fullscreen(wm.client_focused, !wm.client_focused->fullscreen);
|
||||
}
|
||||
|
||||
void toggle_client_maximize(const user_action_arg_t *arg) {
|
||||
if (!wm.client_focused) return;
|
||||
|
||||
client_set_maximize(wm.client_focused, !wm.client_focused->maximize);
|
||||
}
|
||||
|
||||
@@ -57,3 +57,4 @@ void quit(const user_action_arg_t *arg);
|
||||
void raise_or_run(const user_action_arg_t *arg);
|
||||
void toggle_client_floating(const user_action_arg_t *arg);
|
||||
void toggle_client_fullscreen(const user_action_arg_t *arg);
|
||||
void toggle_client_maximize(const user_action_arg_t *arg);
|
||||
|
||||
@@ -14,6 +14,8 @@ _NET_WM_DESKTOP
|
||||
_NET_WM_NAME
|
||||
_NET_WM_STATE
|
||||
_NET_WM_STATE_FULLSCREEN
|
||||
_NET_WM_STATE_MAXIMIZED_HORZ
|
||||
_NET_WM_STATE_MAXIMIZED_VERT
|
||||
_NET_WM_WINDOW_TYPE
|
||||
_NET_WM_WINDOW_TYPE_DIALOG
|
||||
_NET_SUPPORTED
|
||||
|
||||
98
src/client.c
98
src/client.c
@@ -140,6 +140,8 @@ static inline void client_update_names(client_t *c) {
|
||||
}
|
||||
|
||||
static inline void client_init_geometry(client_t *c) {
|
||||
if (c->maximize || c->fullscreen) return;
|
||||
|
||||
area_t workarea = c->monitor->workarea;
|
||||
if (c->geometry.x + client_width(c) > workarea.x + workarea.width) {
|
||||
c->geometry.x = workarea.x + workarea.width - client_width(c);
|
||||
@@ -221,6 +223,7 @@ void client_manage(xcb_window_t window,
|
||||
} else {
|
||||
c->monitor = wm.current_monitor;
|
||||
c->tags = c->monitor->selected_tag->mask;
|
||||
client_apply_rules(c, wm.rules, wm.rules_count);
|
||||
}
|
||||
|
||||
client_init_geometry(c);
|
||||
@@ -429,8 +432,35 @@ void client_set_fullscreen(client_t *client, bool fullscreen) {
|
||||
_NET_WM_STATE, XCB_ATOM_ATOM, 32, 0, 0);
|
||||
client_change_border_width(client, client->old_border_width);
|
||||
client_apply_geometry(client, client->old_geometry);
|
||||
monitor_arrange(client->monitor);
|
||||
}
|
||||
monitor_arrange(client->monitor);
|
||||
client_focus(client);
|
||||
}
|
||||
|
||||
void client_set_maximize(client_t *client, bool maximize) {
|
||||
if (client->maximize == maximize) return;
|
||||
|
||||
client->maximize = maximize;
|
||||
if (maximize) {
|
||||
xcb_atom_t max_atoms[] = {
|
||||
_NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
_NET_WM_STATE_MAXIMIZED_VERT,
|
||||
};
|
||||
xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window,
|
||||
_NET_WM_STATE, XCB_ATOM_ATOM, 32, countof(max_atoms),
|
||||
max_atoms);
|
||||
client->old_border_width = client->border_width;
|
||||
if (client->floating) client->old_geometry = client->geometry;
|
||||
client_change_border_width(client, 0);
|
||||
client_apply_geometry(client, client->monitor->workarea);
|
||||
client_stack_raise(client);
|
||||
} else {
|
||||
xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window,
|
||||
_NET_WM_STATE, XCB_ATOM_ATOM, 32, 0, 0);
|
||||
client_change_border_width(client, client->old_border_width);
|
||||
client_apply_geometry(client, client->old_geometry);
|
||||
}
|
||||
monitor_arrange(client->monitor);
|
||||
client_focus(client);
|
||||
}
|
||||
|
||||
@@ -469,13 +499,22 @@ void client_unmanage(client_t *client) {
|
||||
}
|
||||
|
||||
void client_apply_geometry(client_t *client, area_t geometry) {
|
||||
if (client->geometry.x != geometry.x || client->geometry.y != geometry.y) {
|
||||
client_move_to(client, geometry.x, geometry.y);
|
||||
}
|
||||
if (client->geometry.width != geometry.width ||
|
||||
client->geometry.height != geometry.height) {
|
||||
client_resize(client, geometry.width, geometry.height);
|
||||
uint16_t mask = 0;
|
||||
const xcb_params_configure_window_t params = {
|
||||
.x = geometry.x,
|
||||
.y = geometry.y,
|
||||
.width = geometry.width,
|
||||
.height = geometry.height,
|
||||
};
|
||||
if (client->geometry.x != geometry.x) mask |= XCB_CONFIG_WINDOW_X;
|
||||
if (client->geometry.y != geometry.y) mask |= XCB_CONFIG_WINDOW_Y;
|
||||
if (client->geometry.width != geometry.width) mask |= XCB_CONFIG_WINDOW_WIDTH;
|
||||
if (client->geometry.height != geometry.height) {
|
||||
mask |= XCB_CONFIG_WINDOW_HEIGHT;
|
||||
}
|
||||
|
||||
client->geometry = geometry;
|
||||
xcb_aux_configure_window(wm.xcb_conn, client->window, mask, ¶ms);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,6 +539,51 @@ void client_apply_workarea_geometry(client_t *client, area_t geometry) {
|
||||
client_apply_geometry(client, rect);
|
||||
}
|
||||
|
||||
void client_apply_rules(client_t *client, const rule_t rules[],
|
||||
size_t rules_count) {
|
||||
for (size_t i = 0; i < rules_count; i++) {
|
||||
const rule_t *r = &rules[i];
|
||||
if (!((!r->role || (client->role && strstr(client->role, r->role))) &&
|
||||
(!r->class || (client->class && strstr(client->class, r->class))))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
client->fullscreen = r->fullscreen;
|
||||
client->maximize = r->maximize;
|
||||
client->floating = r->floating;
|
||||
|
||||
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
|
||||
for (tag_t *t = m->tag_list; t; t = t->next) {
|
||||
if (t->index != r->tag_index) continue;
|
||||
|
||||
client->monitor = m;
|
||||
client->tags = t->mask;
|
||||
|
||||
if (r->switch_to_tag) {
|
||||
wm.current_monitor = m;
|
||||
m->selected_tag = t;
|
||||
|
||||
logger("++ switch to tag: %u\n", t->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client->fullscreen) {
|
||||
xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window,
|
||||
_NET_WM_STATE, XCB_ATOM_ATOM, 32, 1,
|
||||
&_NET_WM_STATE_FULLSCREEN);
|
||||
} else if (client->maximize) {
|
||||
xcb_atom_t max_atoms[] = {
|
||||
_NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
_NET_WM_STATE_MAXIMIZED_VERT,
|
||||
};
|
||||
xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window,
|
||||
_NET_WM_STATE, XCB_ATOM_ATOM, 32, countof(max_atoms),
|
||||
max_atoms);
|
||||
}
|
||||
}
|
||||
|
||||
void client_focus(client_t *client) {
|
||||
wm.client_focused = client;
|
||||
xwindow_focus(client ? client->window : XCB_WINDOW_NONE);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "color.h"
|
||||
@@ -52,6 +53,8 @@ void client_kill(client_t *client);
|
||||
void client_unmanage(client_t *client);
|
||||
void client_apply_geometry(client_t *client, area_t geometry);
|
||||
void client_apply_workarea_geometry(client_t *client, area_t geometry);
|
||||
void client_apply_rules(client_t *client, const rule_t rules[],
|
||||
size_t rules_count);
|
||||
|
||||
void client_focus(client_t *client);
|
||||
void client_stack_raise(client_t *client);
|
||||
|
||||
@@ -39,10 +39,12 @@ static const char launcher[] =
|
||||
"rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd";
|
||||
static const char terminal[] = "xterm";
|
||||
static const char terminal_class[] = "XTerm";
|
||||
static const char editor[] = "emacsclient -a '' -r";
|
||||
static const char editor[] = "emacsclient -a '' -r -n";
|
||||
static const char editor_class[] = "Emacs";
|
||||
static const char browser[] = "firefox-bin";
|
||||
static const char browser_class[] = "firefox";
|
||||
static const char chrome[] = "google-chrome-stable";
|
||||
static const char chrome_class[] = "Google-chrome";
|
||||
static const keyboard_t key_list[] = {
|
||||
{modifier_super, XK_r, spawn, {.ptr = launcher}},
|
||||
{modifier_super, XK_Return, spawn, {.ptr = terminal}},
|
||||
@@ -64,6 +66,12 @@ static const keyboard_t key_list[] = {
|
||||
raise_or_run,
|
||||
{.ptr = (const char *[]){browser_class, browser}},
|
||||
},
|
||||
{
|
||||
modifier_super,
|
||||
XK_a,
|
||||
raise_or_run,
|
||||
{.ptr = (const char *[]){chrome_class, chrome}},
|
||||
},
|
||||
{modifier_super | modifier_shift, XK_q, quit, {.b = false}},
|
||||
{modifier_super | modifier_control, XK_r, quit, {.b = true}},
|
||||
{modifier_super | modifier_shift,
|
||||
@@ -89,6 +97,7 @@ static const keyboard_t key_list[] = {
|
||||
|
||||
{modifier_super | modifier_control, XK_space, toggle_client_floating, {0}},
|
||||
{modifier_super, XK_f, toggle_client_fullscreen, {0}},
|
||||
{modifier_super, XK_m, toggle_client_maximize, {0}},
|
||||
};
|
||||
|
||||
static const layout_t layout_list[] = {
|
||||
@@ -103,3 +112,11 @@ static const char *const autostart_list[] = {
|
||||
"fcitx5 -d",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static const rule_t rules[] = {
|
||||
{.role = "dialog", .tag_index = -1, .floating = true},
|
||||
{.class = "firefox", .tag_index = 1, .maximize = true},
|
||||
{.class = "Google-chrome", .tag_index = 1, .maximize = true},
|
||||
{.class = "mpv", .tag_index = 3, .switch_to_tag = true, .fullscreen = true},
|
||||
{.class = "Emacs", .tag_index = 10, .switch_to_tag = true, .maximize = true},
|
||||
};
|
||||
|
||||
20
src/event.c
20
src/event.c
@@ -139,10 +139,22 @@ static void client_message(xcb_client_message_event_t *ev) {
|
||||
for (tag_t *t = c->monitor->tag_list; t; t = t->next) {
|
||||
if ((t->mask & c->tags) == 0) continue;
|
||||
|
||||
wm.current_monitor = c->monitor;
|
||||
monitor_select_tag(c->monitor, t->mask);
|
||||
client_focus(c);
|
||||
return;
|
||||
logger("== _NET_ACTIVE_WINDOW: %s[%u]\n", c->class, ev->data.data32[0]);
|
||||
|
||||
switch (ev->data.data32[0]) {
|
||||
case 2: /* 来自 pager */
|
||||
wm.current_monitor = c->monitor;
|
||||
monitor_select_tag(c->monitor, t->mask);
|
||||
client_focus(c);
|
||||
break;
|
||||
case 1: /* 来自应用程序 */
|
||||
c->urgent = true;
|
||||
monitor_draw_bar(c->monitor);
|
||||
break;
|
||||
case 0: /* 来自老版本标志 */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ev->type == _NET_WM_STATE) {
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
@@ -69,12 +71,14 @@ void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask) {
|
||||
|
||||
for (task_in_tag_t *task = monitor->selected_tag->task_list; task;
|
||||
task = task->next) {
|
||||
if (task->client->floating || task->client->size_freeze) {
|
||||
if (task->client->floating || task->client->size_freeze ||
|
||||
!monitor->selected_tag->layout->arrange) {
|
||||
task->geometry = task->client->geometry;
|
||||
task->client->old_geometry = task->client->geometry;
|
||||
}
|
||||
if (task->client->fullscreen || task->client->maximize ||
|
||||
task->client->minimize) {
|
||||
task->geometry = task->client->old_geometry;
|
||||
task->geometry = task->client->geometry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,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;
|
||||
@@ -241,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;
|
||||
@@ -273,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);
|
||||
}
|
||||
|
||||
@@ -280,25 +322,24 @@ void monitor_arrange(monitor_t *monitor) {
|
||||
tag_t *tag = monitor->selected_tag;
|
||||
if (tag->layout && tag->layout->arrange) tag->layout->arrange(tag);
|
||||
|
||||
for (client_t *c = wm.client_list; c; c = c->next) {
|
||||
if (c->monitor != monitor) continue;
|
||||
for (client_t *c = wm.client_stack_list; c; c = c->stack_next) {
|
||||
if (c->monitor != monitor || c->minimize) continue;
|
||||
|
||||
task_in_tag_t *task = client_get_task_in_tag(c, tag);
|
||||
if (task) {
|
||||
if (c->minimize) continue;
|
||||
if (c->fullscreen) {
|
||||
if (client_need_layout(c)) {
|
||||
client_apply_geometry(c, task->geometry);
|
||||
} else if (c->fullscreen) {
|
||||
client_apply_geometry(c, c->monitor->geometry);
|
||||
} else if (c->maximize) {
|
||||
client_apply_geometry(c, c->monitor->workarea);
|
||||
} else {
|
||||
client_apply_geometry(c, task->geometry);
|
||||
client_move_to(c, task->geometry.x, task->geometry.y);
|
||||
}
|
||||
} else {
|
||||
int16_t x = -client_width(c);
|
||||
int16_t y = -client_height(c);
|
||||
if (x != c->geometry.x || y != c->geometry.y) {
|
||||
client_move_to(c, x, y);
|
||||
}
|
||||
client_move_to(c, x, y);
|
||||
}
|
||||
}
|
||||
xcb_flush(wm.xcb_conn);
|
||||
|
||||
240
src/status.c
Normal file
240
src/status.c
Normal 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(¤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;
|
||||
}
|
||||
40
src/status.h
Normal file
40
src/status.h
Normal 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);
|
||||
11
src/types.h
11
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;
|
||||
|
||||
@@ -100,3 +101,13 @@ typedef struct padding_t {
|
||||
uint16_t bar_y;
|
||||
uint16_t tag_x;
|
||||
} padding_t;
|
||||
|
||||
typedef struct rule_t {
|
||||
const char *role;
|
||||
const char *class;
|
||||
uint32_t tag_index;
|
||||
bool switch_to_tag;
|
||||
bool fullscreen;
|
||||
bool maximize;
|
||||
bool floating;
|
||||
} rule_t;
|
||||
|
||||
13
src/wm.c
13
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);
|
||||
|
||||
@@ -338,6 +341,9 @@ static void wm_setup(void) {
|
||||
wm.layout_list = layout_list;
|
||||
wm.layout_count = (uint16_t)countof(layout_list);
|
||||
|
||||
wm.rules = rules;
|
||||
wm.rules_count = countof(rules);
|
||||
|
||||
wm_get_xres_config();
|
||||
wm_init_color_set();
|
||||
|
||||
@@ -433,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 结尾)
|
||||
@@ -590,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);
|
||||
|
||||
4
src/wm.h
4
src/wm.h
@@ -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;
|
||||
@@ -27,6 +29,8 @@ typedef struct wm_t {
|
||||
char *font_family;
|
||||
uint32_t font_size;
|
||||
uint32_t dpi;
|
||||
const rule_t *rules;
|
||||
uint32_t rules_count;
|
||||
|
||||
xcb_connection_t *xcb_conn;
|
||||
xcb_screen_t *screen;
|
||||
|
||||
Reference in New Issue
Block a user