ScreenManager.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*
  2. htop - ScreenManager.c
  3. (C) 2004-2011 Hisham H. Muhammad
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "ScreenManager.h"
  9. #include <assert.h>
  10. #include <stdbool.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <sys/time.h>
  14. #include "CRT.h"
  15. #include "FunctionBar.h"
  16. #include "Machine.h"
  17. #include "Macros.h"
  18. #include "Object.h"
  19. #include "Platform.h"
  20. #include "Process.h"
  21. #include "ProvideCurses.h"
  22. #include "Settings.h"
  23. #include "Table.h"
  24. #include "XUtils.h"
  25. ScreenManager* ScreenManager_new(Header* header, Machine* host, State* state, bool owner) {
  26. ScreenManager* this;
  27. this = xMalloc(sizeof(ScreenManager));
  28. this->x1 = 0;
  29. this->y1 = 0;
  30. this->x2 = 0;
  31. this->y2 = -1;
  32. this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
  33. this->panelCount = 0;
  34. this->header = header;
  35. this->host = host;
  36. this->state = state;
  37. this->allowFocusChange = true;
  38. return this;
  39. }
  40. void ScreenManager_delete(ScreenManager* this) {
  41. Vector_delete(this->panels);
  42. free(this);
  43. }
  44. inline int ScreenManager_size(const ScreenManager* this) {
  45. return this->panelCount;
  46. }
  47. void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
  48. ScreenManager_insert(this, item, size, Vector_size(this->panels));
  49. }
  50. static int header_height(const ScreenManager* this) {
  51. if (this->state->hideMeters)
  52. return 0;
  53. if (this->header)
  54. return this->header->height;
  55. return 0;
  56. }
  57. void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
  58. int lastX = 0;
  59. if (idx > 0) {
  60. const Panel* last = (const Panel*) Vector_get(this->panels, idx - 1);
  61. lastX = last->x + last->w + 1;
  62. }
  63. int height = LINES - this->y1 - header_height(this) + this->y2;
  64. if (size <= 0) {
  65. size = COLS - this->x1 + this->x2 - lastX;
  66. }
  67. Panel_resize(item, size, height);
  68. Panel_move(item, lastX, this->y1 + header_height(this));
  69. if (idx < this->panelCount) {
  70. for (int i = idx + 1; i <= this->panelCount; i++) {
  71. Panel* p = (Panel*) Vector_get(this->panels, i);
  72. Panel_move(p, p->x + size, p->y);
  73. }
  74. }
  75. Vector_insert(this->panels, idx, item);
  76. item->needsRedraw = true;
  77. this->panelCount++;
  78. }
  79. Panel* ScreenManager_remove(ScreenManager* this, int idx) {
  80. assert(this->panelCount > idx);
  81. int w = ((Panel*) Vector_get(this->panels, idx))->w;
  82. Panel* panel = (Panel*) Vector_remove(this->panels, idx);
  83. this->panelCount--;
  84. if (idx < this->panelCount) {
  85. for (int i = idx; i < this->panelCount; i++) {
  86. Panel* p = (Panel*) Vector_get(this->panels, i);
  87. Panel_move(p, p->x - w, p->y);
  88. }
  89. }
  90. return panel;
  91. }
  92. void ScreenManager_resize(ScreenManager* this) {
  93. int y1_header = this->y1 + header_height(this);
  94. int panels = this->panelCount;
  95. int lastX = 0;
  96. for (int i = 0; i < panels - 1; i++) {
  97. Panel* panel = (Panel*) Vector_get(this->panels, i);
  98. Panel_resize(panel, panel->w, LINES - y1_header + this->y2);
  99. Panel_move(panel, lastX, y1_header);
  100. lastX = panel->x + panel->w + 1;
  101. }
  102. Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
  103. Panel_resize(panel, COLS - this->x1 + this->x2 - lastX, LINES - y1_header + this->y2);
  104. Panel_move(panel, lastX, y1_header);
  105. }
  106. static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) {
  107. Machine* host = this->host;
  108. Platform_gettime_realtime(&host->realtime, &host->realtimeMs);
  109. double newTime = ((double)host->realtime.tv_sec * 10) + ((double)host->realtime.tv_usec / 100000);
  110. *timedOut = (newTime - *oldTime > host->settings->delay);
  111. *rescan |= *timedOut;
  112. if (newTime < *oldTime) {
  113. *rescan = true; // clock was adjusted?
  114. }
  115. if (*rescan) {
  116. *oldTime = newTime;
  117. int oldUidDigits = Process_uidDigits;
  118. if (!this->state->pauseUpdate && (*sortTimeout == 0 || host->settings->ss->treeView)) {
  119. host->activeTable->needsSort = true;
  120. *sortTimeout = 1;
  121. }
  122. // sample current values for system metrics and processes if not paused
  123. Machine_scan(host);
  124. if (!this->state->pauseUpdate)
  125. Machine_scanTables(host);
  126. // always update header, especially to avoid gaps in graph meters
  127. Header_updateData(this->header);
  128. // force redraw if the number of UID digits was changed
  129. if (Process_uidDigits != oldUidDigits) {
  130. *force_redraw = true;
  131. }
  132. *redraw = true;
  133. }
  134. if (*redraw) {
  135. Table_rebuildPanel(host->activeTable);
  136. if (!this->state->hideMeters)
  137. Header_draw(this->header);
  138. }
  139. *rescan = false;
  140. }
  141. static inline bool drawTab(const int* y, int* x, int l, const char* name, bool cur) {
  142. attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
  143. mvaddch(*y, *x, '[');
  144. (*x)++;
  145. if (*x >= l)
  146. return false;
  147. int nameLen = strlen(name);
  148. int n = MINIMUM(l - *x, nameLen);
  149. attrset(CRT_colors[cur ? SCREENS_CUR_TEXT : SCREENS_OTH_TEXT]);
  150. mvaddnstr(*y, *x, name, n);
  151. *x += n;
  152. if (*x >= l)
  153. return false;
  154. attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
  155. mvaddch(*y, *x, ']');
  156. *x += 2;
  157. if (*x >= l)
  158. return false;
  159. return true;
  160. }
  161. static void ScreenManager_drawScreenTabs(ScreenManager* this) {
  162. Settings* settings = this->host->settings;
  163. ScreenSettings** screens = settings->screens;
  164. int cur = settings->ssIndex;
  165. int l = COLS;
  166. Panel* panel = (Panel*) Vector_get(this->panels, 0);
  167. int y = panel->y - 1;
  168. int x = 2;
  169. if (this->name) {
  170. drawTab(&y, &x, l, this->name, true);
  171. return;
  172. }
  173. for (int s = 0; screens[s]; s++) {
  174. bool ok = drawTab(&y, &x, l, screens[s]->heading, s == cur);
  175. if (!ok) {
  176. break;
  177. }
  178. }
  179. attrset(CRT_colors[RESET_COLOR]);
  180. }
  181. static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
  182. Settings* settings = this->host->settings;
  183. if (settings->screenTabs) {
  184. ScreenManager_drawScreenTabs(this);
  185. }
  186. const int nPanels = this->panelCount;
  187. for (int i = 0; i < nPanels; i++) {
  188. Panel* panel = (Panel*) Vector_get(this->panels, i);
  189. Panel_draw(panel,
  190. force_redraw,
  191. i == focus,
  192. panel != (Panel*)this->state->mainPanel || !this->state->hideSelection,
  193. State_hideFunctionBar(this->state));
  194. mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
  195. }
  196. }
  197. void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name) {
  198. bool quit = false;
  199. int focus = 0;
  200. Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
  201. Settings* settings = this->host->settings;
  202. double oldTime = 0.0;
  203. int ch = ERR;
  204. int closeTimeout = 0;
  205. bool timedOut = true;
  206. bool redraw = true;
  207. bool force_redraw = true;
  208. bool rescan = false;
  209. int sortTimeout = 0;
  210. int resetSortTimeout = 5;
  211. this->name = name;
  212. while (!quit) {
  213. if (this->header) {
  214. checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw);
  215. }
  216. if (redraw || force_redraw) {
  217. ScreenManager_drawPanels(this, focus, force_redraw);
  218. force_redraw = false;
  219. if (this->host->iterationsRemaining != -1) {
  220. if (!--this->host->iterationsRemaining) {
  221. quit = true;
  222. continue;
  223. }
  224. }
  225. }
  226. int prevCh = ch;
  227. ch = Panel_getCh(panelFocus);
  228. HandlerResult result = IGNORED;
  229. #ifdef HAVE_GETMOUSE
  230. if (ch == KEY_MOUSE && settings->enableMouse) {
  231. ch = ERR;
  232. MEVENT mevent;
  233. int ok = getmouse(&mevent);
  234. if (ok == OK) {
  235. if (mevent.bstate & BUTTON1_RELEASED) {
  236. if (mevent.y == LINES - 1) {
  237. ch = FunctionBar_synthesizeEvent(panelFocus->currentBar, mevent.x);
  238. } else {
  239. for (int i = 0; i < this->panelCount; i++) {
  240. Panel* panel = (Panel*) Vector_get(this->panels, i);
  241. if (mevent.x >= panel->x && mevent.x <= panel->x + panel->w) {
  242. if (mevent.y == panel->y) {
  243. ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
  244. break;
  245. } else if (settings->screenTabs && mevent.y == panel->y - 1) {
  246. ch = EVENT_SCREEN_TAB_CLICK(mevent.x);
  247. break;
  248. } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) {
  249. ch = KEY_MOUSE;
  250. if (panel == panelFocus || this->allowFocusChange) {
  251. focus = i;
  252. panelFocus = panel;
  253. const Object* oldSelection = Panel_getSelected(panel);
  254. Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
  255. if (Panel_getSelected(panel) == oldSelection) {
  256. ch = KEY_RECLICK;
  257. }
  258. }
  259. break;
  260. }
  261. }
  262. }
  263. }
  264. #if NCURSES_MOUSE_VERSION > 1
  265. } else if (mevent.bstate & BUTTON4_PRESSED) {
  266. ch = KEY_WHEELUP;
  267. } else if (mevent.bstate & BUTTON5_PRESSED) {
  268. ch = KEY_WHEELDOWN;
  269. #endif
  270. }
  271. }
  272. }
  273. #endif
  274. if (ch == ERR) {
  275. if (sortTimeout > 0)
  276. sortTimeout--;
  277. if (prevCh == ch && !timedOut) {
  278. closeTimeout++;
  279. if (closeTimeout == 100) {
  280. break;
  281. }
  282. } else {
  283. closeTimeout = 0;
  284. }
  285. redraw = false;
  286. continue;
  287. }
  288. switch (ch) {
  289. case KEY_ALT('H'): ch = KEY_LEFT; break;
  290. case KEY_ALT('J'): ch = KEY_DOWN; break;
  291. case KEY_ALT('K'): ch = KEY_UP; break;
  292. case KEY_ALT('L'): ch = KEY_RIGHT; break;
  293. }
  294. redraw = true;
  295. if (Panel_eventHandlerFn(panelFocus)) {
  296. result = Panel_eventHandler(panelFocus, ch);
  297. }
  298. if (result & SYNTH_KEY) {
  299. ch = result >> 16;
  300. }
  301. if (result & REFRESH) {
  302. sortTimeout = 0;
  303. }
  304. if (result & REDRAW) {
  305. force_redraw = true;
  306. }
  307. if (result & RESIZE) {
  308. ScreenManager_resize(this);
  309. force_redraw = true;
  310. }
  311. if (result & RESCAN) {
  312. rescan = true;
  313. sortTimeout = 0;
  314. }
  315. if (result & HANDLED) {
  316. continue;
  317. } else if (result & BREAK_LOOP) {
  318. quit = true;
  319. continue;
  320. }
  321. switch (ch) {
  322. case KEY_RESIZE:
  323. {
  324. ScreenManager_resize(this);
  325. continue;
  326. }
  327. case KEY_FOCUS_IN:
  328. case KEY_FOCUS_OUT:
  329. break;
  330. case KEY_LEFT:
  331. case KEY_CTRL('B'):
  332. if (this->panelCount < 2) {
  333. goto defaultHandler;
  334. }
  335. if (!this->allowFocusChange) {
  336. break;
  337. }
  338. tryLeft:
  339. if (focus > 0) {
  340. focus--;
  341. }
  342. panelFocus = (Panel*) Vector_get(this->panels, focus);
  343. if (Panel_size(panelFocus) == 0 && focus > 0) {
  344. goto tryLeft;
  345. }
  346. break;
  347. case KEY_RIGHT:
  348. case KEY_CTRL('F'):
  349. case 9:
  350. if (this->panelCount < 2) {
  351. goto defaultHandler;
  352. }
  353. if (!this->allowFocusChange) {
  354. break;
  355. }
  356. tryRight:
  357. if (focus < this->panelCount - 1) {
  358. focus++;
  359. }
  360. panelFocus = (Panel*) Vector_get(this->panels, focus);
  361. if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
  362. goto tryRight;
  363. }
  364. break;
  365. case '#':
  366. this->state->hideMeters = !this->state->hideMeters;
  367. ScreenManager_resize(this);
  368. force_redraw = true;
  369. break;
  370. case 27:
  371. case 'q':
  372. case KEY_F(10):
  373. quit = true;
  374. continue;
  375. default:
  376. defaultHandler:
  377. sortTimeout = resetSortTimeout;
  378. Panel_onKey(panelFocus, ch);
  379. break;
  380. }
  381. }
  382. if (lastFocus) {
  383. *lastFocus = panelFocus;
  384. }
  385. if (lastKey) {
  386. *lastKey = ch;
  387. }
  388. }