feat: add ambient glow effects with key counter, mouse distance, and top memory stats
build: add XInput2 library dependency for raw key event monitoring
This commit is contained in:
parent
b59c279bb0
commit
deb173e2d2
6
Makefile
6
Makefile
@ -5,7 +5,7 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Wpedantic -Wshadow -O2 -I./include
|
||||
CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2
|
||||
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread -lXtst
|
||||
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread -lXtst -lXi
|
||||
|
||||
# Directories
|
||||
SRC_DIR = src
|
||||
@ -28,8 +28,8 @@ DATADIR = $(PREFIX)/share
|
||||
SYSCONFDIR = /etc
|
||||
|
||||
# Get pkg-config flags (with error handling)
|
||||
PKG_CFLAGS := $(shell pkg-config --cflags x11 xext xinerama xrandr xft fontconfig dbus-1 xtst libpng tesseract lept 2>/dev/null)
|
||||
PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 xtst libpng tesseract lept 2>/dev/null)
|
||||
PKG_CFLAGS := $(shell pkg-config --cflags x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null)
|
||||
PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null)
|
||||
|
||||
# Use pkg-config if available
|
||||
ifneq ($(PKG_LIBS),)
|
||||
|
||||
BIN
build/client.o
BIN
build/client.o
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
BIN
build/news.o
BIN
build/news.o
Binary file not shown.
BIN
build/panel.o
BIN
build/panel.o
Binary file not shown.
BIN
build/systray.o
BIN
build/systray.o
Binary file not shown.
BIN
build/util.o
BIN
build/util.o
Binary file not shown.
@ -203,8 +203,32 @@ typedef struct {
|
||||
bool desktop_shown;
|
||||
Window desktop_minimized[MAX_CLIENTS];
|
||||
int desktop_minimized_count;
|
||||
|
||||
float ambient_phase;
|
||||
float ambient_speed;
|
||||
long typing_activity_time;
|
||||
|
||||
unsigned long key_press_count;
|
||||
long key_delete_flicker_time;
|
||||
int xi2_opcode;
|
||||
double mouse_distance_pixels;
|
||||
int last_mouse_x;
|
||||
int last_mouse_y;
|
||||
bool mouse_tracking_initialized;
|
||||
} DWNState;
|
||||
|
||||
#define PHASE_OFFSET_PANEL_BG 0.0f
|
||||
#define PHASE_OFFSET_WORKSPACES 0.8f
|
||||
#define PHASE_OFFSET_TASKBAR 1.6f
|
||||
#define PHASE_OFFSET_CLOCK 2.4f
|
||||
#define PHASE_OFFSET_STATS 3.2f
|
||||
#define PHASE_OFFSET_TOP_MEM 4.0f
|
||||
#define PHASE_OFFSET_DECORATIONS 4.8f
|
||||
#define PHASE_OFFSET_SYSTRAY 5.6f
|
||||
#define PHASE_OFFSET_NEWS 1.2f
|
||||
#define PHASE_OFFSET_KEY_COUNTER 5.2f
|
||||
#define PHASE_OFFSET_MOUSE_DIST 5.8f
|
||||
|
||||
extern DWNState *dwn;
|
||||
|
||||
int dwn_init(void);
|
||||
|
||||
@ -59,6 +59,8 @@ unsigned long adjust_color_for_background(unsigned long color, unsigned long bg)
|
||||
unsigned long interpolate_color(unsigned long from, unsigned long to, float progress);
|
||||
unsigned long dim_color(unsigned long color, float factor);
|
||||
unsigned long glow_color(unsigned long base, float phase);
|
||||
unsigned long ambient_glow_bg(unsigned long base, float phase);
|
||||
unsigned long ambient_glow_accent(unsigned long base, float phase);
|
||||
|
||||
long get_time_ms(void);
|
||||
void sleep_ms(int ms);
|
||||
|
||||
13
src/client.c
13
src/client.c
@ -1336,7 +1336,8 @@ unsigned long client_get_glow_text_color(Client *client)
|
||||
if (client == NULL || !client->text_glow.active) {
|
||||
return 0xFFFFFF;
|
||||
}
|
||||
return glow_color(client->text_glow.base_color, client->text_glow.phase);
|
||||
float phase = dwn->ambient_phase + client->text_glow.phase;
|
||||
return glow_color(client->text_glow.base_color, phase);
|
||||
}
|
||||
|
||||
void client_update_animations(void)
|
||||
@ -1361,20 +1362,12 @@ void client_update_animations(void)
|
||||
}
|
||||
|
||||
if (c->text_glow.active && c->workspace == (unsigned int)dwn->current_workspace) {
|
||||
c->text_glow.phase += 0.01f * c->text_glow.speed;
|
||||
c->text_glow.phase += 0.005f * c->text_glow.speed;
|
||||
if (c->text_glow.phase > 6.283f) {
|
||||
c->text_glow.phase -= 6.283f;
|
||||
}
|
||||
needs_redraw = true;
|
||||
}
|
||||
|
||||
if (c->frame != None && c->workspace == (unsigned int)dwn->current_workspace) {
|
||||
if (!(c->flags & CLIENT_MINIMIZED)) {
|
||||
Workspace *ws = workspace_get(c->workspace);
|
||||
bool focused = (ws != NULL && ws->focused == c);
|
||||
decorations_render(c, focused);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_redraw) {
|
||||
|
||||
@ -64,7 +64,8 @@ void decorations_render_title_bar(Client *client, bool focused)
|
||||
|
||||
Workspace *ws = workspace_get(client->workspace);
|
||||
bool is_focused = (ws != NULL && ws->focused == client);
|
||||
unsigned long bg_color = is_focused ? colors->title_focused_bg : colors->title_unfocused_bg;
|
||||
unsigned long base_bg = is_focused ? colors->title_focused_bg : colors->title_unfocused_bg;
|
||||
unsigned long bg_color = ambient_glow_bg(base_bg, dwn->ambient_phase + PHASE_OFFSET_DECORATIONS);
|
||||
unsigned long fg_color = client->taskbar_color;
|
||||
fg_color = adjust_color_for_background(fg_color, bg_color);
|
||||
|
||||
@ -173,11 +174,16 @@ void decorations_render_buttons(Client *client, bool focused)
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
Workspace *ws = workspace_get(client->workspace);
|
||||
bool is_focused = (ws != NULL && ws->focused == client);
|
||||
unsigned long bg_color = is_focused ? colors->title_focused_bg : colors->title_unfocused_bg;
|
||||
unsigned long base_bg = is_focused ? colors->title_focused_bg : colors->title_unfocused_bg;
|
||||
unsigned long bg_color = ambient_glow_bg(base_bg, dwn->ambient_phase + PHASE_OFFSET_DECORATIONS);
|
||||
|
||||
unsigned long close_color = ambient_glow_accent(0xff0055, dwn->ambient_phase + PHASE_OFFSET_DECORATIONS);
|
||||
unsigned long max_color = ambient_glow_accent(0x00ff00, dwn->ambient_phase + PHASE_OFFSET_DECORATIONS);
|
||||
unsigned long min_color = ambient_glow_accent(0xffff00, dwn->ambient_phase + PHASE_OFFSET_DECORATIONS);
|
||||
|
||||
XSetForeground(dpy, dwn->gc, bg_color);
|
||||
XFillRectangle(dpy, client->frame, dwn->gc, close_x, button_y, BUTTON_SIZE, BUTTON_SIZE);
|
||||
XSetForeground(dpy, dwn->gc, 0xff0055); // Neon Red/Pink
|
||||
XSetForeground(dpy, dwn->gc, close_color);
|
||||
XDrawLine(dpy, client->frame, dwn->gc,
|
||||
close_x + 3, button_y + 3,
|
||||
close_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 4);
|
||||
@ -187,14 +193,14 @@ void decorations_render_buttons(Client *client, bool focused)
|
||||
|
||||
XSetForeground(dpy, dwn->gc, bg_color);
|
||||
XFillRectangle(dpy, client->frame, dwn->gc, max_x, button_y, BUTTON_SIZE, BUTTON_SIZE);
|
||||
XSetForeground(dpy, dwn->gc, 0x00ff00); // Neon Green
|
||||
XSetForeground(dpy, dwn->gc, max_color);
|
||||
XDrawRectangle(dpy, client->frame, dwn->gc,
|
||||
max_x + 3, button_y + 3,
|
||||
BUTTON_SIZE - 7, BUTTON_SIZE - 7);
|
||||
|
||||
XSetForeground(dpy, dwn->gc, bg_color);
|
||||
XFillRectangle(dpy, client->frame, dwn->gc, min_x, button_y, BUTTON_SIZE, BUTTON_SIZE);
|
||||
XSetForeground(dpy, dwn->gc, 0xffff00); // Neon Yellow
|
||||
XSetForeground(dpy, dwn->gc, min_color);
|
||||
XDrawLine(dpy, client->frame, dwn->gc,
|
||||
min_x + 3, button_y + BUTTON_SIZE - 5,
|
||||
min_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 5);
|
||||
|
||||
@ -436,6 +436,7 @@ void keys_handle_press(XKeyEvent *ev)
|
||||
|
||||
KeySym keysym = XLookupKeysym(ev, 0);
|
||||
|
||||
|
||||
if (keysym == XK_Super_L || keysym == XK_Super_R) {
|
||||
super_pressed = true;
|
||||
super_used_in_combo = false;
|
||||
|
||||
112
src/main.c
112
src/main.c
@ -31,10 +31,14 @@
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <sys/select.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysymdef.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
DWNState *dwn = NULL;
|
||||
static DWNState dwn_state;
|
||||
@ -134,6 +138,71 @@ static bool check_other_wm(void)
|
||||
return wm_detected != 0;
|
||||
}
|
||||
|
||||
static void init_xinput2(void)
|
||||
{
|
||||
int event, error;
|
||||
if (!XQueryExtension(dwn->display, "XInputExtension",
|
||||
&dwn->xi2_opcode, &event, &error)) {
|
||||
LOG_WARN("XInput2 extension not available - key counting disabled");
|
||||
dwn->xi2_opcode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int major = 2, minor = 0;
|
||||
if (XIQueryVersion(dwn->display, &major, &minor) != Success) {
|
||||
LOG_WARN("XInput2 version 2.0 not available - key counting disabled");
|
||||
dwn->xi2_opcode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
XIEventMask evmask;
|
||||
unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {0};
|
||||
|
||||
evmask.deviceid = XIAllMasterDevices;
|
||||
evmask.mask_len = sizeof(mask);
|
||||
evmask.mask = mask;
|
||||
|
||||
XISetMask(mask, XI_RawKeyPress);
|
||||
XISetMask(mask, XI_RawKeyRelease);
|
||||
|
||||
XISelectEvents(dwn->display, dwn->root, &evmask, 1);
|
||||
|
||||
LOG_INFO("XInput2 initialized for raw key event monitoring");
|
||||
}
|
||||
|
||||
static void handle_xi2_event(XGenericEventCookie *cookie)
|
||||
{
|
||||
if (cookie->type != GenericEvent || cookie->extension != dwn->xi2_opcode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!XGetEventData(dwn->display, cookie)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cookie->evtype == XI_RawKeyPress) {
|
||||
XIRawEvent *raw = (XIRawEvent *)cookie->data;
|
||||
KeyCode keycode = (KeyCode)raw->detail;
|
||||
KeySym keysym = XkbKeycodeToKeysym(dwn->display, keycode, 0, 0);
|
||||
|
||||
if (keysym != XK_Super_L && keysym != XK_Super_R &&
|
||||
keysym != XK_Alt_L && keysym != XK_Alt_R &&
|
||||
keysym != XK_Control_L && keysym != XK_Control_R &&
|
||||
keysym != XK_Shift_L && keysym != XK_Shift_R &&
|
||||
keysym != XK_Caps_Lock && keysym != XK_Num_Lock &&
|
||||
keysym != XK_Scroll_Lock) {
|
||||
dwn->key_press_count++;
|
||||
dwn->typing_activity_time = get_time_ms();
|
||||
|
||||
if (keysym == XK_BackSpace || keysym == XK_Delete) {
|
||||
dwn->key_delete_flicker_time = get_time_ms();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XFreeEventData(dwn->display, cookie);
|
||||
}
|
||||
|
||||
static void init_monitors(void)
|
||||
{
|
||||
if (!XineramaIsActive(dwn->display)) {
|
||||
@ -790,6 +859,8 @@ int dwn_init(void)
|
||||
|
||||
init_monitors();
|
||||
|
||||
init_xinput2();
|
||||
|
||||
workspace_init();
|
||||
|
||||
decorations_init();
|
||||
@ -842,6 +913,10 @@ int dwn_init(void)
|
||||
|
||||
dwn->running = true;
|
||||
|
||||
dwn->ambient_phase = 0.0f;
|
||||
dwn->ambient_speed = 0.5f;
|
||||
dwn->typing_activity_time = 0;
|
||||
|
||||
LOG_INFO("DWN initialized successfully");
|
||||
|
||||
return 0;
|
||||
@ -913,6 +988,11 @@ void dwn_run(void)
|
||||
while (XPending(dwn->display)) {
|
||||
XEvent ev;
|
||||
XNextEvent(dwn->display, &ev);
|
||||
|
||||
if (ev.type == GenericEvent && dwn->xi2_opcode > 0) {
|
||||
handle_xi2_event(&ev.xcookie);
|
||||
}
|
||||
|
||||
dwn_handle_event(&ev);
|
||||
}
|
||||
|
||||
@ -930,13 +1010,39 @@ void dwn_run(void)
|
||||
|
||||
demo_update();
|
||||
|
||||
long now = get_time_ms();
|
||||
|
||||
float speed_multiplier = 1.0f;
|
||||
if (now - dwn->typing_activity_time < 500) {
|
||||
speed_multiplier = 3.0f;
|
||||
}
|
||||
|
||||
dwn->ambient_phase += 0.008f * dwn->ambient_speed * speed_multiplier;
|
||||
if (dwn->ambient_phase > 6.283f) {
|
||||
dwn->ambient_phase -= 6.283f;
|
||||
}
|
||||
|
||||
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) {
|
||||
long now_focus = get_time_ms();
|
||||
if (now_focus >= dwn->pending_focus_time) {
|
||||
if (now >= dwn->pending_focus_time) {
|
||||
Workspace *ws = workspace_get_current();
|
||||
if (ws != NULL && ws->focused != dwn->pending_focus_client) {
|
||||
if (dwn->pending_focus_client->workspace == (unsigned int)dwn->current_workspace) {
|
||||
@ -947,8 +1053,6 @@ void dwn_run(void)
|
||||
}
|
||||
}
|
||||
|
||||
long now = get_time_ms();
|
||||
|
||||
if (now - last_news_update >= 16) {
|
||||
news_update();
|
||||
panel_render_all();
|
||||
|
||||
@ -645,6 +645,7 @@ void news_render(Panel *panel, int x, int max_width, int *used_width)
|
||||
|
||||
unsigned long article_color = news_sentiment_color(
|
||||
news_state.articles[current_article].sentiment);
|
||||
article_color = ambient_glow_bg(article_color, dwn->ambient_phase + PHASE_OFFSET_NEWS);
|
||||
|
||||
if (draw_x + news_state.display_widths[current_article] > x) {
|
||||
news_draw_text_clipped(panel->buffer, draw_x, text_y, display_text,
|
||||
@ -654,7 +655,7 @@ void news_render(Panel *panel, int x, int max_width, int *used_width)
|
||||
|
||||
if (draw_x < x + max_width) {
|
||||
news_draw_text_clipped(panel->buffer, draw_x, text_y, NEWS_SEPARATOR,
|
||||
colors->panel_fg, &clip_rect);
|
||||
ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_NEWS), &clip_rect);
|
||||
}
|
||||
draw_x += separator_width;
|
||||
|
||||
|
||||
239
src/panel.c
239
src/panel.c
@ -19,6 +19,8 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <X11/Xft/Xft.h>
|
||||
|
||||
#define PANEL_PADDING 12
|
||||
@ -42,12 +44,18 @@ typedef struct {
|
||||
float load_15min;
|
||||
unsigned long long prev_idle;
|
||||
unsigned long long prev_total;
|
||||
char top_mem_process[64];
|
||||
int top_mem_percent;
|
||||
} SystemStats;
|
||||
|
||||
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_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 int panel_text_width(const char *text, int len)
|
||||
@ -265,7 +273,8 @@ void panel_render(Panel *panel)
|
||||
Display *dpy = dwn->display;
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
|
||||
XSetForeground(dpy, dwn->gc, colors->panel_bg);
|
||||
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, 0, 0, panel->width, panel->height);
|
||||
|
||||
int x = PANEL_PADDING;
|
||||
@ -294,11 +303,24 @@ void panel_render(Panel *panel)
|
||||
int date_width = 0;
|
||||
if (dwn->xft_font != NULL || dwn->font != NULL) {
|
||||
int text_y = panel_text_y(panel->height);
|
||||
unsigned long date_fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_CLOCK);
|
||||
panel_draw_text(panel->buffer, x, text_y, date_buffer,
|
||||
strlen(date_buffer), colors->panel_fg);
|
||||
strlen(date_buffer), date_fg);
|
||||
date_width = panel_text_width(date_buffer, strlen(date_buffer));
|
||||
}
|
||||
|
||||
int key_counter_width = 0;
|
||||
int key_counter_x = PANEL_PADDING + date_width + WIDGET_SPACING;
|
||||
panel_render_key_counter(panel, key_counter_x, &key_counter_width);
|
||||
|
||||
int mouse_dist_width = 0;
|
||||
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 clock_width = panel_text_width(clock_buffer, strlen(clock_buffer));
|
||||
int stats_width = panel_calculate_stats_width();
|
||||
|
||||
@ -308,7 +330,7 @@ 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 = PANEL_PADDING + date_width + WIDGET_SPACING * 2;
|
||||
int news_start = top_mem_x + top_mem_width + WIDGET_SPACING;
|
||||
int news_max_width = stats_x - news_start - WIDGET_SPACING;
|
||||
if (news_max_width > 100) {
|
||||
int news_width = 0;
|
||||
@ -348,8 +370,10 @@ void panel_render_workspaces(Panel *panel, int x, int *width)
|
||||
bool active = (i == dwn->current_workspace);
|
||||
bool has_clients = !workspace_is_empty(i);
|
||||
|
||||
unsigned long bg = active ? colors->workspace_active : colors->panel_bg;
|
||||
XSetForeground(dpy, dwn->gc, bg);
|
||||
unsigned long bg_color = active ?
|
||||
ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES) :
|
||||
ambient_glow_bg(colors->panel_bg, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);
|
||||
XSetForeground(dpy, dwn->gc, bg_color);
|
||||
XFillRectangle(dpy, panel->buffer, dwn->gc,
|
||||
x + i * WORKSPACE_WIDTH, 0,
|
||||
WORKSPACE_WIDTH, panel->height);
|
||||
@ -358,7 +382,7 @@ void panel_render_workspaces(Panel *panel, int x, int *width)
|
||||
snprintf(num, sizeof(num), "%d", i + 1);
|
||||
|
||||
unsigned long fg = active ? colors->panel_bg :
|
||||
(has_clients ? colors->panel_fg : colors->workspace_inactive);
|
||||
ambient_glow_bg(has_clients ? colors->panel_fg : colors->workspace_inactive, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);
|
||||
|
||||
int text_x = x + i * WORKSPACE_WIDTH + (WORKSPACE_WIDTH - panel_text_width(num, strlen(num))) / 2;
|
||||
panel_draw_text(panel->buffer, text_x, text_y, num, strlen(num), fg);
|
||||
@ -454,7 +478,7 @@ void panel_render_taskbar(Panel *panel, int x, int *width)
|
||||
|
||||
unsigned long fg;
|
||||
if (minimized) {
|
||||
fg = colors->panel_fg;
|
||||
fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_TASKBAR);
|
||||
} else {
|
||||
fg = client_get_glow_text_color(c);
|
||||
}
|
||||
@ -483,8 +507,9 @@ void panel_render_clock(Panel *panel, int x, int *width)
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
|
||||
unsigned long fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_CLOCK);
|
||||
panel_draw_text(panel->buffer, x, text_y, clock_buffer,
|
||||
strlen(clock_buffer), colors->panel_fg);
|
||||
strlen(clock_buffer), fg);
|
||||
|
||||
*width = panel_text_width(clock_buffer, strlen(clock_buffer));
|
||||
}
|
||||
@ -511,8 +536,8 @@ void panel_render_layout_indicator(Panel *panel, int x, int *width)
|
||||
Workspace *ws = workspace_get_current();
|
||||
const char *symbol = layout_get_symbol(ws != NULL ? ws->layout : LAYOUT_TILING);
|
||||
|
||||
panel_draw_text(panel->buffer, x, text_y, symbol, strlen(symbol),
|
||||
colors->workspace_active);
|
||||
unsigned long fg = ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);
|
||||
panel_draw_text(panel->buffer, x, text_y, symbol, strlen(symbol), fg);
|
||||
|
||||
*width = panel_text_width(symbol, strlen(symbol));
|
||||
}
|
||||
@ -531,8 +556,8 @@ void panel_render_ai_status(Panel *panel, int x, int *width)
|
||||
|
||||
const char *status = dwn->ai_enabled ? "[AI]" : "";
|
||||
|
||||
panel_draw_text(panel->buffer, x, text_y, status, strlen(status),
|
||||
colors->workspace_active);
|
||||
unsigned long fg = ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_SYSTRAY);
|
||||
panel_draw_text(panel->buffer, x, text_y, status, strlen(status), fg);
|
||||
|
||||
*width = panel_text_width(status, strlen(status));
|
||||
}
|
||||
@ -779,6 +804,74 @@ void panel_update_system_stats(void)
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
update_top_memory_process();
|
||||
}
|
||||
|
||||
static void update_top_memory_process(void)
|
||||
{
|
||||
DIR *proc_dir = opendir("/proc");
|
||||
if (proc_dir == NULL) {
|
||||
sys_stats.top_mem_process[0] = '\0';
|
||||
sys_stats.top_mem_percent = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long max_rss = 0;
|
||||
char max_name[64] = "";
|
||||
unsigned long mem_total_kb = (unsigned long)sys_stats.mem_total_mb * 1024;
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(proc_dir)) != NULL) {
|
||||
bool is_pid = true;
|
||||
size_t name_len = 0;
|
||||
for (int i = 0; entry->d_name[i] != '\0'; i++) {
|
||||
if (!isdigit((unsigned char)entry->d_name[i])) {
|
||||
is_pid = false;
|
||||
break;
|
||||
}
|
||||
name_len++;
|
||||
}
|
||||
if (!is_pid || name_len > 20) continue;
|
||||
|
||||
char status_path[64];
|
||||
snprintf(status_path, sizeof(status_path), "/proc/%.20s/status", entry->d_name);
|
||||
|
||||
FILE *fp = fopen(status_path, "r");
|
||||
if (fp == NULL) continue;
|
||||
|
||||
char line[256];
|
||||
char name[64] = "";
|
||||
unsigned long rss_kb = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (strncmp(line, "Name:", 5) == 0) {
|
||||
char *start = line + 5;
|
||||
while (*start == ' ' || *start == '\t') start++;
|
||||
size_t len = strlen(start);
|
||||
if (len > 0 && start[len - 1] == '\n') start[len - 1] = '\0';
|
||||
snprintf(name, sizeof(name), "%s", start);
|
||||
} else if (strncmp(line, "VmRSS:", 6) == 0) {
|
||||
sscanf(line + 6, " %lu", &rss_kb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (rss_kb > max_rss && name[0] != '\0') {
|
||||
max_rss = rss_kb;
|
||||
snprintf(max_name, sizeof(max_name), "%s", name);
|
||||
}
|
||||
}
|
||||
closedir(proc_dir);
|
||||
|
||||
snprintf(sys_stats.top_mem_process, sizeof(sys_stats.top_mem_process), "%s", max_name);
|
||||
|
||||
if (mem_total_kb > 0) {
|
||||
sys_stats.top_mem_percent = (int)((max_rss * 100) / mem_total_kb);
|
||||
} else {
|
||||
sys_stats.top_mem_percent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int panel_calculate_stats_width(void)
|
||||
@ -852,6 +945,7 @@ static void panel_render_system_stats(Panel *panel, int x, int *width)
|
||||
} else if (sys_stats.cpu_percent >= 70) {
|
||||
cpu_color = 0xFFA500;
|
||||
}
|
||||
cpu_color = ambient_glow_bg(cpu_color, dwn->ambient_phase + PHASE_OFFSET_STATS);
|
||||
|
||||
len = snprintf(stats_buf, sizeof(stats_buf), "CPU:%2d%%", sys_stats.cpu_percent);
|
||||
panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, cpu_color);
|
||||
@ -863,6 +957,7 @@ static void panel_render_system_stats(Panel *panel, int x, int *width)
|
||||
} else if (sys_stats.mem_percent >= 75) {
|
||||
mem_color = 0xFFA500;
|
||||
}
|
||||
mem_color = ambient_glow_bg(mem_color, dwn->ambient_phase + PHASE_OFFSET_STATS);
|
||||
|
||||
if (sys_stats.mem_total_mb >= 1024) {
|
||||
len = snprintf(stats_buf, sizeof(stats_buf), "MEM:%.1fG/%dG",
|
||||
@ -880,6 +975,7 @@ static void panel_render_system_stats(Panel *panel, int x, int *width)
|
||||
} else if (sys_stats.load_1min >= 2.0f) {
|
||||
load_color = 0xFFA500;
|
||||
}
|
||||
load_color = ambient_glow_bg(load_color, dwn->ambient_phase + PHASE_OFFSET_STATS);
|
||||
|
||||
len = snprintf(stats_buf, sizeof(stats_buf), "Load:%.2f %.2f %.2f",
|
||||
sys_stats.load_1min, sys_stats.load_5min, sys_stats.load_15min);
|
||||
@ -896,6 +992,7 @@ static void panel_render_system_stats(Panel *panel, int x, int *width)
|
||||
} else if (multi_bat.any_charging) {
|
||||
bat_color = colors->workspace_active;
|
||||
}
|
||||
bat_color = ambient_glow_bg(bat_color, dwn->ambient_phase + PHASE_OFFSET_STATS);
|
||||
|
||||
const char *bat_icon = multi_bat.any_charging ? "[+]" : "[=]";
|
||||
if (multi_bat.count == 1) {
|
||||
@ -919,6 +1016,124 @@ static void panel_render_system_stats(Panel *panel, int x, int *width)
|
||||
*width = current_x - x;
|
||||
}
|
||||
|
||||
static void panel_render_key_counter(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;
|
||||
}
|
||||
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
|
||||
char buf[32];
|
||||
unsigned long count = dwn->key_press_count;
|
||||
int len;
|
||||
|
||||
if (count >= 1000000) {
|
||||
len = snprintf(buf, sizeof(buf), "Keys:%.1fM", count / 1000000.0);
|
||||
} else if (count >= 1000) {
|
||||
len = snprintf(buf, sizeof(buf), "Keys:%.1fK", count / 1000.0);
|
||||
} else {
|
||||
len = snprintf(buf, sizeof(buf), "Keys:%lu", count);
|
||||
}
|
||||
|
||||
long now = get_time_ms();
|
||||
bool flickering = (now - dwn->key_delete_flicker_time) < 100;
|
||||
|
||||
unsigned long fg;
|
||||
if (flickering) {
|
||||
unsigned long base = colors->panel_fg;
|
||||
int r = (int)((base >> 16) & 0xFF);
|
||||
int g = (int)((base >> 8) & 0xFF);
|
||||
int b = (int)(base & 0xFF);
|
||||
r = r + (255 - r) / 2;
|
||||
g = g + (255 - g) / 2;
|
||||
b = b + (255 - b) / 2;
|
||||
fg = ((unsigned long)r << 16) | ((unsigned long)g << 8) | (unsigned long)b;
|
||||
} else {
|
||||
fg = ambient_glow_bg(colors->panel_fg,
|
||||
dwn->ambient_phase + PHASE_OFFSET_KEY_COUNTER);
|
||||
}
|
||||
|
||||
if (flickering) {
|
||||
panel_draw_text_bold(panel->buffer, x, text_y, buf, len, fg);
|
||||
} else {
|
||||
panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
|
||||
}
|
||||
*width = panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
static void panel_render_mouse_distance(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;
|
||||
}
|
||||
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
|
||||
char buf[32];
|
||||
double cm = dwn->mouse_distance_pixels / 37.8;
|
||||
int len;
|
||||
|
||||
if (cm >= 100000.0) {
|
||||
len = snprintf(buf, sizeof(buf), "Mouse:%.2fkm", cm / 100000.0);
|
||||
} else if (cm >= 100.0) {
|
||||
len = snprintf(buf, sizeof(buf), "Mouse:%.1fm", cm / 100.0);
|
||||
} else {
|
||||
len = snprintf(buf, sizeof(buf), "Mouse:%.0fcm", cm);
|
||||
}
|
||||
|
||||
unsigned long fg = ambient_glow_bg(colors->panel_fg,
|
||||
dwn->ambient_phase + PHASE_OFFSET_MOUSE_DIST);
|
||||
panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
|
||||
*width = panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
static void panel_render_top_mem_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;
|
||||
}
|
||||
if (sys_stats.top_mem_process[0] == '\0') {
|
||||
*width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const ColorScheme *colors = config_get_colors();
|
||||
int text_y = panel_text_y(panel->height);
|
||||
|
||||
unsigned long color = colors->panel_fg;
|
||||
if (sys_stats.top_mem_percent >= 50) {
|
||||
color = colors->workspace_urgent;
|
||||
} else if (sys_stats.top_mem_percent >= 25) {
|
||||
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);
|
||||
*width = panel_text_width(buf, len);
|
||||
}
|
||||
|
||||
|
||||
void panel_init_systray(void)
|
||||
{
|
||||
|
||||
@ -558,7 +558,8 @@ void volume_slider_render(VolumeSlider *slider)
|
||||
int text_width = get_text_width(vol_text);
|
||||
int text_x = (slider->width - text_width) / 2;
|
||||
|
||||
draw_utf8_text(slider->window, text_x, slider->height - 4, vol_text, colors->panel_fg);
|
||||
draw_utf8_text(slider->window, text_x, slider->height - 4, vol_text,
|
||||
ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_SYSTRAY));
|
||||
|
||||
XFlush(dpy);
|
||||
}
|
||||
@ -677,6 +678,7 @@ void systray_render(Panel *panel, int x, int *width)
|
||||
snprintf(audio_label, sizeof(audio_label), "%s%d%%", audio_icon, audio_snap.volume);
|
||||
|
||||
unsigned long audio_color = audio_snap.muted ? colors->workspace_inactive : colors->panel_fg;
|
||||
audio_color = ambient_glow_bg(audio_color, dwn->ambient_phase + PHASE_OFFSET_SYSTRAY);
|
||||
draw_utf8_text(panel->buffer, current_x, text_y, audio_label, audio_color);
|
||||
current_x += get_text_width(audio_label) + SYSTRAY_SPACING;
|
||||
|
||||
@ -692,6 +694,7 @@ void systray_render(Panel *panel, int x, int *width)
|
||||
}
|
||||
|
||||
unsigned long wifi_color = wifi_snap.connected ? colors->panel_fg : colors->workspace_inactive;
|
||||
wifi_color = ambient_glow_bg(wifi_color, dwn->ambient_phase + PHASE_OFFSET_SYSTRAY);
|
||||
draw_utf8_text(panel->buffer, current_x, text_y, wifi_label, wifi_color);
|
||||
current_x += get_text_width(wifi_label);
|
||||
|
||||
|
||||
44
src/util.c
44
src/util.c
@ -818,3 +818,47 @@ unsigned long glow_color(unsigned long base, float phase)
|
||||
|
||||
return rgb_to_pixel(r, g, b);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
r = r + (int)(r * mod);
|
||||
g = g + (int)(g * mod);
|
||||
b = b + (int)(b * mod);
|
||||
|
||||
if (r > 255) r = 255;
|
||||
if (r < 0) r = 0;
|
||||
if (g > 255) g = 255;
|
||||
if (g < 0) g = 0;
|
||||
if (b > 255) b = 255;
|
||||
if (b < 0) b = 0;
|
||||
|
||||
return rgb_to_pixel(r, g, b);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
r = r + (int)(r * mod_r);
|
||||
g = g + (int)(g * mod_g);
|
||||
b = b + (int)(b * mod_b);
|
||||
|
||||
if (r > 255) r = 255;
|
||||
if (r < 0) r = 0;
|
||||
if (g > 255) g = 255;
|
||||
if (g < 0) g = 0;
|
||||
if (b > 255) b = 255;
|
||||
if (b < 0) b = 0;
|
||||
|
||||
return rgb_to_pixel(r, g, b);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user