Phase 1/2: .desktop launcher, volume control in deskbar

- Launcher now loads apps dynamically from /usr/share/applications
  and ~/.local/share/applications instead of a hardcoded list;
  maps desktop Categories to tab system, falls back to built-in list
  on systems without .desktop files (dev/macOS builds)
- Launcher gains scroll support: mouse wheel and PgUp/PgDn scroll
  the app grid, scrollbar drawn on right edge when needed
- Deskbar status bar shows VOL % from pactl (cached, polls every 5s)
- Deskbar status bar: scroll wheel changes volume ±5%, left-click
  toggles mute

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-07-23 13:16:18 -04:00
1 parent 9089c14a19
commit 2b8ea90a57
2 files changed
+267 -96

No files matched your search

+42 -5
View File
@@ -161,6 +161,23 @@ static int read_cpu_percent(void) {
return (int)(100 * (d_total - d_idle) / d_total);
}
static int read_volume_percent(void) {
static int cached = -1;
static int ticks = 0;
if (ticks-- > 0) return cached;
ticks = 4; /* re-poll every 5 seconds */
FILE *f = popen("pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null", "r");
if (!f) return cached;
char line[256];
cached = -1;
if (fgets(line, sizeof(line), f)) {
char *p = strstr(line, "/ ");
if (p) cached = atoi(p + 2);
}
pclose(f);
return cached;
}
static int read_battery_percent(void) {
FILE *f;
char path[80];
@@ -422,16 +439,20 @@ void db_draw(Deskbar *db) {
int mem = read_mem_percent();
int bat = read_battery_percent();
char bst = read_battery_status();
char status[64];
int vol = read_volume_percent();
char status[80];
int pos = 0;
if (vol >= 0)
pos += snprintf(status + pos, sizeof(status) - pos,
"VOL %d%%", vol);
if (cpu >= 0)
pos += snprintf(status + pos, sizeof(status) - pos,
"CPU %d%%", cpu);
"%sCPU %d%%", pos ? " " : "", cpu);
if (mem >= 0)
pos += snprintf(status + pos, sizeof(status) - pos,
" MEM %d%%", mem);
if (bat >= 0) {
const char *arrow = (bst == 'C') ? "↑" : (bst == 'F') ? "=" : "↓";
const char *arrow = (bst == 'C') ? "+" : (bst == 'F') ? "=" : "-";
pos += snprintf(status + pos, sizeof(status) - pos,
" BAT %d%%%s", bat, arrow);
}
@@ -888,8 +909,24 @@ void db_run(Deskbar *db) {
if (e.xexpose.count == 0) db_draw(db);
break;
case ButtonPress:
if (e.xany.window == db->win)
db_click(db, e.xbutton.x, e.xbutton.y);
if (e.xany.window == db->win) {
int sy = db_status_y(db);
if (e.xbutton.button == Button4) {
/* scroll up on status bar = volume up */
if (e.xbutton.y >= sy)
system("pactl set-sink-volume @DEFAULT_SINK@ +5% 2>/dev/null &");
break;
}
if (e.xbutton.button == Button5) {
if (e.xbutton.y >= sy)
system("pactl set-sink-volume @DEFAULT_SINK@ -5% 2>/dev/null &");
break;
}
if (e.xbutton.button == Button1 && e.xbutton.y >= sy)
system("pactl set-sink-mute @DEFAULT_SINK@ toggle 2>/dev/null &");
else
db_click(db, e.xbutton.x, e.xbutton.y);
}
break;
case MotionNotify:
if (e.xany.window == db->win)