btop/src/btop.cpp
aristocratos c0e17a64d3 Squashed commit of the following:
commit c2c4fe47db
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Nov 13 23:15:53 2021 +0100

    Changes from main + fixes

commit bd5d867089
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Nov 13 21:24:01 2021 +0100

    Fixes

commit dc5f0606cb
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Nov 13 20:51:06 2021 +0100

    Fixed leak in Proc::collect()

commit 8b59ab6e11
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Nov 13 19:59:56 2021 +0100

    Fixed process cpu usage calculation

commit 53c8a0325b
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 25 13:01:53 2021 +0200

    Cpu temp set to average of pACC and eACC for mac m1

commit 940cd0a513
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 24 21:12:16 2021 +0200

    limit cpu temp to 20

commit 254ceb9c2d
Author: aristocratos <admin@qvantnet.com>
Date:   Sun Oct 24 11:24:04 2021 +0200

    Fixed up Makefile

commit 6fa3bf41d7
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 21 13:12:45 2021 +0200

    Makefile fixed WARNFLAGS

commit 2c98e2ca29
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 21 13:08:12 2021 +0200

    Updated Makefile

commit e69780e9bc
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 20 23:26:09 2021 +0200

    fix build

commit de62167921
Merge: a590dd3 a0ee404
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 20 23:10:36 2021 +0200

    Merge branch 'main' into OSX

commit a590dd3f67
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 18 11:20:12 2021 +0200

    Ignore format-truncation

commit 4c30742d41
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 18 10:08:01 2021 +0200

    comments about temp sensors

commit 46030de77c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 18 08:10:37 2021 +0200

    available = total - used

commit 4c228de0ef
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:19:41 2021 +0200

    use sysconf to get arg_max - seems simpler

commit c60fc29f0f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:10:50 2021 +0200

    arg_max should be int on macos

commit 0b5a931a6d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 17 22:01:42 2021 +0200

    only fetch max_args once

commit 71d5cd5fd9
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 23:24:07 2021 +0200

    Reverted mutexes back to custom atomic bool based locks

commit 3f34a67df6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:47:55 2021 +0200

    these helpers can be static

commit fc19c46c8a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:44:49 2021 +0200

    code cleanup: put the code in .cpp to enable incremental build

commit c252c618c0
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:09:21 2021 +0200

    don't crash on intel

commit 9f88187c29
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 21:06:18 2021 +0200

    small improvement

commit 808f09c974
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 20:59:48 2021 +0200

    don't iterate 3 times

commit d8408336e3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 20:59:27 2021 +0200

    remove debug

commit 4f078c3beb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 13 23:38:27 2021 +0200

    more temperature (M1 + intel)

commit 289880aaa6
Merge: 88a2528 3ffb212
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 19:37:09 2021 +0200

    Merge branch 'OSX' of github.com:aristocratos/btop into OSX

commit 88a2528ca3
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 19:34:10 2021 +0200

    Merge changes from main

commit 3ffb21203a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 16 19:20:45 2021 +0200

    release a bit more - still has negative temps for 2 cores on my system

commit 70b4871062
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 16 01:59:44 2021 +0200

    Fixed leaks in Mem and attempt at fixing leaks in sensors.cpp

commit fbae907720
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 15 23:41:37 2021 +0200

    temperature sensors via IOKit

commit cef0f0a68d
Author: aristocratos <admin@qvantnet.com>
Date:   Fri Oct 15 18:39:17 2021 +0200

    Process command line arguments

commit 921cfa01ff
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 23:20:15 2021 +0200

    Re-enable setuid and set default SU_GROUP to wheel for OSX

commit a416c888c7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 13 21:16:41 2021 +0200

    temperature

commit e7afe00ce7
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 12:54:43 2021 +0200

    Cpu usage working again

commit 4193ef8921
Author: aristocratos <admin@qvantnet.com>
Date:   Wed Oct 13 10:36:51 2021 +0200

    Fixed cpu lazy sorting

commit 93fcb6ff04
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 22:22:45 2021 +0200

    Update README.md

commit 683354cd2e
Merge: 8a399c4 6d724d6
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 22:19:30 2021 +0200

    Merge pull request #80 from ShrirajHegde/OSX

    Add github workflow for MacOS

commit 8a399c499a
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 21:50:46 2021 +0200

    pointer to smart pointer, first pass

commit 772605003a
Author: aristocratos <admin@qvantnet.com>
Date:   Tue Oct 12 18:54:38 2021 +0200

    Fixed detailed memory not updating

commit 28cb677533
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 11 22:19:25 2021 +0200

    more memory free-up - still leaks like crazy

commit 304457863f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 11 21:48:07 2021 +0200

    more RAII cleanup

commit 82e2e3c55c
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 11 12:40:25 2021 +0200

    Removed non present cpu fields and fixed calculation for selectable cpu field graphs

commit 68603f2b37
Author: aristocratos <admin@qvantnet.com>
Date:   Mon Oct 11 10:57:04 2021 +0200

    RAII Wrappers for Cpu::get_battery()

commit d5cb24fbeb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 10 20:23:11 2021 +0200

    RAII

commit 8fad5a61be
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:44:16 2021 +0200

    get more disk IO stats

commit 7fa903cf16
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:43:48 2021 +0200

    fix build

commit 98036db660
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:18:25 2021 +0200

    remove unnecessary uptime param

commit aae7ae35ca
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:07:23 2021 +0200

    remove debug logging

commit 5187420b04
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:06:43 2021 +0200

    fix process elapsed time

commit 89582c0ea6
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 9 21:06:29 2021 +0200

    don't double free

commit 6d724d6155
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:46:33 2021 +0530

    Change Upload filename

commit 4f94ecc8ad
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:40:11 2021 +0530

    Fix upload path

commit e1d6d0a1f2
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:36:13 2021 +0530

    Skip installing gcc via Homebrew
    Change job name

commit 02cdd9d759
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:30:40 2021 +0530

    Fix uploading
    Remove distclean

commit 39eb6c396f
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:26:57 2021 +0530

    Disable static compilation

commit 099592bccd
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 17:52:10 2021 +0200

    Ignore empty pid 0 to fix tree mode

commit a28e17556e
Author: Shriraj Hegde <shriraj.hegde@gmail.com>
Date:   Sat Oct 9 21:17:11 2021 +0530

    Add workflow for MacOS

commit aee9179c0a
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 17:36:46 2021 +0200

    Disable failed tty mode detection for OSX

commit 4b7b98058d
Author: aristocratos <admin@qvantnet.com>
Date:   Sat Oct 9 11:28:32 2021 +0200

    Fixed disk io and added io activity based on read/write

commit bfa0629e7d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 22:28:10 2021 +0200

    fill in 0 for ioticks

commit a016ff8a03
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 22:16:01 2021 +0200

    disk io from IOreg. Does not show any io though

commit f98606c6db
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 09:32:06 2021 +0200

    per process IO stats

commit c8b50ed488
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 8 00:11:08 2021 +0200

    don't show autofs, it's useless

commit c4df64d440
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:41:49 2021 +0200

    Fixed compile time display for gmake and command timings

commit b3e6f495f7
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:26:15 2021 +0200

    Fixed clk_tck -> clkTck

commit e53799188f
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:25:14 2021 +0200

    Fixed better detection for OSX

commit b864edf984
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 18:24:37 2021 +0200

    Fixed cumulative cpu usage

commit 6a3c5d9b97
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 13:20:30 2021 +0200

    Proc::collect() better cpu percent accurazy

commit 84d0596294
Merge: 3564f8e 98e1e87
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 12:56:55 2021 +0200

    Merge branch 'OSX' of github.com:aristocratos/btop into OSX

commit 3564f8e4c2
Author: aristocratos <admin@qvantnet.com>
Date:   Thu Oct 7 12:56:27 2021 +0200

    Proc::collect() fixed cputimes and cpu percentage calc

commit 98e1e87405
Merge: 60c5636 d96fdd7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Thu Oct 7 11:32:41 2021 +0200

    Merge branch 'main' into OSX

commit 60c5636cd7
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:45:54 2021 +0200

    fix warning

commit 489e446152
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:38:40 2021 +0200

    details + process states

commit 7e5a808c73
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 22:38:19 2021 +0200

    avoid details crash

commit 9c9da4606b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 21:03:21 2021 +0200

    fix quit on macos

commit ec7415384d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:41:37 2021 +0200

    fix mistake in makefile

commit 5ac8fa4c8a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 18:56:13 2021 +0200

    don't show /dev

commit d901bbebd9
Author: aristocratos <gnmjpl@gmail.com>
Date:   Wed Oct 6 17:27:51 2021 +0200

     Ignore tags and other branches

commit c7f1e71e29
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 16:00:41 2021 +0200

    comment

commit b9d58e3faf
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:55:58 2021 +0200

    impossible to get CPU freq on M1 apparently

commit 66072711c2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:33:43 2021 +0200

    detect full

commit 6bb0e930a2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:38:46 2021 +0200

    CPU freq in GHz

commit a5f10f1a0f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:13:18 2021 +0200

    check array length

commit 155c848b97
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 15:05:20 2021 +0200

    switch to other way to get CPU freq (still does not work)

commit cf51ba2ebe
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:45:44 2021 +0200

    remove some warnings

commit 775dff5f72
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:16:45 2021 +0200

    fix link

commit 8c67967775
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:16:38 2021 +0200

    reduce diff more

commit 70b47d2ca8
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 14:10:23 2021 +0200

    reduce diff with main branch

commit ca9cb48054
Merge: d0c6c0a c66b46f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 13:49:20 2021 +0200

    Merge remote-tracking branch 'origin/main' into OSX

commit c66b46f850
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 13:41:57 2021 +0200

    battery state via CoreFoundation

commit d0c6c0a362
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:51:36 2021 +0200

    all disks + load averages

commit ca67526dc1
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:33:55 2021 +0200

    show all disks

commit 56119f99a9
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:17:41 2021 +0200

    procs sorting/filtering

commit 8d86011d72
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:42:17 2021 +0200

    battery states

commit a9b64d62e4
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:24:59 2021 +0200

    battery hack works on M1

commit ce51031142
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:18:22 2021 +0200

    ugly hack to get battery

commit d5e6725c6c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 22:42:42 2021 +0200

    CPU stuff

commit 5c02bd8c83
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 21:25:42 2021 +0200

    network

commit d5da9d4983
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 15:43:05 2021 +0200

    correct cached size

commit 5f11aba504
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 12:03:48 2021 +0200

    vm stats from syscall + swap

commit 776fc96852
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:48:07 2021 +0200

    seems to work indeed

commit 7b40e2835a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:46:14 2021 +0200

    allow override optimization flag

commit 005ea24e4c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:09:24 2021 +0200

    update Makefile

commit af8cec9deb
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:32:55 2021 +0200

    some more params

commit 7ebe4f7594
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:15:55 2021 +0200

    show more disks

commit e50a56394a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 14:52:56 2021 +0200

    disks show something

commit 6497a8c202
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 09:15:35 2021 +0200

    reformat

commit 28e152b80c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 23:21:13 2021 +0200

    decrease diff with upstream

commit 40da88e9ca
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:56:14 2021 +0200

    try to get disks to show

commit eaf2bb56a5
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:42:01 2021 +0200

    don't crash on deque::back()

commit f66b6f712c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:21 2021 +0200

    cpu freq, name & process uid/name

commit 34a8a61f4d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:46:11 2021 +0200

    basic process info

commit 29bb2dcc5f
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:45:39 2021 +0200

    initialize mutex (needed on macos apparently and not on linux)

commit fb5970b000
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:53:41 2021 +0200

    comment

commit 49d16cdddd
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:51:29 2021 +0200

    extract delimiters

commit 3db9d66476
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:28 2021 +0200

    first infos on macos: memory used & free

commit f8acb2f854
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Sep 28 23:37:03 2021 +0200

    make it compile on macos (M1 - arm64). Does not run though

commit bbba17cd35
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:51:36 2021 +0200

    all disks + load averages

commit 548203e93d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 10:33:55 2021 +0200

    show all disks

commit 0ab2be3985
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Oct 6 00:17:41 2021 +0200

    procs sorting/filtering

commit 096104c90b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:42:17 2021 +0200

    battery states

commit 0ad93684c2
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:24:59 2021 +0200

    battery hack works on M1

commit c75b0f1cea
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 23:18:22 2021 +0200

    ugly hack to get battery

commit 600b4f72b3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 22:42:42 2021 +0200

    CPU stuff

commit 4eb812d52c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 21:25:42 2021 +0200

    network

commit 899be68a78
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 15:43:05 2021 +0200

    correct cached size

commit a1c7f935e3
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 12:03:48 2021 +0200

    vm stats from syscall + swap

commit bd1050a740
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:48:07 2021 +0200

    seems to work indeed

commit 5094b73758
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:46:14 2021 +0200

    allow override optimization flag

commit 8811270332
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Oct 5 10:09:24 2021 +0200

    update Makefile

commit 42f966f448
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:32:55 2021 +0200

    some more params

commit c1e6d6a62e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 15:15:55 2021 +0200

    show more disks

commit 50fcdaa854
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 14:52:56 2021 +0200

    disks show something

commit 264bf2d7da
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Mon Oct 4 09:15:35 2021 +0200

    reformat

commit 1fd625086b
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 23:21:13 2021 +0200

    decrease diff with upstream

commit 17f9f3703c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:56:14 2021 +0200

    try to get disks to show

commit 8462ae6431
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:42:01 2021 +0200

    don't crash on deque::back()

commit 78bce5b5a6
Merge: 53e379d f9505a4
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:34 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 53e379d74d
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 22:08:21 2021 +0200

    cpu freq, name & process uid/name

commit 2a44b307ef
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:46:11 2021 +0200

    basic process info

commit 66534eb5b5
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sun Oct 3 21:45:39 2021 +0200

    initialize mutex (needed on macos apparently and not on linux)

commit 0983917f26
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:53:41 2021 +0200

    comment

commit 9732507248
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:51:29 2021 +0200

    extract delimiters

commit 6e704ce838
Merge: fe4db7c 7bfbd83
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:43 2021 +0200

    Merge branch 'main' of github.com:joske/btop

commit fe4db7c16c
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Sat Oct 2 23:48:28 2021 +0200

    first infos on macos: memory used & free

commit 7bfbd83a47
Merge: 8c8139b a15f961
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Oct 1 17:05:26 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 8c8139bd1d
Merge: 679d21c a246c09
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 21:50:00 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 679d21cd22
Merge: 4c70c5b a49b8f9
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 20:23:34 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 4c70c5bdd9
Merge: 84a9746 c70667e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Sep 29 08:30:05 2021 +0200

    Merge branch 'aristocratos:main' into main

commit 84a974695a
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Tue Sep 28 23:37:03 2021 +0200

    make it compile on macos (M1 - arm64). Does not run though

Co-authored-by: Jos Dehaes <jos.dehaes@gmail.com>
2021-11-13 23:37:23 +01:00

930 lines
29 KiB
C++

/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <csignal>
#include <pthread.h>
#include <thread>
#include <numeric>
#include <ranges>
#include <unistd.h>
#include <cmath>
#include <iostream>
#include <exception>
#include <tuple>
#include <regex>
#include <chrono>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
#include <btop_config.hpp>
#include <btop_input.hpp>
#include <btop_theme.hpp>
#include <btop_draw.hpp>
#include <btop_menu.hpp>
using std::string, std::string_view, std::vector, std::atomic, std::endl, std::cout, std::min, std::flush, std::endl;
using std::string_literals::operator""s, std::to_string;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
using namespace std::chrono_literals;
namespace Global {
const vector<array<string, 2>> Banner_src = {
{"#E62525", "██████╗ ████████╗ ██████╗ ██████╗"},
{"#CD2121", "██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗ ██╗ ██╗"},
{"#B31D1D", "██████╔╝ ██║ ██║ ██║██████╔╝ ██████╗██████╗"},
{"#9A1919", "██╔══██╗ ██║ ██║ ██║██╔═══╝ ╚═██╔═╝╚═██╔═╝"},
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const string Version = "1.0.24";
int coreCount;
string overlay;
string clock;
string bg_black = "\x1b[0;40m";
string fg_white = "\x1b[1;97m";
string fg_green = "\x1b[1;92m";
string fg_red = "\x1b[0;91m";
uid_t real_uid, set_uid;
fs::path self_path;
string exit_error_msg;
atomic<bool> thread_exception (false);
bool debuginit = false;
bool debug = false;
bool utf_force = false;
uint64_t start_time;
atomic<bool> resized (false);
atomic<bool> quitting (false);
atomic<bool> should_quit (false);
atomic<bool> should_sleep (false);
atomic<bool> _runner_started (false);
bool arg_tty = false;
bool arg_low_color = false;
int arg_preset = -1;
}
//* A simple argument parser
void argumentParser(const int& argc, char **argv) {
for(int i = 1; i < argc; i++) {
const string argument = argv[i];
if (is_in(argument, "-h", "--help")) {
cout << "usage: btop [-h] [-v] [-/+t] [-p <id>] [--utf-force] [--debug]\n\n"
<< "optional arguments:\n"
<< " -h, --help show this help message and exit\n"
<< " -v, --version show version info and exit\n"
<< " -lc, --low-color disable truecolor, converts 24-bit colors to 256-color\n"
<< " -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n"
<< " +t, --tty_off force (OFF) tty mode\n"
<< " -p, --preset <id> start with preset, integer value between 0-9\n"
<< " --utf-force force start even if no UTF-8 locale was detected\n"
<< " --debug start in DEBUG mode: shows microsecond timer for information collect\n"
<< " and screen draw functions and sets loglevel to DEBUG\n"
<< endl;
exit(0);
}
else if (is_in(argument, "-v", "--version")) {
cout << "btop version: " << Global::Version << endl;
exit(0);
}
else if (is_in(argument, "-lc", "--low-color")) {
Global::arg_low_color = true;
}
else if (is_in(argument, "-t", "--tty_on")) {
Config::set("tty_mode", true);
Global::arg_tty = true;
}
else if (is_in(argument, "+t", "--tty_off")) {
Config::set("tty_mode", false);
Global::arg_tty = true;
}
else if (is_in(argument, "-p", "--preset")) {
if (++i >= argc) {
cout << "ERROR: Preset option needs an argument." << endl;
exit(1);
}
else if (const string val = argv[i]; isint(val) and val.size() == 1) {
Global::arg_preset = std::clamp(stoi(val), 0, 9);
}
else {
cout << "ERROR: Preset option only accepts an integer value between 0-9." << endl;
exit(1);
}
}
else if (argument == "--utf-force")
Global::utf_force = true;
else if (argument == "--debug")
Global::debug = true;
else {
cout << " Unknown argument: " << argument << "\n" <<
" Use -h or --help for help." << endl;
exit(1);
}
}
}
//* Handler for SIGWINCH and general resizing events, does nothing if terminal hasn't been resized unless force=true
void term_resize(bool force) {
static atomic<bool> resizing (false);
atomic_lock lck(resizing, true);
if (auto refreshed = Term::refresh(true); refreshed or force) {
if (force and refreshed) force = false;
}
else return;
static const array<string, 4> all_boxes = {"cpu", "mem", "net", "proc"};
Global::resized = true;
if (Runner::active) Runner::stop();
Term::refresh();
Config::unlock();
auto boxes = Config::getS("shown_boxes");
auto min_size = Term::get_min_size(boxes);
while (not force or (Term::width < min_size.at(0) or Term::height < min_size.at(1))) {
sleep_ms(100);
if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
cout << Term::clear << Global::bg_black << Global::fg_white << Mv::to((Term::height / 2) - 2, (Term::width / 2) - 11)
<< "Terminal size too small:" << Mv::to((Term::height / 2) - 1, (Term::width / 2) - 10)
<< " Width = " << (Term::width < min_size.at(1) ? Global::fg_red : Global::fg_green) << Term::width
<< Global::fg_white << " Height = " << (Term::height < min_size.at(0) ? Global::fg_red : Global::fg_green) << Term::height
<< Mv::to((Term::height / 2) + 1, (Term::width / 2) - 12) << Global::fg_white
<< "Needed for current config:" << Mv::to((Term::height / 2) + 2, (Term::width / 2) - 10)
<< "Width = " << min_size.at(0) << " Height = " << min_size.at(1) << flush;
while (not Term::refresh() and not Input::poll()) sleep_ms(10);
if (Input::poll()) {
auto key = Input::get();
if (key == "q")
exit(0);
else if (is_in(key, "1", "2", "3", "4")) {
Config::current_preset = -1;
Config::toggle_box(all_boxes.at(std::stoi(key) - 1));
boxes = Config::getS("shown_boxes");
}
}
min_size = Term::get_min_size(boxes);
}
else if (not Term::refresh()) break;
}
Input::interrupt = true;
}
//* Exit handler; stops threads, restores terminal and saves config changes
void clean_quit(int sig) {
if (Global::quitting) return;
Global::quitting = true;
Runner::stop();
if (Global::_runner_started) {
#ifdef __APPLE__
if (pthread_join(Runner::runner_id, NULL) != 0) {
Logger::error("Failed to join _runner thread!");
pthread_cancel(Runner::runner_id);
}
#else
struct timespec ts;
ts.tv_sec = 5;
if (pthread_timedjoin_np(Runner::runner_id, NULL, &ts) != 0) {
Logger::error("Failed to join _runner thread!");
pthread_cancel(Runner::runner_id);
}
#endif
}
Config::write();
//? Wait for any remaining Tools::atomic_lock destructors to finish for max 1000ms
for (int i = 0; Tools::active_locks > 0 and i < 100; i++) {
sleep_ms(10);
}
if (Term::initialized) {
Input::clear();
Term::restore();
}
if (not Global::exit_error_msg.empty()) {
sig = 1;
Logger::error(Global::exit_error_msg);
std::cerr << Global::fg_red << "ERROR: " << Global::fg_white << Global::exit_error_msg << Fx::reset << endl;
}
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
//? Assume error if still not cleaned up and call quick_exit to avoid a segfault from Tools::atomic_lock destructor
#ifndef __APPLE__
if (Tools::active_locks > 0) {
quick_exit((sig != -1 ? sig : 0));
}
#endif
if (sig != -1) exit(sig);
}
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
void _sleep() {
Runner::stop();
Term::restore();
std::raise(SIGSTOP);
}
//* Handler for SIGCONT; re-initialize terminal and force a resize event
void _resume() {
Term::init();
term_resize(true);
}
void _exit_handler() {
clean_quit(-1);
}
void _signal_handler(const int sig) {
switch (sig) {
case SIGINT:
if (Runner::active) {
Global::should_quit = true;
Runner::stopping = true;
Input::interrupt = true;
}
else {
clean_quit(0);
}
break;
case SIGTSTP:
if (Runner::active) {
Global::should_sleep = true;
Runner::stopping = true;
Input::interrupt = true;
}
else {
_sleep();
}
break;
case SIGCONT:
_resume();
break;
case SIGWINCH:
term_resize();
break;
}
}
//* Manages secondary thread for collection and drawing of boxes
namespace Runner {
atomic<bool> active (false);
atomic<bool> stopping (false);
atomic<bool> waiting (false);
atomic<bool> redraw (false);
//* Setup semaphore for triggering thread to do work
#if __GNUC__ < 11
#include <semaphore.h>
sem_t do_work;
inline void thread_sem_init() { sem_init(&do_work, 0, 0); }
inline void thread_wait() { sem_wait(&do_work); }
inline void thread_trigger() { sem_post(&do_work); }
#else
#include <semaphore>
std::binary_semaphore do_work(0);
inline void thread_sem_init() { ; }
inline void thread_wait() { do_work.acquire(); }
inline void thread_trigger() { do_work.release(); }
#endif
//* RAII wrapper for pthread_mutex locking
class thread_lock {
pthread_mutex_t& pt_mutex;
public:
int status;
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) { pthread_mutex_init(&pt_mutex, NULL); status = pthread_mutex_lock(&pt_mutex); }
~thread_lock() { if (status == 0) pthread_mutex_unlock(&pt_mutex); }
};
//* Wrapper for raising priviliges when using SUID bit
class gain_priv {
int status = -1;
public:
gain_priv() {
if (Global::real_uid != Global::set_uid) this->status = seteuid(Global::set_uid);
}
~gain_priv() {
if (status == 0) status = seteuid(Global::real_uid);
}
};
string output;
string empty_bg;
bool pause_output = false;
sigset_t mask;
pthread_t runner_id;
pthread_mutex_t mtx;
enum debug_actions {
collect_begin,
draw_begin,
draw_done
};
enum debug_array {
collect,
draw
};
string debug_bg;
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
struct runner_conf {
vector<string> boxes;
bool no_update;
bool force_redraw;
bool background_update;
string overlay;
string clock;
};
struct runner_conf current_conf;
void debug_timer(const char* name, const int action) {
switch (action) {
case collect_begin:
debug_times[name].at(collect) = time_micros();
return;
case draw_begin:
debug_times[name].at(draw) = time_micros();
debug_times[name].at(collect) = debug_times[name].at(draw) - debug_times[name].at(collect);
debug_times["total"].at(collect) += debug_times[name].at(collect);
return;
case draw_done:
debug_times[name].at(draw) = time_micros() - debug_times[name].at(draw);
debug_times["total"].at(draw) += debug_times[name].at(draw);
return;
}
}
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
void * _runner(void * _) {
(void)_;
//? Block some signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
sigemptyset(&mask);
// sigaddset(&mask, SIGINT);
// sigaddset(&mask, SIGTSTP);
sigaddset(&mask, SIGWINCH);
sigaddset(&mask, SIGTERM);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
//? pthread_mutex_lock to lock thread and monitor health from main thread
thread_lock pt_lck(mtx);
if (pt_lck.status != 0) {
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
Global::thread_exception = true;
Input::interrupt = true;
stopping = true;
}
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
while (not Global::quitting) {
thread_wait();
atomic_wait_for(active, true, 5000);
if (active) {
Global::exit_error_msg = "Runner thread failed to get active lock!";
Global::thread_exception = true;
Input::interrupt = true;
stopping = true;
}
if (stopping or Global::resized) {
sleep_ms(1);
continue;
}
//? Atomic lock used for blocking non thread-safe actions in main thread
atomic_lock lck(active);
//? Set effective user if SUID bit is set
gain_priv powers{};
auto& conf = current_conf;
//! DEBUG stats
if (Global::debug) {
if (debug_bg.empty() or redraw) Runner::debug_bg = Draw::createBox(2, 2, 32, 8, "", true, "debug");
debug_times.clear();
debug_times["total"] = {0, 0};
}
output.clear();
//* Run collection and draw functions for all boxes
try {
//? CPU
if (v_contains(conf.boxes, "cpu")) {
try {
if (Global::debug) debug_timer("cpu", collect_begin);
//? Start collect
auto cpu = Cpu::collect(conf.no_update);
if (Global::debug) debug_timer("cpu", draw_begin);
//? Draw box
if (not pause_output) output += Cpu::draw(cpu, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("cpu", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Cpu:: -> " + (string)e.what());
}
}
//? MEM
if (v_contains(conf.boxes, "mem")) {
try {
if (Global::debug) debug_timer("mem", collect_begin);
//? Start collect
auto mem = Mem::collect(conf.no_update);
if (Global::debug) debug_timer("mem", draw_begin);
//? Draw box
if (not pause_output) output += Mem::draw(mem, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("mem", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Mem:: -> " + (string)e.what());
}
}
//? NET
if (v_contains(conf.boxes, "net")) {
try {
if (Global::debug) debug_timer("net", collect_begin);
//? Start collect
auto net = Net::collect(conf.no_update);
if (Global::debug) debug_timer("net", draw_begin);
//? Draw box
if (not pause_output) output += Net::draw(net, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("net", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Net:: -> " + (string)e.what());
}
}
//? PROC
if (v_contains(conf.boxes, "proc")) {
try {
if (Global::debug) debug_timer("proc", collect_begin);
//? Start collect
auto proc = Proc::collect(conf.no_update);
if (Global::debug) debug_timer("proc", draw_begin);
//? Draw box
if (not pause_output) output += Proc::draw(proc, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("proc", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Proc:: -> " + (string)e.what());
}
}
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
Global::thread_exception = true;
Input::interrupt = true;
stopping = true;
}
if (stopping) {
continue;
}
if (redraw or conf.force_redraw) {
empty_bg.clear();
redraw = false;
}
if (not pause_output) output += conf.clock;
if (not conf.overlay.empty() and not conf.background_update) pause_output = true;
if (output.empty() and not pause_output) {
if (empty_bg.empty()) {
const int x = Term::width / 2 - 10, y = Term::height / 2 - 10;
output += Term::clear;
empty_bg += Draw::banner_gen(y, 0, true)
+ Mv::to(y+6, x) + Theme::c("title") + Fx::b + "No boxes shown!"
+ Mv::to(y+8, x) + Theme::c("hi_fg") + "1" + Theme::c("main_fg") + " | Show CPU box"
+ Mv::to(y+9, x) + Theme::c("hi_fg") + "2" + Theme::c("main_fg") + " | Show MEM box"
+ Mv::to(y+10, x) + Theme::c("hi_fg") + "3" + Theme::c("main_fg") + " | Show NET box"
+ Mv::to(y+11, x) + Theme::c("hi_fg") + "4" + Theme::c("main_fg") + " | Show PROC box"
+ Mv::to(y+12, x-2) + Theme::c("hi_fg") + "esc" + Theme::c("main_fg") + " | Show menu"
+ Mv::to(y+13, x) + Theme::c("hi_fg") + "q" + Theme::c("main_fg") + " | Quit";
}
output += empty_bg;
}
//! DEBUG stats -->
if (Global::debug and not Menu::active) {
output += debug_bg + Theme::c("title") + Fx::b + ljust(" Box", 9) + ljust("Collect μs", 12, true) + ljust("Draw μs", 9, true) + Theme::c("main_fg") + Fx::ub;
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
if (not debug_times.contains(name)) debug_times[name] = {0,0};
const auto& [time_collect, time_draw] = debug_times.at(name);
if (name == "total") output += Fx::b;
output += Mv::l(29) + Mv::d(1) + ljust(name, 8) + ljust(to_string(time_collect), 12) + ljust(to_string(time_draw), 9);
}
}
//? If overlay isn't empty, print output without color and then print overlay on top
cout << Term::sync_start << (conf.overlay.empty()
? output
: (output.empty() ? "" : Fx::ub + Theme::c("inactive_fg") + Fx::uncolor(output)) + conf.overlay)
<< Term::sync_end << flush;
}
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
pthread_exit(NULL);
}
//? ------------------------------------------ Secondary thread end -----------------------------------------------
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
void run(const string& box, const bool no_update, const bool force_redraw) {
atomic_wait_for(active, true, 5000);
if (active) {
Logger::error("Stall in Runner thread, restarting!");
active = false;
// exit(1);
pthread_cancel(Runner::runner_id);
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
Global::exit_error_msg = "Failed to re-create _runner thread!";
exit(1);
}
}
if (stopping or Global::resized) return;
if (box == "overlay") {
cout << Term::sync_start << Global::overlay << Term::sync_end << flush;
}
else if (box == "clock") {
cout << Term::sync_start << Global::clock << Term::sync_end << flush;
}
else {
Config::unlock();
Config::lock();
current_conf = {
(box == "all" ? Config::current_boxes : vector{box}),
no_update, force_redraw,
(not Config::getB("tty_mode") and Config::getB("background_update")),
Global::overlay,
Global::clock
};
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
thread_trigger();
atomic_wait_for(active, false, 10);
}
}
//* Stops any work being done in runner thread and checks for thread errors
void stop() {
stopping = true;
int ret = pthread_mutex_trylock(&mtx);
if (ret != EBUSY and not Global::quitting) {
if (active) active = false;
Global::exit_error_msg = "Runner thread died unexpectedly!";
exit(1);
}
else if (ret == EBUSY) {
atomic_wait_for(active, true, 5000);
if (active) {
active = false;
if (Global::quitting) {
return;
}
else {
Global::exit_error_msg = "No response from Runner thread, quitting!";
exit(1);
}
}
thread_trigger();
atomic_wait_for(active, false, 100);
atomic_wait_for(active, true, 100);
}
stopping = false;
}
}
//* --------------------------------------------- Main starts here! ---------------------------------------------------
int main(int argc, char **argv) {
//? ------------------------------------------------ INIT ---------------------------------------------------------
Global::start_time = time_s();
//? Save real and effective userid's and drop priviliges until needed if running with SUID bit set
Global::real_uid = getuid();
Global::set_uid = geteuid();
if (Global::real_uid != Global::set_uid) {
if (seteuid(Global::real_uid) != 0) {
Global::real_uid = Global::set_uid;
Global::exit_error_msg = "Failed to change effective user ID. Unset btop SUID bit to ensure security on this system. Quitting!";
clean_quit(1);
}
}
//? Call argument parser if launched with arguments
if (argc > 1) argumentParser(argc, argv);
//? Setup paths for config, log and user themes
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
if (std::getenv(env) != NULL and access(std::getenv(env), W_OK) != -1) {
Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
break;
}
}
if (Config::conf_dir.empty()) {
cout << "WARNING: Could not get path user HOME folder.\n"
<< "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this." << endl;
}
else {
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
cout << "WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n"
<< "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this." << endl;
}
else {
Config::conf_file = Config::conf_dir / "btop.conf";
Logger::logfile = Config::conf_dir / "btop.log";
Theme::user_theme_dir = Config::conf_dir / "themes";
if (not fs::exists(Theme::user_theme_dir) and not fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear();
}
}
//? Try to find global btop theme path relative to binary path
#if defined(__linux__)
{ std::error_code ec;
Global::self_path = fs::read_symlink("/proc/self/exe", ec).remove_filename();
}
#endif
if (std::error_code ec; not Global::self_path.empty()) {
Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec);
if (ec or not fs::is_directory(Theme::theme_dir) or access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear();
}
//? If relative path failed, check two most common absolute paths
if (Theme::theme_dir.empty()) {
for (auto theme_path : {"/usr/local/share/btop/themes", "/usr/share/btop/themes"}) {
if (fs::is_directory(fs::path(theme_path)) and access(theme_path, R_OK) != -1) {
Theme::theme_dir = fs::path(theme_path);
break;
}
}
}
//? Config init
{ vector<string> load_warnings;
Config::load(Config::conf_file, load_warnings);
if (Config::current_boxes.empty()) Config::check_boxes(Config::getS("shown_boxes"));
Config::set("lowcolor", (Global::arg_low_color ? true : not Config::getB("truecolor")));
if (Global::debug) {
Logger::set("DEBUG");
Logger::debug("Starting in DEBUG mode!");
}
else Logger::set(Config::getS("log_level"));
Logger::info("Logger set to " + (Global::debug ? "DEBUG" : Config::getS("log_level")));
for (const auto& err_str : load_warnings) Logger::warning(err_str);
}
//? Try to find and set a UTF-8 locale
if (std::setlocale(LC_ALL, "") != NULL and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
and str_to_upper(s_replace((string)std::setlocale(LC_ALL, ""), "-", "")).ends_with("UTF8")) {
Logger::debug("Using locale " + (string)std::setlocale(LC_ALL, ""));
}
else {
string found;
bool set_failure = false;
for (const auto loc_env : array{"LANG", "LC_ALL"}) {
if (std::getenv(loc_env) != NULL and str_to_upper(s_replace((string)std::getenv(loc_env), "-", "")).ends_with("UTF8")) {
found = std::getenv(loc_env);
if (std::setlocale(LC_ALL, found.c_str()) == NULL) {
set_failure = true;
Logger::warning("Failed to set locale " + found + " continuing anyway.");
}
}
}
if (found.empty()) {
if (setenv("LC_ALL", "", 1) == 0 and setenv("LANG", "", 1) == 0) {
try {
if (const auto loc = std::locale("").name(); not loc.empty() and loc != "*") {
for (auto& l : ssplit(loc, ';')) {
if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
found = l.substr(l.find('=') + 1);
if (std::setlocale(LC_ALL, found.c_str()) != NULL) {
break;
}
}
}
}
}
catch (...) { found.clear(); }
}
}
if (found.empty() and Global::utf_force)
Logger::warning("No UTF-8 locale detected! Forcing start with --utf-force argument.");
else if (found.empty()) {
Global::exit_error_msg = "No UTF-8 locale detected!\nUse --utf-force argument to force start if you're sure your terminal can handle it.";
clean_quit(1);
}
else if (not set_failure)
Logger::debug("Setting LC_ALL=" + found);
}
//? Initialize terminal and set options
if (not Term::init()) {
Global::exit_error_msg = "No tty detected!\nbtop++ needs an interactive shell to run.";
clean_quit(1);
}
if (Term::current_tty != "unknown") Logger::info("Running on " + Term::current_tty);
if (not Global::arg_tty and Config::getB("force_tty")) {
Config::set("tty_mode", true);
Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols");
}
#ifndef __APPLE__
else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) {
Config::set("tty_mode", true);
Logger::info("Real tty detected: setting 16 color mode and using tty friendly graph symbols");
}
#endif
//? Check for valid terminal dimensions
{
int t_count = 0;
while (Term::width <= 0 or Term::width > 10000 or Term::height <= 0 or Term::height > 10000) {
sleep_ms(10);
Term::refresh();
if (++t_count == 100) {
Global::exit_error_msg = "Failed to get size of terminal!";
clean_quit(1);
}
}
}
//? Platform dependent init and error check
try {
Shared::init();
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what();
clean_quit(1);
}
//? Update list of available themes and generate the selected theme
Theme::updateThemes();
Theme::setTheme();
//? Setup signal handlers for CTRL-C, CTRL-Z, resume and terminal resize
std::atexit(_exit_handler);
std::signal(SIGINT, _signal_handler);
std::signal(SIGTSTP, _signal_handler);
std::signal(SIGCONT, _signal_handler);
std::signal(SIGWINCH, _signal_handler);
//? Start runner thread
Runner::thread_sem_init();
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
Global::exit_error_msg = "Failed to create _runner thread!";
exit(1);
}
else {
Global::_runner_started = true;
}
//? Calculate sizes of all boxes
Config::presetsValid(Config::getS("presets"));
if (Global::arg_preset >= 0) {
Config::current_preset = min(Global::arg_preset, (int)Config::preset_list.size() - 1);
Config::apply_preset(Config::preset_list.at(Config::current_preset));
}
{
const auto [x, y] = Term::get_min_size(Config::getS("shown_boxes"));
if (Term::height < y or Term::width < x) {
term_resize(true);
Global::resized = false;
Input::interrupt = false;
}
}
Draw::calcSizes();
//? Print out box outlines
cout << Term::sync_start << Cpu::box << Mem::box << Net::box << Proc::box << Term::sync_end << flush;
//? ------------------------------------------------ MAIN LOOP ----------------------------------------------------
uint64_t update_ms = Config::getI("update_ms");
auto future_time = time_ms();
try {
while (not true not_eq not false) {
//? Check for exceptions in secondary thread and exit with fail signal if true
if (Global::thread_exception) exit(1);
else if (Global::should_quit) exit(0);
else if (Global::should_sleep) { Global::should_sleep = false; _sleep(); }
//? Make sure terminal size hasn't changed (in case of SIGWINCH not working properly)
term_resize();
//? Trigger secondary thread to redraw if terminal has been resized
if (Global::resized) {
Draw::calcSizes();
Draw::update_clock(true);
Global::resized = false;
if (Menu::active) Menu::process();
else Runner::run("all", true, true);
atomic_wait_for(Runner::active, true, 1000);
}
//? Update clock if needed
if (Draw::update_clock() and not Menu::active) {
Runner::run("clock");
}
//? Start secondary collect & draw thread at the interval set by <update_ms> config value
if (time_ms() >= future_time and not Global::resized) {
Runner::run("all");
update_ms = Config::getI("update_ms");
future_time = time_ms() + update_ms;
}
//? Loop over input polling and input action processing
for (auto current_time = time_ms(); current_time < future_time; current_time = time_ms()) {
//? Check for external clock changes and for changes to the update timer
if (std::cmp_not_equal(update_ms, Config::getI("update_ms"))) {
update_ms = Config::getI("update_ms");
future_time = time_ms() + update_ms;
}
else if (future_time - current_time > update_ms)
future_time = current_time;
//? Poll for input and process any input detected
else if (Input::poll(min((uint64_t)1000, future_time - current_time))) {
if (not Runner::active) Config::unlock();
if (Menu::active) Menu::process(Input::get());
else Input::process(Input::get());
}
//? Break the loop at 1000ms intervals or if input polling was interrupted
else break;
}
}
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
exit(1);
}
}