public inbox for cygwin-apps@cygwin.com
 help / color / mirror / Atom feed
* [ITP] btop
@ 2024-01-24 15:22 Takashi Yano
  2024-01-25 15:11 ` Jon Turney
  0 siblings, 1 reply; 3+ messages in thread
From: Takashi Yano @ 2024-01-24 15:22 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 208 bytes --]

I'd like to propose a new package btop, which is a feature-rich
resource monitor. btop is ready for many Linux distributions
including Fedora.

Thanks in advance.

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

[-- Attachment #2: btop.cygport --]
[-- Type: text/plain, Size: 427 bytes --]

NAME="btop"
VERSION=1.3.0
RELEASE=1
LICENSE="Apache-2.0"
CATEGORY="System"
SUMMARY="Resource monitor with rich features."
DESCRIPTION="Resource monitor that shows usage and stats for processor, memory, disks, network and processes."
HOMEPAGE="https://github.com/aristocratos/btop"
SRC_URI="https://github.com/aristocratos/btop/archive/refs/tags/v${VERSION}.tar.gz"
PATCH_URI="fix-gcc13-warnings.patch"
inherit cmake

[-- Attachment #3: btop-1.3.0-1.src.patch --]
[-- Type: text/plain, Size: 22615 bytes --]

--- origsrc/btop-1.3.0/CMakeLists.txt	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/CMakeLists.txt	2024-01-24 21:19:32.514670900 +0900
@@ -66,6 +66,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeB
   target_sources(btop PRIVATE src/freebsd/btop_collect.cpp)
 elseif(LINUX)
   target_sources(btop PRIVATE src/linux/btop_collect.cpp)
+elseif(CYGWIN)
+  target_sources(btop PRIVATE src/linux/btop_collect.cpp)
 else()
   message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is not supported")
 endif()
@@ -184,6 +186,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeB
     find_package(kvm REQUIRED)
     target_link_libraries(btop elf::elf kvm::kvm)
   endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "CYGWIN")
+  target_link_libraries(btop pdh iphlpapi)
 endif()
 
 install(TARGETS btop RUNTIME)
--- origsrc/btop-1.3.0/Makefile	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/Makefile	2024-01-24 21:19:32.514670900 +0900
@@ -34,7 +34,7 @@ else
 	ARCH ?= $(shell $(CXX) -dumpmachine | cut -d "-" -f 1)
 endif
 
-override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]')
+override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]' | sed 's/-.*$$//' )
 
 #? GPU Support
 ifeq ($(PLATFORM_LC)$(ARCH),linuxx86_64)
@@ -141,6 +141,11 @@ else ifeq ($(PLATFORM_LC),openbsd)
 	override ADDFLAGS += -lkvm
 	export MAKE = gmake
 	SU_GROUP := wheel
+else ifeq ($(PLATFORM_LC),cygwin_nt)
+	PLATFORM_DIR := linux
+	THREADS	:= $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
+	override ADDFLAGS += -lpdh -liphlpapi
+	SU_GROUP := Administrators
 else
 $(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
 endif
--- origsrc/btop-1.3.0/src/btop.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop.cpp	2024-01-24 21:19:32.524679000 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE 1
+#endif
+
 #include <algorithm>
 #include <csignal>
 #include <clocale>
--- origsrc/btop-1.3.0/src/btop_config.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_config.cpp	2024-01-24 21:19:32.524679000 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE
+#endif
+
 #include <array>
 #include <atomic>
 #include <fstream>
@@ -284,7 +288,11 @@ namespace Config {
 		{"swap_disk", true},
 		{"show_disks", true},
 		{"only_physical", true},
+#ifdef __CYGWIN__
+		{"use_fstab", false},
+#else
 		{"use_fstab", true},
+#endif
 		{"zfs_hide_datasets", false},
 		{"show_io_stat", true},
 		{"io_mode", false},
--- origsrc/btop-1.3.0/src/btop_input.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_input.cpp	2024-01-24 21:19:32.524679000 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE 1
+#endif
+
 #include <limits>
 #include <ranges>
 #include <vector>
--- origsrc/btop-1.3.0/src/btop_menu.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_menu.cpp	2024-01-24 21:19:32.524679000 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE
+#endif
+
 #include <deque>
 #include <unordered_map>
 #include <array>
--- origsrc/btop-1.3.0/src/btop_tools.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_tools.cpp	2024-01-24 21:19:32.524679000 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE 1
+#endif
+
 #include <cmath>
 #include <codecvt>
 #include <iostream>
--- origsrc/btop-1.3.0/src/linux/btop_collect.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/linux/btop_collect.cpp	2024-01-24 21:19:32.534678900 +0900
@@ -16,6 +16,10 @@ indent = tab
 tab-size = 4
 */
 
+#ifdef __CYGWIN__
+#define _GNU_SOURCE
+#endif
+
 #include <cstdlib>
 #include <unordered_map>
 #include <unordered_set>
@@ -43,6 +47,102 @@ tab-size = 4
 	#include <pwd.h>
 #endif
 
+#ifdef __CYGWIN__
+typedef uint32_t DWORD;
+typedef long long LONGLONG;
+typedef unsigned long ULONG;
+typedef unsigned long ULONG_PTR;
+typedef long LONG;
+typedef unsigned int UINT;
+typedef uint8_t BYTE;
+typedef char CHAR;
+typedef int BOOL;
+typedef void *HANDLE;
+typedef void *HQUERY;
+typedef void *HCOUNTER;
+typedef struct {
+  DWORD dwLowDateTime;
+  DWORD dwHighDateTime;
+} FILETIME;
+typedef struct {
+  DWORD    CStatus;
+  FILETIME TimeStamp;
+  LONGLONG FirstValue;
+  LONGLONG SecondValue;
+  DWORD    MultiCount;
+} PDH_RAW_COUNTER;
+#define PDH_FMT_DOUBLE ((DWORD) 0x00000200)
+typedef struct {
+  DWORD CStatus;
+  union {
+    long     longValue;
+    double   doubleValue;
+    LONGLONG largeValue;
+    char    *AnsiStringValue;
+    wchar_t *WideStringValue;
+  };
+} PDH_FMT_COUNTERVALUE;
+typedef struct {
+  char *String[4*4];
+} IP_ADDRESS_STRING, IP_MASK_STRING;
+typedef struct _IP_ADDR_STRING {
+  struct _IP_ADDR_STRING *Next;
+  IP_ADDRESS_STRING      IpAddress;
+  IP_MASK_STRING         IpMask;
+  DWORD                  Context;
+} IP_ADDR_STRING, *PIP_ADDR_STRING;
+#define MAX_ADAPTER_ADDRESS_LENGTH 8
+#define MAX_ADAPTER_NAME_LENGTH 256
+#define MAX_ADAPTER_DESCRIPTION_LENGTH 128
+typedef struct _IP_ADAPTER_INFO {
+  struct _IP_ADAPTER_INFO *Next;
+  DWORD                   ComboIndex;
+  char                    AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
+  char                    Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
+  UINT                    AddressLength;
+  BYTE                    Address[MAX_ADAPTER_ADDRESS_LENGTH];
+  DWORD                   Index;
+  UINT                    Type;
+  UINT                    DhcpEnabled;
+  PIP_ADDR_STRING         CurrentIpAddress;
+  IP_ADDR_STRING          IpAddressList;
+  IP_ADDR_STRING          GatewayList;
+  IP_ADDR_STRING          DhcpServer;
+  BOOL                    HaveWins;
+  IP_ADDR_STRING          PrimaryWinsServer;
+  IP_ADDR_STRING          SecondaryWinsServer;
+  time_t                  LeaseObtained;
+  time_t                  LeaseExpires;
+} IP_ADAPTER_INFO;
+#define MAX_PATH 260
+typedef struct {
+  DWORD     dwSize;
+  DWORD     cntUsage;
+  DWORD     th32ProcessID;
+  ULONG_PTR th32DefaultHeapID;
+  DWORD     th32ModuleID;
+  DWORD     cntThreads;
+  DWORD     th32ParentProcessID;
+  LONG      pcPriClassBase;
+  DWORD     dwFlags;
+  CHAR      szExeFile[MAX_PATH];
+} PROCESSENTRY32;
+#define TH32CS_SNAPPROCESS 0x00000002
+extern "C" {
+  ULONG GetAdaptersInfo(IP_ADAPTER_INFO *, ULONG *);
+  DWORD PdhOpenQuery(char *, DWORD *, HQUERY *);
+  DWORD PdhAddCounterA(HQUERY, char *, DWORD *, HCOUNTER *);
+  DWORD PdhCollectQueryData(HQUERY);
+  DWORD PdhGetRawCounterValue(HCOUNTER, DWORD *, PDH_RAW_COUNTER *);
+  DWORD PdhGetFormattedCounterValue(HCOUNTER, DWORD, DWORD *, PDH_FMT_COUNTERVALUE *);
+  DWORD PdhCalculateCounterFromRawValue(HCOUNTER, DWORD, PDH_RAW_COUNTER *, PDH_RAW_COUNTER *, PDH_FMT_COUNTERVALUE *);
+  DWORD PdhCloseQuery(HQUERY);
+  HANDLE CreateToolhelp32Snapshot(DWORD, DWORD);
+  BOOL Process32First(HANDLE, PROCESSENTRY32 *);
+  BOOL Process32Next(HANDLE, PROCESSENTRY32 *);
+}
+#endif
+
 #include "../btop_shared.hpp"
 #include "../btop_config.hpp"
 #include "../btop_tools.hpp"
@@ -232,11 +332,15 @@ namespace Shared {
 			}
 		}
 
+#ifdef __CYGWIN__
+		pageSize = 4096;
+#else
 		pageSize = sysconf(_SC_PAGE_SIZE);
 		if (pageSize <= 0) {
 			pageSize = 4096;
 			Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
 		}
+#endif
 
 		clkTck = sysconf(_SC_CLK_TCK);
 		if (clkTck <= 0) {
@@ -1632,6 +1736,9 @@ namespace Mem {
 			static vector<string> ignore_list;
 			double uptime = system_uptime();
 			auto free_priv = Config::getB("disk_free_priv");
+#ifdef __CYGWIN__
+			string root_dev;
+#endif
 			try {
 				auto& disks_filter = Config::getS("disks_filter");
 				bool filter_exclude = false;
@@ -1639,7 +1746,9 @@ namespace Mem {
 				auto only_physical = Config::getB("only_physical");
 				auto zfs_hide_datasets = Config::getB("zfs_hide_datasets");
 				auto& disks = mem.disks;
+#ifndef __CYGWIN__
 				static std::unordered_map<string, future<pair<disk_info, int>>> disks_stats_promises;
+#endif
 				ifstream diskread;
 
 				vector<string> filter;
@@ -1693,6 +1802,18 @@ namespace Mem {
 				}
 
 				//? Get mounts from /etc/mtab or /proc/self/mounts
+#ifdef __CYGWIN__
+				diskread.open((fs::exists("/etc/mtab") ? fs::path("/etc/mtab") : Shared::procPath / "self/mounts"));
+				if (diskread.good()) {
+					string dev, mountpoint, fstype;
+					while (not diskread.eof()) {
+						diskread >> dev >> mountpoint >> fstype;
+						diskread.ignore(SSmax, '\n');
+						if (mountpoint == "/") root_dev = dev;
+					}
+					diskread.close();
+				}
+#endif
 				diskread.open((fs::exists("/etc/mtab") ? fs::path("/etc/mtab") : Shared::procPath / "self/mounts"));
 				if (diskread.good()) {
 					vector<string> found;
@@ -1715,6 +1836,9 @@ namespace Mem {
 						//? Skip ZFS datasets if zfs_hide_datasets option is enabled
 						size_t zfs_dataset_name_start = 0;
 						if (fstype == "zfs" && (zfs_dataset_name_start = dev.find('/')) != std::string::npos && zfs_hide_datasets) continue;
+#ifdef __CYGWIN__
+						if (not (mountpoint == "/") and dev.starts_with(root_dev)) continue;
+#endif
 
 						if ((not use_fstab and not only_physical)
 						or (use_fstab and v_contains(fstab, mountpoint))
@@ -1779,6 +1903,23 @@ namespace Mem {
 				diskread.close();
 
 				//? Get disk/partition stats
+#ifdef __CYGWIN__
+				/* Cygwin has a bug that handle leak occurs if std::async is used. */
+				for (auto& [mountpoint, disk] : disks) {
+					if (std::error_code ec; not fs::exists(mountpoint, ec) or v_contains(ignore_list, mountpoint)) continue;
+					struct statvfs vfs;
+					if (statvfs(mountpoint.c_str(), &vfs) < 0) {
+						Logger::warning("Failed to get disk/partition stats for mount \""+ mountpoint + "\" with statvfs error code: " + to_string(errno) + ". Ignoring...");
+						ignore_list.push_back(mountpoint);
+						continue;
+					}
+					disk.total = vfs.f_blocks * vfs.f_frsize;
+					disk.free = (free_priv ? vfs.f_bfree : vfs.f_bavail) * vfs.f_frsize;
+					disk.used = disk.total - disk.free;
+					disk.used_percent = round((double)disk.used * 100 / disk.total);
+					disk.free_percent = 100 - disk.used_percent;
+				}
+#else
 				for (auto it = disks.begin(); it != disks.end(); ) {
 					auto &[mountpoint, disk] = *it;
 					if (v_contains(ignore_list, mountpoint) or disk.name == "swap") {
@@ -1821,6 +1962,7 @@ namespace Mem {
 					});
 					++it;
 				}
+#endif
 
 				//? Setup disks order in UI and add swap if enabled
 				mem.disks_order.clear();
@@ -1846,6 +1988,72 @@ namespace Mem {
 					#endif
 
 				//? Get disks IO
+#ifdef __CYGWIN__
+				/* Cygwin does not have means to provide statistics of disk I/O.
+				   Because there is no other choice, use WIN32API instead. */
+				int64_t bytes_read, bytes_write;
+				disk_ios = 0;
+				static std::unordered_map<string, PDH_RAW_COUNTER> RawCounterReadTimeOld, RawCounterWriteTimeOld;
+				for (auto& [mountpoint, disk] : disks) {
+					char perf_counter_read[1024], perf_counter_write[1024];
+					char perf_counter_read_time[1024], perf_counter_write_time[1024];
+					HQUERY hQuery;
+					HCOUNTER hCounterRead, hCounterWrite;
+					HCOUNTER hCounterReadTime, hCounterWriteTime;
+					PDH_RAW_COUNTER RawCounterRead, RawCounterWrite;
+					PDH_RAW_COUNTER RawCounterReadTime, RawCounterWriteTime;
+					PDH_FMT_COUNTERVALUE FmtCounterReadTime, FmtCounterWriteTime;
+					string dev = disk.dev;
+
+					if (dev.empty() or dev.find(':') == std::string::npos) continue;
+
+					sprintf(perf_counter_read, "\\LogicalDisk(%s)\\Disk Read Bytes/sec", dev.substr(0, 2).c_str());
+					sprintf(perf_counter_write, "\\LogicalDisk(%s)\\Disk Write Bytes/sec", dev.substr(0, 2).c_str());
+					sprintf(perf_counter_read_time, "\\LogicalDisk(%s)\\%% Disk Read Time", dev.substr(0, 2).c_str());
+					sprintf(perf_counter_write_time, "\\LogicalDisk(%s)\\%% Disk Write Time", dev.substr(0, 2).c_str());
+
+					PdhOpenQuery(NULL, 0, &hQuery);
+					PdhAddCounterA(hQuery, perf_counter_read, 0, &hCounterRead);
+					PdhAddCounterA(hQuery, perf_counter_write, 0, &hCounterWrite);
+					PdhAddCounterA(hQuery, perf_counter_read_time, 0, &hCounterReadTime);
+					PdhAddCounterA(hQuery, perf_counter_write_time, 0, &hCounterWriteTime);
+					PdhCollectQueryData(hQuery);
+					PdhGetRawCounterValue(hCounterRead, NULL, &RawCounterRead);
+					PdhGetRawCounterValue(hCounterWrite, NULL, &RawCounterWrite);
+					PdhGetRawCounterValue(hCounterReadTime, NULL, &RawCounterReadTime);
+					PdhGetRawCounterValue(hCounterWriteTime, NULL, &RawCounterWriteTime);
+
+					if (RawCounterReadTimeOld.find(mountpoint) == RawCounterReadTimeOld.end()) {
+						FmtCounterReadTime.doubleValue = 0;
+					} else {
+						PdhCalculateCounterFromRawValue(hCounterReadTime, PDH_FMT_DOUBLE,
+								&RawCounterReadTime, &RawCounterReadTimeOld[mountpoint], &FmtCounterReadTime);
+					}
+					if (RawCounterWriteTimeOld.find(mountpoint) == RawCounterWriteTimeOld.end()) {
+						FmtCounterWriteTime.doubleValue = 0;
+					} else {
+						PdhCalculateCounterFromRawValue(hCounterWriteTime, PDH_FMT_DOUBLE,
+								&RawCounterWriteTime, &RawCounterWriteTimeOld[mountpoint], &FmtCounterWriteTime);
+					}
+					PdhCloseQuery(hQuery);
+
+					RawCounterReadTimeOld[mountpoint] = RawCounterReadTime;
+					RawCounterWriteTimeOld[mountpoint] = RawCounterWriteTime;
+
+					bytes_read = RawCounterRead.FirstValue;
+					disk.io_read.push_back(max((int64_t)0, bytes_read - disk.old_io.at(0)));
+					disk.old_io.at(0) = bytes_read;
+					while (cmp_greater(disk.io_read.size(), width * 2)) disk.io_read.pop_front();
+
+					bytes_write = RawCounterWrite.FirstValue;
+					disk.io_write.push_back(max((int64_t)0, bytes_write - disk.old_io.at(1)));
+					disk.old_io.at(1) = bytes_write;
+					while (cmp_greater(disk.io_write.size(), width * 2)) disk.io_write.pop_front();
+
+					disk.io_activity.push_back(clamp((long)round(FmtCounterReadTime.doubleValue + FmtCounterWriteTime.doubleValue), 0l, 100l));
+					while (cmp_greater(disk.io_activity.size(), width * 2)) disk.io_activity.pop_front();
+				}
+#else
 				int64_t sectors_read, sectors_write, io_ticks, io_ticks_temp;
 				disk_ios = 0;
 				for (auto& [ignored, disk] : disks) {
@@ -1929,6 +2137,7 @@ namespace Mem {
 					}
 					diskread.close();
 				}
+#endif
 				old_uptime = uptime;
 			}
 			catch (const std::exception& e) {
@@ -2121,6 +2330,9 @@ namespace Net {
 			//? Iteration over all items in getifaddrs() list
 			for (auto* ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
 				if (ifa->ifa_addr == nullptr) continue;
+#ifdef __CYGWIN__
+				if ((ifa->ifa_flags & IFF_RUNNING) == 0) continue;
+#endif
 				family = ifa->ifa_addr->sa_family;
 				const auto& iface = ifa->ifa_name;
 
@@ -2162,6 +2374,80 @@ namespace Net {
 			}
 
 			//? Get total recieved and transmitted bytes + device address if no ip was found
+#ifdef __CYGWIN__
+			/* Cygwin does not have means to provide statistics of network interfaces.
+			   Because there is no other choice, use WIN32API instead. */
+			do {
+				char buf[65536];
+				ULONG adapters_size = sizeof(buf);
+				IP_ADAPTER_INFO *adapters = (IP_ADAPTER_INFO *) buf;
+				GetAdaptersInfo(adapters, &adapters_size);
+
+				for (const auto& iface : interfaces) {
+					char perf_counter_download[1024], perf_counter_upload[1024];
+					HQUERY hQuery;
+					HCOUNTER hCounterDown, hCounterUp;
+					PDH_RAW_COUNTER RawCounterDown, RawCounterUp;
+					for (IP_ADAPTER_INFO *p = adapters; p; p = p->Next) {
+						if (p->AdapterName == iface) {
+							char *q = p->Description;
+							while ((q = strchr(q, '('))) *q = '[';
+							q = p->Description;
+							while ((q = strchr(q, ')'))) *q = ']';
+							sprintf(perf_counter_download, "\\Network Interface(%s)\\Bytes Received/sec", p->Description);
+							sprintf(perf_counter_upload, "\\Network Interface(%s)\\Bytes Sent/sec", p->Description);
+							break;
+						}
+					}
+					PdhOpenQuery(NULL, 0, &hQuery);
+					PdhAddCounterA(hQuery, perf_counter_download, 0, &hCounterDown);
+					PdhAddCounterA(hQuery, perf_counter_upload, 0, &hCounterUp);
+					PdhCollectQueryData(hQuery);
+					PdhGetRawCounterValue(hCounterDown, NULL, &RawCounterDown);
+					PdhGetRawCounterValue(hCounterUp, NULL, &RawCounterUp);
+					PdhCloseQuery(hQuery);
+					for (const string dir : {"download", "upload"}) {
+						auto& saved_stat = net.at(iface).stat.at(dir);
+						auto& bandwidth = net.at(iface).bandwidth.at(dir);
+						uint64_t val{};
+
+						val = (dir == "download") ? RawCounterDown.FirstValue : RawCounterUp.FirstValue;
+
+						//? Update speed, total and top values
+						if (val < saved_stat.last) {
+							saved_stat.rollover += saved_stat.last;
+							saved_stat.last = 0;
+						}
+						if (cmp_greater((unsigned long long)saved_stat.rollover + (unsigned long long)val, numeric_limits<uint64_t>::max())) {
+							saved_stat.rollover = 0;
+							saved_stat.last = 0;
+						}
+						saved_stat.speed = round((double)(val - saved_stat.last) / ((double)(new_timestamp - timestamp) / 1000));
+						if (saved_stat.speed > saved_stat.top) saved_stat.top = saved_stat.speed;
+						if (saved_stat.offset > val + saved_stat.rollover) saved_stat.offset = 0;
+						saved_stat.total = (val + saved_stat.rollover) - saved_stat.offset;
+						saved_stat.last = val;
+
+						//? Add values to graph
+						bandwidth.push_back(saved_stat.speed);
+						while (cmp_greater(bandwidth.size(), width * 2)) bandwidth.pop_front();
+
+						//? Set counters for auto scaling
+						if (net_auto and selected_iface == iface) {
+							if (net_sync and saved_stat.speed < net.at(iface).stat.at(dir == "download" ? "upload" : "download").speed) continue;
+							if (saved_stat.speed > graph_max[dir]) {
+								++max_count[dir][0];
+								if (max_count[dir][1] > 0) --max_count[dir][1];
+							}
+							else if (graph_max[dir] > 10 << 10 and saved_stat.speed < graph_max[dir] / 10) {
+								++max_count[dir][1];
+								if (max_count[dir][0] > 0) --max_count[dir][0];
+							}
+						}
+					}
+				}
+			} while (false);
+#else
 			for (const auto& iface : interfaces) {
 				if (net.at(iface).ipv4.empty() and net.at(iface).ipv6.empty())
 					net.at(iface).ipv4 = readfile("/sys/class/net/" + iface + "/address");
@@ -2210,6 +2496,7 @@ namespace Net {
 					}
 				}
 			}
+#endif
 
 			//? Clean up net map if needed
 			if (net.size() > interfaces.size()) {
@@ -2376,6 +2663,28 @@ namespace Proc {
 
 		while (cmp_greater(detailed.mem_bytes.size(), width)) detailed.mem_bytes.pop_front();
 
+#ifdef __CYGWIN__
+		/* Cygwin does not have means to provide statistics of I/O.
+		   Because there is no other choice, use WIN32API instead. */
+		char perf_counter_read[1024], perf_counter_write[1024];
+		HQUERY hQuery;
+		HCOUNTER hCounterRead, hCounterWrite;
+		PDH_RAW_COUNTER RawCounterRead, RawCounterWrite;
+
+		sprintf(perf_counter_read, "\\Process(%s)\\IO Read Bytes/sec", p_info->name.c_str());
+		sprintf(perf_counter_write, "\\Process(%s)\\IO Write Bytes/sec", p_info->name.c_str());
+
+		PdhOpenQuery(NULL, 0, &hQuery);
+		PdhAddCounterA(hQuery, perf_counter_read, 0, &hCounterRead);
+		PdhAddCounterA(hQuery, perf_counter_write, 0, &hCounterWrite);
+		PdhCollectQueryData(hQuery);
+		PdhGetRawCounterValue(hCounterRead, NULL, &RawCounterRead);
+		PdhGetRawCounterValue(hCounterWrite, NULL, &RawCounterWrite);
+		PdhCloseQuery(hQuery);
+
+		detailed.io_read = floating_humanizer(RawCounterRead.FirstValue);
+		detailed.io_write = floating_humanizer(RawCounterWrite.FirstValue);
+#else
 		//? Get bytes read and written from proc/[pid]/io
 		if (fs::exists(pid_path / "io")) {
 			d_read.open(pid_path / "io");
@@ -2400,6 +2709,7 @@ namespace Proc {
 			catch (const std::out_of_range&) {}
 			d_read.close();
 		}
+#endif
 	}
 
 	//* Collects and sorts process information from /proc
@@ -2486,6 +2796,11 @@ namespace Proc {
 			else throw std::runtime_error("Failure to read /proc/stat");
 			pread.close();
 
+#ifdef __CYGWIN__
+			/* Cygwin does not have means to provide number of thread in the process.
+			   Because there is no other choice, use WIN32API instead. */
+			HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+#endif
 			//? Iterate over all pids in /proc
 			for (const auto& d: fs::directory_iterator(Shared::procPath)) {
 				if (Runner::stopping)
@@ -2517,10 +2832,12 @@ namespace Proc {
 
 				//? Get program name, command and username
 				if (no_cache) {
+#ifndef __CYGWIN__
 					pread.open(d.path() / "comm");
 					if (not pread.good()) continue;
 					getline(pread, new_proc.name);
 					pread.close();
+#endif
 					//? Check for whitespace characters in name and set offset to get correct fields from stat file
 					new_proc.name_offset = rng::count(new_proc.name, ' ');
 
@@ -2547,6 +2864,10 @@ namespace Proc {
 							pread.ignore();
 							getline(pread, uid, '\t');
 							break;
+#ifdef __CYGWIN__
+						} else if (line == "Name") {
+							pread >> std::skipws >> new_proc.name;
+#endif
 						} else {
 							pread.ignore(SSmax, '\n');
 						}
@@ -2635,6 +2956,27 @@ namespace Proc {
 
 				pread.close();
 
+#ifdef __CYGWIN__
+				/* Cygwin does not have means to provide number of thread in the process.
+				   Because there is no other choice, use WIN32API instead. */
+				pread.open(d.path() / "winpid");
+				if (pread.good()) {
+					DWORD winpid;
+					PROCESSENTRY32 pe;
+					pread >> winpid;
+					pread.close();
+					pe.dwSize = sizeof(PROCESSENTRY32);
+					if (Process32First(snap, &pe)) {
+						do {
+							if (pe.th32ProcessID == winpid) {
+								new_proc.threads = pe.cntThreads;
+								break;
+							}
+						} while (Process32Next(snap, &pe));
+					}
+				}
+#endif
+
 				if (should_filter_kernel and new_proc.ppid == KTHREADD) {
 					kernels_procs.emplace(new_proc.pid);
 					found.pop_back();

[-- Attachment #4: fix-gcc13-warnings.patch --]
[-- Type: text/plain, Size: 4909 bytes --]

diff -ru origsrc/btop-1.3.0/src/btop_draw.cpp src/btop-1.3.0/src/btop_draw.cpp
--- origsrc/btop-1.3.0/src/btop_draw.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_draw.cpp	2024-01-24 23:21:05.802983100 +0900
@@ -797,7 +797,7 @@
 			+ Theme::g("cpu").at(clamp(safeVal(cpu.cpu_percent, "total"s).back(), 0ll, 100ll)) + rjust(to_string(safeVal(cpu.cpu_percent, "total"s).back()), 4) + Theme::c("main_fg") + '%';
 		if (show_temps) {
 			const auto [temp, unit] = celsius_to(safeVal(cpu.temp, 0).back(), temp_scale);
-			const auto& temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, 0).back() * 100 / cpu.temp_max, 0ll, 100ll));
+			const auto temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, 0).back() * 100 / cpu.temp_max, 0ll, 100ll));
 			if ((b_column_size > 1 or b_columns > 1) and temp_graphs.size() >= 1ll)
 				out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5) + temp_color
 					+ temp_graphs.at(0)(safeVal(cpu.temp, 0), data_same or redraw);
@@ -823,7 +823,7 @@
 
 			if (show_temps and not hide_cores and std::cmp_greater_equal(temp_graphs.size(), n)) {
 				const auto [temp, unit] = celsius_to(safeVal(cpu.temp, n+1).back(), temp_scale);
-				const auto& temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, n+1).back() * 100 / cpu.temp_max, 0ll, 100ll));
+				const auto temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, n+1).back() * 100 / cpu.temp_max, 0ll, 100ll));
 				if (b_column_size > 1)
 					out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5)
 						+ temp_graphs.at(n+1)(safeVal(cpu.temp, n+1), data_same or redraw);
@@ -1273,7 +1273,7 @@
 				for (const auto& mount : mem.disks_order) {
 					if (not disks.contains(mount)) continue;
 					if (cy > height - 3) break;
-					const auto& disk = safeVal(disks, mount);
+					const auto disk = safeVal(disks, mount);
 					if (disk.io_read.empty()) continue;
 					const string total = floating_humanizer(disk.total, not big_disk);
 					out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 8) + Mv::to(y+1+cy, x+cx + disks_width - total.size())
@@ -1312,7 +1312,7 @@
 				for (const auto& mount : mem.disks_order) {
 					if (not disks.contains(mount)) continue;
 					if (cy > height - 3) break;
-					const auto& disk = safeVal(disks, mount);
+					const auto disk = safeVal(disks, mount);
 					if (disk.name.empty() or not disk_meters_used.contains(mount)) continue;
 					auto comb_val = (not disk.io_read.empty() ? disk.io_read.back() + disk.io_write.back() : 0ll);
 					const string human_io = (comb_val > 0 ? (disk.io_write.back() > 0 and big_disk ? "â–¼"s : ""s) + (disk.io_read.back() > 0 and big_disk ? "â–²"s : ""s)
@@ -1604,8 +1604,8 @@
 				out += Mv::to(d_y, d_x - 1) + Theme::c("proc_box") + Symbols::div_up + Mv::to(y, d_x - 1) + Symbols::div_down + Theme::c("div_line");
 				for (const int& i : iota(1, 8)) out += Mv::to(d_y + i, d_x - 1) + Symbols::v_line;
 
-				const string& t_color = (not alive or selected > 0 ? Theme::c("inactive_fg") : Theme::c("title"));
-				const string& hi_color = (not alive or selected > 0 ? t_color : Theme::c("hi_fg"));
+				const string t_color = (not alive or selected > 0 ? Theme::c("inactive_fg") : Theme::c("title"));
+				const string hi_color = (not alive or selected > 0 ? t_color : Theme::c("hi_fg"));
 				const string hide = (selected > 0 ? t_color + "hide " : Theme::c("title") + "hide " + Theme::c("hi_fg"));
 				int mouse_x = d_x + 2;
 				out += Mv::to(d_y, d_x + 1);
diff -ru origsrc/btop-1.3.0/src/btop_menu.cpp src/btop-1.3.0/src/btop_menu.cpp
--- origsrc/btop-1.3.0/src/btop_menu.cpp	2024-01-07 23:23:01.000000000 +0900
+++ src/btop-1.3.0/src/btop_menu.cpp	2024-01-24 23:19:42.301762700 +0900
@@ -801,12 +805,12 @@
 	string msgBox::operator()() {
 		string out;
 		int pos = width / 2 - (boxtype == 0 ? 6 : 14);
-		auto& first_color = (selected == 0 ? Theme::c("hi_fg") : Theme::c("div_line"));
+		auto first_color = (selected == 0 ? Theme::c("hi_fg") : Theme::c("div_line"));
 		out = Mv::d(1) + Mv::r(pos) + Fx::b + first_color + button_left + (selected == 0 ? Theme::c("title") : Theme::c("main_fg") + Fx::ub)
 			+ (boxtype == 0 ? "    Ok    " : "    Yes    ") + first_color + button_right;
 		mouse_mappings["button1"] = Input::Mouse_loc{y + height - 4, x + pos + 1, 3, 12 + (boxtype > 0 ? 1 : 0)};
 		if (boxtype > 0) {
-			auto& second_color = (selected == 1 ? Theme::c("hi_fg") : Theme::c("div_line"));
+			auto second_color = (selected == 1 ? Theme::c("hi_fg") : Theme::c("div_line"));
 			out += Mv::r(2) + second_color + button_left + (selected == 1 ? Theme::c("title") : Theme::c("main_fg") + Fx::ub)
 				+ "    No    " + second_color + button_right;
 			mouse_mappings["button2"] = Input::Mouse_loc{y + height - 4, x + pos + 15 + (boxtype > 0 ? 1 : 0), 3, 12};

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [ITP] btop
  2024-01-24 15:22 [ITP] btop Takashi Yano
@ 2024-01-25 15:11 ` Jon Turney
  2024-01-26  3:31   ` Takashi Yano
  0 siblings, 1 reply; 3+ messages in thread
From: Jon Turney @ 2024-01-25 15:11 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-apps

On 24/01/2024 15:22, Takashi Yano via Cygwin-apps wrote:
> I'd like to propose a new package btop, which is a feature-rich
> resource monitor. btop is ready for many Linux distributions
> including Fedora.
> 

Thanks.

>  
> +#ifdef __CYGWIN__
> +#define _GNU_SOURCE 1
> +#endif
> +

I'd suggest adding -D_GNU_SOURCE=1 to CPPFLAGS, rather than patching 
this in everywhere it's needed.

(Setting this unconditionally could be added to the build system 
somewhere, which is theoretically upstreamable, since it's only not 
needed on linux for :reasons:)

> +#ifdef __CYGWIN__
> +typedef uint32_t DWORD

Is it not possible to just #include <windows.h> here?  If not, perhaps a 
comment why the definitions are copied would nice.

Otherwise, this looks great. I added this to your packages.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [ITP] btop
  2024-01-25 15:11 ` Jon Turney
@ 2024-01-26  3:31   ` Takashi Yano
  0 siblings, 0 replies; 3+ messages in thread
From: Takashi Yano @ 2024-01-26  3:31 UTC (permalink / raw)
  To: cygwin-apps

On Thu, 25 Jan 2024 15:11:12 +0000
Jon Turney wrote:
> On 24/01/2024 15:22, Takashi Yano via Cygwin-apps wrote:
> > +#ifdef __CYGWIN__
> > +#define _GNU_SOURCE 1
> > +#endif
> > +
> 
> I'd suggest adding -D_GNU_SOURCE=1 to CPPFLAGS, rather than patching 
> this in everywhere it's needed.
> 
> (Setting this unconditionally could be added to the build system 
> somewhere, which is theoretically upstreamable, since it's only not 
> needed on linux for :reasons:)

Sounds good! Thanks.
I'll add it to CMakeLists.txt and Makfile for CYGWIN for the time being.

> > +#ifdef __CYGWIN__
> > +typedef uint32_t DWORD
> 
> Is it not possible to just #include <windows.h> here?  If not, perhaps a 
> comment why the definitions are copied would nice.

When I included <windows.h> here, many conflicts occured between
posix headers and w32api headers.
However, now I have found that including w32api headers after all
the posix headers and typedef IN6_ADDR like:

#ifdef __CYGWIN__
#include <windows.h>
#include <pdh.h>
#include <tlhelp32.h>
typedef struct {
  union {
    u_char Byte[16];
    u_short Word[8];
  } u;
} IN6_ADDR;
#include <iphlpapi.h>
#endif

solves the conflicts. Thanks!

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2024-01-26  3:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-24 15:22 [ITP] btop Takashi Yano
2024-01-25 15:11 ` Jon Turney
2024-01-26  3:31   ` Takashi Yano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).