This commit is contained in:
2025-10-15 02:04:30 +09:00
commit 7935044025
2 changed files with 187 additions and 0 deletions

32
README.md Normal file
View File

@@ -0,0 +1,32 @@
# power-overlay
because reading Intel RAPL requires root (and also is NOT safe to make it globally readable)
- esc/q to exit
- +/- to change font size
- up/down to change opacity
- drag to move
<img width="250" alt="image" src="https://github.com/user-attachments/assets/5d390a20-6156-4857-acf4-bf7259e0adcf" />
### compile
```
gcc main.c -o power-overlay `sdl2-config --cflags --libs` -lSDL2_gfx -lm
```
### if you want to add a desktop file
power-overlay.sh
```bash
#!/bin/bash
pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY /home/user/.local/bin/power-overlay
```
power-overlay.desktop
```
[Desktop Entry]
Name=power overlay
Exec=power-overlay
Type=Application
```

155
main.c Normal file
View File

@@ -0,0 +1,155 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#define ENERGY_PATH "/sys/class/powercap/intel-rapl:0/energy_uj"
static uint64_t read_energy_uj(const char *path) {
FILE *f = fopen(path, "r");
if (!f) return UINT64_MAX;
unsigned long long val = 0ULL;
if (fscanf(f, "%llu", &val) != 1) {
fclose(f);
return UINT64_MAX;
}
fclose(f);
return (uint64_t)val;
}
static double cur_time() {
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
}
int running = 1;
double current_watts = 0.0;
SDL_mutex *watt_lock;
int watt_reader(void *arg) {
const char *path = (const char *)arg;
uint64_t prev_uj = read_energy_uj(path);
double prev_t = cur_time();
while (running) {
usleep(100000);
uint64_t curr_uj = read_energy_uj(path);
if (curr_uj == UINT64_MAX) continue;
double curr_t = cur_time();
double dJ = (double)((curr_uj >= prev_uj) ? (curr_uj - prev_uj) : 0ULL) / 1e6;
double dt = curr_t - prev_t;
double watts = (dt > 0) ? dJ / dt : 0.0;
prev_uj = curr_uj;
prev_t = curr_t;
SDL_LockMutex(watt_lock);
current_watts = watts;
SDL_UnlockMutex(watt_lock);
}
return 0;
}
int main(int argc, char **argv) {
const char *energy_path = (argc > 1) ? argv[1] : ENERGY_PATH;
int refresh_ms = (argc > 2) ? atoi(argv[2]) : 1000;
float window_opacity = 1.0f;
SDL_SetHint(SDL_HINT_X11_WINDOW_TYPE, "_NET_WM_WINDOW_TYPE_DOCK");
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) return 1;
SDL_Window *win = SDL_CreateWindow("overlay",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
160, 36,
SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SetWindowAlwaysOnTop(win, SDL_TRUE);
SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!ren) ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE);
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetWindowOpacity(win, window_opacity);
int fontScale = 2;
const int pad = 8;
watt_lock = SDL_CreateMutex();
SDL_Thread *reader = SDL_CreateThread(watt_reader, "reader", (void *)energy_path);
int dragging = 0, drag_off_x = 0, drag_off_y = 0;
char text[64] = "--.-- W";
uint32_t next_tick = SDL_GetTicks();
while (running) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT: running = 0; break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym) {
case SDLK_ESCAPE: case SDLK_q: running = 0; break;
case SDLK_UP: window_opacity += 0.05f; if (window_opacity > 1.0f) window_opacity = 1.0f; SDL_SetWindowOpacity(win, window_opacity); break;
case SDLK_DOWN: window_opacity -= 0.05f; if (window_opacity < 0.2f) window_opacity = 0.2f; SDL_SetWindowOpacity(win, window_opacity); break;
case SDLK_RIGHT: refresh_ms += 100; if (refresh_ms > 5000) refresh_ms = 5000; break;
case SDLK_LEFT: refresh_ms -= 100; if (refresh_ms < 100) refresh_ms = 100; break;
case SDLK_EQUALS: case SDLK_PLUS: fontScale++; if (fontScale>6) fontScale=6; break;
case SDLK_MINUS: if (fontScale>1) fontScale--; break;
}
break;
case SDL_MOUSEBUTTONDOWN:
if (e.button.button == SDL_BUTTON_LEFT) {
dragging = 1;
int wx, wy; SDL_GetWindowPosition(win, &wx, &wy);
int mx, my; SDL_GetGlobalMouseState(&mx, &my);
drag_off_x = mx - wx; drag_off_y = my - wy;
}
break;
case SDL_MOUSEBUTTONUP:
if (e.button.button == SDL_BUTTON_LEFT) dragging = 0;
break;
case SDL_MOUSEMOTION:
if (dragging) {
int mx, my; SDL_GetGlobalMouseState(&mx, &my);
SDL_SetWindowPosition(win, mx - drag_off_x, my - drag_off_y);
}
break;
}
}
uint32_t now = SDL_GetTicks();
if ((int)(now - next_tick) >= 0) {
SDL_LockMutex(watt_lock);
double watts = current_watts;
SDL_UnlockMutex(watt_lock);
snprintf(text, sizeof(text), "%.2f W", watts);
int baseW = (int)strlen(text) * 8;
int baseH = 8;
int w = baseW * fontScale + pad*2;
int h = baseH * fontScale + pad*2;
SDL_SetWindowSize(win, w, h);
SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
SDL_RenderClear(ren);
SDL_Texture *tmp = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, baseW, baseH);
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(ren, tmp);
SDL_SetRenderDrawColor(ren, 0,0,0,0); SDL_RenderClear(ren);
stringRGBA(ren, 0, 0, text, 255, 255, 255, 255);
SDL_SetRenderTarget(ren, NULL);
SDL_Rect dst = { pad, pad, baseW*fontScale, baseH*fontScale };
SDL_RenderCopy(ren, tmp, NULL, &dst);
SDL_DestroyTexture(tmp);
SDL_RenderPresent(ren);
next_tick = now + (uint32_t)refresh_ms;
}
SDL_Delay(10);
}
running = 0;
SDL_WaitThread(reader, NULL);
SDL_DestroyMutex(watt_lock);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}