diff --git a/README.md b/README.md index 859abbb6..6004c785 100644 --- a/README.md +++ b/README.md @@ -428,6 +428,7 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `wine` | Show current Wine or Proton version in use | | `winesync` | Show wine sync method in use | | `present_mode` | Shows current vulkan [present mode](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html) or vsync status in opengl | +| `network` | Show network interfaces tx and rx kb/s. You can specify interface with `network=eth0` | Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32` Because comma is also used as option delimiter and needs to be escaped for values with a backslash, you can use `+` like `MANGOHUD_CONFIG=fps_limit=60+30+0` instead. diff --git a/data/MangoHud.conf b/data/MangoHud.conf index 8048819f..bdba6954 100644 --- a/data/MangoHud.conf +++ b/data/MangoHud.conf @@ -212,6 +212,14 @@ frame_timing ## example: Track:;{title};By:;{artist};From:;{album} # media_player_format=title,artist,album +### Network interface throughput +# network +## Network can take arguments but it's not required. +## without arguments it shows all interfaces +## arguments set which interfaces will be displayed +# network=eth0,wlo1 + + ### Change the hud font size # font_size=24 # font_scale=1.0 diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp index 23b445a6..0177841e 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -98,6 +98,7 @@ void HudElements::convert_colors(const struct overlay_params& params) HUDElements.colors.fps_value_med = convert(params.fps_color[1]); HUDElements.colors.fps_value_high = convert(params.fps_color[2]); HUDElements.colors.text_outline = convert(params.text_outline_color); + HUDElements.colors.network = convert(params.network_color); ImGuiStyle& style = ImGui::GetStyle(); style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color); @@ -1442,6 +1443,39 @@ void HudElements::present_mode() { ImGui::PopFont(); } +void HudElements::network() { + if (HUDElements.net && HUDElements.net->should_reset) + HUDElements.net.reset(new Net); + + if (!HUDElements.net) + HUDElements.net = std::make_unique(); + + ImguiNextColumnFirstItem(); + HUDElements.TextColored(HUDElements.colors.network, "%s", "NET"); + ImGui::TableNextColumn(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", "TX"); + ImGui::TableNextColumn(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", "RX"); + + for (auto& iface : HUDElements.net->interfaces){ + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + HUDElements.TextColored(HUDElements.colors.network, "%s", iface.name.c_str()); + ImGui::TableNextColumn(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", iface.txBps / 1000.f); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "KB/s"); + ImGui::PopFont(); + ImGui::TableNextColumn(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", iface.rxBps / 1000.f); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "KB/s"); + ImGui::PopFont(); + } +} + void HudElements::sort_elements(const std::pair& option) { const auto& param = option.first; const auto& value = option.second; @@ -1488,7 +1522,8 @@ void HudElements::sort_elements(const std::pair& optio {"hdr", {hdr}}, {"refresh_rate", {refresh_rate}}, {"winesync", {winesync}}, - {"present_mode", {present_mode}} + {"present_mode", {present_mode}}, + {"network", {network}} }; @@ -1614,6 +1649,8 @@ void HudElements::legacy_elements(){ ordered_functions.push_back({winesync, "winesync", value}); if (params->enabled[OVERLAY_PARAM_ENABLED_present_mode]) ordered_functions.push_back({present_mode, "present_mode", value}); + if (!params->network.empty()) + ordered_functions.push_back({network, "network", value}); } diff --git a/src/hud_elements.h b/src/hud_elements.h index 61d1de0b..5c37d1db 100644 --- a/src/hud_elements.h +++ b/src/hud_elements.h @@ -8,6 +8,8 @@ #include "winesync.h" #include "vulkan/vulkan.h" #include +#include "net.h" +#include "overlay_params.h" struct Function { std::function run; // Using std::function instead of a raw function pointer for more flexibility @@ -50,6 +52,7 @@ class HudElements{ int hdr_status = 0; int refresh = 0; std::unique_ptr winesync_ptr = nullptr; + std::unique_ptr net = nullptr; void sort_elements(const std::pair& option); void legacy_elements(); @@ -95,6 +98,7 @@ class HudElements{ static void refresh_rate(); static void winesync(); static void present_mode(); + static void network(); void convert_colors(const struct overlay_params& params); void convert_colors(bool do_conv, const struct overlay_params& params); @@ -122,7 +126,8 @@ class HudElements{ fps_value_low, fps_value_med, fps_value_high, - text_outline; + text_outline, + network; } colors {}; void TextColored(ImVec4 col, const char *fmt, ...); diff --git a/src/meson.build b/src/meson.build index 5754b72d..573682b4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -91,7 +91,8 @@ if is_unixy 'device.cpp', 'amdgpu.cpp', 'intel.cpp', - 'msm.cpp' + 'msm.cpp', + 'net.cpp' ) opengl_files = files( diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 00000000..ccc68bc0 --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,58 @@ +#include "net.h" +#include "hud_elements.h" + +Net::Net() { + should_reset = false; + fs::path net_dir(NETDIR); + if (fs::exists(net_dir) && fs::is_directory(net_dir)) { + for (const auto& entry : fs::directory_iterator(net_dir)) { + if (fs::is_directory(entry.status())) { + auto val = entry.path().filename().string(); + if (val == "lo") + continue; + + if (!HUDElements.params->network.empty() && HUDElements.params->network.front() == "1") { + interfaces.push_back({entry.path().filename().string(), 0, 0}); + } else if (!HUDElements.params->network.empty()){ + auto it = std::find(HUDElements.params->network.begin(), HUDElements.params->network.end(), val); + if (it != HUDElements.params->network.end()) + interfaces.push_back({entry.path().filename().string(), 0, 0}); + } + } + } + } + + if (interfaces.empty()) + SPDLOG_ERROR("Network: couldn't find any interfaces"); +} + +void Net::update() { + if (!interfaces.empty()) { + for (auto& iface : interfaces) { + // path to tx_bytes and rx_bytes + std::string txfile = (NETDIR + iface.name + TXFILE); + std::string rxfile = (NETDIR + iface.name + RXFILE); + + // amount of bytes at previous update + uint64_t prevTx = iface.txBytes; + uint64_t prevRx = iface.rxBytes; + + // current amount of bytes + iface.txBytes = std::stoll(read_line(txfile)); + iface.rxBytes = std::stoll(read_line(rxfile)); + + auto now = std::chrono::steady_clock::now(); + // calculate the bytes per second since last update + iface.txBps = calculateThroughput(iface.txBytes, prevTx, iface.previousTime, now); + iface.rxBps = calculateThroughput(iface.rxBytes, prevRx, iface.previousTime, now); + iface.previousTime = now; + } + } +} + +uint64_t Net::calculateThroughput(long long currentBytes, long long previousBytes, + std::chrono::steady_clock::time_point previousTime, + std::chrono::steady_clock::time_point currentTime) { + std::chrono::duration elapsed = (currentTime - previousTime); + return static_cast((currentBytes - previousBytes) / elapsed.count()); +} \ No newline at end of file diff --git a/src/net.h b/src/net.h new file mode 100644 index 00000000..f88d49c8 --- /dev/null +++ b/src/net.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include "filesystem.h" +#include "file_utils.h" +#include +#include + +namespace fs = ghc::filesystem; + +#ifndef NETDIR +#define NETDIR "/sys/class/net/" +#endif + +#ifndef TXFILE +#define TXFILE "/statistics/tx_bytes" +#endif + +#ifndef RXFILE +#define RXFILE "/statistics/rx_bytes" +#endif + +class Net { + public: + bool should_reset = false; + struct interface { + std::string name; + uint64_t txBytes; + uint64_t rxBytes; + uint64_t txBps; + uint64_t rxBps; + std::chrono::steady_clock::time_point previousTime; + }; + + Net(); + void update(); + std::vector interfaces = {}; + + private: + uint64_t calculateThroughput(long long currentBytes, long long previousBytes, + std::chrono::steady_clock::time_point previousTime, + std::chrono::steady_clock::time_point currentTime); +}; + +extern std::unique_ptr net; \ No newline at end of file diff --git a/src/overlay.cpp b/src/overlay.cpp index 8bafa87f..bcb96764 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -26,6 +26,7 @@ #include "fps_metrics.h" #include "intel.h" #include "msm.h" +#include "net.h" #ifdef __linux__ #include @@ -268,6 +269,7 @@ void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const stru hw_update_thread->update(¶ms, vendorID); if (fpsmetrics) fpsmetrics->update_thread(); + if (HUDElements.net) HUDElements.net->update(); sw_stats.fps = 1000000000.0 * sw_stats.n_frames_since_update / elapsed; diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp index 8ea1dab5..e6fa28a8 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -483,6 +483,7 @@ parse_fps_metrics(const char *str){ #define parse_text_color(s) parse_color(s) #define parse_media_player_color(s) parse_color(s) #define parse_wine_color(s) parse_color(s) +#define parse_network_color(s) parse_color(s) #define parse_gpu_load_color(s) parse_load_color(s) #define parse_cpu_load_color(s) parse_load_color(s) #define parse_gpu_load_value(s) parse_load_value(s) @@ -498,6 +499,7 @@ parse_fps_metrics(const char *str){ #define parse_text_outline_color(s) parse_color(s) #define parse_text_outline_thickness(s) parse_float(s) #define parse_device_battery(s) parse_str_tokenize(s) +#define parse_network(s) parse_str_tokenize(s) static bool parse_help(const char *str) @@ -752,6 +754,7 @@ static void set_param_defaults(struct overlay_params *params){ params->background_color = 0x020202; params->text_color = 0xffffff; params->media_player_color = 0xffffff; + params->network_color = 0xd66077; params->media_player_name = ""; params->font_scale = 1.0f; params->wine_color = 0xeb5b5b; @@ -869,7 +872,7 @@ parse_overlay_config(struct overlay_params *params, params->font_scale_media_player = 0.55f; // Convert from 0xRRGGBB to ImGui's format - std::array colors = { + std::array colors = { ¶ms->cpu_color, ¶ms->gpu_color, ¶ms->vram_color, @@ -892,6 +895,7 @@ parse_overlay_config(struct overlay_params *params, ¶ms->fps_color[1], ¶ms->fps_color[2], ¶ms->text_outline_color, + ¶ms->network_color, }; for (auto color : colors){ @@ -995,6 +999,8 @@ parse_overlay_config(struct overlay_params *params, mangoapp_cv.notify_one(); g_fsrSharpness = params->fsr_steam_sharpness; #endif + if (HUDElements.net) + HUDElements.net->should_reset = true; } bool parse_preset_config(int preset, struct overlay_params *params){ diff --git a/src/overlay_params.h b/src/overlay_params.h index 1a25edb1..bfebeb9c 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -161,6 +161,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_CUSTOM(text_color) \ OVERLAY_PARAM_CUSTOM(wine_color) \ OVERLAY_PARAM_CUSTOM(battery_color) \ + OVERLAY_PARAM_CUSTOM(network_color) \ OVERLAY_PARAM_CUSTOM(alpha) \ OVERLAY_PARAM_CUSTOM(log_duration) \ OVERLAY_PARAM_CUSTOM(pci_dev) \ @@ -194,6 +195,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_CUSTOM(fps_text) \ OVERLAY_PARAM_CUSTOM(device_battery) \ OVERLAY_PARAM_CUSTOM(fps_metrics) \ + OVERLAY_PARAM_CUSTOM(network) \ enum overlay_param_position { LAYER_POSITION_TOP_LEFT, @@ -266,7 +268,9 @@ struct overlay_params { enum gl_size_query gl_size_query {GL_SIZE_DRAWABLE}; bool gl_dont_flip {false}; int64_t log_duration, log_interval; - unsigned cpu_color, gpu_color, vram_color, ram_color, engine_color, io_color, frametime_color, background_color, text_color, wine_color, battery_color; + unsigned cpu_color, gpu_color, vram_color, ram_color, + engine_color, io_color, frametime_color, background_color, + text_color, wine_color, battery_color, network_color; std::vector gpu_load_color; std::vector cpu_load_color; std::vector gpu_load_value; @@ -316,6 +320,7 @@ struct overlay_params { float text_outline_thickness; std::vector device_battery; std::vector fps_metrics; + std::vector network; }; const extern char *overlay_param_names[];