htop.c 29.7 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
#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
39
#include <time.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
40

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

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

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

52
53
static const char* defaultFunctions[]  = {"Help  ", "Setup ", "Search", "Filter", "Tree  ", "SortBy", "Nice -", "Nice +", "Kill  ", "Quit  ", NULL};
 
54
static void printHelpFlag() {
55
56
   fputs("htop " VERSION " - " COPYRIGHT "\n"
         "Released under the GNU GPL.\n\n"
57
58
59
60
61
62
         "-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"
63
64
65
66
67
68
         "-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
69
70
71
   exit(0);
}

72
73
74
75
76
77
78
79
80
81
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" },
82
   { .key = " F6 + -: ", .info = "expand/collapse tree" },
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
108
   { .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 }
};

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

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

116
   mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
Hisham Muhammad's avatar
Hisham Muhammad committed
117
118
119
120
121
122
   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], "[");
123
   if (pl->detailedCPUTime) {
124
125
126
127
      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("/");
128
      addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
129
      addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
130
131
      addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
      addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
132
133
134
135
      addattrstr(CRT_colors[BAR_SHADOW], " used%");
   } else {
      addattrstr(CRT_colors[CPU_NICE], "low-priority"); addstr("/");
      addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
136
137
138
      addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
      addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
      addattrstr(CRT_colors[BAR_SHADOW], "               used%");
139
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
140
141
142
143
144
145
146
   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");
147
   addattrstr(CRT_colors[BAR_SHADOW], "                            used/total");
Hisham Muhammad's avatar
Hisham Muhammad committed
148
149
150
151
152
   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");
153
   addattrstr(CRT_colors[BAR_SHADOW], "                                          used/total");
Hisham Muhammad's avatar
Hisham Muhammad committed
154
155
   addattrstr(CRT_colors[BAR_BORDER], "]");
   attrset(CRT_colors[DEFAULT_COLOR]);
156
   mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen.");
157
158
159
160
   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");
161
162
   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
163
   attrset(CRT_colors[HELP_BOLD]);
164
165
   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
166
167
168
169
170
171
172
173
174

   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
175
static const char* CategoriesFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "Done  ", NULL};
176

177
178
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
179
   CategoriesPanel* panelCategories = CategoriesPanel_new(settings, scr);
Hisham Muhammad's avatar
Hisham Muhammad committed
180
   ScreenManager_add(scr, (Panel*) panelCategories, FunctionBar_new(CategoriesFunctions, NULL, NULL), 16);
Hisham Muhammad's avatar
Hisham Muhammad committed
181
182
   CategoriesPanel_makeMetersPage(panelCategories);
   Panel* panelFocus;
Hisham Muhammad's avatar
Hisham Muhammad committed
183
   int ch;
Hisham Muhammad's avatar
Hisham Muhammad committed
184
   ScreenManager_run(scr, &panelFocus, &ch);
Hisham Muhammad's avatar
Hisham Muhammad committed
185
186
187
   ScreenManager_delete(scr);
}

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

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

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

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

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

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

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

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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) {
289
290
   pl->sortKey = sortKey;
   pl->direction = 1;
291
   setTreeView(pl, fuBar, false);
292
   settings->changed = true;
293
   ProcessList_printHeader(pl, Panel_getHeader(panel));
294
295
}

296
297
298
299
300
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
301
302
}

303
304
305
306
307
308
309
310
311
312
313
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);
      }
   }
}

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
342
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
343
static void millisleep(unsigned long millisec) {
344
345
346
347
348
349
350
351
352
   struct timespec req = {
      .tv_sec = 0,
      .tv_nsec = millisec * 1000000L
   };
   while(nanosleep(&req,&req)==-1) {
      continue;
   }
}

Hisham Muhammad's avatar
Hisham Muhammad committed
353
354
355
356
357
int main(int argc, char** argv) {

   int delay = -1;
   bool userOnly = false;
   uid_t userId = 0;
358
   int usecolors = 1;
359
360
361
   char *argCopy;
   char *pid;
   Hashtable *pidWhiteList = NULL;
362
363
364
365
366
367
368
369
370
371
372

   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'},
373
      {"pid",      required_argument,   0, 'p'},
374
375
      {0,0,0,0}
   };
376
   int sortKey = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
377

378
379
380
   char *lc_ctype = getenv("LC_CTYPE");
   if(lc_ctype != NULL)
      setlocale(LC_CTYPE, lc_ctype);
381
382
   else if ((lc_ctype = getenv("LC_ALL")))
      setlocale(LC_CTYPE, lc_ctype);
383
   else
384
      setlocale(LC_CTYPE, "");
385

386
   /* Parse arguments */
387
   while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:", long_opts, &opti))) {
388
389
390
391
392
393
394
395
396
      if (opt == EOF) break;
      switch (opt) {
         case 'h':
            printHelpFlag();
            break;
         case 'v':
            printVersionFlag();
            break;
         case 's':
Hisham Muhammad's avatar
Hisham Muhammad committed
397
            if (strcmp(optarg, "help") == 0) {
398
399
400
401
402
403
404
405
406
407
408
409
               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':
410
            if (sscanf(optarg, "%16d", &delay) == 1) {
411
412
413
414
415
416
               if (delay < 1) delay = 1;
               if (delay > 100) delay = 100;
            } else {
               fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
               exit(1);
            }
417
418
            break;
         case 'u':
419
420
421
422
            if (!setUserOnly(optarg, &userOnly, &userId)) {
               fprintf(stderr, "Error: invalid user \"%s\".\n", optarg);
               exit(1);
            }
423
424
425
            break;
         case 'C':
            usecolors=0;
426
            break;
427
        case 'p': {
428
            argCopy = strdup(optarg);
429
430
            char* saveptr;
            pid = strtok_r(argCopy, ",", &saveptr);
431
432
433
434
435
436
437
438

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

            while( pid ) {
                unsigned int num_pid = atoi(pid);
                Hashtable_put(pidWhiteList, num_pid, (void *) 1);
439
                pid = strtok_r(NULL, ",", &saveptr);
440
441
442
            }
            free(argCopy);

443
            break;
444
         }
445
446
         default:
            exit(1);
Hisham Muhammad's avatar
Hisham Muhammad committed
447
448
      }
   }
449
450


451
452
453
454
   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
455
456
457
458
459

   int quit = 0;
   int refreshTimeout = 0;
   int resetRefreshTimeout = 5;
   bool doRefresh = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
460
   bool doRecalculate = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
461
462
463
464
465
   Settings* settings;
   
   ProcessList* pl = NULL;
   UsersTable* ut = UsersTable_new();

466
467
468
469
470
471
472
473
474
475
476
477
478
479
#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

480
   pl = ProcessList_new(ut, pidWhiteList);
481
   Process_getMaxPid();
Hisham Muhammad's avatar
Hisham Muhammad committed
482
483
   
   Header* header = Header_new(pl);
484
   settings = Settings_new(pl, header, pl->cpuCount);
Hisham Muhammad's avatar
Hisham Muhammad committed
485
486
487
488
489
   int headerHeight = Header_calculateHeight(header);

   // FIXME: move delay code to settings
   if (delay != -1)
      settings->delay = delay;
490
491
   if (!usecolors) 
      settings->colorScheme = COLORSCHEME_MONOCHROME;
492

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

495
   Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class);
496
   ProcessList_setPanel(pl, panel);
Hisham Muhammad's avatar
Hisham Muhammad committed
497
   
498
499
500
   FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
   setTreeView(pl, defaultBar, pl->treeView);
   
501
502
   if (sortKey > 0) {
      pl->sortKey = sortKey;
503
      setTreeView(pl, defaultBar, false);
504
505
      pl->direction = 1;
   }
506
   ProcessList_printHeader(pl, Panel_getHeader(panel));
Hisham Muhammad's avatar
Hisham Muhammad committed
507

508
509
   IncSet* inc = IncSet_new(defaultBar);

Hisham Muhammad's avatar
Hisham Muhammad committed
510
   ProcessList_scan(pl);
511
   millisleep(75);
Hisham Muhammad's avatar
Hisham Muhammad committed
512
513
514
515
516
517
518
519
520
   
   FunctionBar_draw(defaultBar, NULL);
   
   int acc = 0;
   bool follow = false;
 
   struct timeval tv;
   double oldTime = 0.0;

521
   int ch = ERR;
Hisham Muhammad's avatar
Hisham Muhammad committed
522
523
   int closeTimeout = 0;

524
525
   bool idle = false;
   
526
527
   bool collapsed = false;
   
Hisham Muhammad's avatar
Hisham Muhammad committed
528
529
   while (!quit) {
      gettimeofday(&tv, NULL);
530
531
      double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
      bool recalculate = (newTime - oldTime > settings->delay);
532
      int following = follow ? selectedPid(panel) : -1;
533
534
      if (recalculate) {
         Header_draw(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
535
         oldTime = newTime;
536
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
537
      if (doRefresh) {
Hisham Muhammad's avatar
Hisham Muhammad committed
538
         if (recalculate || doRecalculate) {
Hisham Muhammad's avatar
Hisham Muhammad committed
539
            ProcessList_scan(pl);
Hisham Muhammad's avatar
Hisham Muhammad committed
540
541
            doRecalculate = false;
         }
542
         if (refreshTimeout == 0 || pl->treeView) {
Hisham Muhammad's avatar
Hisham Muhammad committed
543
544
545
            ProcessList_sort(pl);
            refreshTimeout = 1;
         }
546
         ProcessList_rebuildPanel(pl, true, following, userOnly, userId, IncSet_filter(inc));
547
         idle = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
548
549
      }
      doRefresh = true;
550
551
552
553
554
555
556
557
558
559
560
561
562
563
      
      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
564

565
      if (!idle) {
566
         Panel_draw(panel, true);
567
      }
568
      
Hisham Muhammad's avatar
Hisham Muhammad committed
569
      int prev = ch;
570
      if (inc->active)
571
         move(LINES-1, CRT_cursorX);
Hisham Muhammad's avatar
Hisham Muhammad committed
572
573
574
      ch = getch();

      if (ch == ERR) {
575
         if (!inc->active)
Hisham Muhammad's avatar
Hisham Muhammad committed
576
577
578
            refreshTimeout--;
         if (prev == ch && !recalculate) {
            closeTimeout++;
579
            if (closeTimeout == 100) {
Hisham Muhammad's avatar
Hisham Muhammad committed
580
               break;
581
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
582
583
         } else
            closeTimeout = 0;
584
         idle = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
585
586
         continue;
      }
587
      idle = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
588
589
590
591
592

      if (ch == KEY_MOUSE) {
         MEVENT mevent;
         int ok = getmouse(&mevent);
         if (ok == OK) {
593
594
595
596
597
598
            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);
599
                     setTreeView(pl, defaultBar, false);
600
                  } else {
601
                     setSortKey(pl, defaultBar, field, panel, settings);
602
603
604
605
606
607
608
609
610
611
                  }
                  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) {
612
                  ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
613
               }
614
615
616
617
618
619
            } 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
620
621
622
623
            }
         }
      }

624
625
626
627
628
629
630
631
      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;
632
         for (int i = 0; i < ProcessList_size(pl); i++) {
633
            Panel_setSelected(panel, i);
634
635
636
637
638
            Process* p = (Process*) Panel_getSelected(panel);
            if (p && p->pid == pid) {
               break;
            }
         }
639
640
641
642
643
644
645
646
         acc = pid * 10;
         if (acc > 10000000)
            acc = 0;
         continue;
      } else {
         acc = 0;
      }

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

811
812
813
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         Affinity* affinity = Process_getAffinity(p);
814
         if (!affinity) break;
815
816
         Panel* affinityPanel = AffinityPanel_new(pl, affinity);
         Affinity_delete(affinity);
817

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