Compare commits

..

No commits in common. "60f2c07a6ac747edf984775eeab479fb374ab3e0" and "f27d178b787f39c1109c6a100fc6f3762f997749" have entirely different histories.

21 changed files with 352 additions and 627 deletions

View File

@ -20,8 +20,7 @@ if (NOT TARGET SDL3::SDL3)
#GIT_TAG 9651ca59187c16079846918483c40d6b5c2f454c # tip 09-06-2024
#GIT_TAG 657c0135b1ff1685afa1bad63b0417d92f4bcb46 # tip 09-06-2024
#GIT_TAG d65a8146b950abe31b4fbf779f3e2fea731af9bd # tip 16-07-2024 - before string policy changes breaking imgui
#GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024 - broke bmp surface (before too)
GIT_TAG 67b973b5fad633b3be76d4daf4fd9fece292c25f # tip 29-07-2024
GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024
FIND_PACKAGE_ARGS # for the future
)

View File

@ -19,8 +19,7 @@ if (NOT TARGET SDL3_image::SDL3_image)
#GIT_TAG a34ccf16f961e6d5a480045eb650fc3dddb4bfaa # tip 14-05-2024
#GIT_TAG 2fc5310a9a2700fc856663200f94edebeb5e554a # tip 28-05-2024
#GIT_TAG 8eff782fa33d795c9ea1ac42dbe7e17cc9874c78 # tip 09-06-2024
#GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
GIT_TAG 2a27018eda394a4e005cd8ba6bb3bfd0298809c7 # tip 29-07-2024
GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
FIND_PACKAGE_ARGS # for the future
)
FetchContent_MakeAvailable(SDL3_image)

@ -1 +1 @@
Subproject commit 9728f71c9833baa65995e19e993d3450da750c20
Subproject commit f1dd5107f820fe86cb6b6b9ca22d42e0a3a3cf30

@ -1 +1 @@
Subproject commit 2801fc21fbd6f69479f6638ab1725d00238698f8
Subproject commit 4605d64df28c45096cef7748d5c143e942aefeb1

@ -1 +1 @@
Subproject commit 1a3d9dd1870b1f45e252ff636adfd0c1f0ccf521
Subproject commit 676e50c61aa7dd816dca846fd06493d2e3ae4aab

16
flake.lock generated
View File

@ -63,34 +63,34 @@
"sdl3": {
"flake": false,
"locked": {
"lastModified": 1722282731,
"narHash": "sha256-02y7JB23xAehjqBp6hj7ExNkD06XkFE8Odxnu0hYx8k=",
"lastModified": 1721156595,
"narHash": "sha256-BamJ5xK2uEHv1Q5AM2aNYk+ZDsrKCVsyZ4ium7pTTis=",
"owner": "libsdl-org",
"repo": "SDL",
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f",
"rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
"type": "github"
},
"original": {
"owner": "libsdl-org",
"repo": "SDL",
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f",
"rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
"type": "github"
}
},
"sdl3_image": {
"flake": false,
"locked": {
"lastModified": 1722000822,
"narHash": "sha256-h2vyWcGdLPbg42M6bgJAzskXJfeq9UYTMHi1XdP/otk=",
"lastModified": 1721253495,
"narHash": "sha256-mZjO5Tfu6TyGGC/2AduzOqKHW9NdH4oSzyVGjipW5bQ=",
"owner": "libsdl-org",
"repo": "SDL_image",
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7",
"rev": "8abc07df88cc035997e797967ac2f479b0e50981",
"type": "github"
},
"original": {
"owner": "libsdl-org",
"repo": "SDL_image",
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7",
"rev": "8abc07df88cc035997e797967ac2f479b0e50981",
"type": "github"
}
},

View File

@ -11,11 +11,11 @@
flake = false;
};
sdl3 = {
url = "github:libsdl-org/SDL/67b973b5fad633b3be76d4daf4fd9fece292c25f"; # keep in sync this cmake
url = "github:libsdl-org/SDL/d65a8146b950abe31b4fbf779f3e2fea731af9bd"; # keep in sync this cmake
flake = false;
};
sdl3_image = {
url = "github:libsdl-org/SDL_image/2a27018eda394a4e005cd8ba6bb3bfd0298809c7";
url = "github:libsdl-org/SDL_image/8abc07df88cc035997e797967ac2f479b0e50981";
flake = false;
};
};

View File

@ -1,11 +1,7 @@
#include "./chat_gui4.hpp"
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/msg_components.hpp>
#include <solanaceae/tox_messages/obj_components.hpp>
#include <solanaceae/object_store/meta_components_file.hpp>
#include <solanaceae/tox_messages/components.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/util/utils.hpp>
@ -195,14 +191,13 @@ void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared
ChatGui4::ChatGui4(
ConfigModelI& conf,
ObjectStore2& os,
RegistryMessageModel& rmm,
Contact3Registry& cr,
TextureUploaderI& tu,
ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc,
Theme& theme
) : _conf(conf), _os(os), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) {
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) {
}
ChatGui4::~ChatGui4(void) {
@ -516,7 +511,7 @@ float ChatGui4::render(float time_delta) {
ImGui::TableNextColumn();
if (msg_reg.all_of<Message::Components::MessageText>(e)) {
renderMessageBodyText(msg_reg, e);
} else if (msg_reg.any_of<Message::Components::MessageFileObject>(e)) {
} else if (msg_reg.any_of<Message::Components::Transfer::FileInfo>(e)) { // add more comps?
renderMessageBodyFile(msg_reg, e);
} else {
ImGui::TextDisabled("---");
@ -834,9 +829,8 @@ float ChatGui4::render(float time_delta) {
}
void ChatGui4::sendFilePath(const char* file_path) {
const auto path = std::filesystem::path(file_path);
if (_selected_contact && std::filesystem::is_regular_file(path)) {
_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
if (_selected_contact && std::filesystem::is_regular_file(file_path)) {
_rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().generic_u8string(), file_path);
}
}
@ -946,12 +940,11 @@ void ChatGui4::renderMessageBodyText(Message3Registry& reg, const Message3 e) {
}
void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
auto o = reg.get<Message::Components::MessageFileObject>(e).o;
if (
!_show_chat_avatar_tf
&& (
o.all_of<ObjComp::Tox::FileKind>()
&& o.get<ObjComp::Tox::FileKind>().kind == 1
reg.all_of<Message::Components::Transfer::FileKind>(e)
&& reg.get<Message::Components::Transfer::FileKind>(e).kind == 1
)
) {
// TODO: this looks ugly
@ -959,8 +952,6 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
return;
}
ImGui::BeginGroup();
#if 0
if (msg_reg.all_of<Components::TransferState>(e)) {
switch (msg_reg.get<Components::TransferState>(e).state) {
@ -974,24 +965,51 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
}
#endif
// TODO: better way to display state
if (o.all_of<ObjComp::Ephemeral::File::TagTransferPaused>()) {
if (reg.all_of<Message::Components::Transfer::TagPaused>(e)) {
ImGui::TextUnformatted("paused");
//} else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) {
// ImGui::TextUnformatted("done");
} else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) {
ImGui::TextUnformatted("done");
} else {
// TODO: missing other states
ImGui::TextUnformatted("running");
}
if (reg.all_of<Message::Components::Transfer::TagHaveAll, Message::Components::Transfer::FileInfoLocal>(e)) {
// hack lul
ImGui::SameLine();
if (ImGui::SmallButton("forward")) {
ImGui::OpenPopup("forward to contact");
}
if (ImGui::BeginPopup("forward to contact")) {
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
// filter
if (_cr.any_of<Contact::Components::RequestIncoming, Contact::Components::TagRequestOutgoing>(c)) {
continue;
}
// TODO: check for contact capability
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 1, false, true, false)) {
//if (renderContactListContactSmall(c, false)) {
//_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
const auto& fil = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
for (const auto& path : fil.file_list) {
_rmm.sendFilePath(c, std::filesystem::path{path}.filename().generic_u8string(), path);
}
}
}
ImGui::EndPopup();
}
}
// if in offered state
// paused, never started
if (
!o.all_of<ObjComp::F::TagLocalHaveAll>() &&
//reg.all_of<Message::Components::Transfer::TagReceiving>(e) &&
o.all_of<ObjComp::Ephemeral::File::TagTransferPaused>() &&
reg.all_of<Message::Components::Transfer::TagReceiving>(e) &&
reg.all_of<Message::Components::Transfer::TagPaused>(e) &&
// TODO: how does restarting a broken/incomplete transfer look like?
!o.all_of<ObjComp::F::SingleInfoLocal>() &&
!o.all_of<ObjComp::Ephemeral::File::ActionTransferAccept>()
!reg.all_of<Message::Components::Transfer::FileInfoLocal>(e) &&
!reg.all_of<Message::Components::Transfer::ActionAccept>(e)
) {
if (ImGui::Button("save to")) {
_fss.requestFile(
@ -1000,13 +1018,11 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
path.remove_filename();
return std::filesystem::is_directory(path);
},
[this, o](const auto& path) {
if (static_cast<bool>(o)) { // still valid
[this, &reg, e](const auto& path) {
if (reg.valid(e)) { // still valid
// TODO: trim file?
o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path.generic_u8string());
//_rmm.throwEventUpdate(reg, e);
// TODO: block recursion
_os.throwEventUpdate(o);
reg.emplace<Message::Components::Transfer::ActionAccept>(e, path.string());
_rmm.throwEventUpdate(reg, e);
}
},
[](){}
@ -1014,75 +1030,97 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
}
}
// hacky
const auto* fts = o.try_get<ObjComp::Ephemeral::File::TransferStats>();
if (fts != nullptr && o.any_of<ObjComp::F::SingleInfo, ObjComp::F::CollectionInfo>()) {
const auto total_size =
o.all_of<ObjComp::F::SingleInfo>() ?
o.get<ObjComp::F::SingleInfo>().file_size :
o.get<ObjComp::F::CollectionInfo>().total_size
;
// down progress
if (reg.all_of<Message::Components::Transfer::TagReceiving>(e)) {
ImGui::TextUnformatted("down");
if (reg.all_of<Message::Components::Transfer::BytesReceived>(e)) {
ImGui::SameLine();
uint64_t total {0u};
float rate {0.f};
if (o.all_of<ObjComp::F::TagLocalHaveAll>() && fts->total_down <= 0) {
// if have all AND no dl -> show upload progress
ImGui::TextUnformatted(" up");
total = fts->total_up;
rate = fts->rate_up;
} else {
// else show download progress
ImGui::TextUnformatted("down");
total = fts->total_down;
rate = fts->rate_down;
}
ImGui::SameLine();
float fraction = float(reg.get<Message::Components::Transfer::BytesReceived>(e).total) / reg.get<Message::Components::Transfer::FileInfo>(e).total_size;
float fraction = float(total) / total_size;
char overlay_buf[32];
if (rate > 0.000001f) {
const char* byte_suffix = "???";
int64_t byte_divider = sizeToHumanReadable(rate, byte_suffix);
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%% @ %.1f%s/s", fraction * 100 + 0.01f, rate/byte_divider, byte_suffix);
} else {
char overlay_buf[32];
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f);
ImGui::ProgressBar(
fraction,
{-FLT_MIN, TEXT_BASE_HEIGHT},
overlay_buf
);
// TODO: numbers
}
}
// (can be both)
// up progess
if (reg.all_of<Message::Components::Transfer::TagSending>(e)) {
ImGui::TextUnformatted(" up");
if (reg.all_of<Message::Components::Transfer::BytesSent>(e)) {
ImGui::SameLine();
float fraction = float(reg.get<Message::Components::Transfer::BytesSent>(e).total) / reg.get<Message::Components::Transfer::FileInfo>(e).total_size;
char overlay_buf[32];
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f);
ImGui::ProgressBar(
fraction,
{-FLT_MIN, TEXT_BASE_HEIGHT},
overlay_buf
);
// TODO: numbers
}
}
const auto file_list = reg.get<Message::Components::Transfer::FileInfo>(e).file_list;
// if has local, display save base path?
for (size_t i = 0; i < file_list.size(); i++) {
ImGui::PushID(i);
const char* byte_suffix = "???";
int64_t byte_divider = sizeToHumanReadable(file_list[i].file_size, byte_suffix);
// TODO: selectable text widget ?
ImGui::Bullet(); ImGui::Text("%s (%.2lf %s)", file_list[i].file_name.c_str(), double(file_list[i].file_size)/byte_divider, byte_suffix);
if (ImGui::BeginItemTooltip()) {
ImGui::Text("TODO: file path?");
ImGui::Text("%lu bytes", file_list[i].file_size);
ImGui::EndTooltip();
}
ImGui::ProgressBar(
fraction,
{-FLT_MIN, TEXT_BASE_HEIGHT},
overlay_buf
);
} else {
// infinite scrolling progressbar fallback
ImGui::TextUnformatted(" ??");
ImGui::SameLine();
ImGui::ProgressBar(
-0.333f * ImGui::GetTime(),
{-FLT_MIN, TEXT_BASE_HEIGHT},
"?%"
);
if (reg.all_of<Message::Components::Transfer::FileInfoLocal>(e)) {
const auto& local_info = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
if (local_info.file_list.size() > i && ImGui::BeginPopupContextItem("##file_c")) {
if (ImGui::MenuItem("open")) {
const std::string url {file_path_to_file_url(local_info.file_list.at(i))};
std::cout << "opening file '" << url << "'\n";
SDL_OpenURL(url.c_str());
}
if (ImGui::MenuItem("copy file")) {
const std::string url {file_path_to_file_url(local_info.file_list.at(i))};
//ImGui::SetClipboardText(url.c_str());
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
}
if (ImGui::MenuItem("copy filepath")) {
const auto file_path = std::filesystem::canonical(local_info.file_list.at(i)).u8string(); //TODO: use generic over native?
ImGui::SetClipboardText(file_path.c_str());
}
ImGui::EndPopup();
}
}
ImGui::PopID();
}
if (!o.all_of<ObjComp::F::TagLocalHaveAll>() && o.all_of<ObjComp::F::LocalHaveBitset>()) {
// texture based on have bitset
// TODO: missing have chunks/chunksize to get the correct size
//const auto& bitest = o.get<ObjComp::F::LocalHaveBitset>().have;
// generate 1bit sdlsurface zerocopy using bitset.data() and bitset.size_bytes()
// optionally scale down filtered (would copy)
// update texture? in cache?
}
if (o.all_of<ObjComp::F::FrameDims>()) {
const auto& frame_dims = o.get<ObjComp::F::FrameDims>();
if (reg.all_of<Message::Components::FrameDims>(e)) {
const auto& frame_dims = reg.get<Message::Components::FrameDims>(e);
// TODO: config
const auto max_inline_height = 10*TEXT_BASE_HEIGHT;
float width = frame_dims.w;
float height = frame_dims.h;
float height = frame_dims.height;
float width = frame_dims.width;
if (height > max_inline_height) {
const float scale = max_inline_height / height;
@ -1091,10 +1129,13 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
}
ImVec2 orig_curser_pos = ImGui::GetCursorPos();
ImGui::Dummy(ImVec2{width, height});
// deploy dummy of framedim size and check visibility
// +2 for border
ImGui::Dummy(ImVec2{width+2, height+2});
if (ImGui::IsItemVisible() && o.all_of<ObjComp::F::TagLocalHaveAll, ObjComp::F::SingleInfo, ObjComp::Ephemeral::Backend>()) {
bool image_preview_visible = ImGui::IsItemVisible();
if (image_preview_visible && file_list.size() == 1 && reg.all_of<Message::Components::Transfer::FileInfoLocal>(e)) {
ImGui::SetCursorPos(orig_curser_pos); // reset for actual img
auto [id, img_width, img_height] = _msg_tc.get(Message3Handle{reg, e});
@ -1111,152 +1152,19 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
//width *= scale;
//}
ImGui::Image(
id,
ImVec2{width, height},
{0.f, 0.f}, // default
{1.f, 1.f}, // default
{1.f, 1.f, 1.f, 1.f}, // default
{0.5f, 0.5f, 0.5f, 0.8f} // border
);
ImGui::Image(id, ImVec2{width, height});
// TODO: clickable to open in internal image viewer
}
} else if (o.all_of<ObjComp::F::SingleInfo>()) { // only show info if not inlined image
// just filename
const auto& si = o.get<ObjComp::F::SingleInfo>();
const char* byte_suffix = "???";
int64_t byte_divider = sizeToHumanReadable(si.file_size, byte_suffix);
ImGui::Text("%s (%.2lf %s)", si.file_name.c_str(), double(si.file_size)/byte_divider, byte_suffix);
} else if (o.all_of<ObjComp::F::CollectionInfo>()) {
// same old bulletpoint list
const auto& file_list = o.get<ObjComp::F::CollectionInfo>().file_list;
// if has local, display save base path?, do we have base save path?
for (size_t i = 0; i < file_list.size(); i++) {
ImGui::PushID(i);
const char* byte_suffix = "???";
int64_t byte_divider = sizeToHumanReadable(file_list[i].file_size, byte_suffix);
// TODO: selectable text widget ?
ImGui::Bullet(); ImGui::Text("%s (%.2lf %s)", file_list[i].file_name.c_str(), double(file_list[i].file_size)/byte_divider, byte_suffix);
if (o.all_of<ObjComp::F::CollectionInfoLocal>()) {
const auto& local_info = o.get<ObjComp::F::CollectionInfoLocal>();
if (local_info.file_list.size() > i && ImGui::BeginPopupContextItem("##file_c")) {
if (ImGui::MenuItem("open")) {
const std::string url {file_path_to_file_url(local_info.file_list.at(i).file_path)};
std::cout << "opening file '" << url << "'\n";
SDL_OpenURL(url.c_str());
}
if (ImGui::MenuItem("copy file")) {
const std::string url {file_path_to_file_url(local_info.file_list.at(i).file_path)};
//ImGui::SetClipboardText(url.c_str());
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
}
if (ImGui::MenuItem("copy filepath")) {
const auto file_path = std::filesystem::canonical(local_info.file_list.at(i).file_path).u8string(); //TODO: use generic over native?
ImGui::SetClipboardText(file_path.c_str());
}
ImGui::EndPopup();
}
}
ImGui::PopID();
}
} else {
ImGui::TextDisabled("neither info available");
}
ImGui::EndGroup();
if (o.all_of<ObjComp::F::SingleInfoLocal>()) {
const auto& local_info = o.get<ObjComp::F::SingleInfoLocal>();
if (!local_info.file_path.empty() && ImGui::BeginPopupContextItem("##file_c")) {
if (o.all_of<ObjComp::F::TagLocalHaveAll>()) {
if (ImGui::BeginMenu("forward")) {
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
// filter
if (_cr.any_of<Contact::Components::RequestIncoming, Contact::Components::TagRequestOutgoing>(c)) {
continue;
}
// TODO: check for contact capability
// or just error popup?/noti/toast
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 1, false, true, false)) {
// TODO: try object interface first instead, then fall back to send with SingleInfoLocal
//_rmm.sendFileObj(c, o);
std::filesystem::path path = o.get<ObjComp::F::SingleInfoLocal>().file_path;
_rmm.sendFilePath(c, path.filename().generic_u8string(), path.generic_u8string());
}
}
ImGui::EndMenu();
}
}
ImGui::Separator();
if (ImGui::MenuItem("open")) {
const std::string url {file_path_to_file_url(local_info.file_path)};
std::cout << "opening file '" << url << "'\n";
SDL_OpenURL(url.c_str());
}
if (ImGui::MenuItem("copy file")) {
const std::string url {file_path_to_file_url(local_info.file_path)};
//ImGui::SetClipboardText(url.c_str());
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
}
if (ImGui::MenuItem("copy filepath")) {
const auto file_path = std::filesystem::canonical(local_info.file_path).u8string(); //TODO: use generic over native?
ImGui::SetClipboardText(file_path.c_str());
}
ImGui::EndPopup();
}
}
// TODO: how to collections?
if (ImGui::BeginItemTooltip()) {
if (o.all_of<ObjComp::F::SingleInfo>()) {
ImGui::SeparatorText("single info");
const auto& si = o.get<ObjComp::F::SingleInfo>();
ImGui::Text("file name: '%s'", si.file_name.c_str());
ImGui::Text("file size: %lu Bytes", si.file_size);
if (o.all_of<ObjComp::F::SingleInfoLocal>()) {
ImGui::Text("local path: '%s'", o.get<ObjComp::F::SingleInfoLocal>().file_path.c_str());
}
} else if (o.all_of<ObjComp::F::CollectionInfo>()) {
ImGui::SeparatorText("collection info");
const auto& ci = o.get<ObjComp::F::CollectionInfo>();
ImGui::Text("total size: %lu Bytes", ci.total_size);
}
if (fts != nullptr) {
ImGui::SeparatorText("transfer stats");
ImGui::Text("rate up : %.1f Bytes/s", fts->rate_up);
ImGui::Text("rate down : %.1f Bytes/s", fts->rate_down);
ImGui::Text("total up : %lu Bytes", fts->total_up);
ImGui::Text("total down: %lu Bytes", fts->total_down);
}
ImGui::EndTooltip();
}
}
void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) {
if (reg.all_of<Message::Components::MessageFileObject>(e)) {
const auto o = reg.get<Message::Components::MessageFileObject>(e).o;
ImGui::TextDisabled("o:%u", entt::to_integral(o.entity()));
if (o.all_of<ObjComp::Tox::FileKind>()) {
ImGui::TextDisabled("fk:%lu", o.get<ObjComp::Tox::FileKind>().kind);
}
if (o.all_of<ObjComp::Ephemeral::ToxTransferFriend>()) {
ImGui::TextDisabled("ttf:%u", o.get<ObjComp::Ephemeral::ToxTransferFriend>().transfer_number);
}
if (reg.all_of<Message::Components::Transfer::FileKind>(e)) {
ImGui::TextDisabled("fk:%lu", reg.get<Message::Components::Transfer::FileKind>(e).kind);
}
if (reg.all_of<Message::Components::Transfer::ToxTransferFriend>(e)) {
ImGui::TextDisabled("ttf:%u", reg.get<Message::Components::Transfer::ToxTransferFriend>(e).transfer_number);
}
if (reg.all_of<Message::Components::ToxGroupMessageID>(e)) {
@ -1336,6 +1244,31 @@ bool ChatGui4::renderContactListContactSmall(const Contact3 c, const bool select
return ImGui::Selectable(label.c_str(), selected);
}
#if 0
bool ChatGui4::renderSubContactListContact(const Contact3 c, const bool selected) const {
std::string label;
if (_cr.all_of<Contact::Components::ConnectionState>(c)) {
const auto c_state = _cr.get<Contact::Components::ConnectionState>(c).state;
if (c_state == Contact::Components::ConnectionState::State::direct) {
label += "[D] ";
} else if (c_state == Contact::Components::ConnectionState::State::cloud) {
label += "[C] ";
} else {
label += "[-] ";
}
} else {
label += "[?] ";
}
label += (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name.c_str() : "<unk>");
label += "###";
label += std::to_string(entt::to_integral(c));
return ImGui::Selectable(label.c_str(), selected);
}
#endif
void ChatGui4::pasteFile(const char* mime_type) {
size_t data_size = 0;
void* data = SDL_GetClipboardData(mime_type, &data_size);

View File

@ -1,6 +1,5 @@
#pragma once
#include <solanaceae/object_store/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/util/config_model.hpp>
@ -26,7 +25,6 @@ using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoad
class ChatGui4 {
ConfigModelI& _conf;
ObjectStore2& _os;
RegistryMessageModel& _rmm;
Contact3Registry& _cr;
@ -59,7 +57,6 @@ class ChatGui4 {
public:
ChatGui4(
ConfigModelI& conf,
ObjectStore2& os,
RegistryMessageModel& rmm,
Contact3Registry& cr,
TextureUploaderI& tu,

View File

@ -3,7 +3,6 @@
#include <SDL3/SDL.h>
#include <iostream>
#include <cassert>
ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
ImageInfo res;
@ -15,9 +14,6 @@ ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t
return res;
}
assert(surf->w >= 0);
assert(surf->h >= 0);
res.width = surf->w;
res.height = surf->h;
res.file_ext = "bmp";
@ -28,48 +24,32 @@ ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t
}
ImageLoaderSDLBMP::ImageResult ImageLoaderSDLBMP::loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) {
ImageResult res;
auto* ios = SDL_IOFromConstMem(data, data_size);
SDL_Surface* surf = SDL_LoadBMP_IO(ios, SDL_TRUE);
if (surf == nullptr) {
return {};
return res;
}
SDL_Surface* conv_surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
SDL_DestroySurface(surf);
if (conv_surf == nullptr) {
return {};
return res;
}
assert(conv_surf->w >= 0);
assert(conv_surf->h >= 0);
if (conv_surf->w > 16*1024 || conv_surf->h > 10*1024) {
std::cerr << "IL_SDLBMP error: image too large\n";
return {};
}
ImageResult res;
if (SDL_MUSTLOCK(conv_surf)) {
if (SDL_LockSurface(conv_surf) < 0) {
std::cerr << "IL_SDLBMP error: " << SDL_GetError() << "\n";
SDL_DestroySurface(conv_surf);
return {};
}
}
res.width = conv_surf->w;
res.height = conv_surf->h;
res.width = surf->w;
res.height = surf->h;
res.file_ext = "bmp";
SDL_LockSurface(conv_surf);
auto& new_frame = res.frames.emplace_back();
new_frame.ms = 0;
new_frame.data = {(const uint8_t*)conv_surf->pixels, ((const uint8_t*)conv_surf->pixels) + (conv_surf->w*conv_surf->h*4)};
new_frame.data = {(const uint8_t*)conv_surf->pixels, ((const uint8_t*)conv_surf->pixels) + (surf->w*surf->h*4)};
if (SDL_MUSTLOCK(conv_surf)) {
SDL_UnlockSurface(conv_surf);
}
SDL_UnlockSurface(conv_surf);
SDL_DestroySurface(conv_surf);
std::cout << "IL_SDLBMP: loaded img " << res.width << "x" << res.height << "\n";

View File

@ -26,17 +26,17 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme
#endif
tcm(cr, tc, tc),
tmm(rmm, cr, tcm, tc, tc),
ttm(rmm, cr, tcm, tc, tc, os),
ttm(rmm, cr, tcm, tc, tc),
tffom(cr, rmm, tcm, tc, tc),
theme(theme_),
mmil(rmm),
tam(/*rmm, */ os, cr, conf),
tam(rmm, cr, conf),
sdlrtu(renderer_),
tal(cr),
contact_tc(tal, sdlrtu),
mil(),
msg_tc(mil, sdlrtu),
cg(conf, os, rmm, cr, sdlrtu, contact_tc, msg_tc, theme),
cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc, theme),
sw(conf),
osui(os),
tuiu(tc, conf),

View File

@ -7,117 +7,71 @@
#include <solanaceae/message3/components.hpp>
#include "./os_comps.hpp"
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/file/file2.hpp>
#include <limits>
#include <fstream>
#include <iostream>
void MediaMetaInfoLoader::handleMessage(const Message3Handle& m) {
if (!static_cast<bool>(m)) {
if (m.any_of<Message::Components::TagNotImage, Message::Components::FrameDims>()) {
return;
}
// move to obj
if (m.any_of<Message::Components::TagNotImage>()) {
if (!m.all_of<Message::Components::Transfer::FileInfoLocal, Message::Components::Transfer::TagHaveAll>()) {
return;
}
if (!m.all_of<Message::Components::MessageFileObject>()) {
// not a file message
return;
}
const auto& o = m.get<Message::Components::MessageFileObject>().o;
if (!static_cast<bool>(o)) {
std::cerr << "MMIL error: invalid object in file message\n";
const auto& fil = m.get<Message::Components::Transfer::FileInfoLocal>();
if (fil.file_list.size() != 1) {
return;
}
if (o.any_of<ObjComp::F::FrameDims>()) {
return;
}
std::ifstream file(fil.file_list.front(), std::ios::binary);
if (file.is_open()) {
// figure out size
file.seekg(0, file.end);
if (file.tellg() > 50*1024*1024) {
// TODO: conf
// dont try load files larger 50mb
return;
}
file.seekg(0, file.beg);
if (!o.all_of<ObjComp::F::TagLocalHaveAll>()) {
return; // we dont have all data
}
if (!o.all_of<ObjComp::Ephemeral::Backend, ObjComp::F::SingleInfo>()) {
std::cerr << "MMIL error: object missing backend/file info (?)\n";
return;
}
// TODO: handle collections
const auto file_size = o.get<ObjComp::F::SingleInfo>().file_size;
if (file_size > 50*1024*1024) {
std::cerr << "MMIL error: image file too large\n";
return;
}
if (file_size == 0) {
std::cerr << "MMIL warning: empty file\n";
return;
}
if (!o.all_of<ObjComp::F::TagLocalHaveAll>()) {
// not ready yet
return;
}
auto* file_backend = o.get<ObjComp::Ephemeral::Backend>().ptr;
if (file_backend == nullptr) {
std::cerr << "MMIL error: object backend nullptr\n";
return;
}
auto file2 = file_backend->file2(o, StorageBackendI::FILE2_READ);
if (!file2 || !file2->isGood() || !file2->can_read) {
std::cerr << "MMIL error: creating file2 from object via backendI\n";
return;
}
auto read_data = file2->read(file_size, 0);
if (read_data.ptr == nullptr) {
std::cerr << "MMIL error: reading from file2 returned nullptr\n";
return;
}
if (read_data.size != file_size) {
std::cerr << "MMIL error: reading from file2 size missmatch, should be " << file_size << ", is " << read_data.size << "\n";
return;
}
// try all loaders after another
for (auto& il : _image_loaders) {
// TODO: impl callback based load
auto res = il->loadInfoFromMemory(read_data.ptr, read_data.size);
if (res.height == 0 || res.width == 0) {
continue;
std::vector<uint8_t> tmp_buffer;
while (file.good()) {
auto ch = file.get();
if (ch == EOF) {
break;
} else {
tmp_buffer.push_back(ch);
}
}
o.emplace<ObjComp::F::FrameDims>(
static_cast<uint16_t>(std::min<uint32_t>(res.width, std::numeric_limits<uint16_t>::max())),
static_cast<uint16_t>(std::min<uint32_t>(res.height, std::numeric_limits<uint16_t>::max()))
);
bool could_load {false};
// try all loaders after another
for (auto& il : _image_loaders) {
// TODO: impl callback based load
auto res = il->loadInfoFromMemory(tmp_buffer.data(), tmp_buffer.size());
if (res.height == 0 || res.width == 0) {
continue;
}
std::cout << "MMIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
m.emplace<Message::Components::FrameDims>(res.width, res.height);
_rmm.throwEventUpdate(m);
return;
could_load = true;
std::cout << "MMIL loaded image info " << fil.file_list.front() << "\n";
_rmm.throwEventUpdate(m);
break;
}
if (!could_load) {
m.emplace<Message::Components::TagNotImage>();
std::cout << "MMIL loading failed image info " << fil.file_list.front() << "\n";
_rmm.throwEventUpdate(m);
}
}
m.emplace<Message::Components::TagNotImage>();
std::cout << "MMIL: loading failed image info o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
// TODO: update object too
// recursion
_rmm.throwEventUpdate(m);
}
MediaMetaInfoLoader::MediaMetaInfoLoader(RegistryMessageModel& rmm) : _rmm(rmm) {

View File

@ -6,6 +6,11 @@
namespace Message::Components {
struct FrameDims {
uint32_t width {0};
uint32_t height {0};
};
struct TagNotImage {};
} // Message::Components
@ -26,10 +31,5 @@ class MediaMetaInfoLoader : public RegistryMessageModelEventI {
protected: // rmm
bool onEvent(const Message::Events::MessageConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& e) override;
//protected: // os
// bool onEvent(const ObjectStore::Events::ObjectConstruct& e) override;
// should listen on update
// bool onEvent(const ObjectStore::Events::ObjectUpdate& e) override;
};

View File

@ -8,15 +8,10 @@
#include <solanaceae/message3/components.hpp>
#include "./os_comps.hpp"
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/file/file2.hpp>
#include <entt/entity/entity.hpp>
#include <iostream>
#include <fstream>
#include <cassert>
#include <vector>
// fwd
namespace Message {
@ -39,88 +34,51 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
return std::nullopt;
}
if (!m.all_of<Message::Components::MessageFileObject>()) {
// not a file message
return std::nullopt;
}
const auto& o = m.get<Message::Components::MessageFileObject>().o;
if (m.all_of<Message::Components::Transfer::FileInfoLocal>()) {
const auto& file_list = m.get<Message::Components::Transfer::FileInfoLocal>().file_list;
assert(!file_list.empty());
const auto& file_path = file_list.front();
if (!static_cast<bool>(o)) {
std::cerr << "MIL error: invalid object in file message\n";
return std::nullopt;
}
std::ifstream file(file_path, std::ios::binary);
if (file.is_open()) {
std::vector<uint8_t> tmp_buffer;
while (file.good()) {
auto ch = file.get();
if (ch == EOF) {
break;
} else {
tmp_buffer.push_back(ch);
}
}
if (!o.all_of<ObjComp::Ephemeral::Backend, ObjComp::F::SingleInfo>()) {
std::cerr << "MIL error: object missing backend (?)\n";
return std::nullopt;
}
// try all loaders after another
for (auto& il : _image_loaders) {
auto res = il->loadFromMemoryRGBA(tmp_buffer.data(), tmp_buffer.size());
if (res.frames.empty() || res.height == 0 || res.width == 0) {
continue;
}
// TODO: handle collections
const auto file_size = o.get<ObjComp::F::SingleInfo>().file_size;
TextureEntry new_entry;
new_entry.timestamp_last_rendered = Message::getTimeMS();
new_entry.current_texture = 0;
for (const auto& [ms, data] : res.frames) {
const auto n_t = tu.uploadRGBA(data.data(), res.width, res.height);
new_entry.textures.push_back(n_t);
new_entry.frame_duration.push_back(ms);
}
if (file_size > 50*1024*1024) {
std::cerr << "MIL error: image file too large\n";
return std::nullopt;
}
new_entry.width = res.width;
new_entry.height = res.height;
if (file_size == 0) {
std::cerr << "MIL warning: empty file\n";
return std::nullopt;
}
std::cout << "MIL: loaded image file " << file_path << "\n";
if (!o.all_of<ObjComp::F::TagLocalHaveAll>()) {
// not ready yet
return std::nullopt;
}
auto* file_backend = o.get<ObjComp::Ephemeral::Backend>().ptr;
if (file_backend == nullptr) {
std::cerr << "MIL error: object backend nullptr\n";
return std::nullopt;
}
auto file2 = file_backend->file2(o, StorageBackendI::FILE2_READ);
if (!file2 || !file2->isGood() || !file2->can_read) {
std::cerr << "MIL error: creating file2 from object via backendI\n";
return std::nullopt;
}
auto read_data = file2->read(file_size, 0);
if (read_data.ptr == nullptr) {
std::cerr << "MMIL error: reading from file2 returned nullptr\n";
return std::nullopt;
}
if (read_data.size != file_size) {
std::cerr << "MIL error: reading from file2 size missmatch, should be " << file_size << ", is " << read_data.size << "\n";
return std::nullopt;
}
// try all loaders after another
for (auto& il : _image_loaders) {
auto res = il->loadFromMemoryRGBA(read_data.ptr, read_data.size);
if (res.frames.empty() || res.height == 0 || res.width == 0) {
continue;
return new_entry;
}
}
TextureEntry new_entry;
new_entry.timestamp_last_rendered = Message::getTimeMS();
new_entry.current_texture = 0;
for (const auto& [ms, data] : res.frames) {
const auto n_t = tu.uploadRGBA(data.data(), res.width, res.height);
new_entry.textures.push_back(n_t);
new_entry.frame_duration.push_back(ms);
}
new_entry.width = res.width;
new_entry.height = res.height;
std::cout << "MIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
return new_entry;
}
std::cerr << "MIL error: failed to load message (unhandled format)\n";
std::cerr << "MIL: failed to load message\n";
return std::nullopt;
}

View File

@ -15,3 +15,13 @@ class MessageImageLoader {
std::optional<TextureEntry> load(TextureUploaderI& tu, Message3Handle c);
};
// TODO: move to rmm
template<>
struct std::hash<Message3Handle> {
std::size_t operator()(Message3Handle const& m) const noexcept {
const std::size_t h1 = reinterpret_cast<std::size_t>(m.registry());
const std::size_t h2 = entt::to_integral(m.entity());
return (h1 << 3) ^ (h2 * 11400714819323198485llu);
}
};

View File

@ -2,25 +2,20 @@
#include <solanaceae/util/utils.hpp>
#include <solanaceae/object_store/meta_components.hpp>
#include <solanaceae/object_store/meta_components_file.hpp>
#include "./os_comps.hpp"
#include <imgui/imgui.h>
#include <solanaceae/message3/components.hpp>
namespace MM {
template<> void ComponentEditorWidget<ObjectStore::Components::ID>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjectStore::Components::ID>(entity);
const auto str = bin2hex(c.v);
if (ImGui::SmallButton("copy")) {
ImGui::SetClipboardText(str.c_str());
}
ImGui::SameLine();
ImGui::TextUnformatted(str.c_str());
}
#if 0
template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfo>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::FileInfo>(entity);
@ -59,30 +54,14 @@ template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfoLoc
}
}
#endif
template<> void ComponentEditorWidget<ObjComp::F::SingleInfo>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjComp::F::SingleInfo>(entity);
if (!c.file_name.empty()) {
ImGui::Text("file name: %s", c.file_name.c_str());
}
ImGui::Text("file size: %lu", c.file_size);
template<> void ComponentEditorWidget<Message::Components::Transfer::BytesReceived>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::BytesReceived>(entity);
ImGui::Text("total bytes received: %lu", c.total);
}
template<> void ComponentEditorWidget<ObjComp::F::SingleInfoLocal>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjComp::F::SingleInfoLocal>(entity);
if (!c.file_path.empty()) {
ImGui::Text("file path: %s", c.file_path.c_str());
}
}
template<> void ComponentEditorWidget<ObjComp::Ephemeral::File::TransferStats>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjComp::Ephemeral::File::TransferStats>(entity);
ImGui::Text("upload rate : %.1f bytes/s", c.rate_up);
ImGui::Text("download rate: %.1f bytes/s", c.rate_down);
ImGui::Text("total bytes uploaded : %lu bytes", c.total_up);
ImGui::Text("total bytes downloaded: %lu bytes", c.total_down);
template<> void ComponentEditorWidget<Message::Components::Transfer::BytesSent>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::BytesSent>(entity);
ImGui::Text("total bytes sent: %lu", c.total);
}
} // MM
@ -95,20 +74,10 @@ ObjectStoreUI::ObjectStoreUI(
_ee.registerComponent<ObjectStore::Components::ID>("ID");
_ee.registerComponent<ObjectStore::Components::DataCompressionType>("DataCompressionType");
_ee.registerComponent<ObjComp::Ephemeral::FilePath>("Ephemeral::FilePath");
_ee.registerComponent<ObjComp::F::SingleInfo>("File::SingleInfo");
_ee.registerComponent<ObjComp::F::SingleInfoLocal>("File::SingleInfoLocal");
_ee.registerComponent<ObjComp::F::Collection>("File::Collection");
_ee.registerComponent<ObjComp::F::CollectionInfo>("File::CollectionInfo");
_ee.registerComponent<ObjComp::F::CollectionInfoLocal>("File::CollectionInfoLocal");
_ee.registerComponent<ObjComp::F::LocalHaveBitset>("File::LocalHaveBitset");
_ee.registerComponent<ObjComp::F::RemoteHaveBitset>("File::RemoteHaveBitset");
_ee.registerComponent<ObjComp::Ephemeral::File::DownloadPriority>("Ephemeral::File::DownloadPriority");
_ee.registerComponent<ObjComp::Ephemeral::File::ReadHeadHint>("Ephemeral::File::ReadHeadHint");
_ee.registerComponent<ObjComp::Ephemeral::File::TransferStats>("Ephemeral::File::TransferStats");
_ee.registerComponent<ObjComp::Ephemeral::File::TransferStatsSeparated>("Ephemeral::File::TransferStatsSeparated");
_ee.registerComponent<Message::Components::Transfer::FileInfo>("Transfer::FileInfo");
_ee.registerComponent<Message::Components::Transfer::FileInfoLocal>("Transfer::FileInfoLocal");
_ee.registerComponent<Message::Components::Transfer::BytesReceived>("Transfer::BytesReceived");
_ee.registerComponent<Message::Components::Transfer::BytesSent>("Transfer::BytesSent");
}
void ObjectStoreUI::render(void) {

View File

@ -1,41 +0,0 @@
#pragma once
#include <solanaceae/object_store/meta_components_file.hpp>
#include <solanaceae/contact/contact_model3.hpp>
#include <entt/container/dense_map.hpp>
namespace ObjectStore::Components {
// until i find a better name
namespace File {
// ephemeral?, not sure saving this to disk makes sense
// tag remove have all?
struct RemoteHaveBitset {
struct Entry {
bool have_all {false};
BitSet have;
};
entt::dense_map<Contact3, Entry> others;
};
} // File
namespace Ephemeral {
namespace File {
struct TransferStatsSeparated {
entt::dense_map<Contact3, TransferStats> stats;
};
} // File
} // Ephemeral
} // ObjectStore::Components
#include "./os_comps_id.inl"

View File

@ -1,26 +0,0 @@
#pragma once
#include "./os_comps.hpp"
#include <entt/core/type_info.hpp>
// TODO: move more central
#define DEFINE_COMP_ID(x) \
template<> \
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
using namespace entt::literals; \
return #x##_hs; \
} \
template<> \
constexpr std::string_view entt::type_name<x>::value() noexcept { \
return #x; \
}
// cross compile(r) stable ids
DEFINE_COMP_ID(ObjComp::F::RemoteHaveBitset)
DEFINE_COMP_ID(ObjComp::Ephemeral::File::TransferStatsSeparated)
#undef DEFINE_COMP_ID

View File

@ -1,13 +1,10 @@
#include "./tox_avatar_manager.hpp"
// TODO: this whole thing needs a rewrite and is currently disabled
#include <solanaceae/util/config_model.hpp>
//#include <solanaceae/message3/components.hpp>
#include <solanaceae/object_store/meta_components_file.hpp>
#include <solanaceae/message3/components.hpp>
// for comp transfer tox filekind (TODO: generalize -> content system?)
#include <solanaceae/tox_messages/obj_components.hpp>
#include <solanaceae/tox_messages/components.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp>
@ -29,13 +26,12 @@ namespace Components {
};
ToxAvatarManager::ToxAvatarManager(
//RegistryMessageModel& rmm,
ObjectStore2& os,
RegistryMessageModel& rmm,
Contact3Registry& cr,
ConfigModelI& conf
) : /*_rmm(rmm)*/ _os(os), _cr(cr), _conf(conf) {
_os.subscribe(this, ObjectStore_Event::object_construct);
_os.subscribe(this, ObjectStore_Event::object_update);
) : _rmm(rmm), _cr(cr), _conf(conf) {
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
if (!_conf.has_string("ToxAvatarManager", "save_path")) {
// or on linux: $HOME/.config/tox/avatars/
@ -68,15 +64,15 @@ void ToxAvatarManager::iterate(void) {
// cancel queue
// accept queue
for (auto& [o, path] : _accept_queue) {
if (o.any_of<ObjComp::Ephemeral::File::ActionTransferAccept, ObjComp::F::TagLocalHaveAll>()) {
continue; // already accepted / done
for (auto& [m, path] : _accept_queue) {
if (m.all_of<Message::Components::Transfer::ActionAccept>()) {
continue; // already accepted
}
o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path, true);
m.emplace<Message::Components::Transfer::ActionAccept>(path, true);
std::cout << "TAM: auto accepted transfer\n";
_os.throwEventUpdate(o);
_rmm.throwEventUpdate(m);
}
_accept_queue.clear();
@ -91,6 +87,9 @@ std::string ToxAvatarManager::getAvatarPath(const ToxKey& key) const {
}
void ToxAvatarManager::addAvatarFileToContact(const Contact3 c, const ToxKey& key) {
//const std::string_view avatar_save_path {_conf.get_string("ToxAvatarManager", "save_path").value()};
//const auto pub_key_string = bin2hex({key.data.cbegin(), key.data.cend()});
//const auto file_path = std::filesystem::path(avatar_save_path) / (pub_key_string + ".png");
const auto file_path = getAvatarPath(key);
if (std::filesystem::is_regular_file(file_path)) {
// avatar file png file exists
@ -107,62 +106,58 @@ void ToxAvatarManager::clearAvatarFromContact(const Contact3 c) {
}
}
void ToxAvatarManager::checkObj(ObjectHandle o) {
if (o.any_of<
ObjComp::Ephemeral::File::ActionTransferAccept,
void ToxAvatarManager::checkMsg(Message3Handle h) {
if (h.any_of<
Message::Components::Transfer::ActionAccept,
Components::TagAvatarImageHandled
>()) {
return; // already accepted or handled
}
if (!o.any_of<
ObjComp::Ephemeral::File::TagTransferPaused,
ObjComp::F::TagLocalHaveAll
if (!h.any_of<
Message::Components::Transfer::TagPaused,
Message::Components::Transfer::TagHaveAll
>()) {
// we only handle unaccepted or finished
return;
}
if (!o.all_of<
ObjComp::Tox::TagIncomming,
//ObjComp::Ephemeral::Backend,
ObjComp::F::SingleInfo,
ObjComp::Tox::FileKind
// TODO: mesage? how do we know where a file is from??
//Message::Components::ContactFrom // should always be there, just making sure
if (!h.all_of<
Message::Components::Transfer::TagReceiving,
Message::Components::Transfer::FileInfo,
Message::Components::Transfer::FileKind,
Message::Components::ContactFrom // should always be there, just making sure
>()) {
return;
}
// TCS-2.2.11 (big list, should have been sub points ...)
if (o.get<ObjComp::Tox::FileKind>().kind != 1) {
if (h.get<Message::Components::Transfer::FileKind>().kind != 1) {
// not an avatar
return;
}
const auto& file_info = o.get<ObjComp::F::SingleInfo>();
const auto& file_info = h.get<Message::Components::Transfer::FileInfo>();
//const auto contact = h.get<Message::Components::ContactFrom>().c;
const auto contact = h.get<Message::Components::ContactFrom>().c;
// TCS-2.2.4
if (file_info.file_size > 65536ul) {
if (file_info.total_size > 65536ul) {
// TODO: mark handled?
return; // too large
}
#if 0 // TODO: make avatars work again !!!!!
// TCS-2.2.10
if (file_info.file_name.empty() || file_info.file_size == 0) {
if (file_info.file_list.empty() || file_info.file_list.front().file_name.empty() || file_info.total_size == 0) {
// reset
clearAvatarFromContact(contact);
// TODO: cancel
return;
}
if (!o.all_of<
ObjComp::Tox::FileID
if (!h.all_of<
Message::Components::Transfer::FileID
>()) {
return;
}
@ -178,7 +173,7 @@ void ToxAvatarManager::checkObj(ObjectHandle o) {
return;
}
if (o.all_of<ObjComp::F::TagLocalHaveAll>()) {
if (h.all_of<Message::Components::Transfer::TagHaveAll>()) {
std::cout << "TAM: full avatar received\n";
if (_cr.all_of<Contact::Components::ToxFriendPersistent>(contact)) {
@ -189,7 +184,7 @@ void ToxAvatarManager::checkObj(ObjectHandle o) {
std::cerr << "TAM error: cant get toxkey for contact\n";
}
o.emplace_or_replace<Components::TagAvatarImageHandled>();
h.emplace_or_replace<Components::TagAvatarImageHandled>();
} else {
// check file id for existing hash
if (std::filesystem::is_regular_file(file_path)) {
@ -204,18 +199,17 @@ void ToxAvatarManager::checkObj(ObjectHandle o) {
std::cout << "TAM: accepted avatar ft\n";
// if not already on disk
_accept_queue.push_back(AcceptEntry{o, file_path});
_accept_queue.push_back(AcceptEntry{h, file_path});
}
#endif
}
bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectConstruct& e) {
checkObj(e.e);
bool ToxAvatarManager::onEvent(const Message::Events::MessageConstruct& e) {
checkMsg(e.e);
return false;
}
bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectUpdate& e) {
checkObj(e.e);
bool ToxAvatarManager::onEvent(const Message::Events::MessageUpdated& e) {
checkMsg(e.e);
return false;
}

View File

@ -1,6 +1,6 @@
#pragma once
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/contact/contact_model3.hpp>
#include <string>
@ -12,20 +12,20 @@
struct ConfigModelI;
struct ToxKey;
class ToxAvatarManager : public ObjectStoreEventI {
ObjectStore2& _os;
class ToxAvatarManager : public RegistryMessageModelEventI {
RegistryMessageModel& _rmm;
Contact3Registry& _cr;
ConfigModelI& _conf;
struct AcceptEntry {
ObjectHandle m;
Message3Handle m;
std::string file_path;
};
std::vector<AcceptEntry> _accept_queue;
public:
ToxAvatarManager(
ObjectStore2& os,
RegistryMessageModel& rmm,
Contact3Registry& cr,
ConfigModelI& conf
);
@ -33,14 +33,13 @@ class ToxAvatarManager : public ObjectStoreEventI {
void iterate(void);
protected:
// TODO: become backend and work in objects instead
std::string getAvatarPath(const ToxKey& key) const;
void addAvatarFileToContact(const Contact3 c, const ToxKey& key);
void clearAvatarFromContact(const Contact3 c);
void checkObj(ObjectHandle o);
void checkMsg(Message3Handle h);
protected: // os
bool onEvent(const ObjectStore::Events::ObjectConstruct& e) override;
bool onEvent(const ObjectStore::Events::ObjectUpdate& e) override;
protected: // mm
bool onEvent(const Message::Events::MessageConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& e) override;
};

View File

@ -5,7 +5,7 @@
#include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/msg_components.hpp>
#include <solanaceae/tox_messages/components.hpp>
#include <limits>
#include <cstdint>