i dont even know what i changed
This commit is contained in:
parent
e4e80568da
commit
d776ddf807
10 changed files with 160 additions and 95 deletions
|
@ -20,6 +20,7 @@ IndentWidth: 2
|
|||
NamespaceIndentation: All
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpacesBeforeTrailingComments: 1
|
||||
QualifierAlignment: Left
|
||||
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
|
|
|
@ -22,6 +22,7 @@ Checks: >
|
|||
-llvm-namespace-comment,
|
||||
-llvmlibc-*,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-modernize-use-nullptr,
|
||||
-readability-braces-around-statements,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-implicit-bool-conversion,
|
||||
|
|
11
.clangd
11
.clangd
|
@ -1,4 +1,9 @@
|
|||
CompileFlags:
|
||||
Add: [
|
||||
-D_HAS_CXX20_COROUTINES=1,
|
||||
-D_HAS_STD_BYTE=0
|
||||
]
|
||||
Diagnostics:
|
||||
Suppress: >
|
||||
-Wmissing-template-arg-list-after-template-kw,
|
||||
-Wctad-maybe-unsupported
|
||||
Suppress: >
|
||||
-Wmissing-template-arg-list-after-template-kw,
|
||||
-Wctad-maybe-unsupported
|
26
meson.build
26
meson.build
|
@ -6,8 +6,6 @@ project(
|
|||
'default_library=static',
|
||||
'warning_level=everything',
|
||||
'buildtype=debug',
|
||||
'cpp_args=-fvisibility=hidden',
|
||||
'cpp_std=c++latest'
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -45,6 +43,22 @@ if cpp.get_id() == 'msvc' or cpp.get_id() == 'clang-cl'
|
|||
'-DNOMINMAX',
|
||||
]
|
||||
|
||||
if cpp.get_id() == 'clang-cl'
|
||||
common_cpp_args += [
|
||||
'-std=c++26',
|
||||
'-Wno-c++20-compat',
|
||||
'-Wno-c++20-extensions',
|
||||
'-Wno-c++98-compat',
|
||||
'-Wno-c++98-compat-pedantic',
|
||||
'-Wno-disabled-macro-expansion',
|
||||
'-Wno-missing-prototypes',
|
||||
'-Wno-padded',
|
||||
'-Wno-pre-c++20-compat-pedantic',
|
||||
'-Wunused-function',
|
||||
'-fno-strict-enums',
|
||||
]
|
||||
endif
|
||||
|
||||
add_project_arguments(common_cpp_args, language: 'cpp')
|
||||
else
|
||||
common_cpp_args += [
|
||||
|
@ -63,10 +77,13 @@ else
|
|||
'-march=native',
|
||||
]
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
common_cpp_args += '-DCURL_STATICLIB'
|
||||
endif
|
||||
|
||||
add_project_arguments(cpp.get_supported_arguments(common_cpp_args), language: 'cpp')
|
||||
endif
|
||||
|
||||
|
||||
source_file_names = ['src/main.cpp', 'src/config/config.cpp', 'src/config/weather.cpp']
|
||||
|
||||
if host_machine.system() == 'linux'
|
||||
|
@ -90,6 +107,7 @@ endforeach
|
|||
|
||||
deps = [
|
||||
dependency('fmt', include_type: 'system', static: true),
|
||||
dependency('openssl', include_type: 'system', static: true, required: false),
|
||||
dependency('libcurl', include_type: 'system', static: true),
|
||||
dependency('tomlplusplus', include_type: 'system', static: true),
|
||||
]
|
||||
|
@ -143,7 +161,6 @@ if cpp.get_id() == 'msvc'
|
|||
cmake_opts.add_cmake_defines(
|
||||
{
|
||||
'CMAKE_CXX_FLAGS': '/w',
|
||||
'REFLECTCPP_USE_STD_EXPECTED': 'ON',
|
||||
},
|
||||
)
|
||||
cmake_opts.append_compile_args('cpp', '/w')
|
||||
|
@ -151,7 +168,6 @@ else
|
|||
cmake_opts.add_cmake_defines(
|
||||
{
|
||||
'CMAKE_CXX_FLAGS': '-Wno-everything -std=c++26',
|
||||
'REFLECTCPP_USE_STD_EXPECTED': 'ON',
|
||||
},
|
||||
)
|
||||
cmake_opts.append_compile_args('cpp', '-Wno-everything -std=c++26')
|
||||
|
|
|
@ -9,14 +9,18 @@ using rfl::Result;
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
inline fn GetConfigPath() -> fs::path {
|
||||
fn GetConfigPath() -> fs::path {
|
||||
#ifdef _WIN32
|
||||
const char* localAppData = std::getenv("LOCALAPPDATA");
|
||||
char* rawPtr = nullptr;
|
||||
size_t bufferSize = 0;
|
||||
|
||||
if (!localAppData)
|
||||
throw std::runtime_error("Environment variable LOCALAPPDATA is not set");
|
||||
if (_dupenv_s(&rawPtr, &bufferSize, "LOCALAPPDATA") != 0 || !rawPtr)
|
||||
throw std::runtime_error("Environment variable LOCALAPPDATA is not set or could not be accessed");
|
||||
|
||||
return fs::path(localAppData);
|
||||
// Use unique_ptr with custom deleter to handle memory automatically
|
||||
const std::unique_ptr<char, decltype(&free)> localAppData(rawPtr, free);
|
||||
fs::path path(localAppData.get());
|
||||
return path;
|
||||
#else
|
||||
const char* home = std::getenv("HOME");
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/Field.hpp>
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace {
|
|||
|
||||
DEBUG_LOG("Reading from cache file...");
|
||||
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
const std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator<char>());
|
||||
|
||||
rfl::Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content);
|
||||
if (!result)
|
||||
|
@ -66,7 +66,7 @@ namespace {
|
|||
if (!ofs.is_open())
|
||||
return std::unexpected("Failed to open temp file: "s + tempPath.string());
|
||||
|
||||
std::string json = rfl::json::write(data);
|
||||
const std::string json = rfl::json::write(data);
|
||||
ofs << json;
|
||||
|
||||
if (!ofs)
|
||||
|
@ -85,7 +85,7 @@ namespace {
|
|||
return {};
|
||||
}
|
||||
|
||||
fn WriteCallback(void* contents, size_t size, size_t nmemb, std::string* str) -> size_t {
|
||||
fn WriteCallback(void* contents, const size_t size, const size_t nmemb, std::string* str) -> size_t {
|
||||
const size_t totalSize = size * nmemb;
|
||||
str->append(static_cast<char*>(contents), totalSize);
|
||||
return totalSize;
|
||||
|
@ -130,10 +130,10 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
|
|||
using namespace std::chrono;
|
||||
|
||||
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
|
||||
const WeatherOutput& dataVal = *data;
|
||||
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||
const WeatherOutput& dataVal = *data;
|
||||
|
||||
if (cacheAge < 10min) {
|
||||
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||
cacheAge < 10min) {
|
||||
DEBUG_LOG("Using valid cache");
|
||||
return dataVal;
|
||||
}
|
||||
|
|
116
src/main.cpp
116
src/main.cpp
|
@ -14,7 +14,7 @@
|
|||
#include "ftxui/screen/color.hpp"
|
||||
#include "os/os.h"
|
||||
|
||||
constexpr const bool SHOW_ICONS = true;
|
||||
constexpr bool SHOW_ICONS = true;
|
||||
|
||||
struct BytesToGiB {
|
||||
u64 value;
|
||||
|
@ -35,8 +35,8 @@ struct fmt::formatter<BytesToGiB> : fmt::formatter<double> {
|
|||
namespace {
|
||||
fn GetDate() -> std::string {
|
||||
// Get current local time
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm localTime;
|
||||
const std::time_t now = std::time(nullptr);
|
||||
std::tm localTime;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (localtime_s(&localTime, &now) != 0)
|
||||
|
@ -133,7 +133,7 @@ namespace {
|
|||
Elements circles(16);
|
||||
|
||||
std::generate_n(circles.begin(), 16, [colorIndex = 0]() mutable {
|
||||
return hbox({ text("◯") | bold | color(Color::Palette256(colorIndex++)), text(" ") });
|
||||
return hbox({ text("◯") | bold | color(static_cast<Color::Palette256>(colorIndex++)), text(" ") });
|
||||
});
|
||||
|
||||
return hbox(circles);
|
||||
|
@ -158,30 +158,34 @@ namespace {
|
|||
musicIcon = " ";
|
||||
}
|
||||
|
||||
const Color::Palette16 labelColor = Color::Yellow;
|
||||
const Color::Palette16 valueColor = Color::White;
|
||||
const Color::Palette16 borderColor = Color::GrayLight;
|
||||
const Color::Palette16 iconColor = Color::Cyan;
|
||||
constexpr Color::Palette16 labelColor = Color::Yellow;
|
||||
constexpr Color::Palette16 valueColor = Color::White;
|
||||
constexpr Color::Palette16 borderColor = Color::GrayLight;
|
||||
constexpr Color::Palette16 iconColor = Color::Cyan;
|
||||
|
||||
Elements content;
|
||||
|
||||
content.push_back(text(" Hello " + name + "! ") | bold | color(Color::Cyan));
|
||||
content.push_back(separator() | color(borderColor));
|
||||
content.push_back(hbox({
|
||||
text(" ") | color(iconColor), // Palette icon
|
||||
CreateColorCircles(),
|
||||
}));
|
||||
content.push_back(hbox(
|
||||
{
|
||||
text(" ") | color(iconColor), // Palette icon
|
||||
CreateColorCircles(),
|
||||
}
|
||||
));
|
||||
content.push_back(separator() | color(borderColor));
|
||||
|
||||
// Helper function for aligned rows
|
||||
fn createRow = [&](const std::string& icon, const std::string& label, const std::string& value) {
|
||||
return hbox({
|
||||
text(icon) | color(iconColor),
|
||||
text(label) | color(labelColor),
|
||||
filler(),
|
||||
text(value) | color(valueColor),
|
||||
text(" "),
|
||||
});
|
||||
return hbox(
|
||||
{
|
||||
text(icon) | color(iconColor),
|
||||
text(label) | color(labelColor),
|
||||
filler(),
|
||||
text(value) | color(valueColor),
|
||||
text(" "),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// System info rows
|
||||
|
@ -192,31 +196,39 @@ namespace {
|
|||
const WeatherOutput& weatherInfo = data.weather_info.value();
|
||||
|
||||
if (weather.show_town_name)
|
||||
content.push_back(hbox({
|
||||
text(weatherIcon) | color(iconColor),
|
||||
text("Weather") | color(labelColor),
|
||||
filler(),
|
||||
content.push_back(hbox(
|
||||
{
|
||||
text(weatherIcon) | color(iconColor),
|
||||
text("Weather") | color(labelColor),
|
||||
filler(),
|
||||
|
||||
hbox({
|
||||
text(fmt::format("{}°F ", std::lround(weatherInfo.main.temp))),
|
||||
text("in "),
|
||||
text(weatherInfo.name),
|
||||
text(" "),
|
||||
}) |
|
||||
color(valueColor),
|
||||
}));
|
||||
hbox(
|
||||
{
|
||||
text(fmt::format("{}°F ", std::lround(weatherInfo.main.temp))),
|
||||
text("in "),
|
||||
text(weatherInfo.name),
|
||||
text(" "),
|
||||
}
|
||||
) |
|
||||
color(valueColor),
|
||||
}
|
||||
));
|
||||
else
|
||||
content.push_back(hbox({
|
||||
text(weatherIcon) | color(iconColor),
|
||||
text("Weather") | color(labelColor),
|
||||
filler(),
|
||||
content.push_back(hbox(
|
||||
{
|
||||
text(weatherIcon) | color(iconColor),
|
||||
text("Weather") | color(labelColor),
|
||||
filler(),
|
||||
|
||||
hbox({
|
||||
text(fmt::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
|
||||
text(" "),
|
||||
}) |
|
||||
color(valueColor),
|
||||
}));
|
||||
hbox(
|
||||
{
|
||||
text(fmt::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
|
||||
text(" "),
|
||||
}
|
||||
) |
|
||||
color(valueColor),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
content.push_back(separator() | color(borderColor));
|
||||
|
@ -262,14 +274,16 @@ namespace {
|
|||
const std::string& npText = *nowPlayingResult;
|
||||
|
||||
content.push_back(separator() | color(borderColor));
|
||||
content.push_back(hbox({
|
||||
text(musicIcon) | color(iconColor),
|
||||
text("Playing") | color(labelColor),
|
||||
text(" "),
|
||||
filler(),
|
||||
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, 30),
|
||||
text(" "),
|
||||
}));
|
||||
content.push_back(hbox(
|
||||
{
|
||||
text(musicIcon) | color(iconColor),
|
||||
text("Playing") | color(labelColor),
|
||||
text(" "),
|
||||
filler(),
|
||||
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, 30),
|
||||
text(" "),
|
||||
}
|
||||
));
|
||||
} else {
|
||||
const NowPlayingError& error = nowPlayingResult.error();
|
||||
|
||||
|
@ -300,8 +314,8 @@ namespace {
|
|||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
const Config& config = Config::getInstance();
|
||||
SystemData data = SystemData::fetchSystemData(config);
|
||||
const Config& config = Config::getInstance();
|
||||
const SystemData data = SystemData::fetchSystemData(config);
|
||||
|
||||
// Add vertical box with forced newline
|
||||
Element document = vbox({ hbox({ SystemInfoBox(config, data), filler() }), text("") });
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
using RtlGetVersionPtr = NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW);
|
||||
|
||||
// NOLINTBEGIN(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||
namespace {
|
||||
fn GetRegistryValue(const HKEY& hKey, const string& subKey, const string& valueName) -> string {
|
||||
HKEY key = nullptr;
|
||||
|
@ -52,9 +53,10 @@ namespace {
|
|||
// Add these function implementations
|
||||
fn GetRunningProcesses() -> std::vector<string> {
|
||||
std::vector<string> processes;
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
// ReSharper disable once CppLocalVariableMayBeConst
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE) // NOLINT(*-no-int-to-ptr)
|
||||
return processes;
|
||||
|
||||
PROCESSENTRY32 pe32;
|
||||
|
@ -78,6 +80,7 @@ namespace {
|
|||
}
|
||||
|
||||
fn GetParentProcessId(DWORD pid) -> DWORD {
|
||||
// ReSharper disable once CppLocalVariableMayBeConst
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
@ -101,7 +104,8 @@ namespace {
|
|||
return parentPid;
|
||||
}
|
||||
|
||||
fn GetProcessName(DWORD pid) -> string {
|
||||
fn GetProcessName(const DWORD pid) -> string {
|
||||
// ReSharper disable once CppLocalVariableMayBeConst
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
return "";
|
||||
|
@ -113,8 +117,8 @@ namespace {
|
|||
if (Process32First(hSnapshot, &pe32)) {
|
||||
while (true) {
|
||||
if (pe32.th32ProcessID == pid) {
|
||||
// Explicitly cast array to string to avoid implicit array decay
|
||||
processName = string(reinterpret_cast<const char*>(pe32.szExeFile));
|
||||
// ReSharper disable once CppRedundantCastExpression
|
||||
processName = string(static_cast<const char*>(pe32.szExeFile));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -130,9 +134,10 @@ namespace {
|
|||
fn GetMemInfo() -> expected<u64, string> {
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||
if (!GlobalMemoryStatusEx(&memInfo)) {
|
||||
|
||||
if (!GlobalMemoryStatusEx(&memInfo))
|
||||
return std::unexpected("Failed to get memory status");
|
||||
}
|
||||
|
||||
return memInfo.ullTotalPhys;
|
||||
}
|
||||
|
||||
|
@ -206,11 +211,10 @@ fn GetHost() -> string {
|
|||
|
||||
fn GetKernelVersion() -> string {
|
||||
std::stringstream versionStream;
|
||||
HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll");
|
||||
|
||||
if (ntdllHandle) {
|
||||
auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"));
|
||||
if (rtlGetVersion) {
|
||||
// ReSharper disable once CppLocalVariableMayBeConst
|
||||
if (HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) {
|
||||
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
|
||||
RTL_OSVERSIONINFOW osInfo = {};
|
||||
|
||||
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
|
||||
|
@ -300,19 +304,28 @@ fn GetDesktopEnvironment() -> optional<string> {
|
|||
|
||||
fn GetShell() -> string {
|
||||
// Detect MSYS2/MinGW shells
|
||||
if (getenv("MSYSTEM")) {
|
||||
const char* shell = getenv("SHELL");
|
||||
string shellExe;
|
||||
char* msystemEnv = nullptr;
|
||||
if (_dupenv_s(&msystemEnv, nullptr, "MSYSTEM") == 0 && msystemEnv != nullptr) {
|
||||
const std::unique_ptr<char, decltype(&free)> msystemEnvGuard(msystemEnv, free);
|
||||
char* shell = nullptr;
|
||||
size_t shellLen = 0;
|
||||
_dupenv_s(&shell, &shellLen, "SHELL");
|
||||
const std::unique_ptr<char, decltype(&free)> shellGuard(shell, free);
|
||||
string shellExe;
|
||||
|
||||
// First try SHELL, then LOGINSHELL
|
||||
if (!shell || strlen(shell) == 0) {
|
||||
shell = getenv("LOGINSHELL");
|
||||
char* loginShell = nullptr;
|
||||
size_t loginShellLen = 0;
|
||||
_dupenv_s(&loginShell, &loginShellLen, "LOGINSHELL");
|
||||
const std::unique_ptr<char, decltype(&free)> loginShellGuard(loginShell, free);
|
||||
shell = loginShell;
|
||||
}
|
||||
|
||||
if (shell) {
|
||||
string shellPath = shell;
|
||||
size_t lastSlash = shellPath.find_last_of("\\/");
|
||||
shellExe = (lastSlash != string::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
||||
const string shellPath = shell;
|
||||
const size_t lastSlash = shellPath.find_last_of("\\/");
|
||||
shellExe = (lastSlash != string::npos) ? shellPath.substr(lastSlash + 1) : shellPath;
|
||||
std::ranges::transform(shellExe, shellExe.begin(), ::tolower);
|
||||
}
|
||||
|
||||
|
@ -322,7 +335,7 @@ fn GetShell() -> string {
|
|||
|
||||
while (pid != 0) {
|
||||
string processName = GetProcessName(pid);
|
||||
std::ranges::transform(processName, processName.begin(), [](unsigned char character) {
|
||||
std::ranges::transform(processName, processName.begin(), [](const u8 character) {
|
||||
return static_cast<char>(std::tolower(character));
|
||||
});
|
||||
|
||||
|
@ -380,5 +393,6 @@ fn GetDiskUsage() -> std::pair<u64, u64> {
|
|||
|
||||
return { 0, 0 };
|
||||
}
|
||||
// NOLINTEND(*-pro-type-cstyle-cast,*-no-int-to-ptr)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,15 +15,15 @@
|
|||
|
||||
namespace log_colors {
|
||||
using fmt::terminal_color;
|
||||
constexpr fmt::terminal_color debug = terminal_color::cyan, info = terminal_color::green,
|
||||
warn = terminal_color::yellow, error = terminal_color::red,
|
||||
timestamp = terminal_color::bright_white, file_info = terminal_color::bright_white;
|
||||
constexpr auto debug = terminal_color::cyan, info = terminal_color::green, warn = terminal_color::yellow,
|
||||
error = terminal_color::red, timestamp = terminal_color::bright_white,
|
||||
file_info = terminal_color::bright_white;
|
||||
}
|
||||
|
||||
enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
|
||||
|
||||
template <typename... Args>
|
||||
void LogImpl(LogLevel level, const std::source_location& loc, fmt::format_string<Args...> fmt, Args&&... args) {
|
||||
void LogImpl(const LogLevel level, const std::source_location& loc, fmt::format_string<Args...> fmt, Args&&... args) {
|
||||
const time_t now = std::time(nullptr);
|
||||
const auto [color, levelStr] = [&] {
|
||||
switch (level) {
|
||||
|
@ -38,8 +38,16 @@ void LogImpl(LogLevel level, const std::source_location& loc, fmt::format_string
|
|||
}
|
||||
}();
|
||||
|
||||
const string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
||||
const struct tm time = *std::localtime(&now);
|
||||
const string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
|
||||
std::tm time;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (localtime_s(&time, &now) != 0)
|
||||
throw std::runtime_error("localtime_s failed");
|
||||
#else
|
||||
if (localtime_r(&now, &localTime) == nullptr)
|
||||
throw std::runtime_error("localtime_r failed");
|
||||
#endif
|
||||
|
||||
// Timestamp and level
|
||||
fmt::print(fg(log_colors::timestamp), "[{:%H:%M:%S}] ", time);
|
||||
|
|
Loading…
Add table
Reference in a new issue