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 9651ca59187c16079846918483c40d6b5c2f454c # tip 09-06-2024
#GIT_TAG 657c0135b1ff1685afa1bad63b0417d92f4bcb46 # 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 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 e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024
GIT_TAG 67b973b5fad633b3be76d4daf4fd9fece292c25f # tip 29-07-2024
FIND_PACKAGE_ARGS # for the future 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 a34ccf16f961e6d5a480045eb650fc3dddb4bfaa # tip 14-05-2024
#GIT_TAG 2fc5310a9a2700fc856663200f94edebeb5e554a # tip 28-05-2024 #GIT_TAG 2fc5310a9a2700fc856663200f94edebeb5e554a # tip 28-05-2024
#GIT_TAG 8eff782fa33d795c9ea1ac42dbe7e17cc9874c78 # tip 09-06-2024 #GIT_TAG 8eff782fa33d795c9ea1ac42dbe7e17cc9874c78 # tip 09-06-2024
#GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024 GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
GIT_TAG 2a27018eda394a4e005cd8ba6bb3bfd0298809c7 # tip 29-07-2024
FIND_PACKAGE_ARGS # for the future FIND_PACKAGE_ARGS # for the future
) )
FetchContent_MakeAvailable(SDL3_image) 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": { "sdl3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1722282731, "lastModified": 1721156595,
"narHash": "sha256-02y7JB23xAehjqBp6hj7ExNkD06XkFE8Odxnu0hYx8k=", "narHash": "sha256-BamJ5xK2uEHv1Q5AM2aNYk+ZDsrKCVsyZ4ium7pTTis=",
"owner": "libsdl-org", "owner": "libsdl-org",
"repo": "SDL", "repo": "SDL",
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f", "rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "libsdl-org", "owner": "libsdl-org",
"repo": "SDL", "repo": "SDL",
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f", "rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
"type": "github" "type": "github"
} }
}, },
"sdl3_image": { "sdl3_image": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1722000822, "lastModified": 1721253495,
"narHash": "sha256-h2vyWcGdLPbg42M6bgJAzskXJfeq9UYTMHi1XdP/otk=", "narHash": "sha256-mZjO5Tfu6TyGGC/2AduzOqKHW9NdH4oSzyVGjipW5bQ=",
"owner": "libsdl-org", "owner": "libsdl-org",
"repo": "SDL_image", "repo": "SDL_image",
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7", "rev": "8abc07df88cc035997e797967ac2f479b0e50981",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "libsdl-org", "owner": "libsdl-org",
"repo": "SDL_image", "repo": "SDL_image",
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7", "rev": "8abc07df88cc035997e797967ac2f479b0e50981",
"type": "github" "type": "github"
} }
}, },

View File

@ -11,11 +11,11 @@
flake = false; flake = false;
}; };
sdl3 = { 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; flake = false;
}; };
sdl3_image = { sdl3_image = {
url = "github:libsdl-org/SDL_image/2a27018eda394a4e005cd8ba6bb3bfd0298809c7"; url = "github:libsdl-org/SDL_image/8abc07df88cc035997e797967ac2f479b0e50981";
flake = false; flake = false;
}; };
}; };

View File

@ -1,11 +1,7 @@
#include "./chat_gui4.hpp" #include "./chat_gui4.hpp"
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/msg_components.hpp> #include <solanaceae/tox_messages/components.hpp>
#include <solanaceae/tox_messages/obj_components.hpp>
#include <solanaceae/object_store/meta_components_file.hpp>
#include <solanaceae/contact/components.hpp> #include <solanaceae/contact/components.hpp>
#include <solanaceae/util/utils.hpp> #include <solanaceae/util/utils.hpp>
@ -195,14 +191,13 @@ void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared
ChatGui4::ChatGui4( ChatGui4::ChatGui4(
ConfigModelI& conf, ConfigModelI& conf,
ObjectStore2& os,
RegistryMessageModel& rmm, RegistryMessageModel& rmm,
Contact3Registry& cr, Contact3Registry& cr,
TextureUploaderI& tu, TextureUploaderI& tu,
ContactTextureCache& contact_tc, ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc, MessageTextureCache& msg_tc,
Theme& theme 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) { ChatGui4::~ChatGui4(void) {
@ -516,7 +511,7 @@ float ChatGui4::render(float time_delta) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (msg_reg.all_of<Message::Components::MessageText>(e)) { if (msg_reg.all_of<Message::Components::MessageText>(e)) {
renderMessageBodyText(msg_reg, 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); renderMessageBodyFile(msg_reg, e);
} else { } else {
ImGui::TextDisabled("---"); ImGui::TextDisabled("---");
@ -834,9 +829,8 @@ float ChatGui4::render(float time_delta) {
} }
void ChatGui4::sendFilePath(const char* file_path) { void ChatGui4::sendFilePath(const char* file_path) {
const auto path = std::filesystem::path(file_path); if (_selected_contact && std::filesystem::is_regular_file(file_path)) {
if (_selected_contact && std::filesystem::is_regular_file(path)) { _rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().generic_u8string(), file_path);
_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
} }
} }
@ -946,12 +940,11 @@ void ChatGui4::renderMessageBodyText(Message3Registry& reg, const Message3 e) {
} }
void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
auto o = reg.get<Message::Components::MessageFileObject>(e).o;
if ( if (
!_show_chat_avatar_tf !_show_chat_avatar_tf
&& ( && (
o.all_of<ObjComp::Tox::FileKind>() reg.all_of<Message::Components::Transfer::FileKind>(e)
&& o.get<ObjComp::Tox::FileKind>().kind == 1 && reg.get<Message::Components::Transfer::FileKind>(e).kind == 1
) )
) { ) {
// TODO: this looks ugly // TODO: this looks ugly
@ -959,8 +952,6 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
return; return;
} }
ImGui::BeginGroup();
#if 0 #if 0
if (msg_reg.all_of<Components::TransferState>(e)) { if (msg_reg.all_of<Components::TransferState>(e)) {
switch (msg_reg.get<Components::TransferState>(e).state) { switch (msg_reg.get<Components::TransferState>(e).state) {
@ -974,24 +965,51 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
} }
#endif #endif
// TODO: better way to display state // 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"); ImGui::TextUnformatted("paused");
//} else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) { } else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) {
// ImGui::TextUnformatted("done"); ImGui::TextUnformatted("done");
} else { } else {
// TODO: missing other states // TODO: missing other states
ImGui::TextUnformatted("running"); 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 // if in offered state
// paused, never started // paused, never started
if ( if (
!o.all_of<ObjComp::F::TagLocalHaveAll>() && reg.all_of<Message::Components::Transfer::TagReceiving>(e) &&
//reg.all_of<Message::Components::Transfer::TagReceiving>(e) && reg.all_of<Message::Components::Transfer::TagPaused>(e) &&
o.all_of<ObjComp::Ephemeral::File::TagTransferPaused>() &&
// TODO: how does restarting a broken/incomplete transfer look like? // TODO: how does restarting a broken/incomplete transfer look like?
!o.all_of<ObjComp::F::SingleInfoLocal>() && !reg.all_of<Message::Components::Transfer::FileInfoLocal>(e) &&
!o.all_of<ObjComp::Ephemeral::File::ActionTransferAccept>() !reg.all_of<Message::Components::Transfer::ActionAccept>(e)
) { ) {
if (ImGui::Button("save to")) { if (ImGui::Button("save to")) {
_fss.requestFile( _fss.requestFile(
@ -1000,13 +1018,11 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
path.remove_filename(); path.remove_filename();
return std::filesystem::is_directory(path); return std::filesystem::is_directory(path);
}, },
[this, o](const auto& path) { [this, &reg, e](const auto& path) {
if (static_cast<bool>(o)) { // still valid if (reg.valid(e)) { // still valid
// TODO: trim file? // TODO: trim file?
o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path.generic_u8string()); reg.emplace<Message::Components::Transfer::ActionAccept>(e, path.string());
//_rmm.throwEventUpdate(reg, e); _rmm.throwEventUpdate(reg, e);
// TODO: block recursion
_os.throwEventUpdate(o);
} }
}, },
[](){} [](){}
@ -1014,75 +1030,97 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
} }
} }
// hacky // down progress
const auto* fts = o.try_get<ObjComp::Ephemeral::File::TransferStats>(); if (reg.all_of<Message::Components::Transfer::TagReceiving>(e)) {
if (fts != nullptr && o.any_of<ObjComp::F::SingleInfo, ObjComp::F::CollectionInfo>()) { ImGui::TextUnformatted("down");
const auto total_size = if (reg.all_of<Message::Components::Transfer::BytesReceived>(e)) {
o.all_of<ObjComp::F::SingleInfo>() ? ImGui::SameLine();
o.get<ObjComp::F::SingleInfo>().file_size :
o.get<ObjComp::F::CollectionInfo>().total_size
;
uint64_t total {0u}; float fraction = float(reg.get<Message::Components::Transfer::BytesReceived>(e).total) / reg.get<Message::Components::Transfer::FileInfo>(e).total_size;
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(total) / total_size; char overlay_buf[32];
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 {
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f); 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( if (reg.all_of<Message::Components::Transfer::FileInfoLocal>(e)) {
fraction, const auto& local_info = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
{-FLT_MIN, TEXT_BASE_HEIGHT}, if (local_info.file_list.size() > i && ImGui::BeginPopupContextItem("##file_c")) {
overlay_buf if (ImGui::MenuItem("open")) {
); const std::string url {file_path_to_file_url(local_info.file_list.at(i))};
} else { std::cout << "opening file '" << url << "'\n";
// infinite scrolling progressbar fallback SDL_OpenURL(url.c_str());
ImGui::TextUnformatted(" ??"); }
ImGui::SameLine(); if (ImGui::MenuItem("copy file")) {
ImGui::ProgressBar( const std::string url {file_path_to_file_url(local_info.file_list.at(i))};
-0.333f * ImGui::GetTime(), //ImGui::SetClipboardText(url.c_str());
{-FLT_MIN, TEXT_BASE_HEIGHT}, 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>()) { if (reg.all_of<Message::Components::FrameDims>(e)) {
// texture based on have bitset const auto& frame_dims = reg.get<Message::Components::FrameDims>(e);
// 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>();
// TODO: config // TODO: config
const auto max_inline_height = 10*TEXT_BASE_HEIGHT; const auto max_inline_height = 10*TEXT_BASE_HEIGHT;
float width = frame_dims.w; float height = frame_dims.height;
float height = frame_dims.h; float width = frame_dims.width;
if (height > max_inline_height) { if (height > max_inline_height) {
const float scale = max_inline_height / 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(); ImVec2 orig_curser_pos = ImGui::GetCursorPos();
ImGui::Dummy(ImVec2{width, height});
// deploy dummy of framedim size and check visibility // deploy dummy of framedim size and check visibility
// +2 for border bool image_preview_visible = ImGui::IsItemVisible();
ImGui::Dummy(ImVec2{width+2, height+2});
if (ImGui::IsItemVisible() && o.all_of<ObjComp::F::TagLocalHaveAll, ObjComp::F::SingleInfo, ObjComp::Ephemeral::Backend>()) {
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 ImGui::SetCursorPos(orig_curser_pos); // reset for actual img
auto [id, img_width, img_height] = _msg_tc.get(Message3Handle{reg, e}); 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; //width *= scale;
//} //}
ImGui::Image( ImGui::Image(id, ImVec2{width, height});
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
);
// TODO: clickable to open in internal image viewer // 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) { void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) {
if (reg.all_of<Message::Components::MessageFileObject>(e)) { if (reg.all_of<Message::Components::Transfer::FileKind>(e)) {
const auto o = reg.get<Message::Components::MessageFileObject>(e).o; ImGui::TextDisabled("fk:%lu", reg.get<Message::Components::Transfer::FileKind>(e).kind);
}
ImGui::TextDisabled("o:%u", entt::to_integral(o.entity())); if (reg.all_of<Message::Components::Transfer::ToxTransferFriend>(e)) {
ImGui::TextDisabled("ttf:%u", reg.get<Message::Components::Transfer::ToxTransferFriend>(e).transfer_number);
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::ToxGroupMessageID>(e)) { 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); 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) { void ChatGui4::pasteFile(const char* mime_type) {
size_t data_size = 0; size_t data_size = 0;
void* data = SDL_GetClipboardData(mime_type, &data_size); void* data = SDL_GetClipboardData(mime_type, &data_size);

View File

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

View File

@ -3,7 +3,6 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <iostream> #include <iostream>
#include <cassert>
ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) { ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
ImageInfo res; ImageInfo res;
@ -15,9 +14,6 @@ ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t
return res; return res;
} }
assert(surf->w >= 0);
assert(surf->h >= 0);
res.width = surf->w; res.width = surf->w;
res.height = surf->h; res.height = surf->h;
res.file_ext = "bmp"; 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) { ImageLoaderSDLBMP::ImageResult ImageLoaderSDLBMP::loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) {
ImageResult res;
auto* ios = SDL_IOFromConstMem(data, data_size); auto* ios = SDL_IOFromConstMem(data, data_size);
SDL_Surface* surf = SDL_LoadBMP_IO(ios, SDL_TRUE); SDL_Surface* surf = SDL_LoadBMP_IO(ios, SDL_TRUE);
if (surf == nullptr) { if (surf == nullptr) {
return {}; return res;
} }
SDL_Surface* conv_surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32); SDL_Surface* conv_surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
SDL_DestroySurface(surf); SDL_DestroySurface(surf);
if (conv_surf == nullptr) { if (conv_surf == nullptr) {
return {}; return res;
} }
assert(conv_surf->w >= 0); res.width = surf->w;
assert(conv_surf->h >= 0); res.height = surf->h;
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.file_ext = "bmp"; res.file_ext = "bmp";
SDL_LockSurface(conv_surf);
auto& new_frame = res.frames.emplace_back(); auto& new_frame = res.frames.emplace_back();
new_frame.ms = 0; 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); SDL_DestroySurface(conv_surf);
std::cout << "IL_SDLBMP: loaded img " << res.width << "x" << res.height << "\n"; 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 #endif
tcm(cr, tc, tc), tcm(cr, tc, tc),
tmm(rmm, cr, tcm, 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), tffom(cr, rmm, tcm, tc, tc),
theme(theme_), theme(theme_),
mmil(rmm), mmil(rmm),
tam(/*rmm, */ os, cr, conf), tam(rmm, cr, conf),
sdlrtu(renderer_), sdlrtu(renderer_),
tal(cr), tal(cr),
contact_tc(tal, sdlrtu), contact_tc(tal, sdlrtu),
mil(), mil(),
msg_tc(mil, sdlrtu), 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), sw(conf),
osui(os), osui(os),
tuiu(tc, conf), tuiu(tc, conf),

View File

@ -7,117 +7,71 @@
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
#include "./os_comps.hpp" #include <fstream>
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/file/file2.hpp>
#include <limits>
#include <iostream> #include <iostream>
void MediaMetaInfoLoader::handleMessage(const Message3Handle& m) { void MediaMetaInfoLoader::handleMessage(const Message3Handle& m) {
if (!static_cast<bool>(m)) { if (m.any_of<Message::Components::TagNotImage, Message::Components::FrameDims>()) {
return; return;
} }
// move to obj if (!m.all_of<Message::Components::Transfer::FileInfoLocal, Message::Components::Transfer::TagHaveAll>()) {
if (m.any_of<Message::Components::TagNotImage>()) {
return; return;
} }
if (!m.all_of<Message::Components::MessageFileObject>()) { const auto& fil = m.get<Message::Components::Transfer::FileInfoLocal>();
// not a file message if (fil.file_list.size() != 1) {
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";
return; return;
} }
if (o.any_of<ObjComp::F::FrameDims>()) { std::ifstream file(fil.file_list.front(), std::ios::binary);
return; 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>()) { std::vector<uint8_t> tmp_buffer;
return; // we dont have all data while (file.good()) {
} auto ch = file.get();
if (ch == EOF) {
if (!o.all_of<ObjComp::Ephemeral::Backend, ObjComp::F::SingleInfo>()) { break;
std::cerr << "MMIL error: object missing backend/file info (?)\n"; } else {
return; tmp_buffer.push_back(ch);
} }
// 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;
} }
o.emplace<ObjComp::F::FrameDims>( bool could_load {false};
static_cast<uint16_t>(std::min<uint32_t>(res.width, std::numeric_limits<uint16_t>::max())), // try all loaders after another
static_cast<uint16_t>(std::min<uint32_t>(res.height, std::numeric_limits<uint16_t>::max())) 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); could_load = true;
return;
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) { MediaMetaInfoLoader::MediaMetaInfoLoader(RegistryMessageModel& rmm) : _rmm(rmm) {

View File

@ -6,6 +6,11 @@
namespace Message::Components { namespace Message::Components {
struct FrameDims {
uint32_t width {0};
uint32_t height {0};
};
struct TagNotImage {}; struct TagNotImage {};
} // Message::Components } // Message::Components
@ -26,10 +31,5 @@ class MediaMetaInfoLoader : public RegistryMessageModelEventI {
protected: // rmm protected: // rmm
bool onEvent(const Message::Events::MessageConstruct& e) override; bool onEvent(const Message::Events::MessageConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& 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 <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 <iostream>
#include <fstream>
#include <cassert>
#include <vector>
// fwd // fwd
namespace Message { namespace Message {
@ -39,88 +34,51 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
return std::nullopt; return std::nullopt;
} }
if (!m.all_of<Message::Components::MessageFileObject>()) { if (m.all_of<Message::Components::Transfer::FileInfoLocal>()) {
// not a file message const auto& file_list = m.get<Message::Components::Transfer::FileInfoLocal>().file_list;
return std::nullopt; assert(!file_list.empty());
} const auto& file_path = file_list.front();
const auto& o = m.get<Message::Components::MessageFileObject>().o;
if (!static_cast<bool>(o)) { std::ifstream file(file_path, std::ios::binary);
std::cerr << "MIL error: invalid object in file message\n"; if (file.is_open()) {
return std::nullopt; 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>()) { // try all loaders after another
std::cerr << "MIL error: object missing backend (?)\n"; for (auto& il : _image_loaders) {
return std::nullopt; auto res = il->loadFromMemoryRGBA(tmp_buffer.data(), tmp_buffer.size());
} if (res.frames.empty() || res.height == 0 || res.width == 0) {
continue;
}
// TODO: handle collections TextureEntry new_entry;
const auto file_size = o.get<ObjComp::F::SingleInfo>().file_size; 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) { new_entry.width = res.width;
std::cerr << "MIL error: image file too large\n"; new_entry.height = res.height;
return std::nullopt;
}
if (file_size == 0) { std::cout << "MIL: loaded image file " << file_path << "\n";
std::cerr << "MIL warning: empty file\n";
return std::nullopt;
}
if (!o.all_of<ObjComp::F::TagLocalHaveAll>()) { return new_entry;
// 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;
} }
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; return std::nullopt;
} }

View File

@ -15,3 +15,13 @@ class MessageImageLoader {
std::optional<TextureEntry> load(TextureUploaderI& tu, Message3Handle c); 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/util/utils.hpp>
#include <solanaceae/object_store/meta_components.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 <imgui/imgui.h>
#include <solanaceae/message3/components.hpp>
namespace MM { namespace MM {
template<> void ComponentEditorWidget<ObjectStore::Components::ID>(entt::basic_registry<Object>& registry, Object entity) { template<> void ComponentEditorWidget<ObjectStore::Components::ID>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjectStore::Components::ID>(entity); auto& c = registry.get<ObjectStore::Components::ID>(entity);
const auto str = bin2hex(c.v); const auto str = bin2hex(c.v);
if (ImGui::SmallButton("copy")) {
ImGui::SetClipboardText(str.c_str());
}
ImGui::SameLine();
ImGui::TextUnformatted(str.c_str()); ImGui::TextUnformatted(str.c_str());
} }
#if 0
template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfo>(entt::basic_registry<Object>& registry, Object entity) { template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfo>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::FileInfo>(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<Message::Components::Transfer::BytesReceived>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::BytesReceived>(entity);
template<> void ComponentEditorWidget<ObjComp::F::SingleInfo>(entt::basic_registry<Object>& registry, Object entity) { ImGui::Text("total bytes received: %lu", c.total);
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<ObjComp::F::SingleInfoLocal>(entt::basic_registry<Object>& registry, Object entity) { template<> void ComponentEditorWidget<Message::Components::Transfer::BytesSent>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<ObjComp::F::SingleInfoLocal>(entity); auto& c = registry.get<Message::Components::Transfer::BytesSent>(entity);
if (!c.file_path.empty()) { ImGui::Text("total bytes sent: %lu", c.total);
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);
} }
} // MM } // MM
@ -95,20 +74,10 @@ ObjectStoreUI::ObjectStoreUI(
_ee.registerComponent<ObjectStore::Components::ID>("ID"); _ee.registerComponent<ObjectStore::Components::ID>("ID");
_ee.registerComponent<ObjectStore::Components::DataCompressionType>("DataCompressionType"); _ee.registerComponent<ObjectStore::Components::DataCompressionType>("DataCompressionType");
_ee.registerComponent<ObjComp::Ephemeral::FilePath>("Ephemeral::FilePath"); _ee.registerComponent<Message::Components::Transfer::FileInfo>("Transfer::FileInfo");
_ee.registerComponent<Message::Components::Transfer::FileInfoLocal>("Transfer::FileInfoLocal");
_ee.registerComponent<ObjComp::F::SingleInfo>("File::SingleInfo"); _ee.registerComponent<Message::Components::Transfer::BytesReceived>("Transfer::BytesReceived");
_ee.registerComponent<ObjComp::F::SingleInfoLocal>("File::SingleInfoLocal"); _ee.registerComponent<Message::Components::Transfer::BytesSent>("Transfer::BytesSent");
_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");
} }
void ObjectStoreUI::render(void) { 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" #include "./tox_avatar_manager.hpp"
// TODO: this whole thing needs a rewrite and is currently disabled
#include <solanaceae/util/config_model.hpp> #include <solanaceae/util/config_model.hpp>
//#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
#include <solanaceae/object_store/meta_components_file.hpp>
// for comp transfer tox filekind (TODO: generalize -> content system?) // 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/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp> #include <solanaceae/tox_contacts/components.hpp>
@ -29,13 +26,12 @@ namespace Components {
}; };
ToxAvatarManager::ToxAvatarManager( ToxAvatarManager::ToxAvatarManager(
//RegistryMessageModel& rmm, RegistryMessageModel& rmm,
ObjectStore2& os,
Contact3Registry& cr, Contact3Registry& cr,
ConfigModelI& conf ConfigModelI& conf
) : /*_rmm(rmm)*/ _os(os), _cr(cr), _conf(conf) { ) : _rmm(rmm), _cr(cr), _conf(conf) {
_os.subscribe(this, ObjectStore_Event::object_construct); _rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
_os.subscribe(this, ObjectStore_Event::object_update); _rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
if (!_conf.has_string("ToxAvatarManager", "save_path")) { if (!_conf.has_string("ToxAvatarManager", "save_path")) {
// or on linux: $HOME/.config/tox/avatars/ // or on linux: $HOME/.config/tox/avatars/
@ -68,15 +64,15 @@ void ToxAvatarManager::iterate(void) {
// cancel queue // cancel queue
// accept queue // accept queue
for (auto& [o, path] : _accept_queue) { for (auto& [m, path] : _accept_queue) {
if (o.any_of<ObjComp::Ephemeral::File::ActionTransferAccept, ObjComp::F::TagLocalHaveAll>()) { if (m.all_of<Message::Components::Transfer::ActionAccept>()) {
continue; // already accepted / done 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"; std::cout << "TAM: auto accepted transfer\n";
_os.throwEventUpdate(o); _rmm.throwEventUpdate(m);
} }
_accept_queue.clear(); _accept_queue.clear();
@ -91,6 +87,9 @@ std::string ToxAvatarManager::getAvatarPath(const ToxKey& key) const {
} }
void ToxAvatarManager::addAvatarFileToContact(const Contact3 c, const ToxKey& key) { 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); const auto file_path = getAvatarPath(key);
if (std::filesystem::is_regular_file(file_path)) { if (std::filesystem::is_regular_file(file_path)) {
// avatar file png file exists // avatar file png file exists
@ -107,62 +106,58 @@ void ToxAvatarManager::clearAvatarFromContact(const Contact3 c) {
} }
} }
void ToxAvatarManager::checkObj(ObjectHandle o) { void ToxAvatarManager::checkMsg(Message3Handle h) {
if (o.any_of< if (h.any_of<
ObjComp::Ephemeral::File::ActionTransferAccept, Message::Components::Transfer::ActionAccept,
Components::TagAvatarImageHandled Components::TagAvatarImageHandled
>()) { >()) {
return; // already accepted or handled return; // already accepted or handled
} }
if (!o.any_of< if (!h.any_of<
ObjComp::Ephemeral::File::TagTransferPaused, Message::Components::Transfer::TagPaused,
ObjComp::F::TagLocalHaveAll Message::Components::Transfer::TagHaveAll
>()) { >()) {
// we only handle unaccepted or finished // we only handle unaccepted or finished
return; return;
} }
if (!o.all_of< if (!h.all_of<
ObjComp::Tox::TagIncomming, Message::Components::Transfer::TagReceiving,
//ObjComp::Ephemeral::Backend, Message::Components::Transfer::FileInfo,
ObjComp::F::SingleInfo, Message::Components::Transfer::FileKind,
ObjComp::Tox::FileKind Message::Components::ContactFrom // should always be there, just making sure
// TODO: mesage? how do we know where a file is from??
//Message::Components::ContactFrom // should always be there, just making sure
>()) { >()) {
return; return;
} }
// TCS-2.2.11 (big list, should have been sub points ...) // 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 // not an avatar
return; 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 // TCS-2.2.4
if (file_info.file_size > 65536ul) { if (file_info.total_size > 65536ul) {
// TODO: mark handled? // TODO: mark handled?
return; // too large return; // too large
} }
#if 0 // TODO: make avatars work again !!!!!
// TCS-2.2.10 // 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 // reset
clearAvatarFromContact(contact); clearAvatarFromContact(contact);
// TODO: cancel // TODO: cancel
return; return;
} }
if (!o.all_of< if (!h.all_of<
ObjComp::Tox::FileID Message::Components::Transfer::FileID
>()) { >()) {
return; return;
} }
@ -178,7 +173,7 @@ void ToxAvatarManager::checkObj(ObjectHandle o) {
return; return;
} }
if (o.all_of<ObjComp::F::TagLocalHaveAll>()) { if (h.all_of<Message::Components::Transfer::TagHaveAll>()) {
std::cout << "TAM: full avatar received\n"; std::cout << "TAM: full avatar received\n";
if (_cr.all_of<Contact::Components::ToxFriendPersistent>(contact)) { 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"; std::cerr << "TAM error: cant get toxkey for contact\n";
} }
o.emplace_or_replace<Components::TagAvatarImageHandled>(); h.emplace_or_replace<Components::TagAvatarImageHandled>();
} else { } else {
// check file id for existing hash // check file id for existing hash
if (std::filesystem::is_regular_file(file_path)) { if (std::filesystem::is_regular_file(file_path)) {
@ -204,18 +199,17 @@ void ToxAvatarManager::checkObj(ObjectHandle o) {
std::cout << "TAM: accepted avatar ft\n"; std::cout << "TAM: accepted avatar ft\n";
// if not already on disk // 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) { bool ToxAvatarManager::onEvent(const Message::Events::MessageConstruct& e) {
checkObj(e.e); checkMsg(e.e);
return false; return false;
} }
bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectUpdate& e) { bool ToxAvatarManager::onEvent(const Message::Events::MessageUpdated& e) {
checkObj(e.e); checkMsg(e.e);
return false; return false;
} }

View File

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

View File

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