htop.c 29.5 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
/*
htop - htop.c
3
(C) 2004-2011 Hisham H. Muhammad
Hisham Muhammad's avatar
Hisham Muhammad committed
4
5
6
7
8
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9

Hisham Muhammad's avatar
Hisham Muhammad committed
10
#include "CRT.h"
11
#include "Panel.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12
13
14
15
16
17
#include "UsersTable.h"
#include "RichString.h"
#include "Settings.h"
#include "ScreenManager.h"
#include "FunctionBar.h"
#include "ListItem.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
18
19
#include "String.h"
#include "ColumnsPanel.h"
20
21
#include "CategoriesPanel.h"
#include "SignalsPanel.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
22
#include "TraceScreen.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
23
#include "OpenFilesScreen.h"
24
#include "AffinityPanel.h"
25
#include "IOPriorityPanel.h"
26
#include "IncSet.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
27

Hisham Muhammad's avatar
Hisham Muhammad committed
28
29
30
31
32
33
34
35
36
37
38
39
#include <unistd.h>
#include <math.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <locale.h>
#include <getopt.h>
#include <pwd.h>
#include <string.h>
#include <sys/param.h>
#include <sys/time.h>

Hisham Muhammad's avatar
Hisham Muhammad committed
40
41
//#link m

42
#define COPYRIGHT "(C) 2004-2012 Hisham Muhammad"
43

44
static void printVersionFlag() {
45
46
47
   fputs("htop " VERSION " - " COPYRIGHT "\n"
         "Released under the GNU GPL.\n\n",
         stdout);
Hisham Muhammad's avatar
Hisham Muhammad committed
48
49
50
   exit(0);
}

51
52
static const char* defaultFunctions[]  = {"Help  ", "Setup ", "Search", "Filter", "Tree  ", "SortBy", "Nice -", "Nice +", "Kill  ", "Quit  ", NULL};
 
53
static void printHelpFlag() {
54
55
   fputs("htop " VERSION " - " COPYRIGHT "\n"
         "Released under the GNU GPL.\n\n"
56
57
58
59
60
61
         "-C --no-color               Use a monochrome color scheme\n"
         "-d --delay=DELAY            Set the delay between updates, in tenths of seconds\n"
         "-h --help                   Print this help screen\n"
         "-s --sort-key=COLUMN        Sort by COLUMN (try --sort-key=help for a list)\n"
         "-u --user=USERNAME          Show only processes of a given user\n"
         "-p --pid=PID,[,PID,PID...]  Show only the given PIDs\n"
62
63
64
65
66
67
         "-v --version          Print version info\n"
         "\n"
         "Long options may be passed with a single dash.\n\n"
         "Press F1 inside htop for online help.\n"
         "See 'man htop' for more information.\n",
         stdout);
Hisham Muhammad's avatar
Hisham Muhammad committed
68
69
70
   exit(0);
}

71
72
73
74
75
76
77
78
79
80
static struct { const char* key; const char* info; } helpLeft[] = {
   { .key = " Arrows: ", .info = "scroll process list" },
   { .key = " Digits: ", .info = "incremental PID search" },
   { .key = "   F3 /: ", .info = "incremental name search" },
   { .key = "   F4 \\: ",.info = "incremental name filtering" },
   { .key = "   F5 t: ", .info = "tree view" },
   { .key = "      u: ", .info = "show processes of a single user" },
   { .key = "      H: ", .info = "hide/show user threads" },
   { .key = "      K: ", .info = "hide/show kernel threads" },
   { .key = "      F: ", .info = "cursor follows process" },
81
   { .key = " F6 + -: ", .info = "expand/collapse tree" },
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
   { .key = "  P M T: ", .info = "sort by CPU%, MEM% or TIME" },
   { .key = "      I: ", .info = "invert sort order" },
   { .key = "   F6 >: ", .info = "select sort column" },
   { .key = NULL, .info = NULL }
};

static struct { const char* key; const char* info; } helpRight[] = {
   { .key = "  Space: ", .info = "tag process" },
   { .key = "      c: ", .info = "tag process and its children" },
   { .key = "      U: ", .info = "untag all processes" },
   { .key = "   F9 k: ", .info = "kill process/tagged processes" },
   { .key = "   F7 ]: ", .info = "higher priority (root only)" },
   { .key = "   F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
   { .key = "      a: ", .info = "set CPU affinity" },
#endif
   { .key = "      i: ", .info = "set IO prority" },
   { .key = "      l: ", .info = "list open files with lsof" },
   { .key = "      s: ", .info = "trace syscalls with strace" },
   { .key = "         ", .info = "" },
   { .key = "   F2 S: ", .info = "setup" },
   { .key = "   F1 h: ", .info = "show this help screen" },
   { .key = "  F10 q: ", .info = "quit" },
   { .key = NULL, .info = NULL }
};

108
static void showHelp(ProcessList* pl) {
Hisham Muhammad's avatar
Hisham Muhammad committed
109
110
   clear();
   attrset(CRT_colors[HELP_BOLD]);
111
112
113
114

   for (int i = 0; i < LINES-1; i++)
      mvhline(i, 0, ' ', COLS);

115
   mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
Hisham Muhammad's avatar
Hisham Muhammad committed
116
117
118
119
120
121
   mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");

   attrset(CRT_colors[DEFAULT_COLOR]);
   mvaddstr(3, 0, "CPU usage bar: ");
   #define addattrstr(a,s) attrset(a);addstr(s)
   addattrstr(CRT_colors[BAR_BORDER], "[");
122
   if (pl->detailedCPUTime) {
123
124
125
126
      addattrstr(CRT_colors[CPU_NICE], "low"); addstr("/");
      addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
      addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
      addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
127
      addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
128
      addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
129
130
      addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
      addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
131
132
133
134
      addattrstr(CRT_colors[BAR_SHADOW], " used%");
   } else {
      addattrstr(CRT_colors[CPU_NICE], "low-priority"); addstr("/");
      addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
135
136
137
      addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
      addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
      addattrstr(CRT_colors[BAR_SHADOW], "               used%");
138
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
139
140
141
142
143
144
145
   addattrstr(CRT_colors[BAR_BORDER], "]");
   attrset(CRT_colors[DEFAULT_COLOR]);
   mvaddstr(4, 0, "Memory bar:    ");
   addattrstr(CRT_colors[BAR_BORDER], "[");
   addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
   addattrstr(CRT_colors[MEMORY_BUFFERS], "buffers"); addstr("/");
   addattrstr(CRT_colors[MEMORY_CACHE], "cache");
146
   addattrstr(CRT_colors[BAR_SHADOW], "                            used/total");
Hisham Muhammad's avatar
Hisham Muhammad committed
147
148
149
150
151
   addattrstr(CRT_colors[BAR_BORDER], "]");
   attrset(CRT_colors[DEFAULT_COLOR]);
   mvaddstr(5, 0, "Swap bar:      ");
   addattrstr(CRT_colors[BAR_BORDER], "[");
   addattrstr(CRT_colors[SWAP], "used");
152
   addattrstr(CRT_colors[BAR_SHADOW], "                                          used/total");
Hisham Muhammad's avatar
Hisham Muhammad committed
153
154
   addattrstr(CRT_colors[BAR_BORDER], "]");
   attrset(CRT_colors[DEFAULT_COLOR]);
155
   mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen.");
156
157
158
159
   if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
      mvaddstr(7, 0, "In monochrome, meters are displayed through different chars, in order: |#*@$%&");
   }
   mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
160
161
   for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9,  helpLeft[i].info); }
   for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); }
Hisham Muhammad's avatar
Hisham Muhammad committed
162
   attrset(CRT_colors[HELP_BOLD]);
163
164
   for (int i = 0; helpLeft[i].key;  i++) { mvaddstr(9+i, 0,  helpLeft[i].key); }
   for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
Hisham Muhammad's avatar
Hisham Muhammad committed
165
166
167
168
169
170
171
172
173

   attrset(CRT_colors[HELP_BOLD]);
   mvaddstr(23,0, "Press any key to return.");
   attrset(CRT_colors[DEFAULT_COLOR]);
   refresh();
   CRT_readKey();
   clear();
}

Hisham Muhammad's avatar
Hisham Muhammad committed
174
static const char* CategoriesFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "Done  ", NULL};
175

176
177
static void Setup_run(Settings* settings, const Header* header) {
   ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, true);
Hisham Muhammad's avatar
Hisham Muhammad committed
178
   CategoriesPanel* panelCategories = CategoriesPanel_new(settings, scr);
Hisham Muhammad's avatar
Hisham Muhammad committed
179
   ScreenManager_add(scr, (Panel*) panelCategories, FunctionBar_new(CategoriesFunctions, NULL, NULL), 16);
Hisham Muhammad's avatar
Hisham Muhammad committed
180
181
   CategoriesPanel_makeMetersPage(panelCategories);
   Panel* panelFocus;
Hisham Muhammad's avatar
Hisham Muhammad committed
182
   int ch;
Hisham Muhammad's avatar
Hisham Muhammad committed
183
   ScreenManager_run(scr, &panelFocus, &ch);
Hisham Muhammad's avatar
Hisham Muhammad committed
184
185
186
   ScreenManager_delete(scr);
}

187
188
189
typedef bool(*ForeachProcessFn)(Process*, size_t);

static bool foreachProcess(Panel* panel, ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
190
   bool ok = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
191
   bool anyTagged = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
192
   for (int i = 0; i < Panel_size(panel); i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
193
      Process* p = (Process*) Panel_get(panel, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
194
      if (p->tag) {
195
         ok = fn(p, arg) && ok;
Hisham Muhammad's avatar
Hisham Muhammad committed
196
197
198
199
         anyTagged = true;
      }
   }
   if (!anyTagged) {
Hisham Muhammad's avatar
Hisham Muhammad committed
200
      Process* p = (Process*) Panel_getSelected(panel);
201
      if (p) ok = fn(p, arg) && ok;
Hisham Muhammad's avatar
Hisham Muhammad committed
202
   }
203
204
205
206
207
208
209
210
   if (wasAnyTagged)
      *wasAnyTagged = anyTagged;
   return ok;
}

static bool changePriority(Panel* panel, int delta) {
   bool anyTagged;
   bool ok = foreachProcess(panel, (ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged);
211
212
   if (!ok)
      beep();
Hisham Muhammad's avatar
Hisham Muhammad committed
213
214
215
   return anyTagged;
}

216
217
218
219
220
221
222
223
static int selectedPid(Panel* panel) {
   Process* p = (Process*) Panel_getSelected(panel);
   if (p) {
      return p->pid;
   }
   return -1;
}

224
static Object* pickFromVector(Panel* panel, Panel* list, int x, int y, const char** keyLabels, FunctionBar* prevBar, Header* header) {
Hisham Muhammad's avatar
Hisham Muhammad committed
225
   const char* fuKeys[] = {"Enter", "Esc", NULL};
226
   int fuEvents[] = {13, 27};
227
   ScreenManager* scr = ScreenManager_new(0, y, 0, -1, HORIZONTAL, header, false);
228
   scr->allowFocusChange = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
229
   ScreenManager_add(scr, list, FunctionBar_new(keyLabels, fuKeys, fuEvents), x - 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
230
231
   ScreenManager_add(scr, panel, NULL, -1);
   Panel* panelFocus;
Hisham Muhammad's avatar
Hisham Muhammad committed
232
   int ch;
233
   bool unfollow = false;
234
   int pid = selectedPid(panel);
235
   if (header->pl->following == -1) {
236
      header->pl->following = pid;
237
238
      unfollow = true;
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
239
   ScreenManager_run(scr, &panelFocus, &ch);
240
241
242
   if (unfollow) {
      header->pl->following = -1;
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
243
   ScreenManager_delete(scr);
Hisham Muhammad's avatar
Hisham Muhammad committed
244
245
   Panel_move(panel, 0, y);
   Panel_resize(panel, COLS, LINES-y-1);
Hisham Muhammad's avatar
Hisham Muhammad committed
246
   FunctionBar_draw(prevBar, NULL);
Hisham Muhammad's avatar
Hisham Muhammad committed
247
   if (panelFocus == list && ch == 13) {
248
      Process* selected = (Process*)Panel_getSelected(panel);
249
      if (selected && selected->pid == pid)
250
251
252
         return Panel_getSelected(list);
      else
         beep();
Hisham Muhammad's avatar
Hisham Muhammad committed
253
254
255
256
   }
   return NULL;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
257
static void addUserToVector(int key, void* userCast, void* panelCast) {
Hisham Muhammad's avatar
Hisham Muhammad committed
258
   char* user = (char*) userCast;
Hisham Muhammad's avatar
Hisham Muhammad committed
259
260
   Panel* panel = (Panel*) panelCast;
   Panel_add(panel, (Object*) ListItem_new(user, key));
Hisham Muhammad's avatar
Hisham Muhammad committed
261
262
}

263
static bool setUserOnly(const char* userName, bool* userOnly, uid_t* userId) {
Hisham Muhammad's avatar
Hisham Muhammad committed
264
265
266
267
   struct passwd* user = getpwnam(userName);
   if (user) {
      *userOnly = true;
      *userId = user->pw_uid;
268
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
269
   }
270
   return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
}

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
static void setTreeView(ProcessList* pl, FunctionBar* fuBar, bool mode) {
   if (mode) {
      FunctionBar_setLabel(fuBar, KEY_F(5), "Sorted");
      FunctionBar_setLabel(fuBar, KEY_F(6), "Collap");
   } else {
      FunctionBar_setLabel(fuBar, KEY_F(5), "Tree  ");
      FunctionBar_setLabel(fuBar, KEY_F(6), "SortBy");
   }
   if (mode != pl->treeView) {
      FunctionBar_draw(fuBar, NULL);
   }
   pl->treeView = mode;
}

static inline void setSortKey(ProcessList* pl, FunctionBar* fuBar, ProcessField sortKey, Panel* panel, Settings* settings) {
288
289
   pl->sortKey = sortKey;
   pl->direction = 1;
290
   setTreeView(pl, fuBar, false);
291
   settings->changed = true;
292
   ProcessList_printHeader(pl, Panel_getHeader(panel));
293
294
}

295
296
297
298
299
static const char* getMainPanelValue(Panel* panel, int i) {
   Process* p = (Process*) Panel_get(panel, i);
   if (p)
      return p->comm;
   return "";
Hisham Muhammad's avatar
Hisham Muhammad committed
300
301
}

302
303
304
305
306
307
308
309
310
311
312
static void tagAllChildren(Panel* panel, Process* parent) {
   parent->tag = true;
   pid_t ppid = parent->pid;
   for (int i = 0; i < Panel_size(panel); i++) {
      Process* p = (Process*) Panel_get(panel, i);
      if (!p->tag && p->ppid == ppid) {
         tagAllChildren(panel, p);
      }
   }
}

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
static bool expandCollapse(Panel* panel) {
   Process* p = (Process*) Panel_getSelected(panel);
   if (!p) return false;
   p->showChildren = !p->showChildren;
   return true;
}

void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header) {
   Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
   Panel_setHeader(sortPanel, "Sort by");
   const char* fuFunctions[] = {"Sort  ", "Cancel ", NULL};
   ProcessField* fields = pl->fields;
   for (int i = 0; fields[i]; i++) {
      char* name = String_trim(Process_fieldNames[fields[i]]);
      Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
      if (fields[i] == pl->sortKey)
         Panel_setSelected(sortPanel, i);
      free(name);
   }
   ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
   if (field) {
      settings->changed = true;
      setSortKey(pl, defaultBar, field->key, panel, settings);
   } else {
      ProcessList_printHeader(pl, Panel_getHeader(panel));
   }
   Object_delete(sortPanel);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
342
343
344
345
346
int main(int argc, char** argv) {

   int delay = -1;
   bool userOnly = false;
   uid_t userId = 0;
347
   int usecolors = 1;
348
349
350
   char *argCopy;
   char *pid;
   Hashtable *pidWhiteList = NULL;
351
352
353
354
355
356
357
358
359
360
361

   int opt, opti=0;
   static struct option long_opts[] =
   {
      {"help",     no_argument,         0, 'h'},
      {"version",  no_argument,         0, 'v'},
      {"delay",    required_argument,   0, 'd'},
      {"sort-key", required_argument,   0, 's'},
      {"user",     required_argument,   0, 'u'},
      {"no-color", no_argument,         0, 'C'},
      {"no-colour",no_argument,         0, 'C'},
362
      {"pid",      required_argument,   0, 'p'},
363
364
      {0,0,0,0}
   };
365
   int sortKey = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
366

367
368
369
   char *lc_ctype = getenv("LC_CTYPE");
   if(lc_ctype != NULL)
      setlocale(LC_CTYPE, lc_ctype);
370
371
   else if ((lc_ctype = getenv("LC_ALL")))
      setlocale(LC_CTYPE, lc_ctype);
372
   else
373
      setlocale(LC_CTYPE, "");
374

375
   /* Parse arguments */
376
   while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:", long_opts, &opti))) {
377
378
379
380
381
382
383
384
385
      if (opt == EOF) break;
      switch (opt) {
         case 'h':
            printHelpFlag();
            break;
         case 'v':
            printVersionFlag();
            break;
         case 's':
Hisham Muhammad's avatar
Hisham Muhammad committed
386
            if (strcmp(optarg, "help") == 0) {
387
388
389
390
391
392
393
394
395
396
397
398
               for (int j = 1; j < LAST_PROCESSFIELD; j++)
                  printf ("%s\n", Process_fieldNames[j]);
               exit(0);
            }

            sortKey = ColumnsPanel_fieldNameToIndex(optarg);
            if (sortKey == -1) {
               fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
               exit(1);
            }
            break;
         case 'd':
399
            if (sscanf(optarg, "%16d", &delay) == 1) {
400
401
402
403
404
405
               if (delay < 1) delay = 1;
               if (delay > 100) delay = 100;
            } else {
               fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
               exit(1);
            }
406
407
            break;
         case 'u':
408
409
410
411
            if (!setUserOnly(optarg, &userOnly, &userId)) {
               fprintf(stderr, "Error: invalid user \"%s\".\n", optarg);
               exit(1);
            }
412
413
414
            break;
         case 'C':
            usecolors=0;
415
            break;
416
        case 'p': {
417
            argCopy = strdup(optarg);
418
419
            char* saveptr;
            pid = strtok_r(argCopy, ",", &saveptr);
420
421
422
423
424
425
426
427

            if( !pidWhiteList ) {
               pidWhiteList = Hashtable_new(8, false);
            }

            while( pid ) {
                unsigned int num_pid = atoi(pid);
                Hashtable_put(pidWhiteList, num_pid, (void *) 1);
428
                pid = strtok_r(NULL, ",", &saveptr);
429
430
431
            }
            free(argCopy);

432
            break;
433
         }
434
435
         default:
            exit(1);
Hisham Muhammad's avatar
Hisham Muhammad committed
436
437
      }
   }
438
439


440
441
442
443
   if (access(PROCDIR, R_OK) != 0) {
      fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
      exit(1);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
444
445
446
447
448

   int quit = 0;
   int refreshTimeout = 0;
   int resetRefreshTimeout = 5;
   bool doRefresh = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
449
   bool doRecalculate = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
450
451
452
453
454
   Settings* settings;
   
   ProcessList* pl = NULL;
   UsersTable* ut = UsersTable_new();

455
456
457
458
459
460
461
462
463
464
465
466
467
468
#ifdef HAVE_LIBNCURSESW
   char *locale = setlocale(LC_ALL, NULL);
   if (locale == NULL || locale[0] == '\0')
      locale = setlocale(LC_CTYPE, NULL);
   if (locale != NULL &&
       (strstr(locale, "UTF-8") ||
        strstr(locale, "utf-8") ||
        strstr(locale, "UTF8")  ||
        strstr(locale, "utf8")))
      CRT_utf8 = true;
   else
      CRT_utf8 = false;
#endif

469
   pl = ProcessList_new(ut, pidWhiteList);
470
   Process_getMaxPid();
Hisham Muhammad's avatar
Hisham Muhammad committed
471
472
   
   Header* header = Header_new(pl);
473
   settings = Settings_new(pl, header, pl->cpuCount);
Hisham Muhammad's avatar
Hisham Muhammad committed
474
475
476
477
478
   int headerHeight = Header_calculateHeight(header);

   // FIXME: move delay code to settings
   if (delay != -1)
      settings->delay = delay;
479
480
   if (!usecolors) 
      settings->colorScheme = COLORSCHEME_MONOCHROME;
481

Hisham Muhammad's avatar
Hisham Muhammad committed
482
   CRT_init(settings->delay, settings->colorScheme);
483

484
   Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class);
485
   ProcessList_setPanel(pl, panel);
Hisham Muhammad's avatar
Hisham Muhammad committed
486
   
487
488
489
   FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
   setTreeView(pl, defaultBar, pl->treeView);
   
490
491
   if (sortKey > 0) {
      pl->sortKey = sortKey;
492
      setTreeView(pl, defaultBar, false);
493
494
      pl->direction = 1;
   }
495
   ProcessList_printHeader(pl, Panel_getHeader(panel));
Hisham Muhammad's avatar
Hisham Muhammad committed
496

497
498
   IncSet* inc = IncSet_new(defaultBar);

Hisham Muhammad's avatar
Hisham Muhammad committed
499
500
501
502
503
504
505
506
507
508
509
   ProcessList_scan(pl);
   usleep(75000);
   
   FunctionBar_draw(defaultBar, NULL);
   
   int acc = 0;
   bool follow = false;
 
   struct timeval tv;
   double oldTime = 0.0;

510
   int ch = ERR;
Hisham Muhammad's avatar
Hisham Muhammad committed
511
512
   int closeTimeout = 0;

513
514
   bool idle = false;
   
515
516
   bool collapsed = false;
   
Hisham Muhammad's avatar
Hisham Muhammad committed
517
518
   while (!quit) {
      gettimeofday(&tv, NULL);
519
520
      double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
      bool recalculate = (newTime - oldTime > settings->delay);
521
      int following = follow ? selectedPid(panel) : -1;
522
523
      if (recalculate) {
         Header_draw(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
524
         oldTime = newTime;
525
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
526
      if (doRefresh) {
Hisham Muhammad's avatar
Hisham Muhammad committed
527
         if (recalculate || doRecalculate) {
Hisham Muhammad's avatar
Hisham Muhammad committed
528
            ProcessList_scan(pl);
Hisham Muhammad's avatar
Hisham Muhammad committed
529
530
            doRecalculate = false;
         }
531
         if (refreshTimeout == 0 || pl->treeView) {
Hisham Muhammad's avatar
Hisham Muhammad committed
532
533
534
            ProcessList_sort(pl);
            refreshTimeout = 1;
         }
535
         ProcessList_rebuildPanel(pl, true, following, userOnly, userId, IncSet_filter(inc));
536
         idle = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
537
538
      }
      doRefresh = true;
539
540
541
542
543
544
545
546
547
548
549
550
551
552
      
      if (pl->treeView) {
         Process* p = (Process*) Panel_getSelected(panel);
         if (p) {
            if (!p->showChildren && !collapsed) {
               FunctionBar_setLabel(defaultBar, KEY_F(6), "Expand");
               FunctionBar_draw(defaultBar, NULL);
            } else if (p->showChildren && collapsed) {
               FunctionBar_setLabel(defaultBar, KEY_F(6), "Collap");
               FunctionBar_draw(defaultBar, NULL);
            }
            collapsed = !p->showChildren;
         }
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
553

554
      if (!idle) {
555
         Panel_draw(panel, true);
556
      }
557
      
Hisham Muhammad's avatar
Hisham Muhammad committed
558
      int prev = ch;
559
      if (inc->active)
560
         move(LINES-1, CRT_cursorX);
Hisham Muhammad's avatar
Hisham Muhammad committed
561
562
563
      ch = getch();

      if (ch == ERR) {
564
         if (!inc->active)
Hisham Muhammad's avatar
Hisham Muhammad committed
565
566
567
            refreshTimeout--;
         if (prev == ch && !recalculate) {
            closeTimeout++;
568
            if (closeTimeout == 100) {
Hisham Muhammad's avatar
Hisham Muhammad committed
569
               break;
570
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
571
572
         } else
            closeTimeout = 0;
573
         idle = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
574
575
         continue;
      }
576
      idle = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
577
578
579
580
581

      if (ch == KEY_MOUSE) {
         MEVENT mevent;
         int ok = getmouse(&mevent);
         if (ok == OK) {
582
583
584
585
586
587
            if (mevent.bstate & BUTTON1_CLICKED) {
               if (mevent.y == panel->y) {
                  int x = panel->scrollH + mevent.x + 1;
                  ProcessField field = ProcessList_keyAt(pl, x);
                  if (field == pl->sortKey) {
                     ProcessList_invertSortOrder(pl);
588
                     setTreeView(pl, defaultBar, false);
589
                  } else {
590
                     setSortKey(pl, defaultBar, field, panel, settings);
591
592
593
594
595
596
597
598
599
600
                  }
                  refreshTimeout = 0;
                  continue;
               } else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) {
                  Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
                  doRefresh = false;
                  refreshTimeout = resetRefreshTimeout;
                  follow = true;
                  continue;
               } if (mevent.y == LINES - 1) {
601
                  ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
602
               }
603
604
605
606
607
608
            } else if (mevent.bstate & BUTTON4_CLICKED) {
               ch = KEY_UP;
            #if NCURSES_MOUSE_VERSION > 1
            } else if (mevent.bstate & BUTTON5_CLICKED) {
               ch = KEY_DOWN;
            #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
609
610
611
612
            }
         }
      }

613
614
615
616
617
618
619
620
      if (inc->active) {
         doRefresh = IncSet_handleKey(inc, ch, panel, getMainPanelValue, NULL);
         continue;
      }
      
      if (isdigit((char)ch)) {
         if (Panel_size(panel) == 0) continue;
         pid_t pid = ch-48 + acc;
621
         for (int i = 0; i < ProcessList_size(pl); i++) {
622
            Panel_setSelected(panel, i);
623
624
625
626
627
            Process* p = (Process*) Panel_getSelected(panel);
            if (p && p->pid == pid) {
               break;
            }
         }
628
629
630
631
632
633
634
635
         acc = pid * 10;
         if (acc > 10000000)
            acc = 0;
         continue;
      } else {
         acc = 0;
      }

Hisham Muhammad's avatar
Hisham Muhammad committed
636
637
      switch (ch) {
      case KEY_RESIZE:
Hisham Muhammad's avatar
Hisham Muhammad committed
638
         Panel_resize(panel, COLS, LINES-headerHeight-1);
639
         IncSet_drawBar(inc);
Hisham Muhammad's avatar
Hisham Muhammad committed
640
641
642
643
         break;
      case 'M':
      {
         refreshTimeout = 0;
644
         setSortKey(pl, defaultBar, PERCENT_MEM, panel, settings);
Hisham Muhammad's avatar
Hisham Muhammad committed
645
646
647
648
649
         break;
      }
      case 'T':
      {
         refreshTimeout = 0;
650
         setSortKey(pl, defaultBar, TIME, panel, settings);
Hisham Muhammad's avatar
Hisham Muhammad committed
651
652
         break;
      }
653
654
655
656
657
658
659
      case 'c':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         tagAllChildren(panel, p);
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
660
661
      case 'U':
      {
Hisham Muhammad's avatar
Hisham Muhammad committed
662
         for (int i = 0; i < Panel_size(panel); i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
663
            Process* p = (Process*) Panel_get(panel, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
664
665
666
667
668
669
670
671
            p->tag = false;
         }
         doRefresh = true;
         break;
      }
      case 'P':
      {
         refreshTimeout = 0;
672
         setSortKey(pl, defaultBar, PERCENT_CPU, panel, settings);
Hisham Muhammad's avatar
Hisham Muhammad committed
673
674
675
676
         break;
      }
      case KEY_F(1):
      case 'h':
677
      case '?':
Hisham Muhammad's avatar
Hisham Muhammad committed
678
      {
679
         showHelp(pl);
Hisham Muhammad's avatar
Hisham Muhammad committed
680
681
682
683
684
685
686
687
688
689
690
691
692
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case '\014': // Ctrl+L
      {
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case ' ':
      {
Hisham Muhammad's avatar
Hisham Muhammad committed
693
         Process* p = (Process*) Panel_getSelected(panel);
694
         if (!p) break;
Hisham Muhammad's avatar
Hisham Muhammad committed
695
         Process_toggleTag(p);
Hisham Muhammad's avatar
Hisham Muhammad committed
696
         Panel_onKey(panel, KEY_DOWN);
Hisham Muhammad's avatar
Hisham Muhammad committed
697
698
699
700
         break;
      }
      case 's':
      {
701
702
703
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         TraceScreen* ts = TraceScreen_new(p);
Hisham Muhammad's avatar
Hisham Muhammad committed
704
705
706
707
708
709
710
711
         TraceScreen_run(ts);
         TraceScreen_delete(ts);
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         CRT_enableDelay();
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
712
713
      case 'l':
      {
714
715
716
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         OpenFilesScreen* ts = OpenFilesScreen_new(p);
Hisham Muhammad's avatar
Hisham Muhammad committed
717
718
719
720
721
722
723
724
         OpenFilesScreen_run(ts);
         OpenFilesScreen_delete(ts);
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         CRT_enableDelay();
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
725
726
727
728
      case 'S':
      case 'C':
      case KEY_F(2):
      {
729
         Setup_run(settings, header);
Hisham Muhammad's avatar
Hisham Muhammad committed
730
         // TODO: shouldn't need this, colors should be dynamic
731
         ProcessList_printHeader(pl, Panel_getHeader(panel));
Hisham Muhammad's avatar
Hisham Muhammad committed
732
         headerHeight = Header_calculateHeight(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
733
734
         Panel_move(panel, 0, headerHeight);
         Panel_resize(panel, COLS, LINES-headerHeight-1);
Hisham Muhammad's avatar
Hisham Muhammad committed
735
736
737
738
739
740
741
742
743
744
745
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case 'F':
      {
         follow = true;
         continue;
      }
      case 'u':
      {
746
         Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
Hisham Muhammad's avatar
Hisham Muhammad committed
747
         Panel_setHeader(usersPanel, "Show processes of:");
Hisham Muhammad's avatar
Hisham Muhammad committed
748
         UsersTable_foreach(ut, addUserToVector, usersPanel);
749
         Vector_insertionSort(usersPanel->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
750
         ListItem* allUsers = ListItem_new("All users", -1);
Hisham Muhammad's avatar
Hisham Muhammad committed
751
         Panel_insert(usersPanel, 0, (Object*) allUsers);
Hisham Muhammad's avatar
Hisham Muhammad committed
752
         const char* fuFunctions[] = {"Show    ", "Cancel ", NULL};
753
         ListItem* picked = (ListItem*) pickFromVector(panel, usersPanel, 20, headerHeight, fuFunctions, defaultBar, header);
Hisham Muhammad's avatar
Hisham Muhammad committed
754
755
756
757
758
759
760
         if (picked) {
            if (picked == allUsers) {
               userOnly = false;
            } else {
               setUserOnly(ListItem_getRef(picked), &userOnly, &userId);
            }
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
761
         Panel_delete((Object*)usersPanel);
Hisham Muhammad's avatar
Hisham Muhammad committed
762
763
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
764
765
766
767
      case '+':
      case '=':
      case '-':
      {
768
769
770
771
         if (expandCollapse(panel)) {
            doRecalculate = true;         
            refreshTimeout = 0;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
772
773
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
774
775
776
      case KEY_F(9):
      case 'k':
      {
777
         Panel* signalsPanel = (Panel*) SignalsPanel_new();
Hisham Muhammad's avatar
Hisham Muhammad committed
778
         const char* fuFunctions[] = {"Send  ", "Cancel ", NULL};
779
         ListItem* sgn = (ListItem*) pickFromVector(panel, signalsPanel, 15, headerHeight, fuFunctions, defaultBar, header);
Hisham Muhammad's avatar
Hisham Muhammad committed
780
         if (sgn) {
Hisham Muhammad's avatar
Hisham Muhammad committed
781
            if (sgn->key != 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
782
783
               Panel_setHeader(panel, "Sending...");
               Panel_draw(panel, true);
Hisham Muhammad's avatar
Hisham Muhammad committed
784
               refresh();
785
               foreachProcess(panel, (ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
Hisham Muhammad's avatar
Hisham Muhammad committed
786
787
788
               napms(500);
            }
         }
789
         ProcessList_printHeader(pl, Panel_getHeader(panel));
790
         Panel_delete((Object*)signalsPanel);
Hisham Muhammad's avatar
Hisham Muhammad committed
791
792
793
         refreshTimeout = 0;
         break;
      }
794
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
795
796
      case 'a':
      {
797
         if (pl->cpuCount == 1)
798
799
            break;

800
801
802
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         Affinity* affinity = Process_getAffinity(p);
803
         if (!affinity) break;
804
805
         Panel* affinityPanel = AffinityPanel_new(pl, affinity);
         Affinity_delete(affinity);
806

Hisham Muhammad's avatar
Hisham Muhammad committed
807
         const char* fuFunctions[] = {"Set    ", "Cancel ", NULL};
808
         void* set = pickFromVector(panel, affinityPanel, 15, headerHeight, fuFunctions, defaultBar, header);
809
         if (set) {
810
            Affinity* affinity = AffinityPanel_getAffinity(affinityPanel);
811
812
            bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
            if (!ok) beep();
813
            Affinity_delete(affinity);
814
         }
815
         Panel_delete((Object*)affinityPanel);
816
         ProcessList_printHeader(pl, Panel_getHeader(panel));
817
818
819
         refreshTimeout = 0;
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
820
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
821
822
823
824
825
826
827
828
      case KEY_F(10):
      case 'q':
         quit = 1;
         break;
      case '<':
      case ',':
      case '>':
      case '.':
829
830
831
832
833
834
      {
         sortBy(panel, pl, settings, headerHeight, defaultBar, header);
         refreshTimeout = 0;
         break;
      }
      case KEY_F(18):
Hisham Muhammad's avatar
Hisham Muhammad committed
835
836
      case KEY_F(6):
      {
837
838
839
840
841
         if (pl->treeView) {
            if (expandCollapse(panel)) {
               doRecalculate = true;
               refreshTimeout = 0;
            }
842
         } else {
843
844
            sortBy(panel, pl, settings, headerHeight, defaultBar, header);
            refreshTimeout = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
845
846
847
         }
         break;
      }
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
      case 'i':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         IOPriority ioprio = p->ioPriority;
         Panel* ioprioPanel = IOPriorityPanel_new(ioprio);
         const char* fuFunctions[] = {"Set    ", "Cancel ", NULL};
         void* set = pickFromVector(panel, ioprioPanel, 21, headerHeight, fuFunctions, defaultBar, header);
         if (set) {
            IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
            bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setIOPriority, (size_t) ioprio, NULL);
            if (!ok)
               beep();
         }
         Panel_delete((Object*)ioprioPanel);
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         refreshTimeout = 0;
         break;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
867
868
869
870
871
872
873
874
875
876
      case 'I':
      {
         refreshTimeout = 0;
         settings->changed = true;
         ProcessList_invertSortOrder(pl);
         break;
      }
      case KEY_F(8):
      case '[':
      {
Hisham Muhammad's avatar
Hisham Muhammad committed
877
         doRefresh = changePriority(panel, 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
878
879
880
881
882
         break;
      }
      case KEY_F(7):
      case ']':
      {
Hisham Muhammad's avatar
Hisham Muhammad committed
883
         doRefresh = changePriority(panel, -1);
Hisham Muhammad's avatar
Hisham Muhammad committed
884
885
886
887
         break;
      }
      case KEY_F(3):
      case '/':
888
         IncSet_activate(inc, INC_SEARCH);
Hisham Muhammad's avatar
Hisham Muhammad committed
889
         break;
Hisham Muhammad's avatar
Hisham Muhammad committed
890
891
      case KEY_F(4):
      case '\\':
892
         IncSet_activate(inc, INC_FILTER);
Hisham Muhammad's avatar
Hisham Muhammad committed
893
894
895
         refreshTimeout = 0;
         doRefresh = true;
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
896
897
898
      case 't':
      case KEY_F(5):
         refreshTimeout = 0;
899
900
         collapsed = false;
         setTreeView(pl, defaultBar, !pl->treeView);
901
902
         if (pl->treeView) pl->direction = 1;
         ProcessList_printHeader(pl, Panel_getHeader(panel));
903
         ProcessList_expandTree(pl);
Hisham Muhammad's avatar
Hisham Muhammad committed
904
         settings->changed = true;
905
         if (following != -1) continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
906
907
         break;
      case 'H':
Hisham Muhammad's avatar
Hisham Muhammad committed
908
         doRecalculate = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
909
         refreshTimeout = 0;
910
911
         pl->hideUserlandThreads = !pl->hideUserlandThreads;
         pl->hideThreads = pl->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
912
913
914
         settings->changed = true;
         break;
      case 'K':
Hisham Muhammad's avatar
Hisham Muhammad committed
915
         doRecalculate = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
916
917
918
919
920
921
922
         refreshTimeout = 0;
         pl->hideKernelThreads = !pl->hideKernelThreads;
         settings->changed = true;
         break;
      default:
         doRefresh = false;
         refreshTimeout = resetRefreshTimeout;
Hisham Muhammad's avatar
Hisham Muhammad committed
923
         Panel_onKey(panel, ch);
Hisham Muhammad's avatar
Hisham Muhammad committed
924
925
926
927
928
929
930
931
932
933
934
935
936
937
         break;
      }
      follow = false;
   }
   attron(CRT_colors[RESET_COLOR]);
   mvhline(LINES-1, 0, ' ', COLS);
   attroff(CRT_colors[RESET_COLOR]);
   refresh();
   
   CRT_done();
   if (settings->changed)
      Settings_write(settings);
   Header_delete(header);
   ProcessList_delete(pl);
938
   IncSet_delete(inc);
Hisham Muhammad's avatar
Hisham Muhammad committed
939
   FunctionBar_delete((Object*)defaultBar);
Hisham Muhammad's avatar
Hisham Muhammad committed
940
   Panel_delete((Object*)panel);
Hisham Muhammad's avatar
Hisham Muhammad committed
941
942
   UsersTable_delete(ut);
   Settings_delete(settings);
943
944
945
   if(pidWhiteList) {
      Hashtable_delete(pidWhiteList);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
946
947
   return 0;
}