feat: fetch app icons from pulsestreams
This commit is contained in:
parent
5b6ef9b939
commit
ab8cf39f59
@ -16,7 +16,7 @@ if (WIN32)
|
||||
target_compile_options(soundux PRIVATE /W4)
|
||||
else()
|
||||
add_executable(soundux ${src})
|
||||
target_compile_options(soundux PRIVATE -Wall -Wextra -Werror -pedantic -Wno-unused-lambda-capture)
|
||||
target_compile_options(soundux PRIVATE -Wall -Wextra -Werror -pedantic -Wno-unused-lambda-capture -DWNCK_I_KNOW_THIS_IS_UNSTABLE)
|
||||
endif()
|
||||
|
||||
target_include_directories(soundux SYSTEM PRIVATE "lib/miniaudio")
|
||||
@ -31,7 +31,13 @@ target_link_libraries(soundux PRIVATE Threads::Threads ${CMAKE_DL_LIBS})
|
||||
if (UNIX)
|
||||
find_package(X11 REQUIRED)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
target_link_libraries(soundux PRIVATE ${X11_LIBRARIES} ${X11_Xinput_LIB})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WNCK REQUIRED libwnck-3.0)
|
||||
|
||||
add_definitions(${WNCK_CFLAGS})
|
||||
include_directories(${WNCK_LIBRARY_DIRS})
|
||||
target_link_libraries(soundux PRIVATE ${X11_LIBRARIES} ${X11_Xinput_LIB} ${WNCK_LIBRARIES})
|
||||
endif()
|
||||
if (WIN32)
|
||||
target_compile_definitions(soundux PRIVATE _CRT_SECURE_NO_WARNINGS=1 _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS=1 _UNICODE=1)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#if defined(__linux__)
|
||||
#include "../../helper/audio/linux/pulse.hpp"
|
||||
#endif
|
||||
#include "../../helper/icons/icons.hpp"
|
||||
#include "../../helper/threads/processing.hpp"
|
||||
#include "../../ui/ui.hpp"
|
||||
#include "../config/config.hpp"
|
||||
@ -18,6 +19,7 @@ namespace Soundux
|
||||
inline Objects::Audio gAudio;
|
||||
#if defined(__linux__)
|
||||
inline Objects::Pulse gPulse;
|
||||
inline Objects::IconFetcher gIcons;
|
||||
#endif
|
||||
inline Objects::Config gConfig;
|
||||
inline Objects::Hotkeys gHotKeys;
|
||||
|
@ -367,7 +367,7 @@ namespace Soundux::Objects
|
||||
{
|
||||
std::vector<PulseRecordingStream> fetchedStreams;
|
||||
static const auto recordingStreamRegex = std::regex(
|
||||
R"rgx((^.*#(\d+)$)|(Driver: (.+))|(Source: (\d+))|(.*process.*binary.* = "(.+)")|(Resample method: (.+)|(.*application.name.* = "(.+)")))rgx");
|
||||
R"rgx((^.*#(\d+)$)|(Driver: (.+))|(Source: (\d+))|(.*process.*binary.* = "(.+)")|(Resample method: (.+)|(.*application.name.* = "(.+)"))|(.*application\.process.\id\.* = "(\d+)"))rgx");
|
||||
|
||||
PulseRecordingStream stream;
|
||||
std::smatch match;
|
||||
@ -406,6 +406,10 @@ namespace Soundux::Objects
|
||||
{
|
||||
stream.application = match[12];
|
||||
}
|
||||
else if (match[14].matched)
|
||||
{
|
||||
stream.pid = std::stoi(match[14]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream)
|
||||
@ -424,7 +428,7 @@ namespace Soundux::Objects
|
||||
{
|
||||
std::vector<PulsePlaybackStream> fetchedStreams;
|
||||
static const auto playbackStreamRegex = std::regex(
|
||||
R"rgx((^.*#(\d+)$)|(Driver: (.+))|(Sink: (\d+))|(.*application\.name.* = "(.+)")|(.*process.*binary.* = "(.+)"))rgx");
|
||||
R"rgx((^.*#(\d+)$)|(Driver: (.+))|(Sink: (\d+))|(.*application\.name.* = "(.+)")|(.*process\.binary.* = "(.+)")|(.*application\.process.\id\.* = "(\d+)"))rgx");
|
||||
|
||||
PulsePlaybackStream stream;
|
||||
std::smatch match;
|
||||
@ -459,6 +463,10 @@ namespace Soundux::Objects
|
||||
{
|
||||
stream.name = match[10];
|
||||
}
|
||||
else if (match[12].matched)
|
||||
{
|
||||
stream.pid = std::stoi(match[12]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream && stream.name != "soundux")
|
||||
|
@ -15,8 +15,10 @@ namespace Soundux
|
||||
{
|
||||
std::uint32_t id;
|
||||
std::string name;
|
||||
std::int32_t pid;
|
||||
std::string driver;
|
||||
std::string source;
|
||||
std::string appIcon;
|
||||
std::string application;
|
||||
std::string resampleMethod;
|
||||
|
||||
@ -30,7 +32,9 @@ namespace Soundux
|
||||
std::uint32_t id;
|
||||
std::string sink;
|
||||
std::string name;
|
||||
std::int32_t pid;
|
||||
std::string driver;
|
||||
std::string appIcon;
|
||||
std::string application;
|
||||
|
||||
operator bool() const
|
||||
|
309
src/helper/base64/base64.cpp
Normal file
309
src/helper/base64/base64.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
base64 encoding and decoding with C++.
|
||||
More information at
|
||||
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
|
||||
|
||||
Version: 2.rc.08 (release candidate)
|
||||
|
||||
Copyright (C) 2004-2017, 2020, 2021 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
#include "base64.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
//
|
||||
// Depending on the url parameter in base64_chars, one of
|
||||
// two sets of base64 characters needs to be chosen.
|
||||
// They differ in their last two characters.
|
||||
//
|
||||
static const char *base64_chars[2] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"+/",
|
||||
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"-_"};
|
||||
|
||||
static unsigned int pos_of_char(const unsigned char chr)
|
||||
{
|
||||
//
|
||||
// Return the position of chr within base64_encode()
|
||||
//
|
||||
|
||||
if (chr >= 'A' && chr <= 'Z')
|
||||
return chr - 'A';
|
||||
else if (chr >= 'a' && chr <= 'z')
|
||||
return chr - 'a' + ('Z' - 'A') + 1;
|
||||
else if (chr >= '0' && chr <= '9')
|
||||
return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
|
||||
else if (chr == '+' || chr == '-')
|
||||
return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
|
||||
else if (chr == '/' || chr == '_')
|
||||
return 63; // Ditto for '/' and '_'
|
||||
else
|
||||
//
|
||||
// 2020-10-23: Throw std::exception rather than const char*
|
||||
//(Pablo Martin-Gomez, https://github.com/Bouska)
|
||||
//
|
||||
throw std::runtime_error("Input is not valid base64-encoded data.");
|
||||
}
|
||||
|
||||
static std::string insert_linebreaks(std::string str, size_t distance)
|
||||
{
|
||||
//
|
||||
// Provided by https://github.com/JomaCorpFX, adapted by me.
|
||||
//
|
||||
if (!str.length())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t pos = distance;
|
||||
|
||||
while (pos < str.size())
|
||||
{
|
||||
str.insert(pos, "\n");
|
||||
pos += distance + 1;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename String, unsigned int line_length> static std::string encode_with_line_breaks(String s)
|
||||
{
|
||||
return insert_linebreaks(base64_encode(s, false), line_length);
|
||||
}
|
||||
|
||||
template <typename String> static std::string encode_pem(String s)
|
||||
{
|
||||
return encode_with_line_breaks<String, 64>(s);
|
||||
}
|
||||
|
||||
template <typename String> static std::string encode_mime(String s)
|
||||
{
|
||||
return encode_with_line_breaks<String, 76>(s);
|
||||
}
|
||||
|
||||
template <typename String> static std::string encode(String s, bool url)
|
||||
{
|
||||
return base64_encode(reinterpret_cast<const unsigned char *>(s.data()), s.length(), url);
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url)
|
||||
{
|
||||
|
||||
size_t len_encoded = (in_len + 2) / 3 * 4;
|
||||
|
||||
unsigned char trailing_char = url ? '.' : '=';
|
||||
|
||||
//
|
||||
// Choose set of base64 characters. They differ
|
||||
// for the last two positions, depending on the url
|
||||
// parameter.
|
||||
// A bool (as is the parameter url) is guaranteed
|
||||
// to evaluate to either 0 or 1 in C++ therefore,
|
||||
// the correct character set is chosen by subscripting
|
||||
// base64_chars with url.
|
||||
//
|
||||
const char *base64_chars_ = base64_chars[url];
|
||||
|
||||
std::string ret;
|
||||
ret.reserve(len_encoded);
|
||||
|
||||
unsigned int pos = 0;
|
||||
|
||||
while (pos < in_len)
|
||||
{
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
|
||||
|
||||
if (pos + 1 < in_len)
|
||||
{
|
||||
ret.push_back(
|
||||
base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
|
||||
|
||||
if (pos + 2 < in_len)
|
||||
{
|
||||
ret.push_back(
|
||||
base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
|
||||
ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & 0x3f]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
|
||||
ret.push_back(trailing_char);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
|
||||
ret.push_back(trailing_char);
|
||||
ret.push_back(trailing_char);
|
||||
}
|
||||
|
||||
pos += 3;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename String> static std::string decode(String encoded_string, bool remove_linebreaks)
|
||||
{
|
||||
//
|
||||
// decode(…) is templated so that it can be used with String = const std::string&
|
||||
// or std::string_view (requires at least C++17)
|
||||
//
|
||||
|
||||
if (encoded_string.empty())
|
||||
return std::string();
|
||||
|
||||
if (remove_linebreaks)
|
||||
{
|
||||
|
||||
std::string copy(encoded_string);
|
||||
|
||||
copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
|
||||
|
||||
return base64_decode(copy, false);
|
||||
}
|
||||
|
||||
size_t length_of_string = encoded_string.length();
|
||||
size_t pos = 0;
|
||||
|
||||
//
|
||||
// The approximate length (bytes) of the decoded string might be one or
|
||||
// two bytes smaller, depending on the amount of trailing equal signs
|
||||
// in the encoded string. This approximation is needed to reserve
|
||||
// enough space in the string to be returned.
|
||||
//
|
||||
size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
|
||||
std::string ret;
|
||||
ret.reserve(approx_length_of_decoded_string);
|
||||
|
||||
while (pos < length_of_string)
|
||||
{
|
||||
//
|
||||
// Iterate over encoded input string in chunks. The size of all
|
||||
// chunks except the last one is 4 bytes.
|
||||
//
|
||||
// The last chunk might be padded with equal signs or dots
|
||||
// in order to make it 4 bytes in size as well, but this
|
||||
// is not required as per RFC 2045.
|
||||
//
|
||||
// All chunks except the last one produce three output bytes.
|
||||
//
|
||||
// The last chunk produces at least one and up to three bytes.
|
||||
//
|
||||
|
||||
size_t pos_of_char_1 = pos_of_char(encoded_string[pos + 1]);
|
||||
|
||||
//
|
||||
// Emit the first output byte that is produced in each chunk:
|
||||
//
|
||||
ret.push_back(static_cast<std::string::value_type>(((pos_of_char(encoded_string[pos + 0])) << 2) +
|
||||
((pos_of_char_1 & 0x30) >> 4)));
|
||||
|
||||
if ((pos + 2 <
|
||||
length_of_string) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045)
|
||||
encoded_string[pos + 2] != '=' &&
|
||||
encoded_string[pos + 2] != '.' // accept URL-safe base 64 strings, too, so check for '.' also.
|
||||
)
|
||||
{
|
||||
//
|
||||
// Emit a chunk's second byte (which might not be produced in the last chunk).
|
||||
//
|
||||
unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos + 2]);
|
||||
ret.push_back(
|
||||
static_cast<std::string::value_type>(((pos_of_char_1 & 0x0f) << 4) + ((pos_of_char_2 & 0x3c) >> 2)));
|
||||
|
||||
if ((pos + 3 < length_of_string) && encoded_string[pos + 3] != '=' && encoded_string[pos + 3] != '.')
|
||||
{
|
||||
//
|
||||
// Emit a chunk's third byte (which might not be produced in the last chunk).
|
||||
//
|
||||
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_2 & 0x03) << 6) +
|
||||
pos_of_char(encoded_string[pos + 3])));
|
||||
}
|
||||
}
|
||||
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const &s, bool remove_linebreaks)
|
||||
{
|
||||
return decode(s, remove_linebreaks);
|
||||
}
|
||||
|
||||
std::string base64_encode(std::string const &s, bool url)
|
||||
{
|
||||
return encode(s, url);
|
||||
}
|
||||
|
||||
std::string base64_encode_pem(std::string const &s)
|
||||
{
|
||||
return encode_pem(s);
|
||||
}
|
||||
|
||||
std::string base64_encode_mime(std::string const &s)
|
||||
{
|
||||
return encode_mime(s);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
//
|
||||
// Interface with std::string_view rather than const std::string&
|
||||
// Requires C++17
|
||||
// Provided by Yannic Bonenberger (https://github.com/Yannic)
|
||||
//
|
||||
|
||||
std::string base64_encode(std::string_view s, bool url)
|
||||
{
|
||||
return encode(s, url);
|
||||
}
|
||||
|
||||
std::string base64_encode_pem(std::string_view s)
|
||||
{
|
||||
return encode_pem(s);
|
||||
}
|
||||
|
||||
std::string base64_encode_mime(std::string_view s)
|
||||
{
|
||||
return encode_mime(s);
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string_view s, bool remove_linebreaks)
|
||||
{
|
||||
return decode(s, remove_linebreaks);
|
||||
}
|
||||
|
||||
#endif // __cplusplus >= 201703L
|
35
src/helper/base64/base64.hpp
Normal file
35
src/helper/base64/base64.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// base64 encoding and decoding with C++.
|
||||
// Version: 2.rc.08 (release candidate)
|
||||
//
|
||||
|
||||
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
|
||||
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
|
||||
|
||||
#include <string>
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#include <string_view>
|
||||
#endif // __cplusplus >= 201703L
|
||||
|
||||
std::string base64_encode(std::string const &s, bool url = false);
|
||||
std::string base64_encode_pem(std::string const &s);
|
||||
std::string base64_encode_mime(std::string const &s);
|
||||
|
||||
std::string base64_decode(std::string const &s, bool remove_linebreaks = false);
|
||||
std::string base64_encode(unsigned char const *, size_t len, bool url = false);
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
//
|
||||
// Interface with std::string_view rather than const std::string&
|
||||
// Requires C++17
|
||||
// Provided by Yannic Bonenberger (https://github.com/Yannic)
|
||||
//
|
||||
std::string base64_encode(std::string_view s, bool url = false);
|
||||
std::string base64_encode_pem(std::string_view s);
|
||||
std::string base64_encode_mime(std::string_view s);
|
||||
|
||||
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
||||
#endif // __cplusplus >= 201703L
|
||||
|
||||
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
77
src/helper/icons/icons.cpp
Normal file
77
src/helper/icons/icons.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#if defined(__linux__)
|
||||
#include "icons.hpp"
|
||||
#include "../base64/base64.hpp"
|
||||
#include "../misc/misc.hpp"
|
||||
#include <fancy.hpp>
|
||||
#include <optional>
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
||||
void IconFetcher::setup()
|
||||
{
|
||||
gdk_init(nullptr, nullptr);
|
||||
screen = wnck_screen_get_default();
|
||||
if (!screen)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Failed to get default screen!" << std::endl;
|
||||
}
|
||||
}
|
||||
std::optional<std::string> IconFetcher::getIcon(int pid, bool recursive)
|
||||
{
|
||||
if (recursive)
|
||||
{
|
||||
auto parentProcess = Helpers::getPpid(pid);
|
||||
if (parentProcess)
|
||||
{
|
||||
auto recursiveResult = getIcon(*parentProcess, false);
|
||||
if (recursiveResult)
|
||||
{
|
||||
return recursiveResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cache.find(pid) != cache.end())
|
||||
{
|
||||
return cache.at(pid);
|
||||
}
|
||||
|
||||
wnck_screen_force_update(screen);
|
||||
auto *windows = wnck_screen_get_windows(screen);
|
||||
|
||||
for (auto *item = windows; item != nullptr; item = item->next)
|
||||
{
|
||||
auto *window = reinterpret_cast<WnckWindow *>(item->data);
|
||||
auto _pid = wnck_window_get_pid(window);
|
||||
|
||||
if (pid == _pid)
|
||||
{
|
||||
auto *icon = wnck_window_get_icon(window);
|
||||
|
||||
gsize size = 2048;
|
||||
auto *iconBuff = new gchar[2048];
|
||||
|
||||
GError *error = nullptr;
|
||||
if (gdk_pixbuf_save_to_buffer(icon, &iconBuff, &size, "png", &error, NULL) != TRUE)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Failed to save icon to buffer, error: " << error->message
|
||||
<< "(" << error->code << ")" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto base64 = base64_encode(reinterpret_cast<const unsigned char *>(iconBuff), 2048, false);
|
||||
delete[] iconBuff;
|
||||
|
||||
if (cache.find(pid) == cache.end())
|
||||
{
|
||||
cache.insert({pid, base64});
|
||||
}
|
||||
|
||||
return base64;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Could not find proccess with id " >> pid << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace Soundux::Objects
|
||||
#endif
|
23
src/helper/icons/icons.hpp
Normal file
23
src/helper/icons/icons.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#if defined(__linux__)
|
||||
#pragma once
|
||||
#include <libwnck-3.0/libwnck/libwnck.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
class IconFetcher
|
||||
{
|
||||
WnckScreen *screen;
|
||||
std::map<int, std::string> cache;
|
||||
|
||||
public:
|
||||
void setup();
|
||||
std::optional<std::string> getIcon(int pid, bool recursive = true);
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
||||
#endif
|
@ -153,18 +153,22 @@ namespace nlohmann
|
||||
static void to_json(json &j, const Soundux::Objects::PulseRecordingStream &obj)
|
||||
{
|
||||
j = {{"id", obj.id},
|
||||
{"pid", obj.pid},
|
||||
{"name", obj.name},
|
||||
{"driver", obj.driver},
|
||||
{"source", obj.source},
|
||||
{"appIcon", obj.appIcon},
|
||||
{"application", obj.application},
|
||||
{"resampleMethod", obj.resampleMethod}};
|
||||
}
|
||||
static void from_json(const json &j, Soundux::Objects::PulseRecordingStream &obj)
|
||||
{
|
||||
j.at("id").get_to(obj.id);
|
||||
j.at("pid").get_to(obj.pid);
|
||||
j.at("name").get_to(obj.name);
|
||||
j.at("driver").get_to(obj.driver);
|
||||
j.at("source").get_to(obj.source);
|
||||
j.at("appIcon").get_to(obj.appIcon);
|
||||
j.at("application").get_to(obj.application);
|
||||
j.at("resampleMethod").get_to(obj.resampleMethod);
|
||||
}
|
||||
@ -174,17 +178,21 @@ namespace nlohmann
|
||||
static void to_json(json &j, const Soundux::Objects::PulsePlaybackStream &obj)
|
||||
{
|
||||
j = {{"id", obj.id},
|
||||
{"pid", obj.pid},
|
||||
{"name", obj.name},
|
||||
{"sink", obj.sink},
|
||||
{"driver", obj.driver},
|
||||
{"appIcon", obj.appIcon},
|
||||
{"application", obj.application}};
|
||||
}
|
||||
static void from_json(const json &j, Soundux::Objects::PulsePlaybackStream &obj)
|
||||
{
|
||||
j.at("id").get_to(obj.id);
|
||||
j.at("pid").get_to(obj.pid);
|
||||
j.at("name").get_to(obj.name);
|
||||
j.at("sink").get_to(obj.sink);
|
||||
j.at("driver").get_to(obj.driver);
|
||||
j.at("appIcon").get_to(obj.appIcon);
|
||||
j.at("application").get_to(obj.application);
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,10 @@
|
||||
#include "misc.hpp"
|
||||
#include <array>
|
||||
#include <fancy.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -50,5 +55,37 @@ namespace Soundux::Helpers
|
||||
|
||||
return pclose(pipe) == 0;
|
||||
}
|
||||
std::optional<int> getPpid(int pid)
|
||||
{
|
||||
std::filesystem::path path("/proc/" + std::to_string(pid));
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
auto statusFile = path / "status";
|
||||
if (std::filesystem::exists(statusFile) && std::filesystem::is_regular_file(statusFile))
|
||||
{
|
||||
static const std::regex pidRegex(R"(PPid:(\ +|\t)(\d+))");
|
||||
std::ifstream statusStream(statusFile);
|
||||
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
while (std::getline(statusStream, line))
|
||||
{
|
||||
if (std::regex_search(line, match, pidRegex))
|
||||
{
|
||||
if (match[2].matched)
|
||||
{
|
||||
return std::stoi(match[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << ", process does not exist" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
} // namespace Soundux::Helpers
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -10,6 +11,7 @@ namespace Soundux
|
||||
std::wstring widen(const std::string &s);
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
std::optional<int> getPpid(int pid);
|
||||
bool exec(const std::string &command, std::string &result);
|
||||
#endif
|
||||
std::vector<std::string> splitByNewLine(const std::string &str);
|
||||
|
@ -40,6 +40,8 @@ int main()
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
Soundux::Globals::gIcons.setup();
|
||||
|
||||
if (!Soundux::Globals::gPulse.isSwitchOnConnectLoaded())
|
||||
{
|
||||
Soundux::Globals::gPulse.setup();
|
||||
|
@ -551,12 +551,17 @@ namespace Soundux::Objects
|
||||
//* duplicates here.
|
||||
auto streams = Globals::gPulse.getRecordingStreams();
|
||||
std::vector<PulseRecordingStream> uniqueStreams;
|
||||
for (const auto &stream : streams)
|
||||
for (auto &stream : streams)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream.name == _stream.name; });
|
||||
if (item == std::end(uniqueStreams))
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(stream.pid);
|
||||
if (icon)
|
||||
{
|
||||
stream.appIcon = *icon;
|
||||
}
|
||||
uniqueStreams.emplace_back(stream);
|
||||
}
|
||||
}
|
||||
@ -567,12 +572,17 @@ namespace Soundux::Objects
|
||||
{
|
||||
auto streams = Globals::gPulse.getPlaybackStreams();
|
||||
std::vector<PulsePlaybackStream> uniqueStreams;
|
||||
for (const auto &stream : streams)
|
||||
for (auto &stream : streams)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream.name == _stream.name; });
|
||||
if (item == std::end(uniqueStreams))
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(stream.pid);
|
||||
if (icon)
|
||||
{
|
||||
stream.appIcon = *icon;
|
||||
}
|
||||
uniqueStreams.emplace_back(stream);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user