docs: expand README with design philosophy, features, and architecture sections while adding panel dirty tracking, Xft color caching, and XInput2 mouse distance monitoring
This commit is contained in:
+12
-3
@@ -1340,13 +1340,17 @@ unsigned long client_get_glow_text_color(Client *client)
|
||||
return glow_color(client->text_glow.base_color, phase);
|
||||
}
|
||||
|
||||
#define TEXT_GLOW_THROTTLE_MS 100
|
||||
|
||||
void client_update_animations(void)
|
||||
{
|
||||
if (dwn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
static long last_glow_update = 0;
|
||||
long now = get_time_ms();
|
||||
bool should_update_glow = (now - last_glow_update >= TEXT_GLOW_THROTTLE_MS);
|
||||
bool needs_redraw = false;
|
||||
|
||||
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
|
||||
@@ -1361,8 +1365,9 @@ void client_update_animations(void)
|
||||
needs_redraw = true;
|
||||
}
|
||||
|
||||
if (c->text_glow.active && c->workspace == (unsigned int)dwn->current_workspace) {
|
||||
c->text_glow.phase += 0.005f * c->text_glow.speed;
|
||||
if (should_update_glow && c->text_glow.active &&
|
||||
c->workspace == (unsigned int)dwn->current_workspace) {
|
||||
c->text_glow.phase += 0.05f * c->text_glow.speed;
|
||||
if (c->text_glow.phase > 6.283f) {
|
||||
c->text_glow.phase -= 6.283f;
|
||||
}
|
||||
@@ -1370,7 +1375,11 @@ void client_update_animations(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (should_update_glow) {
|
||||
last_glow_update = now;
|
||||
}
|
||||
|
||||
if (needs_redraw) {
|
||||
panel_render_all();
|
||||
panel_invalidate_taskbar();
|
||||
}
|
||||
}
|
||||
|
||||
+83
-36
@@ -19,6 +19,83 @@
|
||||
|
||||
#define RESIZE_EDGE 8
|
||||
|
||||
#define DEC_XFT_COLOR_CACHE_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
unsigned long pixel;
|
||||
XftColor xft_color;
|
||||
bool valid;
|
||||
int lru_counter;
|
||||
} DecCachedXftColor;
|
||||
|
||||
static DecCachedXftColor dec_xft_color_cache[DEC_XFT_COLOR_CACHE_SIZE];
|
||||
static Visual *dec_cached_visual = NULL;
|
||||
static int dec_xft_color_lru_counter = 0;
|
||||
|
||||
static XftDraw *dec_cached_xft_draw = NULL;
|
||||
static Window dec_cached_window = None;
|
||||
|
||||
static XftDraw *dec_get_xft_draw(Window w)
|
||||
{
|
||||
if (dec_cached_window == w && dec_cached_xft_draw != NULL) {
|
||||
return dec_cached_xft_draw;
|
||||
}
|
||||
if (dec_cached_xft_draw != NULL) {
|
||||
XftDrawDestroy(dec_cached_xft_draw);
|
||||
}
|
||||
if (dec_cached_visual == NULL) {
|
||||
dec_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
dec_cached_xft_draw = XftDrawCreate(dwn->display, w, dec_cached_visual, dwn->colormap);
|
||||
dec_cached_window = w;
|
||||
return dec_cached_xft_draw;
|
||||
}
|
||||
|
||||
static XftColor *dec_get_cached_xft_color(unsigned long color)
|
||||
{
|
||||
if (dec_cached_visual == NULL) {
|
||||
dec_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
|
||||
for (int i = 0; i < DEC_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (dec_xft_color_cache[i].valid && dec_xft_color_cache[i].pixel == color) {
|
||||
dec_xft_color_cache[i].lru_counter = ++dec_xft_color_lru_counter;
|
||||
return &dec_xft_color_cache[i].xft_color;
|
||||
}
|
||||
}
|
||||
|
||||
int oldest_idx = 0;
|
||||
int oldest_lru = dec_xft_color_cache[0].lru_counter;
|
||||
for (int i = 1; i < DEC_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (!dec_xft_color_cache[i].valid) {
|
||||
oldest_idx = i;
|
||||
break;
|
||||
}
|
||||
if (dec_xft_color_cache[i].lru_counter < oldest_lru) {
|
||||
oldest_lru = dec_xft_color_cache[i].lru_counter;
|
||||
oldest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (dec_xft_color_cache[oldest_idx].valid) {
|
||||
XftColorFree(dwn->display, dec_cached_visual, dwn->colormap,
|
||||
&dec_xft_color_cache[oldest_idx].xft_color);
|
||||
}
|
||||
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, dec_cached_visual, dwn->colormap,
|
||||
&render_color, &dec_xft_color_cache[oldest_idx].xft_color);
|
||||
dec_xft_color_cache[oldest_idx].pixel = color;
|
||||
dec_xft_color_cache[oldest_idx].valid = true;
|
||||
dec_xft_color_cache[oldest_idx].lru_counter = ++dec_xft_color_lru_counter;
|
||||
|
||||
return &dec_xft_color_cache[oldest_idx].xft_color;
|
||||
}
|
||||
|
||||
void decorations_init(void)
|
||||
{
|
||||
@@ -117,27 +194,12 @@ void decorations_render_title_bar(Client *client, bool focused)
|
||||
strncat(display_title, "...", sizeof(display_title) - strlen(display_title) - 1);
|
||||
}
|
||||
|
||||
XftDraw *xft_draw = XftDrawCreate(dpy, client->frame,
|
||||
DefaultVisual(dpy, dwn->screen),
|
||||
dwn->colormap);
|
||||
XftDraw *xft_draw = dec_get_xft_draw(client->frame);
|
||||
if (xft_draw != NULL) {
|
||||
XftColor xft_color;
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((fg_color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((fg_color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (fg_color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dpy, DefaultVisual(dpy, dwn->screen),
|
||||
dwn->colormap, &render_color, &xft_color);
|
||||
|
||||
XftDrawStringUtf8(xft_draw, &xft_color, title_font,
|
||||
XftColor *xft_color = dec_get_cached_xft_color(fg_color);
|
||||
XftDrawStringUtf8(xft_draw, xft_color, title_font,
|
||||
text_x, text_y,
|
||||
(const FcChar8 *)display_title, strlen(display_title));
|
||||
|
||||
XftColorFree(dpy, DefaultVisual(dpy, dwn->screen),
|
||||
dwn->colormap, &xft_color);
|
||||
XftDrawDestroy(xft_draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,26 +396,11 @@ void decorations_draw_text(Window window, GC gc, int x, int y,
|
||||
}
|
||||
|
||||
if (dwn->xft_font != NULL) {
|
||||
XftDraw *xft_draw = XftDrawCreate(dwn->display, window,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap);
|
||||
XftDraw *xft_draw = dec_get_xft_draw(window);
|
||||
if (xft_draw != NULL) {
|
||||
XftColor xft_color;
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &render_color, &xft_color);
|
||||
|
||||
XftDrawStringUtf8(xft_draw, &xft_color, dwn->xft_font,
|
||||
XftColor *xft_color = dec_get_cached_xft_color(color);
|
||||
XftDrawStringUtf8(xft_draw, xft_color, dwn->xft_font,
|
||||
x, y, (const FcChar8 *)text, strlen(text));
|
||||
|
||||
XftColorFree(dwn->display, DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &xft_color);
|
||||
XftDrawDestroy(xft_draw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-20
@@ -164,10 +164,11 @@ static void init_xinput2(void)
|
||||
|
||||
XISetMask(mask, XI_RawKeyPress);
|
||||
XISetMask(mask, XI_RawKeyRelease);
|
||||
XISetMask(mask, XI_RawMotion);
|
||||
|
||||
XISelectEvents(dwn->display, dwn->root, &evmask, 1);
|
||||
|
||||
LOG_INFO("XInput2 initialized for raw key event monitoring");
|
||||
LOG_INFO("XInput2 initialized for raw key and mouse event monitoring");
|
||||
}
|
||||
|
||||
static void handle_xi2_event(XGenericEventCookie *cookie)
|
||||
@@ -198,6 +199,20 @@ static void handle_xi2_event(XGenericEventCookie *cookie)
|
||||
dwn->key_delete_flicker_time = get_time_ms();
|
||||
}
|
||||
}
|
||||
} else if (cookie->evtype == XI_RawMotion) {
|
||||
XIRawEvent *raw = (XIRawEvent *)cookie->data;
|
||||
if (raw->valuators.mask_len > 0) {
|
||||
double dx = 0.0, dy = 0.0;
|
||||
double *values = raw->raw_values;
|
||||
int idx = 0;
|
||||
if (XIMaskIsSet(raw->valuators.mask, 0)) {
|
||||
dx = values[idx++];
|
||||
}
|
||||
if (XIMaskIsSet(raw->valuators.mask, 1)) {
|
||||
dy = values[idx];
|
||||
}
|
||||
dwn->mouse_distance_pixels += sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
}
|
||||
|
||||
XFreeEventData(dwn->display, cookie);
|
||||
@@ -393,7 +408,7 @@ static void handle_property_notify(XPropertyEvent *ev)
|
||||
|
||||
if (ev->atom == icccm.WM_NAME || ev->atom == ewmh.NET_WM_NAME) {
|
||||
client_update_title(c);
|
||||
panel_render_all();
|
||||
panel_invalidate_taskbar();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1024,21 +1039,6 @@ void dwn_run(void)
|
||||
|
||||
client_update_animations();
|
||||
|
||||
Window root_ret, child_ret;
|
||||
int root_x, root_y, win_x, win_y;
|
||||
unsigned int mask_ret;
|
||||
if (XQueryPointer(dwn->display, dwn->root, &root_ret, &child_ret,
|
||||
&root_x, &root_y, &win_x, &win_y, &mask_ret)) {
|
||||
if (dwn->mouse_tracking_initialized) {
|
||||
int dx = root_x - dwn->last_mouse_x;
|
||||
int dy = root_y - dwn->last_mouse_y;
|
||||
dwn->mouse_distance_pixels += sqrt((double)(dx * dx + dy * dy));
|
||||
}
|
||||
dwn->last_mouse_x = root_x;
|
||||
dwn->last_mouse_y = root_y;
|
||||
dwn->mouse_tracking_initialized = true;
|
||||
}
|
||||
|
||||
notifications_update();
|
||||
|
||||
if (dwn->pending_focus_client != NULL) {
|
||||
@@ -1053,12 +1053,23 @@ void dwn_run(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (now - last_news_update >= 16) {
|
||||
bool news_rendered = false;
|
||||
|
||||
if (now - last_news_update >= 50) {
|
||||
news_update();
|
||||
panel_render_all();
|
||||
if (panel_news_needs_render()) {
|
||||
panel_render_news_only();
|
||||
news_rendered = true;
|
||||
}
|
||||
last_news_update = now;
|
||||
}
|
||||
|
||||
if (panel_needs_render()) {
|
||||
panel_render_all();
|
||||
} else if (news_rendered) {
|
||||
XFlush(dwn->display);
|
||||
}
|
||||
|
||||
if (now - last_clock_update >= 1000) {
|
||||
panel_update_clock();
|
||||
panel_update_system_stats();
|
||||
@@ -1075,7 +1086,7 @@ void dwn_run(void)
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 16000;
|
||||
tv.tv_usec = 50000;
|
||||
|
||||
int max_fd = x11_fd;
|
||||
if (dbus_fd > max_fd) max_fd = dbus_fd;
|
||||
|
||||
+104
-23
@@ -261,6 +261,86 @@ static int parse_news_json(const char *json_str)
|
||||
|
||||
static int news_find_article_at_x(int click_x);
|
||||
|
||||
static XftDraw *news_xft_draw = NULL;
|
||||
static Drawable news_xft_drawable = None;
|
||||
static Visual *news_cached_visual = NULL;
|
||||
|
||||
#define NEWS_XFT_COLOR_CACHE_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
unsigned long pixel;
|
||||
XftColor xft_color;
|
||||
bool valid;
|
||||
int lru_counter;
|
||||
} NewsCachedColor;
|
||||
|
||||
static NewsCachedColor news_color_cache[NEWS_XFT_COLOR_CACHE_SIZE];
|
||||
static int news_color_lru_counter = 0;
|
||||
|
||||
static int cached_separator_width = -1;
|
||||
|
||||
static XftDraw *news_get_xft_draw(Drawable d)
|
||||
{
|
||||
if (news_xft_drawable == d && news_xft_draw != NULL) {
|
||||
return news_xft_draw;
|
||||
}
|
||||
if (news_xft_draw != NULL) {
|
||||
XftDrawDestroy(news_xft_draw);
|
||||
}
|
||||
if (news_cached_visual == NULL) {
|
||||
news_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
news_xft_draw = XftDrawCreate(dwn->display, d, news_cached_visual, dwn->colormap);
|
||||
news_xft_drawable = d;
|
||||
return news_xft_draw;
|
||||
}
|
||||
|
||||
static XftColor *news_get_cached_color(unsigned long color)
|
||||
{
|
||||
if (news_cached_visual == NULL) {
|
||||
news_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NEWS_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (news_color_cache[i].valid && news_color_cache[i].pixel == color) {
|
||||
news_color_cache[i].lru_counter = ++news_color_lru_counter;
|
||||
return &news_color_cache[i].xft_color;
|
||||
}
|
||||
}
|
||||
|
||||
int oldest_idx = 0;
|
||||
int oldest_lru = news_color_cache[0].lru_counter;
|
||||
for (int i = 1; i < NEWS_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (!news_color_cache[i].valid) {
|
||||
oldest_idx = i;
|
||||
break;
|
||||
}
|
||||
if (news_color_cache[i].lru_counter < oldest_lru) {
|
||||
oldest_lru = news_color_cache[i].lru_counter;
|
||||
oldest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (news_color_cache[oldest_idx].valid) {
|
||||
XftColorFree(dwn->display, news_cached_visual, dwn->colormap,
|
||||
&news_color_cache[oldest_idx].xft_color);
|
||||
}
|
||||
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, news_cached_visual, dwn->colormap,
|
||||
&render_color, &news_color_cache[oldest_idx].xft_color);
|
||||
news_color_cache[oldest_idx].pixel = color;
|
||||
news_color_cache[oldest_idx].valid = true;
|
||||
news_color_cache[oldest_idx].lru_counter = ++news_color_lru_counter;
|
||||
|
||||
return &news_color_cache[oldest_idx].xft_color;
|
||||
}
|
||||
|
||||
static int news_text_width(const char *text)
|
||||
{
|
||||
if (dwn == NULL || text == NULL) return 0;
|
||||
@@ -285,9 +365,7 @@ static void news_draw_text_clipped(Drawable d, int x, int y, const char *text,
|
||||
if (dwn == NULL || dwn->display == NULL || text == NULL) return;
|
||||
|
||||
if (dwn->xft_font != NULL) {
|
||||
XftDraw *xft_draw = XftDrawCreate(dwn->display, d,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap);
|
||||
XftDraw *xft_draw = news_get_xft_draw(d);
|
||||
if (xft_draw != NULL) {
|
||||
if (clip != NULL) {
|
||||
Region region = XCreateRegion();
|
||||
@@ -296,25 +374,13 @@ static void news_draw_text_clipped(Drawable d, int x, int y, const char *text,
|
||||
XDestroyRegion(region);
|
||||
}
|
||||
|
||||
XftColor xft_color;
|
||||
XRenderColor render_color;
|
||||
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &render_color, &xft_color);
|
||||
|
||||
XftDrawStringUtf8(xft_draw, &xft_color, dwn->xft_font,
|
||||
XftColor *xft_color = news_get_cached_color(color);
|
||||
XftDrawStringUtf8(xft_draw, xft_color, dwn->xft_font,
|
||||
x, y, (const FcChar8 *)text, strlen(text));
|
||||
|
||||
XftColorFree(dwn->display,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &xft_color);
|
||||
XftDrawDestroy(xft_draw);
|
||||
if (clip != NULL) {
|
||||
XftDrawSetClip(xft_draw, None);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -349,7 +415,10 @@ static void news_recalc_widths(void)
|
||||
return;
|
||||
}
|
||||
|
||||
int separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
if (cached_separator_width < 0) {
|
||||
cached_separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
}
|
||||
int separator_width = cached_separator_width;
|
||||
news_state.total_width = 0;
|
||||
|
||||
for (int i = 0; i < news_state.article_count; i++) {
|
||||
@@ -479,16 +548,22 @@ void news_update(void)
|
||||
pthread_mutex_lock(&news_mutex);
|
||||
}
|
||||
|
||||
bool scrolled = false;
|
||||
if (!news_state.interactive_mode && news_state.article_count > 0) {
|
||||
if (news_state.last_scroll_update > 0) {
|
||||
long delta_ms = now - news_state.last_scroll_update;
|
||||
double scroll_amount = (SCROLL_SPEED_PPS * delta_ms) / 1000.0;
|
||||
news_state.scroll_offset += scroll_amount;
|
||||
scrolled = true;
|
||||
}
|
||||
news_state.last_scroll_update = now;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&news_mutex);
|
||||
|
||||
if (scrolled) {
|
||||
panel_invalidate_news();
|
||||
}
|
||||
}
|
||||
|
||||
void news_next_article(void)
|
||||
@@ -554,7 +629,10 @@ static int news_find_article_at_x(int click_x)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
if (cached_separator_width < 0) {
|
||||
cached_separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
}
|
||||
int separator_width = cached_separator_width;
|
||||
int scroll_offset = (int)news_state.scroll_offset % news_state.total_width;
|
||||
int rel_x = click_x - news_state.render_x;
|
||||
|
||||
@@ -618,7 +696,10 @@ void news_render(Panel *panel, int x, int max_width, int *used_width)
|
||||
news_state.render_x = x;
|
||||
news_state.render_width = max_width;
|
||||
|
||||
int separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
if (cached_separator_width < 0) {
|
||||
cached_separator_width = news_text_width(NEWS_SEPARATOR);
|
||||
}
|
||||
int separator_width = cached_separator_width;
|
||||
int scroll_offset = (int)news_state.scroll_offset % news_state.total_width;
|
||||
|
||||
XRectangle clip_rect = { x, 0, max_width, panel->height };
|
||||
|
||||
+558
-61
@@ -21,19 +21,60 @@
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <X11/Xft/Xft.h>
|
||||
|
||||
#define PANEL_PADDING 12
|
||||
#define WIDGET_SPACING 16
|
||||
#define WORKSPACE_WIDTH 36
|
||||
#define TASKBAR_ITEM_WIDTH 180
|
||||
#define TOP_PROCESS_NAME_MAX_LEN 12
|
||||
|
||||
#define CLOCK_FORMAT "%H:%M:%S"
|
||||
#define DATE_FORMAT "%Y-%m-%d"
|
||||
|
||||
#define XFT_COLOR_CACHE_SIZE 16
|
||||
#define PROCESS_SCAN_INTERVAL_MS 30000
|
||||
#define PROC_CPU_HISTORY_SIZE 1024
|
||||
#define DISK_UPDATE_INTERVAL_MS 60000
|
||||
|
||||
static char clock_buffer[32] = "";
|
||||
static char date_buffer[32] = "";
|
||||
|
||||
static double disk_free_gb = 0.0;
|
||||
static double disk_total_gb = 0.0;
|
||||
static long disk_last_update = 0;
|
||||
|
||||
static bool panels_dirty = true;
|
||||
|
||||
static int news_region_x = 0;
|
||||
static int news_region_width = 0;
|
||||
static bool news_region_dirty = false;
|
||||
|
||||
typedef struct {
|
||||
unsigned long pixel;
|
||||
XftColor xft_color;
|
||||
bool valid;
|
||||
int lru_counter;
|
||||
} CachedXftColor;
|
||||
|
||||
static CachedXftColor xft_color_cache[XFT_COLOR_CACHE_SIZE];
|
||||
static Visual *cached_visual = NULL;
|
||||
static int xft_color_lru_counter = 0;
|
||||
|
||||
static XftDraw *cached_xft_draw = NULL;
|
||||
static Drawable cached_drawable = None;
|
||||
|
||||
static long last_memory_scan = 0;
|
||||
|
||||
typedef struct {
|
||||
int pid;
|
||||
unsigned long long prev_utime;
|
||||
unsigned long long prev_stime;
|
||||
unsigned long long prev_total_cpu;
|
||||
} ProcCpuState;
|
||||
|
||||
typedef struct {
|
||||
int cpu_percent;
|
||||
int mem_percent;
|
||||
@@ -44,18 +85,32 @@ typedef struct {
|
||||
float load_15min;
|
||||
unsigned long long prev_idle;
|
||||
unsigned long long prev_total;
|
||||
char top_mem_process[64];
|
||||
int top_mem_percent;
|
||||
char top_mem_processes[3][64];
|
||||
int top_mem_percents[3];
|
||||
int top_mem_count;
|
||||
char top_cpu_processes[3][64];
|
||||
int top_cpu_percents[3];
|
||||
int top_cpu_count;
|
||||
int process_display_index;
|
||||
int process_category;
|
||||
long process_last_cycle;
|
||||
} SystemStats;
|
||||
|
||||
static ProcCpuState proc_cpu_history[PROC_CPU_HISTORY_SIZE];
|
||||
static int proc_cpu_history_count = 0;
|
||||
static unsigned long long prev_total_cpu_time = 0;
|
||||
|
||||
static SystemStats sys_stats = {0};
|
||||
|
||||
static void panel_render_system_stats(Panel *panel, int x, int *width);
|
||||
static int panel_calculate_stats_width(void);
|
||||
static void panel_render_top_mem_process(Panel *panel, int x, int *width);
|
||||
static void panel_render_top_process(Panel *panel, int x, int *width);
|
||||
static int panel_calculate_top_process_width(void);
|
||||
static void panel_render_key_counter(Panel *panel, int x, int *width);
|
||||
static void panel_render_mouse_distance(Panel *panel, int x, int *width);
|
||||
static void update_top_memory_process(void);
|
||||
static void panel_render_disk_space(Panel *panel, int x, int *width);
|
||||
static void update_disk_space(void);
|
||||
static void update_top_processes(void);
|
||||
|
||||
|
||||
static int panel_text_width(const char *text, int len)
|
||||
@@ -76,32 +131,79 @@ static int panel_text_width(const char *text, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static XftDraw *panel_get_xft_draw(Drawable d)
|
||||
{
|
||||
if (cached_drawable == d && cached_xft_draw != NULL) {
|
||||
return cached_xft_draw;
|
||||
}
|
||||
if (cached_xft_draw != NULL) {
|
||||
XftDrawDestroy(cached_xft_draw);
|
||||
}
|
||||
if (cached_visual == NULL) {
|
||||
cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
cached_xft_draw = XftDrawCreate(dwn->display, d, cached_visual, dwn->colormap);
|
||||
cached_drawable = d;
|
||||
return cached_xft_draw;
|
||||
}
|
||||
|
||||
static XftColor *panel_get_cached_xft_color(unsigned long color)
|
||||
{
|
||||
if (cached_visual == NULL) {
|
||||
cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
|
||||
for (int i = 0; i < XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (xft_color_cache[i].valid && xft_color_cache[i].pixel == color) {
|
||||
xft_color_cache[i].lru_counter = ++xft_color_lru_counter;
|
||||
return &xft_color_cache[i].xft_color;
|
||||
}
|
||||
}
|
||||
|
||||
int oldest_idx = 0;
|
||||
int oldest_lru = xft_color_cache[0].lru_counter;
|
||||
for (int i = 1; i < XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (!xft_color_cache[i].valid) {
|
||||
oldest_idx = i;
|
||||
break;
|
||||
}
|
||||
if (xft_color_cache[i].lru_counter < oldest_lru) {
|
||||
oldest_lru = xft_color_cache[i].lru_counter;
|
||||
oldest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (xft_color_cache[oldest_idx].valid) {
|
||||
XftColorFree(dwn->display, cached_visual, dwn->colormap,
|
||||
&xft_color_cache[oldest_idx].xft_color);
|
||||
}
|
||||
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, cached_visual, dwn->colormap,
|
||||
&render_color, &xft_color_cache[oldest_idx].xft_color);
|
||||
xft_color_cache[oldest_idx].pixel = color;
|
||||
xft_color_cache[oldest_idx].valid = true;
|
||||
xft_color_cache[oldest_idx].lru_counter = ++xft_color_lru_counter;
|
||||
|
||||
return &xft_color_cache[oldest_idx].xft_color;
|
||||
}
|
||||
|
||||
static void panel_draw_text_with_font(Drawable d, int x, int y, const char *text,
|
||||
int len, unsigned long color, XftFont *font)
|
||||
{
|
||||
if (text == NULL || dwn == NULL || dwn->display == NULL) return;
|
||||
|
||||
if (font != NULL) {
|
||||
XftDraw *xft_draw = XftDrawCreate(dwn->display, d,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap);
|
||||
XftDraw *xft_draw = panel_get_xft_draw(d);
|
||||
if (xft_draw != NULL) {
|
||||
XftColor xft_color;
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &render_color, &xft_color);
|
||||
|
||||
XftDrawStringUtf8(xft_draw, &xft_color, font,
|
||||
XftColor *xft_color = panel_get_cached_xft_color(color);
|
||||
XftDrawStringUtf8(xft_draw, xft_color, font,
|
||||
x, y, (const FcChar8 *)text, len);
|
||||
|
||||
XftColorFree(dwn->display, DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &xft_color);
|
||||
XftDrawDestroy(xft_draw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -317,12 +419,13 @@ void panel_render(Panel *panel)
|
||||
int mouse_dist_x = key_counter_x + key_counter_width + WIDGET_SPACING;
|
||||
panel_render_mouse_distance(panel, mouse_dist_x, &mouse_dist_width);
|
||||
|
||||
int top_mem_width = 0;
|
||||
int top_mem_x = mouse_dist_x + mouse_dist_width + WIDGET_SPACING;
|
||||
panel_render_top_mem_process(panel, top_mem_x, &top_mem_width);
|
||||
int disk_space_width = 0;
|
||||
int disk_space_x = mouse_dist_x + mouse_dist_width + WIDGET_SPACING;
|
||||
panel_render_disk_space(panel, disk_space_x, &disk_space_width);
|
||||
|
||||
int clock_width = panel_text_width(clock_buffer, strlen(clock_buffer));
|
||||
int stats_width = panel_calculate_stats_width();
|
||||
int top_proc_width = panel_calculate_top_process_width();
|
||||
|
||||
int clock_x = panel->width - clock_width - PANEL_PADDING;
|
||||
panel_render_clock(panel, clock_x, &width);
|
||||
@@ -330,9 +433,14 @@ void panel_render(Panel *panel)
|
||||
int stats_x = clock_x - stats_width - WIDGET_SPACING;
|
||||
panel_render_system_stats(panel, stats_x, &width);
|
||||
|
||||
int news_start = top_mem_x + top_mem_width + WIDGET_SPACING;
|
||||
int news_max_width = stats_x - news_start - WIDGET_SPACING;
|
||||
int top_proc_x = stats_x - top_proc_width - WIDGET_SPACING;
|
||||
int actual_top_proc_width = 0;
|
||||
panel_render_top_process(panel, top_proc_x, &actual_top_proc_width);
|
||||
|
||||
int news_start = disk_space_x + disk_space_width + WIDGET_SPACING;
|
||||
int news_max_width = top_proc_x - news_start - WIDGET_SPACING;
|
||||
if (news_max_width > 100) {
|
||||
panel_set_news_region(news_start, news_max_width);
|
||||
int news_width = 0;
|
||||
news_render(panel, news_start, news_max_width, &news_width);
|
||||
}
|
||||
@@ -340,18 +448,20 @@ void panel_render(Panel *panel)
|
||||
|
||||
XCopyArea(dpy, panel->buffer, panel->window, dwn->gc,
|
||||
0, 0, panel->width, panel->height, 0, 0);
|
||||
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
void panel_render_all(void)
|
||||
{
|
||||
if (dwn->top_panel != NULL) {
|
||||
panel_render(dwn->top_panel);
|
||||
dwn->top_panel->dirty = false;
|
||||
}
|
||||
if (dwn->bottom_panel != NULL) {
|
||||
panel_render(dwn->bottom_panel);
|
||||
dwn->bottom_panel->dirty = false;
|
||||
}
|
||||
panels_dirty = false;
|
||||
XFlush(dwn->display);
|
||||
}
|
||||
|
||||
void panel_render_workspaces(Panel *panel, int x, int *width)
|
||||
@@ -456,9 +566,9 @@ void panel_render_taskbar(Panel *panel, int x, int *width)
|
||||
|
||||
int max_text_width = item_width - 8;
|
||||
bool truncated = false;
|
||||
while (panel_text_width(title, strlen(title)) > max_text_width && strlen(title) > 3) {
|
||||
size_t len = strlen(title);
|
||||
size_t cut = len - 1;
|
||||
size_t title_len = strlen(title);
|
||||
while (panel_text_width(title, (int)title_len) > max_text_width && title_len > 3) {
|
||||
size_t cut = title_len - 1;
|
||||
while (cut > 0 && (title[cut] & 0xC0) == 0x80) {
|
||||
cut--;
|
||||
}
|
||||
@@ -470,10 +580,12 @@ void panel_render_taskbar(Panel *panel, int x, int *width)
|
||||
cut = sizeof(title) - 4;
|
||||
}
|
||||
title[cut] = '\0';
|
||||
title_len = cut;
|
||||
truncated = true;
|
||||
}
|
||||
if (truncated) {
|
||||
strncat(title, "...", sizeof(title) - strlen(title) - 1);
|
||||
strncat(title, "...", sizeof(title) - title_len - 1);
|
||||
title_len += 3;
|
||||
}
|
||||
|
||||
unsigned long fg;
|
||||
@@ -805,22 +917,100 @@ void panel_update_system_stats(void)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
update_top_memory_process();
|
||||
long now = get_time_ms();
|
||||
if (now - last_memory_scan >= PROCESS_SCAN_INTERVAL_MS) {
|
||||
update_top_processes();
|
||||
last_memory_scan = now;
|
||||
}
|
||||
|
||||
static int prev_cpu = -1;
|
||||
static int prev_mem = -1;
|
||||
bool stats_changed = (prev_cpu != sys_stats.cpu_percent) ||
|
||||
(prev_mem != sys_stats.mem_percent);
|
||||
if (stats_changed) {
|
||||
prev_cpu = sys_stats.cpu_percent;
|
||||
prev_mem = sys_stats.mem_percent;
|
||||
panel_invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
static void update_top_memory_process(void)
|
||||
static ProcCpuState *find_cpu_history(int pid)
|
||||
{
|
||||
for (int i = 0; i < proc_cpu_history_count; i++) {
|
||||
if (proc_cpu_history[i].pid == pid) {
|
||||
return &proc_cpu_history[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ProcCpuState *add_cpu_history(int pid)
|
||||
{
|
||||
if (proc_cpu_history_count < PROC_CPU_HISTORY_SIZE) {
|
||||
ProcCpuState *state = &proc_cpu_history[proc_cpu_history_count++];
|
||||
state->pid = pid;
|
||||
state->prev_utime = 0;
|
||||
state->prev_stime = 0;
|
||||
state->prev_total_cpu = 0;
|
||||
return state;
|
||||
}
|
||||
|
||||
char path[32];
|
||||
for (int i = 0; i < PROC_CPU_HISTORY_SIZE; i++) {
|
||||
snprintf(path, sizeof(path), "/proc/%d", proc_cpu_history[i].pid);
|
||||
if (access(path, F_OK) != 0) {
|
||||
proc_cpu_history[i].pid = pid;
|
||||
proc_cpu_history[i].prev_utime = 0;
|
||||
proc_cpu_history[i].prev_stime = 0;
|
||||
proc_cpu_history[i].prev_total_cpu = 0;
|
||||
return &proc_cpu_history[i];
|
||||
}
|
||||
}
|
||||
|
||||
proc_cpu_history[0].pid = pid;
|
||||
proc_cpu_history[0].prev_utime = 0;
|
||||
proc_cpu_history[0].prev_stime = 0;
|
||||
proc_cpu_history[0].prev_total_cpu = 0;
|
||||
return &proc_cpu_history[0];
|
||||
}
|
||||
|
||||
static unsigned long long read_total_cpu_time(void)
|
||||
{
|
||||
FILE *fp = fopen("/proc/stat", "r");
|
||||
if (fp == NULL) return 0;
|
||||
|
||||
char line[256];
|
||||
unsigned long long total = 0;
|
||||
if (fgets(line, sizeof(line), fp) != NULL) {
|
||||
unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
|
||||
if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
|
||||
&user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal) >= 4) {
|
||||
total = user + nice + system + idle + iowait + irq + softirq + steal;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return total;
|
||||
}
|
||||
|
||||
static void update_top_processes(void)
|
||||
{
|
||||
DIR *proc_dir = opendir("/proc");
|
||||
if (proc_dir == NULL) {
|
||||
sys_stats.top_mem_process[0] = '\0';
|
||||
sys_stats.top_mem_percent = 0;
|
||||
sys_stats.top_mem_count = 0;
|
||||
sys_stats.top_cpu_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long max_rss = 0;
|
||||
char max_name[64] = "";
|
||||
unsigned long long current_total_cpu = read_total_cpu_time();
|
||||
unsigned long long cpu_delta = current_total_cpu - prev_total_cpu_time;
|
||||
|
||||
unsigned long top_rss[3] = {0, 0, 0};
|
||||
char top_mem_names[3][64] = {"", "", ""};
|
||||
unsigned long mem_total_kb = (unsigned long)sys_stats.mem_total_mb * 1024;
|
||||
|
||||
int top_cpu_pcts[3] = {0, 0, 0};
|
||||
char top_cpu_names[3][64] = {"", "", ""};
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(proc_dir)) != NULL) {
|
||||
bool is_pid = true;
|
||||
@@ -834,8 +1024,10 @@ static void update_top_memory_process(void)
|
||||
}
|
||||
if (!is_pid || name_len > 20) continue;
|
||||
|
||||
int pid = atoi(entry->d_name);
|
||||
|
||||
char status_path[64];
|
||||
snprintf(status_path, sizeof(status_path), "/proc/%.20s/status", entry->d_name);
|
||||
snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid);
|
||||
|
||||
FILE *fp = fopen(status_path, "r");
|
||||
if (fp == NULL) continue;
|
||||
@@ -858,19 +1050,91 @@ static void update_top_memory_process(void)
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (rss_kb > max_rss && name[0] != '\0') {
|
||||
max_rss = rss_kb;
|
||||
snprintf(max_name, sizeof(max_name), "%s", name);
|
||||
if (rss_kb > 0 && name[0] != '\0') {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (rss_kb > top_rss[i]) {
|
||||
for (int j = 2; j > i; j--) {
|
||||
top_rss[j] = top_rss[j - 1];
|
||||
memcpy(top_mem_names[j], top_mem_names[j - 1], 64);
|
||||
}
|
||||
top_rss[i] = rss_kb;
|
||||
memcpy(top_mem_names[i], name, 64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char stat_path[64];
|
||||
snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", pid);
|
||||
fp = fopen(stat_path, "r");
|
||||
if (fp == NULL) continue;
|
||||
|
||||
unsigned long long utime = 0, stime = 0;
|
||||
char stat_line[512];
|
||||
if (fgets(stat_line, sizeof(stat_line), fp) != NULL) {
|
||||
char *close_paren = strrchr(stat_line, ')');
|
||||
if (close_paren != NULL) {
|
||||
if (sscanf(close_paren + 2, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu",
|
||||
&utime, &stime) == 2) {
|
||||
ProcCpuState *hist = find_cpu_history(pid);
|
||||
if (hist == NULL) {
|
||||
hist = add_cpu_history(pid);
|
||||
}
|
||||
|
||||
if (hist->prev_total_cpu > 0 && cpu_delta > 0) {
|
||||
unsigned long long proc_delta = (utime + stime) - (hist->prev_utime + hist->prev_stime);
|
||||
int num_cpus = (int)sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (num_cpus < 1) num_cpus = 1;
|
||||
int cpu_pct = (int)((proc_delta * 100 * (unsigned long long)num_cpus) / cpu_delta);
|
||||
if (cpu_pct > 100) cpu_pct = 100;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (cpu_pct > top_cpu_pcts[i] || top_cpu_names[i][0] == '\0') {
|
||||
if (cpu_pct > top_cpu_pcts[i]) {
|
||||
for (int j = 2; j > i; j--) {
|
||||
top_cpu_pcts[j] = top_cpu_pcts[j - 1];
|
||||
memcpy(top_cpu_names[j], top_cpu_names[j - 1], 64);
|
||||
}
|
||||
}
|
||||
top_cpu_pcts[i] = cpu_pct;
|
||||
memcpy(top_cpu_names[i], name, 64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hist->prev_utime = utime;
|
||||
hist->prev_stime = stime;
|
||||
hist->prev_total_cpu = current_total_cpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
closedir(proc_dir);
|
||||
|
||||
snprintf(sys_stats.top_mem_process, sizeof(sys_stats.top_mem_process), "%s", max_name);
|
||||
prev_total_cpu_time = current_total_cpu;
|
||||
|
||||
if (mem_total_kb > 0) {
|
||||
sys_stats.top_mem_percent = (int)((max_rss * 100) / mem_total_kb);
|
||||
} else {
|
||||
sys_stats.top_mem_percent = 0;
|
||||
sys_stats.top_mem_count = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (top_mem_names[i][0] != '\0') {
|
||||
memcpy(sys_stats.top_mem_processes[i], top_mem_names[i], 64);
|
||||
if (mem_total_kb > 0) {
|
||||
sys_stats.top_mem_percents[i] = (int)((top_rss[i] * 100) / mem_total_kb);
|
||||
} else {
|
||||
sys_stats.top_mem_percents[i] = 0;
|
||||
}
|
||||
sys_stats.top_mem_count++;
|
||||
}
|
||||
}
|
||||
|
||||
sys_stats.top_cpu_count = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (top_cpu_names[i][0] != '\0') {
|
||||
memcpy(sys_stats.top_cpu_processes[i], top_cpu_names[i], 64);
|
||||
sys_stats.top_cpu_percents[i] = top_cpu_pcts[i];
|
||||
sys_stats.top_cpu_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1100,7 +1364,23 @@ static void panel_render_mouse_distance(Panel *panel, int x, int *width)
|
||||
*width = panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
static void panel_render_top_mem_process(Panel *panel, int x, int *width)
|
||||
static void update_disk_space(void)
|
||||
{
|
||||
long now = get_time_ms();
|
||||
if (now - disk_last_update < DISK_UPDATE_INTERVAL_MS && disk_last_update > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct statvfs stat;
|
||||
if (statvfs("/", &stat) == 0) {
|
||||
unsigned long long block_size = stat.f_frsize;
|
||||
disk_free_gb = (double)(stat.f_bavail * block_size) / (1024.0 * 1024.0 * 1024.0);
|
||||
disk_total_gb = (double)(stat.f_blocks * block_size) / (1024.0 * 1024.0 * 1024.0);
|
||||
}
|
||||
disk_last_update = now;
|
||||
}
|
||||
|
||||
static void panel_render_disk_space(Panel *panel, int x, int *width)
|
||||
{
|
||||
if (panel == NULL || width == NULL) {
|
||||
*width = 0;
|
||||
@@ -1110,30 +1390,247 @@ static void panel_render_top_mem_process(Panel *panel, int x, int *width)
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
if (sys_stats.top_mem_process[0] == '\0') {
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
update_disk_space();
|
||||
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
|
||||
char buf[32];
|
||||
int len;
|
||||
|
||||
if (disk_total_gb >= 1000.0) {
|
||||
len = snprintf(buf, sizeof(buf), "Disk:%.1fT/%.1fT",
|
||||
disk_free_gb / 1024.0, disk_total_gb / 1024.0);
|
||||
} else {
|
||||
len = snprintf(buf, sizeof(buf), "Disk:%.0fG/%.0fG",
|
||||
disk_free_gb, disk_total_gb);
|
||||
}
|
||||
|
||||
int used_percent = 0;
|
||||
if (disk_total_gb > 0.0) {
|
||||
used_percent = (int)(((disk_total_gb - disk_free_gb) / disk_total_gb) * 100.0);
|
||||
}
|
||||
|
||||
unsigned long color = colors->panel_fg;
|
||||
if (sys_stats.top_mem_percent >= 50) {
|
||||
if (used_percent >= 90) {
|
||||
color = colors->workspace_urgent;
|
||||
} else if (sys_stats.top_mem_percent >= 25) {
|
||||
} else if (used_percent >= 75) {
|
||||
color = 0xFFA500;
|
||||
}
|
||||
color = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_TOP_MEM);
|
||||
|
||||
char buf[128];
|
||||
int len = snprintf(buf, sizeof(buf), "%s %d%%",
|
||||
sys_stats.top_mem_process, sys_stats.top_mem_percent);
|
||||
|
||||
panel_draw_text(panel->buffer, x, text_y, buf, len, color);
|
||||
unsigned long fg = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_DISK_SPACE);
|
||||
panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
|
||||
*width = panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
static int panel_calculate_top_process_width(void)
|
||||
{
|
||||
if (dwn->xft_font == NULL && dwn->font == NULL) return 0;
|
||||
|
||||
int mem_count = sys_stats.top_mem_count;
|
||||
int cpu_count = sys_stats.top_cpu_count;
|
||||
if (mem_count == 0 && cpu_count == 0) return 0;
|
||||
|
||||
int idx = sys_stats.process_display_index;
|
||||
char buf[128];
|
||||
char name_buf[TOP_PROCESS_NAME_MAX_LEN + 1];
|
||||
const char *proc_name;
|
||||
size_t name_len;
|
||||
int len;
|
||||
int total = 0;
|
||||
|
||||
if (mem_count > 0) {
|
||||
int mem_idx = (idx >= mem_count) ? 0 : idx;
|
||||
proc_name = sys_stats.top_mem_processes[mem_idx];
|
||||
name_len = strlen(proc_name);
|
||||
if (name_len > TOP_PROCESS_NAME_MAX_LEN) {
|
||||
strncpy(name_buf, proc_name, TOP_PROCESS_NAME_MAX_LEN - 2);
|
||||
name_buf[TOP_PROCESS_NAME_MAX_LEN - 2] = '\0';
|
||||
strncat(name_buf, "..", 3);
|
||||
proc_name = name_buf;
|
||||
}
|
||||
len = snprintf(buf, sizeof(buf), "M:%s %d%%", proc_name, sys_stats.top_mem_percents[mem_idx]);
|
||||
total += panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
if (cpu_count > 0) {
|
||||
if (total > 0) total += WIDGET_SPACING;
|
||||
int cpu_idx = (idx >= cpu_count) ? 0 : idx;
|
||||
proc_name = sys_stats.top_cpu_processes[cpu_idx];
|
||||
name_len = strlen(proc_name);
|
||||
if (name_len > TOP_PROCESS_NAME_MAX_LEN) {
|
||||
strncpy(name_buf, proc_name, TOP_PROCESS_NAME_MAX_LEN - 2);
|
||||
name_buf[TOP_PROCESS_NAME_MAX_LEN - 2] = '\0';
|
||||
strncat(name_buf, "..", 3);
|
||||
proc_name = name_buf;
|
||||
}
|
||||
len = snprintf(buf, sizeof(buf), "C:%s %d%%", proc_name, sys_stats.top_cpu_percents[cpu_idx]);
|
||||
total += panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static void panel_render_top_process(Panel *panel, int x, int *width)
|
||||
{
|
||||
if (panel == NULL || width == NULL) {
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
if (dwn->xft_font == NULL && dwn->font == NULL) {
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int mem_count = sys_stats.top_mem_count;
|
||||
int cpu_count = sys_stats.top_cpu_count;
|
||||
if (mem_count == 0 && cpu_count == 0) {
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
long now = get_time_ms();
|
||||
if (now - sys_stats.process_last_cycle >= 3000) {
|
||||
sys_stats.process_display_index++;
|
||||
if (sys_stats.process_display_index >= 3) {
|
||||
sys_stats.process_display_index = 0;
|
||||
}
|
||||
sys_stats.process_last_cycle = now;
|
||||
}
|
||||
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
int idx = sys_stats.process_display_index;
|
||||
|
||||
char buf[128];
|
||||
char name_buf[TOP_PROCESS_NAME_MAX_LEN + 1];
|
||||
int len;
|
||||
unsigned long color;
|
||||
const char *proc_name;
|
||||
size_t name_len;
|
||||
int text_width;
|
||||
|
||||
int total_width = panel_calculate_top_process_width();
|
||||
int right_edge = x + total_width;
|
||||
int current_right = right_edge;
|
||||
|
||||
if (cpu_count > 0) {
|
||||
int cpu_idx = (idx >= cpu_count) ? 0 : idx;
|
||||
int percent = sys_stats.top_cpu_percents[cpu_idx];
|
||||
color = colors->panel_fg;
|
||||
if (percent >= 50) {
|
||||
color = 0x4169E1;
|
||||
} else if (percent >= 25) {
|
||||
color = 0x00CED1;
|
||||
}
|
||||
color = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_TOP_CPU);
|
||||
proc_name = sys_stats.top_cpu_processes[cpu_idx];
|
||||
name_len = strlen(proc_name);
|
||||
if (name_len > TOP_PROCESS_NAME_MAX_LEN) {
|
||||
strncpy(name_buf, proc_name, TOP_PROCESS_NAME_MAX_LEN - 2);
|
||||
name_buf[TOP_PROCESS_NAME_MAX_LEN - 2] = '\0';
|
||||
strncat(name_buf, "..", 3);
|
||||
proc_name = name_buf;
|
||||
}
|
||||
len = snprintf(buf, sizeof(buf), "C:%s %d%%", proc_name, percent);
|
||||
text_width = panel_text_width(buf, len);
|
||||
int text_x = current_right - text_width;
|
||||
panel_draw_text(panel->buffer, text_x, text_y, buf, len, color);
|
||||
current_right = text_x - WIDGET_SPACING;
|
||||
}
|
||||
|
||||
if (mem_count > 0) {
|
||||
int mem_idx = (idx >= mem_count) ? 0 : idx;
|
||||
int percent = sys_stats.top_mem_percents[mem_idx];
|
||||
color = colors->panel_fg;
|
||||
if (percent >= 50) {
|
||||
color = colors->workspace_urgent;
|
||||
} else if (percent >= 25) {
|
||||
color = 0xFFA500;
|
||||
}
|
||||
color = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_TOP_MEM);
|
||||
proc_name = sys_stats.top_mem_processes[mem_idx];
|
||||
name_len = strlen(proc_name);
|
||||
if (name_len > TOP_PROCESS_NAME_MAX_LEN) {
|
||||
strncpy(name_buf, proc_name, TOP_PROCESS_NAME_MAX_LEN - 2);
|
||||
name_buf[TOP_PROCESS_NAME_MAX_LEN - 2] = '\0';
|
||||
strncat(name_buf, "..", 3);
|
||||
proc_name = name_buf;
|
||||
}
|
||||
len = snprintf(buf, sizeof(buf), "M:%s %d%%", proc_name, percent);
|
||||
text_width = panel_text_width(buf, len);
|
||||
int text_x = current_right - text_width;
|
||||
panel_draw_text(panel->buffer, text_x, text_y, buf, len, color);
|
||||
}
|
||||
|
||||
*width = total_width;
|
||||
}
|
||||
|
||||
|
||||
void panel_invalidate(void)
|
||||
{
|
||||
panels_dirty = true;
|
||||
if (dwn->top_panel) dwn->top_panel->dirty = true;
|
||||
if (dwn->bottom_panel) dwn->bottom_panel->dirty = true;
|
||||
}
|
||||
|
||||
void panel_invalidate_taskbar(void)
|
||||
{
|
||||
panels_dirty = true;
|
||||
if (dwn->top_panel) dwn->top_panel->dirty = true;
|
||||
}
|
||||
|
||||
void panel_invalidate_news(void)
|
||||
{
|
||||
news_region_dirty = true;
|
||||
}
|
||||
|
||||
void panel_set_news_region(int x, int width)
|
||||
{
|
||||
news_region_x = x;
|
||||
news_region_width = width;
|
||||
}
|
||||
|
||||
void panel_render_news_only(void)
|
||||
{
|
||||
if (dwn->bottom_panel == NULL || !news_region_dirty) {
|
||||
return;
|
||||
}
|
||||
if (news_region_width <= 0) {
|
||||
news_region_dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Panel *panel = dwn->bottom_panel;
|
||||
Display *dpy = dwn->display;
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
|
||||
unsigned long bg = ambient_glow_bg(colors->panel_bg,
|
||||
dwn->ambient_phase + PHASE_OFFSET_PANEL_BG);
|
||||
XSetForeground(dpy, dwn->gc, bg);
|
||||
XFillRectangle(dpy, panel->buffer, dwn->gc,
|
||||
news_region_x, 0, (unsigned int)news_region_width, (unsigned int)panel->height);
|
||||
|
||||
int width;
|
||||
news_render(panel, news_region_x, news_region_width, &width);
|
||||
|
||||
XCopyArea(dpy, panel->buffer, panel->window, dwn->gc,
|
||||
news_region_x, 0, (unsigned int)news_region_width, (unsigned int)panel->height,
|
||||
news_region_x, 0);
|
||||
|
||||
news_region_dirty = false;
|
||||
}
|
||||
|
||||
bool panel_needs_render(void)
|
||||
{
|
||||
return panels_dirty;
|
||||
}
|
||||
|
||||
bool panel_news_needs_render(void)
|
||||
{
|
||||
return news_region_dirty;
|
||||
}
|
||||
|
||||
void panel_init_systray(void)
|
||||
{
|
||||
|
||||
+80
-21
@@ -63,6 +63,83 @@ static int xembed_icons_x = 0;
|
||||
static void wifi_update_state_internal(void);
|
||||
static void audio_update_state_internal(void);
|
||||
|
||||
static XftDraw *systray_xft_draw = NULL;
|
||||
static Drawable systray_xft_drawable = None;
|
||||
static Visual *systray_cached_visual = NULL;
|
||||
|
||||
#define SYSTRAY_XFT_COLOR_CACHE_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
unsigned long pixel;
|
||||
XftColor xft_color;
|
||||
bool valid;
|
||||
int lru_counter;
|
||||
} SystrayCachedColor;
|
||||
|
||||
static SystrayCachedColor systray_color_cache[SYSTRAY_XFT_COLOR_CACHE_SIZE];
|
||||
static int systray_color_lru_counter = 0;
|
||||
|
||||
static XftDraw *systray_get_xft_draw(Drawable d)
|
||||
{
|
||||
if (systray_xft_drawable == d && systray_xft_draw != NULL) {
|
||||
return systray_xft_draw;
|
||||
}
|
||||
if (systray_xft_draw != NULL) {
|
||||
XftDrawDestroy(systray_xft_draw);
|
||||
}
|
||||
if (systray_cached_visual == NULL) {
|
||||
systray_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
systray_xft_draw = XftDrawCreate(dwn->display, d, systray_cached_visual, dwn->colormap);
|
||||
systray_xft_drawable = d;
|
||||
return systray_xft_draw;
|
||||
}
|
||||
|
||||
static XftColor *systray_get_cached_color(unsigned long color)
|
||||
{
|
||||
if (systray_cached_visual == NULL) {
|
||||
systray_cached_visual = DefaultVisual(dwn->display, dwn->screen);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SYSTRAY_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (systray_color_cache[i].valid && systray_color_cache[i].pixel == color) {
|
||||
systray_color_cache[i].lru_counter = ++systray_color_lru_counter;
|
||||
return &systray_color_cache[i].xft_color;
|
||||
}
|
||||
}
|
||||
|
||||
int oldest_idx = 0;
|
||||
int oldest_lru = systray_color_cache[0].lru_counter;
|
||||
for (int i = 1; i < SYSTRAY_XFT_COLOR_CACHE_SIZE; i++) {
|
||||
if (!systray_color_cache[i].valid) {
|
||||
oldest_idx = i;
|
||||
break;
|
||||
}
|
||||
if (systray_color_cache[i].lru_counter < oldest_lru) {
|
||||
oldest_lru = systray_color_cache[i].lru_counter;
|
||||
oldest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (systray_color_cache[oldest_idx].valid) {
|
||||
XftColorFree(dwn->display, systray_cached_visual, dwn->colormap,
|
||||
&systray_color_cache[oldest_idx].xft_color);
|
||||
}
|
||||
|
||||
XRenderColor render_color;
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display, systray_cached_visual, dwn->colormap,
|
||||
&render_color, &systray_color_cache[oldest_idx].xft_color);
|
||||
systray_color_cache[oldest_idx].pixel = color;
|
||||
systray_color_cache[oldest_idx].valid = true;
|
||||
systray_color_cache[oldest_idx].lru_counter = ++systray_color_lru_counter;
|
||||
|
||||
return &systray_color_cache[oldest_idx].xft_color;
|
||||
}
|
||||
|
||||
static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned long color)
|
||||
{
|
||||
@@ -71,29 +148,11 @@ static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned
|
||||
}
|
||||
|
||||
if (dwn->xft_font != NULL) {
|
||||
XftDraw *xft_draw = XftDrawCreate(dwn->display, d,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap);
|
||||
XftDraw *xft_draw = systray_get_xft_draw(d);
|
||||
if (xft_draw != NULL) {
|
||||
XftColor xft_color;
|
||||
XRenderColor render_color;
|
||||
|
||||
render_color.red = ((color >> 16) & 0xFF) * 257;
|
||||
render_color.green = ((color >> 8) & 0xFF) * 257;
|
||||
render_color.blue = (color & 0xFF) * 257;
|
||||
render_color.alpha = 0xFFFF;
|
||||
|
||||
XftColorAllocValue(dwn->display,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &render_color, &xft_color);
|
||||
|
||||
XftDrawStringUtf8(xft_draw, &xft_color, dwn->xft_font,
|
||||
XftColor *xft_color = systray_get_cached_color(color);
|
||||
XftDrawStringUtf8(xft_draw, xft_color, dwn->xft_font,
|
||||
x, y, (const FcChar8 *)text, strlen(text));
|
||||
|
||||
XftColorFree(dwn->display,
|
||||
DefaultVisual(dwn->display, dwn->screen),
|
||||
dwn->colormap, &xft_color);
|
||||
XftDrawDestroy(xft_draw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-7
@@ -796,14 +796,39 @@ unsigned long dim_color(unsigned long color, float factor)
|
||||
return rgb_to_pixel(r, g, b);
|
||||
}
|
||||
|
||||
#define SINE_TABLE_SIZE 256
|
||||
#define TWO_PI 6.283185307f
|
||||
|
||||
static float sine_table[SINE_TABLE_SIZE];
|
||||
static bool sine_table_initialized = false;
|
||||
|
||||
static float fast_sin(float phase)
|
||||
{
|
||||
if (!sine_table_initialized) {
|
||||
for (int i = 0; i < SINE_TABLE_SIZE; i++) {
|
||||
sine_table[i] = sinf((float)i * TWO_PI / (float)SINE_TABLE_SIZE);
|
||||
}
|
||||
sine_table_initialized = true;
|
||||
}
|
||||
|
||||
while (phase < 0.0f) phase += TWO_PI;
|
||||
while (phase >= TWO_PI) phase -= TWO_PI;
|
||||
|
||||
int idx = (int)(phase * (float)SINE_TABLE_SIZE / TWO_PI);
|
||||
if (idx >= SINE_TABLE_SIZE) idx = SINE_TABLE_SIZE - 1;
|
||||
if (idx < 0) idx = 0;
|
||||
|
||||
return sine_table[idx];
|
||||
}
|
||||
|
||||
unsigned long glow_color(unsigned long base, float phase)
|
||||
{
|
||||
int r, g, b;
|
||||
color_to_rgb(base, &r, &g, &b);
|
||||
|
||||
float mod_r = 0.25f * sinf(phase);
|
||||
float mod_g = 0.25f * sinf(phase + 2.094f);
|
||||
float mod_b = 0.25f * sinf(phase + 4.189f);
|
||||
float mod_r = 0.25f * fast_sin(phase);
|
||||
float mod_g = 0.25f * fast_sin(phase + 2.094f);
|
||||
float mod_b = 0.25f * fast_sin(phase + 4.189f);
|
||||
|
||||
r = r + (int)(r * mod_r);
|
||||
g = g + (int)(g * mod_g);
|
||||
@@ -824,7 +849,7 @@ unsigned long ambient_glow_bg(unsigned long base, float phase)
|
||||
int r, g, b;
|
||||
color_to_rgb(base, &r, &g, &b);
|
||||
|
||||
float mod = 0.25f * sinf(phase);
|
||||
float mod = 0.25f * fast_sin(phase);
|
||||
|
||||
r = r + (int)(r * mod);
|
||||
g = g + (int)(g * mod);
|
||||
@@ -845,9 +870,9 @@ unsigned long ambient_glow_accent(unsigned long base, float phase)
|
||||
int r, g, b;
|
||||
color_to_rgb(base, &r, &g, &b);
|
||||
|
||||
float mod_r = 0.45f * sinf(phase);
|
||||
float mod_g = 0.45f * sinf(phase + 2.094f);
|
||||
float mod_b = 0.45f * sinf(phase + 4.189f);
|
||||
float mod_r = 0.45f * fast_sin(phase);
|
||||
float mod_g = 0.45f * fast_sin(phase + 2.094f);
|
||||
float mod_b = 0.45f * fast_sin(phase + 4.189f);
|
||||
|
||||
r = r + (int)(r * mod_r);
|
||||
g = g + (int)(g * mod_g);
|
||||
|
||||
Reference in New Issue
Block a user