Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ target_include_directories(SysMood PRIVATE
${CMAKE_SOURCE_DIR}/include
)

if(WIN32)
target_link_libraries(SysMood PRIVATE Iphlpapi)
endif()

# Enable testing (used later)
include(CTest)
enable_testing()
Expand Down
120 changes: 120 additions & 0 deletions include/network_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#ifndef NETWORK_MONITOR_H
#define NETWORK_MONITOR_H

// Written by: Anderson Yu-Hong Cai (https://github.com/AndersonTsaiTW)
// Written on: 2025-10-11
// License: MIT
// Version: 2.0
//
#include <cstdint>

#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0602
#endif

#include <windows.h>
#include <iphlpapi.h>
#include <set>
#include <stdlib.h>

// Note: linking still required for iphlpapi when using g++: -liphlpapi

static bool collect_if_counters(uint64_t &out_sent, uint64_t &out_recv) {
out_sent = 0;
out_recv = 0;

// Fallback to GetIfTable which is widely available (MinGW). We'll
// allocate the table buffer dynamically as required.
MIB_IFTABLE *pIfTable = NULL;
DWORD dwSize = 0;
DWORD dwRetVal = GetIfTable(NULL, &dwSize, FALSE);
if (dwRetVal != ERROR_INSUFFICIENT_BUFFER) {
return false;
}

pIfTable = (MIB_IFTABLE*)malloc(dwSize);
if (pIfTable == NULL) return false;

dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE);
if (dwRetVal != NO_ERROR) {
free(pIfTable);
return false;
}

std::set<DWORD> seenIndexes;
for (DWORD i = 0; i < pIfTable->dwNumEntries; ++i) {
MIB_IFROW &row = pIfTable->table[i];

// De-duplicate by dwIndex (if available). Some stacks list
// virtual/alias rows that may duplicate counters.
if (seenIndexes.find(row.dwIndex) != seenIndexes.end()) continue;
seenIndexes.insert(row.dwIndex);

// Heuristic: include an interface if it shows any octets OR has a
// non-zero oper status. This avoids summing unused loopback/host
// entries while still counting active interfaces.
uint64_t outOct = (uint64_t)row.dwOutOctets;
uint64_t inOct = (uint64_t)row.dwInOctets;
if (outOct == 0 && inOct == 0 && row.dwOperStatus == 0) {
continue;
}

out_sent += outOct;
out_recv += inOct;
}

free(pIfTable);
return true;
}

// Helper: compute difference with 64-bit wrap-around handling
static inline uint64_t counter_diff(uint64_t end, uint64_t start) {
if (end >= start) return end - start;
// wrapped
return (UINT64_MAX - start) + end + 1ULL;
}

// Public API: samples counters for ~1s and returns KB/s
static void get_network_stats(double &sent_rate_kbps, double &received_rate_kbps) {
uint64_t sent0 = 0, recv0 = 0;
uint64_t sent1 = 0, recv1 = 0;

if (!collect_if_counters(sent0, recv0)) {
sent_rate_kbps = 0.0;
received_rate_kbps = 0.0;
return;
}

ULONGLONG t0 = GetTickCount64();
Sleep(3000); // 3-second interval

if (!collect_if_counters(sent1, recv1)) {
sent_rate_kbps = 0.0;
received_rate_kbps = 0.0;
return;
}

ULONGLONG t1 = GetTickCount64();

double elapsed_s = (double)(t1 - t0) / 1000.0;
if (elapsed_s <= 0.0) elapsed_s = 1.0; // safety

uint64_t ds = counter_diff(sent1, sent0);
uint64_t dr = counter_diff(recv1, recv0);

// convert to KB/s
sent_rate_kbps = ((double)ds / 1024.0) / elapsed_s;
received_rate_kbps = ((double)dr / 1024.0) / elapsed_s;
}

#else
// Non-Windows platform: provide stub implementation
static void get_network_stats(double &sent_rate_kbps, double &received_rate_kbps) {
// TODO: Implement network monitoring for Linux/macOS
sent_rate_kbps = 0.0;
received_rate_kbps = 0.0;
}
#endif // _WIN32

#endif // NETWORK_MONITOR_H
19 changes: 19 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <ctime>
#include "cpu_monitor.h"
#include "memory_monitor.h"
#include "network_monitor.h"

int main()
{
Expand All @@ -40,6 +41,9 @@ int main()
int mem_available = memory.memory_available();
int mem_total = memory.memory_total();
int mem_used = memory.memory_used();
double sent_rate_kbps, received_rate_kbps;
get_network_stats(sent_rate_kbps, received_rate_kbps);
double sum_rate_kbs = sent_rate_kbps + received_rate_kbps;
std::cout <<
R"(________ ___ ___ ________ _____ ______ ________ ________ ________
|\ ____\ |\ \ / /|\ ____\|\ _ \ _ \|\ __ \|\ __ \|\ ___ \
Expand All @@ -66,6 +70,8 @@ int main()
std::cout << "Memory Available: " << mem_available << " MB" << '\n';
std::cout << "Memory Total: " << mem_total << " MB" << '\n';
std::cout << "Memory Used: " << mem_used << " MB" << '\n';
std::cout << "Network Sent: " << sent_rate_kbps << " KB/s" << std::endl;
std::cout << "Network Received: " << received_rate_kbps << " KB/s" << std::endl;
std::cout << "=========================================================== " << '\n';

std::cout << "========================System Mood======================== " << '\n';
Expand Down Expand Up @@ -93,6 +99,19 @@ int main()
{
std::cout << "So much free memory! I could host a party in here! " << '\n';
}

if (sum_rate_kbs <= 0.0) {
std::cout << "Hello...? Anyone out there? I think I'm alone in the digital void..." << std::endl;
} else if (sum_rate_kbs <= 10.0) { // 1–10 KB/s:low
std::cout << "I can barely feel the connection... maybe send a ping to keep me alive?" << std::endl;
} else if (sum_rate_kbs <= 100.0) { // 11–100 KB/s:moderate
std::cout << "Hmm... data's trickling in. Not bad, but I'm starting to warm up!" << std::endl;
} else if (sum_rate_kbs <= 1000.0) { // 101–1000 KB/s:good
std::cout << "Nice flow! I can stream tunes, fetch memes, and still feel chill~" << std::endl;
} else { // > 1000 KB/s:fast
std::cout << "Wooosh! Data's flying—pages load before you blink. I'm unstoppable!!! " << std::endl;
}

std::cout << "=========================================================== " << '\n';

return 0;
Expand Down