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:
2026-01-27 01:50:47 +00:00
parent 2f096a55ac
commit a0d5b1b091
18 changed files with 1216 additions and 336 deletions
+12 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);