mirror of
https://github.com/morgan9e/power-overlay
synced 2026-04-14 00:14:06 +09:00
156 lines
5.9 KiB
C
156 lines
5.9 KiB
C
#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;
|
|
}
|