From c2108e5a482dfb8760849bb01264ed4bdb9f604f Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 23 Jan 2015 03:08:21 -0200 Subject: [PATCH] Another mega-patch for the refactoring process. Kinda runs, but functionality from the original main loop is still missing. Patience. --- Action.c | 55 ++------- Action.h | 6 - BatteryMeter.c | 4 +- CategoriesPanel.c | 2 - Header.c | 27 +++-- Header.h | 11 +- Meter.c | 4 +- Meter.h | 2 + Panel.c | 10 +- Panel.h | 10 +- ScreenManager.c | 54 ++++++--- Settings.c | 2 +- Settings.h | 2 +- TasksMeter.c | 5 +- htop.c | 11 +- linux/Battery.c | 289 ++++++++++++++++++++++++++++------------------ linux/Battery.h | 23 +++- linux/Platform.c | 20 +--- linux/Platform.h | 3 +- 19 files changed, 300 insertions(+), 240 deletions(-) diff --git a/Action.c b/Action.c index b56f195a..f968fc4a 100644 --- a/Action.c +++ b/Action.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include "AffinityPanel.h" #include "CategoriesPanel.h" #include "CRT.h" +#include "MainPanel.h" #include "OpenFilesScreen.h" #include "Process.h" #include "ScreenManager.h" @@ -58,37 +59,8 @@ typedef struct State_ { Header* header; } State; -typedef bool(*Action_ForeachProcessFn)(Process*, size_t); - }*/ -int Action_selectedPid(Panel* panel) { - Process* p = (Process*) Panel_getSelected(panel); - if (p) { - return p->pid; - } - return -1; -} - -bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bool* wasAnyTagged) { - bool ok = true; - bool anyTagged = false; - for (int i = 0; i < Panel_size(panel); i++) { - Process* p = (Process*) Panel_get(panel, i); - if (p->tag) { - ok = fn(p, arg) && ok; - anyTagged = true; - } - } - if (!anyTagged) { - Process* p = (Process*) Panel_getSelected(panel); - if (p) ok = fn(p, arg) && ok; - } - if (wasAnyTagged) - *wasAnyTagged = anyTagged; - return ok; -} - Object* Action_pickFromVector(State* st, Panel* list, int x, const char** keyLabels) { Panel* panel = st->panel; Header* header = st->header; @@ -97,14 +69,14 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, const char** keyLab int y = panel->y; const char* fuKeys[] = {"Enter", "Esc", NULL}; int fuEvents[] = {13, 27}; - ScreenManager* scr = ScreenManager_new(0, y, 0, -1, HORIZONTAL, header, settings, false); + ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false); scr->allowFocusChange = false; ScreenManager_add(scr, list, FunctionBar_new(keyLabels, fuKeys, fuEvents), x - 1); ScreenManager_add(scr, panel, NULL, -1); Panel* panelFocus; int ch; bool unfollow = false; - int pid = Action_selectedPid(panel); + int pid = MainPanel_selectedPid((MainPanel*)panel); if (header->pl->following == -1) { header->pl->following = pid; unfollow = true; @@ -141,9 +113,9 @@ static void Setup_run(Settings* settings, const Header* header, ProcessList* pl) ScreenManager_delete(scr); } -static bool changePriority(Panel* panel, int delta) { +static bool changePriority(MainPanel* panel, int delta) { bool anyTagged; - bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged); + bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged); if (!ok) beep(); return anyTagged; @@ -165,13 +137,6 @@ bool Action_setUserOnly(const char* userName, uid_t* userId) { return false; } -static const char* getMainPanelValue(Panel* panel, int i) { - Process* p = (Process*) Panel_get(panel, i); - if (p) - return p->comm; - return ""; -} - static void tagAllChildren(Panel* panel, Process* parent) { parent->tag = true; pid_t ppid = parent->pid; @@ -266,12 +231,12 @@ static Htop_Reaction actionIncSearch(State* st) { } static Htop_Reaction actionHigherPriority(State* st) { - bool changed = changePriority(st->panel, -1); + bool changed = changePriority((MainPanel*)st->panel, -1); return changed ? HTOP_REFRESH : HTOP_OK; } static Htop_Reaction actionLowerPriority(State* st) { - bool changed = changePriority(st->panel, 1); + bool changed = changePriority((MainPanel*)st->panel, 1); return changed ? HTOP_REFRESH : HTOP_OK; } @@ -314,7 +279,7 @@ static Htop_Reaction actionSetAffinity(State* st) { void* set = Action_pickFromVector(st, affinityPanel, 15, fuFunctions); if (set) { Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); - bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL); + bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL); if (!ok) beep(); Affinity_delete(affinity); } @@ -332,7 +297,7 @@ static Htop_Reaction actionKill(State* st) { Panel_setHeader(st->panel, "Sending..."); Panel_draw(st->panel, true); refresh(); - Action_foreachProcess(st->panel, (Action_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL); + MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL); napms(500); } } @@ -361,7 +326,7 @@ static Htop_Reaction actionFilterByUser(State* st) { } static Htop_Reaction actionFollow(State* st) { - st->pl->following = Action_selectedPid(st->panel); + st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel); return HTOP_KEEP_FOLLOWING; } diff --git a/Action.h b/Action.h index 0a1bf024..dd11fdb4 100644 --- a/Action.h +++ b/Action.h @@ -39,12 +39,6 @@ typedef struct State_ { Header* header; } State; -typedef bool(*Action_ForeachProcessFn)(Process*, size_t); - - -int Action_selectedPid(Panel* panel); - -bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bool* wasAnyTagged); Object* Action_pickFromVector(State* st, Panel* list, int x, const char** keyLabels); diff --git a/BatteryMeter.c b/BatteryMeter.c index 5cd0c389..d0dff92e 100644 --- a/BatteryMeter.c +++ b/BatteryMeter.c @@ -9,7 +9,7 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). #include "BatteryMeter.h" -#include "Platform.h" +#include "Battery.h" #include "ProcessList.h" #include "CRT.h" #include "String.h" @@ -35,7 +35,7 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) { ACPresence isOnAC; double percent; - Platform_getBatteryLevel(&percent, &isOnAC); + Battery_getData(&percent, &isOnAC); if (percent == -1) { this->values[0] = 0; diff --git a/CategoriesPanel.c b/CategoriesPanel.c index cbad064d..d691d7f4 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -111,7 +111,6 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { result = IGNORED; break; } - if (result == HANDLED) { int size = ScreenManager_size(this->scr); for (int i = 1; i < size; i++) @@ -131,7 +130,6 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { break; } } - return result; } diff --git a/Header.c b/Header.c index 8b7cef6d..6e99a681 100644 --- a/Header.c +++ b/Header.c @@ -18,15 +18,16 @@ in the source distribution for its full text. /*{ #include "Meter.h" +#include "Settings.h" #include "Vector.h" typedef struct Header_ { Vector** columns; + Settings* settings; struct ProcessList_* pl; - int height; - int pad; int nrColumns; - bool margin; + int pad; + int height; } Header; }*/ @@ -39,15 +40,15 @@ typedef struct Header_ { #define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++) #endif -Header* Header_new(struct ProcessList_* pl, int nrColumns) { +Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { Header* this = calloc(1, sizeof(Header)); this->columns = calloc(nrColumns, sizeof(Vector*)); + this->settings = settings; + this->pl = pl; this->nrColumns = nrColumns; Header_forEachColumn(this, i) { this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); } - this->margin = true; - this->pl = pl; return this; } @@ -59,6 +60,17 @@ void Header_delete(Header* this) { free(this); } +void Header_populateFromSettings(Header* this) { + Header_forEachColumn(this, col) { + MeterColumnSettings* colSettings = &this->settings->columns[col]; + for (int i = 0; i < colSettings->len; i++) { + Header_addMeterByName(this, colSettings->names[i], col); + Header_setMode(this, i, colSettings->modes[i], col); + } + } + Header_calculateHeight(this); +} + MeterModeId Header_addMeterByName(Header* this, char* name, int column) { Vector* meters = this->columns[column]; @@ -157,7 +169,7 @@ void Header_draw(const Header* this) { } int Header_calculateHeight(Header* this) { - int pad = this->margin ? 2 : 0; + int pad = this->settings->headerMargin ? 2 : 0; int maxHeight = pad; Header_forEachColumn(this, col) { @@ -170,5 +182,6 @@ int Header_calculateHeight(Header* this) { maxHeight = MAX(maxHeight, height); } this->height = maxHeight; + this->pad = pad; return maxHeight; } diff --git a/Header.h b/Header.h index 11c04960..13811a9e 100644 --- a/Header.h +++ b/Header.h @@ -10,15 +10,16 @@ in the source distribution for its full text. */ #include "Meter.h" +#include "Settings.h" #include "Vector.h" typedef struct Header_ { Vector** columns; + Settings* settings; struct ProcessList_* pl; - int height; - int pad; int nrColumns; - bool margin; + int pad; + int height; } Header; @@ -30,10 +31,12 @@ typedef struct Header_ { #define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++) #endif -Header* Header_new(struct ProcessList_* pl, int nrColumns); +Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); void Header_delete(Header* this); +void Header_populateFromSettings(Header* this); + MeterModeId Header_addMeterByName(Header* this, char* name, int column); void Header_setMode(Header* this, int i, MeterModeId mode, int column); diff --git a/Meter.c b/Meter.c index 1d4e6e3b..b1083cf0 100644 --- a/Meter.c +++ b/Meter.c @@ -23,6 +23,8 @@ in the source distribution for its full text. #define METER_BUFFER_LEN 256 +#define GRAPH_DELAY (DEFAULT_DELAY/2) + /*{ #include "ListItem.h" @@ -342,7 +344,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { struct timeval now; gettimeofday(&now, NULL); if (!timercmp(&now, &(data->time), <)) { - struct timeval delay = { .tv_sec = (int)(DEFAULT_DELAY/10), .tv_usec = (DEFAULT_DELAY-((int)(DEFAULT_DELAY/10)*10)) * 100000 }; + struct timeval delay = { .tv_sec = (int)(GRAPH_DELAY/10), .tv_usec = (GRAPH_DELAY-((int)(GRAPH_DELAY/10)*10)) * 100000 }; timeradd(&now, &delay, &(data->time)); for (int i = 0; i < nValues - 1; i++) diff --git a/Meter.h b/Meter.h index 97efc1ad..67df33b2 100644 --- a/Meter.h +++ b/Meter.h @@ -11,6 +11,8 @@ in the source distribution for its full text. #define METER_BUFFER_LEN 256 +#define GRAPH_DELAY (DEFAULT_DELAY/2) + #include "ListItem.h" #include diff --git a/Panel.c b/Panel.c index 8982cc2e..de120d7c 100644 --- a/Panel.c +++ b/Panel.c @@ -28,11 +28,11 @@ in the source distribution for its full text. typedef struct Panel_ Panel; typedef enum HandlerResult_ { - HANDLED = 0x00, - IGNORED = 0x01, - BREAK_LOOP = 0x02, - REFRESH = 0x04, - RECALCULATE = 0x08, + HANDLED = 0x01, + IGNORED = 0x02, + BREAK_LOOP = 0x04, + REFRESH = 0x08, + RECALCULATE = 0x10, } HandlerResult; #define EVENT_SETSELECTED -1 diff --git a/Panel.h b/Panel.h index 717f6dde..baac7016 100644 --- a/Panel.h +++ b/Panel.h @@ -17,11 +17,11 @@ in the source distribution for its full text. typedef struct Panel_ Panel; typedef enum HandlerResult_ { - HANDLED = 0x00, - IGNORED = 0x01, - BREAK_LOOP = 0x02, - REFRESH = 0x04, - RECALCULATE = 0x08, + HANDLED = 0x01, + IGNORED = 0x02, + BREAK_LOOP = 0x04, + REFRESH = 0x08, + RECALCULATE = 0x10, } HandlerResult; #define EVENT_SETSELECTED -1 diff --git a/ScreenManager.c b/ScreenManager.c index aad1b024..d7353b79 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -81,10 +81,11 @@ void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); lastX = last->x + last->w + 1; } + int height = LINES - this->y1 + this->y2; if (size > 0) { - Panel_resize(item, size, LINES-this->y1+this->y2); + Panel_resize(item, size, height); } else { - Panel_resize(item, COLS-this->x1+this->x2-lastX, LINES-this->y1+this->y2); + Panel_resize(item, COLS-this->x1+this->x2-lastX, height); } Panel_move(item, lastX, this->y1); } @@ -114,16 +115,19 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) { this->x2 = x2; this->y2 = y2; int panels = this->panelCount; - int lastX = 0; - for (int i = 0; i < panels - 1; i++) { - Panel* panel = (Panel*) Vector_get(this->panels, i); - Panel_resize(panel, panel->w, LINES-y1+y2); + if (this->orientation == HORIZONTAL) { + int lastX = 0; + for (int i = 0; i < panels - 1; i++) { + Panel* panel = (Panel*) Vector_get(this->panels, i); + Panel_resize(panel, panel->w, LINES-y1+y2); + Panel_move(panel, lastX, y1); + lastX = panel->x + panel->w + 1; + } + Panel* panel = (Panel*) Vector_get(this->panels, panels-1); + Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2); Panel_move(panel, lastX, y1); - lastX = panel->x + panel->w + 1; } - Panel* panel = (Panel*) Vector_get(this->panels, panels-1); - Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2); - Panel_move(panel, lastX, y1); + // TODO: VERTICAL } void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { @@ -154,25 +158,29 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); timeToRecalculate = (newTime - oldTime > this->settings->delay); if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted? - - if (timeToRecalculate) { - Header_draw(this->header); - oldTime = newTime; - } - +//fprintf(stderr, "\n%p %f ", this, newTime); if (doRefresh) { if (timeToRecalculate || forceRecalculate) { ProcessList_scan(this->header->pl); - forceRecalculate = false; +//fprintf(stderr, "scan "); } +//fprintf(stderr, "sortTo=%d ", sortTimeout); if (sortTimeout == 0 || this->settings->treeView) { ProcessList_sort(this->header->pl); +//fprintf(stderr, "sort "); sortTimeout = 1; } //this->header->pl->incFilter = IncSet_filter(inc); ProcessList_rebuildPanel(this->header->pl); +//fprintf(stderr, "rebuild "); drawPanel = true; } + if (timeToRecalculate || forceRecalculate) { + Header_draw(this->header); +//fprintf(stderr, "drawHeader "); + oldTime = newTime; + forceRecalculate = false; + } doRefresh = true; } @@ -180,6 +188,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { for (int i = 0; i < panels; i++) { Panel* panel = (Panel*) Vector_get(this->panels, i); Panel_draw(panel, i == focus); +//fprintf(stderr, "drawPanel "); if (i < panels) { if (this->orientation == HORIZONTAL) { mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1); @@ -196,6 +205,8 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { int prevCh = ch; ch = getch(); + +//fprintf(stderr, "ch=%d ", ch); if (ch == KEY_MOUSE) { MEVENT mevent; @@ -218,9 +229,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { } } } - if (Panel_eventHandlerFn(panelFocus)) { HandlerResult result = Panel_eventHandler(panelFocus, ch); +//fprintf(stderr, "eventResult=%d ", result); if (result & REFRESH) { doRefresh = true; sortTimeout = 0; @@ -230,14 +241,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { sortTimeout = 0; } if (result & HANDLED) { + drawPanel = true; continue; } else if (result & BREAK_LOOP) { quit = true; continue; } } - if (ch == ERR) { + sortTimeout--; if (prevCh == ch && !timeToRecalculate) { closeTimeout++; if (closeTimeout == 100) { @@ -246,6 +258,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { } else closeTimeout = 0; drawPanel = false; +//fprintf(stderr, "err "); continue; } drawPanel = true; @@ -285,9 +298,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { quit = true; continue; default: +//fprintf(stderr, "onKey "); + sortTimeout = resetSortTimeout; Panel_onKey(panelFocus, ch); break; } +//fprintf(stderr, "loop "); } if (lastFocus) diff --git a/Settings.c b/Settings.c index e78f9918..8f2db0e8 100644 --- a/Settings.c +++ b/Settings.c @@ -26,7 +26,7 @@ typedef struct { char** names; int* modes; } MeterColumnSettings; - + typedef struct Settings_ { char* filename; diff --git a/Settings.h b/Settings.h index 2ac431f0..af1fefb9 100644 --- a/Settings.h +++ b/Settings.h @@ -19,7 +19,7 @@ typedef struct { char** names; int* modes; } MeterColumnSettings; - + typedef struct Settings_ { char* filename; diff --git a/TasksMeter.c b/TasksMeter.c index adcc55d9..7a3e5a47 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -20,7 +20,10 @@ int TasksMeter_attributes[] = { static void TasksMeter_setValues(Meter* this, char* buffer, int len) { Platform_setTasksValues(this); - snprintf(buffer, len, "%d/%d", (int) this->values[0], (int) this->total); + if (this->pl->settings->hideKernelThreads) { + this->values[0] = 0; + } + snprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total); } static void TasksMeter_display(Object* cast, RichString* out) { diff --git a/htop.c b/htop.c index bb948b80..ef3182de 100644 --- a/htop.c +++ b/htop.c @@ -188,10 +188,13 @@ int main(int argc, char** argv) { UsersTable* ut = UsersTable_new(); ProcessList* pl = ProcessList_new(ut, flags.pidWhiteList, flags.userId); - Header* header = Header_new(pl, 2); Settings* settings = Settings_new(pl->cpuCount); pl->settings = settings; + Header* header = Header_new(pl, settings, 2); + + Header_populateFromSettings(header); + if (flags.delay != -1) settings->delay = flags.delay; if (!flags.useColors) @@ -225,8 +228,8 @@ int main(int argc, char** argv) { }; MainPanel_setState(panel, &state); - ScreenManager* scr = ScreenManager_new(0, 0, 0, -1, HORIZONTAL, header, settings, true); - ScreenManager_add(scr, (Panel*) panel, defaultBar, 0); + ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true); + ScreenManager_add(scr, (Panel*) panel, defaultBar, -1); ProcessList_scan(pl); millisleep(75); @@ -265,7 +268,7 @@ int main(int argc, char** argv) { double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); bool timeToRecalculate = (newTime - oldTime > settings->delay); if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted? - int following = follow ? Action_selectedPid(panel) : -1; + int following = follow ? MainPanel_selectedPid((MainPanel*)panel) : -1; if (timeToRecalculate) { Header_draw(header); oldTime = newTime; diff --git a/linux/Battery.c b/linux/Battery.c index eb458af8..f766098f 100644 --- a/linux/Battery.c +++ b/linux/Battery.c @@ -11,28 +11,24 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat #define _GNU_SOURCE #endif #include +#include #include #include #include +#include +#include #include "BatteryMeter.h" #include "String.h" -static unsigned long int parseUevent(FILE * file, const char *key) { - char line[100]; - unsigned long int dValue = 0; - char* saveptr; +#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply" - while (fgets(line, sizeof line, file)) { - if (strncmp(line, key, strlen(key)) == 0) { - char *value; - strtok_r(line, "=", &saveptr); - value = strtok_r(NULL, "=", &saveptr); - dValue = atoi(value); - break; - } - } - return dValue; -} +// ---------------------------------------- +// READ FROM /proc +// ---------------------------------------- + +// This implementation reading from from /proc/acpi is really inefficient, +// but I think this is on the way out so I did not rewrite it. +// The /sys implementation below does things the right way. static unsigned long int parseBatInfo(const char *fileName, const unsigned short int lineNum, const unsigned short int wordNum) { const char batteryPath[] = PROCDIR "/acpi/battery/"; @@ -94,15 +90,15 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short static ACPresence procAcpiCheck() { ACPresence isOn = AC_ERROR; const char *power_supplyPath = PROCDIR "/acpi/ac_adapter"; - DIR *power_supplyDir = opendir(power_supplyPath); - if (!power_supplyDir) { + DIR *dir = opendir(power_supplyPath); + if (!dir) { return AC_ERROR; } struct dirent result; struct dirent* dirEntry; for (;;) { - int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry); + int err = readdir_r((DIR *) dir, &result, &dirEntry); if (err || !dirEntry) break; @@ -139,62 +135,12 @@ static ACPresence procAcpiCheck() { } } - if (power_supplyDir) - closedir(power_supplyDir); + if (dir) + closedir(dir); return isOn; } -static ACPresence sysCheck() { - ACPresence isOn = AC_ERROR; - const char *power_supplyPath = "/sys/class/power_supply"; - DIR *power_supplyDir = opendir(power_supplyPath); - if (!power_supplyDir) { - return AC_ERROR; - } - - struct dirent result; - struct dirent* dirEntry; - for (;;) { - int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry); - if (err || !dirEntry) - break; - - char* entryName = (char *) dirEntry->d_name; - if (strncmp(entryName, "A", 1)) { - continue; - } - char onlinePath[50]; - snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName); - FILE* file = fopen(onlinePath, "r"); - if (!file) { - isOn = AC_ERROR; - } else { - isOn = (fgetc(file) - '0'); - fclose(file); - if (isOn == AC_PRESENT) { - // If any AC adapter is being used then stop - break; - } - } - } - - if (power_supplyDir) - closedir(power_supplyDir); - - return isOn; -} - -ACPresence Battery_isOnAC() { - if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) { - return procAcpiCheck(); - } else if (access("/sys/class/power_supply", F_OK) == 0) { - return sysCheck(); - } else { - return AC_ERROR; - } -} - -double Battery_getProcBatData() { +static double Battery_getProcBatData() { const unsigned long int totalFull = parseBatInfo("info", 3, 4); if (totalFull == 0) return 0; @@ -206,11 +152,58 @@ double Battery_getProcBatData() { return totalRemain * 100.0 / (double) totalFull; } -double Battery_getSysBatData() { - const char *power_supplyPath = "/sys/class/power_supply/"; - DIR *power_supplyDir = opendir(power_supplyPath); - if (!power_supplyDir) - return 0; +static void Battery_getProcData(double* level, ACPresence* isOnAC) { + *level = Battery_getProcBatData(); + *isOnAC = procAcpiCheck(); +} + +// ---------------------------------------- +// READ FROM /sys +// ---------------------------------------- + +static inline ssize_t xread(int fd, void *buf, size_t count) { + // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested. + size_t alreadyRead = 0; + for(;;) { + ssize_t res = read(fd, buf, count); + if (res == -1 && errno == EINTR) continue; + if (res > 0) { + buf = ((char*)buf)+res; + count -= res; + alreadyRead += res; + } + if (res == -1) return -1; + if (count == 0 || res == 0) return alreadyRead; + } +} + +/** + * Returns a pointer to the suffix of `str` if its beginning matches `prefix`. + * Returns NULL if the prefix does not match. + * Examples: + * match("hello world", "hello "); -> "world" + * match("hello world", "goodbye "); -> NULL + */ +static inline const char* match(const char* str, const char* prefix) { + for (;;) { + if (*prefix == '\0') { + return str; + } + if (*prefix != *str) { + return NULL; + } + prefix++; str++; + } +} + +static void Battery_getSysData(double* level, ACPresence* isOnAC) { + + *level = 0; + *isOnAC = AC_ERROR; + + DIR *dir = opendir(SYS_POWERSUPPLY_DIR); + if (!dir) + return; unsigned long int totalFull = 0; unsigned long int totalRemain = 0; @@ -218,52 +211,120 @@ double Battery_getSysBatData() { struct dirent result; struct dirent* dirEntry; for (;;) { - int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry); + int err = readdir_r((DIR *) dir, &result, &dirEntry); if (err || !dirEntry) break; char* entryName = (char *) dirEntry->d_name; + const char filePath[50]; - if (strncmp(entryName, "BAT", 3)) { - continue; - } - - const char ueventPath[50]; - - snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName); - - FILE *file; - if ((file = fopen(ueventPath, "r")) == NULL) { - closedir(power_supplyDir); - return 0; - } - - if ((totalFull += parseUevent(file, "POWER_SUPPLY_ENERGY_FULL="))) { - totalRemain += parseUevent(file, "POWER_SUPPLY_ENERGY_NOW="); - } else { - //reset file pointer - if (fseek(file, 0, SEEK_SET) < 0) { - closedir(power_supplyDir); - fclose(file); - return 0; + if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') { + + snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); + int fd = open(filePath, O_RDONLY); + if (fd == -1) { + closedir(dir); + return; + } + char buffer[1024]; + ssize_t buflen = xread(fd, buffer, 1023); + close(fd); + if (buflen < 1) { + closedir(dir); + return; + } + buffer[buflen] = '\0'; + char *buf = buffer; + char *line = NULL; + bool full = false; + bool now = false; + while ((line = strsep(&buf, "\n")) != NULL) { + const char* ps = match(line, "POWER_SUPPLY_"); + if (!ps) { + continue; + } + const char* energy = match(ps, "ENERGY_"); + if (!energy) { + energy = match(ps, "CHARGE_"); + } + if (!energy) { + continue; + } + const char* value = (!full) ? match(energy, "FULL=") : NULL; + if (value) { + totalFull += atoi(value); + full = true; + if (now) break; + continue; + } + value = (!now) ? match(energy, "NOW=") : NULL; + if (value) { + totalRemain += atoi(value); + now = true; + if (full) break; + continue; + } + } + } else if (entryName[0] == 'A') { + if (*isOnAC != AC_ERROR) { + continue; + } + + snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); + int fd = open(filePath, O_RDONLY); + if (fd == -1) { + closedir(dir); + return; + } + char buffer[2] = ""; + for(;;) { + ssize_t res = read(fd, buffer, 1); + if (res == -1 && errno == EINTR) continue; + break; + } + close(fd); + if (buffer[0] == '0') { + *isOnAC = AC_ABSENT; + } else if (buffer[0] == '1') { + *isOnAC = AC_PRESENT; } } + } + closedir(dir); + *level = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0; +} - //Some systems have it as CHARGE instead of ENERGY. - if ((totalFull += parseUevent(file, "POWER_SUPPLY_CHARGE_FULL="))) { - totalRemain += parseUevent(file, "POWER_SUPPLY_CHARGE_NOW="); - } else { - //reset file pointer - if (fseek(file, 0, SEEK_SET) < 0) { - closedir(power_supplyDir); - fclose(file); - return 0; - } - } +static enum { BAT_PROC, BAT_SYS, BAT_ERR } Battery_method = BAT_PROC; - fclose(file); +static time_t Battery_cacheTime = 0; +static double Battery_cacheLevel = 0; +static ACPresence Battery_cacheIsOnAC = 0; + +void Battery_getData(double* level, ACPresence* isOnAC) { + time_t now = time(NULL); + // update battery reading is slow. Update it each 10 seconds only. + if (now < Battery_cacheTime + 10) { + *level = Battery_cacheLevel; + *isOnAC = Battery_cacheIsOnAC; + return; } - const double percent = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0; - closedir(power_supplyDir); - return percent; + if (Battery_method == BAT_PROC) { + Battery_getProcData(level, isOnAC); + if (*level == 0) { + Battery_method = BAT_SYS; + } + } + if (Battery_method == BAT_SYS) { + Battery_getSysData(level, isOnAC); + if (*level == 0) { + Battery_method = BAT_ERR; + } + } + if (Battery_method == BAT_ERR) { + *level = -1; + *isOnAC = AC_ERROR; + } + Battery_cacheLevel = *level; + Battery_cacheIsOnAC = *isOnAC; + Battery_cacheTime = now; } diff --git a/linux/Battery.h b/linux/Battery.h index 88ba2b01..4cb22a89 100644 --- a/linux/Battery.h +++ b/linux/Battery.h @@ -15,10 +15,27 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat #define _GNU_SOURCE #endif -ACPresence Battery_isOnAC(); +#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply" -double Battery_getProcBatData(); +// ---------------------------------------- +// READ FROM /proc +// ---------------------------------------- -double Battery_getSysBatData(); +// This implementation reading from from /proc/acpi is really inefficient, +// but I think this is on the way out so I did not rewrite it. +// The /sys implementation below does things the right way. + +// ---------------------------------------- +// READ FROM /sys +// ---------------------------------------- + +/** + * Returns a pointer to the suffix of `str` if its beginning matches `prefix`. + * Returns NULL if the prefix does not match. + * Examples: + * match("hello world", "hello "); -> "world" + * match("hello world", "goodbye "); -> NULL + */ +void Battery_getData(double* level, ACPresence* isOnAC); #endif diff --git a/linux/Platform.c b/linux/Platform.c index a5704d23..0a763362 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -27,6 +27,7 @@ in the source distribution for its full text. /*{ #include "Action.h" +#include "MainPanel.h" #include "BatteryMeter.h" }*/ @@ -41,7 +42,7 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) { void* set = Action_pickFromVector(st, ioprioPanel, 21, fuFunctions); if (set) { IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel); - bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) LinuxProcess_setIOPriority, (size_t) ioprio, NULL); + bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (size_t) ioprio, NULL); if (!ok) beep(); } @@ -106,23 +107,6 @@ int Platform_getMaxPid() { return maxPid; } -void Platform_getBatteryLevel(double* level, ACPresence* isOnAC) { - - double percent = Battery_getProcBatData(); - - if (percent == 0) { - percent = Battery_getSysBatData(); - if (percent == 0) { - *level = -1; - *isOnAC = AC_ERROR; - return; - } - } - - *isOnAC = Battery_isOnAC(); - *level = percent; -} - double Platform_setCPUValues(Meter* this, int cpu) { LinuxProcessList* pl = (LinuxProcessList*) this->pl; CPUData* cpuData = &(pl->cpus[cpu]); diff --git a/linux/Platform.h b/linux/Platform.h index 6fb6b543..c4a5cd24 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -10,6 +10,7 @@ in the source distribution for its full text. */ #include "Action.h" +#include "MainPanel.h" #include "BatteryMeter.h" void Platform_setBindings(Htop_Action* keys); @@ -22,8 +23,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(); -void Platform_getBatteryLevel(double* level, ACPresence* isOnAC); - double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this);