#include #include #include #include #include #include #include #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; }