Compare commits

..

2 Commits

Author SHA1 Message Date
60f2c07a6a
update sdl and sdl_image
Some checks failed
ContinuousDelivery / windows (push) Waiting to run
ContinuousDelivery / windows-asan (push) Waiting to run
ContinuousDelivery / release (push) Blocked by required conditions
ContinuousIntegration / linux (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousIntegration / macos (push) Waiting to run
ContinuousIntegration / windows (push) Waiting to run
ContinuousDelivery / linux-ubuntu (push) Failing after 5m27s
ContinuousDelivery / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Failing after 6m27s
ContinuousDelivery / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Has been cancelled
2024-07-31 18:14:41 +02:00
Erik Scholz
2abf09ac06
refactor file from messages to objects (#27)
* part 1 move files from messages to objects tomato - did not touch chat_gui yet, but image loaders and other stuff
* part 1.1
* part 2, mostly chatgui - also ngcft1 behind the scenes
* part 3 - port over rest, except for avatar_manager, which is effectivly disabled
* fix surface missused causing bmp loader to crash
* fixing small issues and small forward refactor
2024-07-31 18:10:52 +02:00
21 changed files with 635 additions and 360 deletions

View File

@ -20,7 +20,8 @@ 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 #GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024 - broke bmp surface (before too)
GIT_TAG 67b973b5fad633b3be76d4daf4fd9fece292c25f # tip 29-07-2024
FIND_PACKAGE_ARGS # for the future FIND_PACKAGE_ARGS # for the future
) )

View File

@ -19,7 +19,8 @@ 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 f1dd5107f820fe86cb6b6b9ca22d42e0a3a3cf30 Subproject commit 9728f71c9833baa65995e19e993d3450da750c20

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

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

16
flake.lock generated
View File

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

View File

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

View File

@ -1,7 +1,11 @@
#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/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/contact/components.hpp> #include <solanaceae/contact/components.hpp>
#include <solanaceae/util/utils.hpp> #include <solanaceae/util/utils.hpp>
@ -191,13 +195,14 @@ 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), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) { ) : _conf(conf), _os(os), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) {
} }
ChatGui4::~ChatGui4(void) { ChatGui4::~ChatGui4(void) {
@ -511,7 +516,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::Transfer::FileInfo>(e)) { // add more comps? } else if (msg_reg.any_of<Message::Components::MessageFileObject>(e)) {
renderMessageBodyFile(msg_reg, e); renderMessageBodyFile(msg_reg, e);
} else { } else {
ImGui::TextDisabled("---"); ImGui::TextDisabled("---");
@ -829,8 +834,9 @@ float ChatGui4::render(float time_delta) {
} }
void ChatGui4::sendFilePath(const char* file_path) { void ChatGui4::sendFilePath(const char* file_path) {
if (_selected_contact && std::filesystem::is_regular_file(file_path)) { const auto path = std::filesystem::path(file_path);
_rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().generic_u8string(), file_path); if (_selected_contact && std::filesystem::is_regular_file(path)) {
_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
} }
} }
@ -940,11 +946,12 @@ 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
&& ( && (
reg.all_of<Message::Components::Transfer::FileKind>(e) o.all_of<ObjComp::Tox::FileKind>()
&& reg.get<Message::Components::Transfer::FileKind>(e).kind == 1 && o.get<ObjComp::Tox::FileKind>().kind == 1
) )
) { ) {
// TODO: this looks ugly // TODO: this looks ugly
@ -952,6 +959,8 @@ 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) {
@ -965,51 +974,24 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
} }
#endif #endif
// TODO: better way to display state // TODO: better way to display state
if (reg.all_of<Message::Components::Transfer::TagPaused>(e)) { if (o.all_of<ObjComp::Ephemeral::File::TagTransferPaused>()) {
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 (
reg.all_of<Message::Components::Transfer::TagReceiving>(e) && !o.all_of<ObjComp::F::TagLocalHaveAll>() &&
reg.all_of<Message::Components::Transfer::TagPaused>(e) && //reg.all_of<Message::Components::Transfer::TagReceiving>(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?
!reg.all_of<Message::Components::Transfer::FileInfoLocal>(e) && !o.all_of<ObjComp::F::SingleInfoLocal>() &&
!reg.all_of<Message::Components::Transfer::ActionAccept>(e) !o.all_of<ObjComp::Ephemeral::File::ActionTransferAccept>()
) { ) {
if (ImGui::Button("save to")) { if (ImGui::Button("save to")) {
_fss.requestFile( _fss.requestFile(
@ -1018,11 +1000,13 @@ 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, &reg, e](const auto& path) { [this, o](const auto& path) {
if (reg.valid(e)) { // still valid if (static_cast<bool>(o)) { // still valid
// TODO: trim file? // TODO: trim file?
reg.emplace<Message::Components::Transfer::ActionAccept>(e, path.string()); o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path.generic_u8string());
_rmm.throwEventUpdate(reg, e); //_rmm.throwEventUpdate(reg, e);
// TODO: block recursion
_os.throwEventUpdate(o);
} }
}, },
[](){} [](){}
@ -1030,97 +1014,75 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
} }
} }
// down progress // hacky
if (reg.all_of<Message::Components::Transfer::TagReceiving>(e)) { const auto* fts = o.try_get<ObjComp::Ephemeral::File::TransferStats>();
ImGui::TextUnformatted("down"); if (fts != nullptr && o.any_of<ObjComp::F::SingleInfo, ObjComp::F::CollectionInfo>()) {
if (reg.all_of<Message::Components::Transfer::BytesReceived>(e)) { const auto total_size =
ImGui::SameLine(); o.all_of<ObjComp::F::SingleInfo>() ?
o.get<ObjComp::F::SingleInfo>().file_size :
o.get<ObjComp::F::CollectionInfo>().total_size
;
float fraction = float(reg.get<Message::Components::Transfer::BytesReceived>(e).total) / reg.get<Message::Components::Transfer::FileInfo>(e).total_size; 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();
char overlay_buf[32]; 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 {
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
} }
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},
"?%"
);
} }
// (can be both) if (!o.all_of<ObjComp::F::TagLocalHaveAll>() && o.all_of<ObjComp::F::LocalHaveBitset>()) {
// up progess // texture based on have bitset
if (reg.all_of<Message::Components::Transfer::TagSending>(e)) { // TODO: missing have chunks/chunksize to get the correct size
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; //const auto& bitest = o.get<ObjComp::F::LocalHaveBitset>().have;
// generate 1bit sdlsurface zerocopy using bitset.data() and bitset.size_bytes()
char overlay_buf[32]; // optionally scale down filtered (would copy)
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f); // update texture? in cache?
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 (o.all_of<ObjComp::F::FrameDims>()) {
const auto& frame_dims = o.get<ObjComp::F::FrameDims>();
// 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();
}
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 (reg.all_of<Message::Components::FrameDims>(e)) {
const auto& frame_dims = reg.get<Message::Components::FrameDims>(e);
// TODO: config // TODO: config
const auto max_inline_height = 10*TEXT_BASE_HEIGHT; const auto max_inline_height = 10*TEXT_BASE_HEIGHT;
float height = frame_dims.height; float width = frame_dims.w;
float width = frame_dims.width; float height = frame_dims.h;
if (height > max_inline_height) { if (height > max_inline_height) {
const float scale = max_inline_height / height; const float scale = max_inline_height / height;
@ -1129,13 +1091,10 @@ 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
bool image_preview_visible = ImGui::IsItemVisible(); // +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>()) {
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});
@ -1152,19 +1111,152 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
//width *= scale; //width *= scale;
//} //}
ImGui::Image(id, ImVec2{width, height}); 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
);
// 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::Transfer::FileKind>(e)) { if (reg.all_of<Message::Components::MessageFileObject>(e)) {
ImGui::TextDisabled("fk:%lu", reg.get<Message::Components::Transfer::FileKind>(e).kind); const auto o = reg.get<Message::Components::MessageFileObject>(e).o;
}
if (reg.all_of<Message::Components::Transfer::ToxTransferFriend>(e)) { ImGui::TextDisabled("o:%u", entt::to_integral(o.entity()));
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)) {
@ -1244,31 +1336,6 @@ 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,5 +1,6 @@
#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>
@ -25,6 +26,7 @@ using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoad
class ChatGui4 { class ChatGui4 {
ConfigModelI& _conf; ConfigModelI& _conf;
ObjectStore2& _os;
RegistryMessageModel& _rmm; RegistryMessageModel& _rmm;
Contact3Registry& _cr; Contact3Registry& _cr;
@ -57,6 +59,7 @@ 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,6 +3,7 @@
#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;
@ -14,6 +15,9 @@ 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";
@ -24,32 +28,48 @@ 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 res; return {};
} }
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 res; return {};
} }
res.width = surf->w; assert(conv_surf->w >= 0);
res.height = surf->h; assert(conv_surf->h >= 0);
res.file_ext = "bmp";
SDL_LockSurface(conv_surf); 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";
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) + (surf->w*surf->h*4)}; new_frame.data = {(const uint8_t*)conv_surf->pixels, ((const uint8_t*)conv_surf->pixels) + (conv_surf->w*conv_surf->h*4)};
SDL_UnlockSurface(conv_surf); if (SDL_MUSTLOCK(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), ttm(rmm, cr, tcm, tc, tc, os),
tffom(cr, rmm, tcm, tc, tc), tffom(cr, rmm, tcm, tc, tc),
theme(theme_), theme(theme_),
mmil(rmm), mmil(rmm),
tam(rmm, cr, conf), tam(/*rmm, */ os, 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, rmm, cr, sdlrtu, contact_tc, msg_tc, theme), cg(conf, os, 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,71 +7,117 @@
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
#include <fstream> #include "./os_comps.hpp"
#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 (m.any_of<Message::Components::TagNotImage, Message::Components::FrameDims>()) { if (!static_cast<bool>(m)) {
return; return;
} }
if (!m.all_of<Message::Components::Transfer::FileInfoLocal, Message::Components::Transfer::TagHaveAll>()) { // move to obj
if (m.any_of<Message::Components::TagNotImage>()) {
return; return;
} }
const auto& fil = m.get<Message::Components::Transfer::FileInfoLocal>(); if (!m.all_of<Message::Components::MessageFileObject>()) {
if (fil.file_list.size() != 1) { // 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";
return; return;
} }
std::ifstream file(fil.file_list.front(), std::ios::binary); if (o.any_of<ObjComp::F::FrameDims>()) {
if (file.is_open()) { return;
// 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);
std::vector<uint8_t> tmp_buffer;
while (file.good()) {
auto ch = file.get();
if (ch == EOF) {
break;
} else {
tmp_buffer.push_back(ch);
}
}
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;
}
m.emplace<Message::Components::FrameDims>(res.width, res.height);
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);
}
} }
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;
}
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()))
);
std::cout << "MMIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
_rmm.throwEventUpdate(m);
return;
}
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,11 +6,6 @@
namespace Message::Components { namespace Message::Components {
struct FrameDims {
uint32_t width {0};
uint32_t height {0};
};
struct TagNotImage {}; struct TagNotImage {};
} // Message::Components } // Message::Components
@ -31,5 +26,10 @@ 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,10 +8,15 @@
#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 {
@ -34,51 +39,88 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
return std::nullopt; return std::nullopt;
} }
if (m.all_of<Message::Components::Transfer::FileInfoLocal>()) { if (!m.all_of<Message::Components::MessageFileObject>()) {
const auto& file_list = m.get<Message::Components::Transfer::FileInfoLocal>().file_list; // not a file message
assert(!file_list.empty()); return std::nullopt;
const auto& file_path = file_list.front(); }
const auto& o = m.get<Message::Components::MessageFileObject>().o;
std::ifstream file(file_path, std::ios::binary); if (!static_cast<bool>(o)) {
if (file.is_open()) { std::cerr << "MIL error: invalid object in file message\n";
std::vector<uint8_t> tmp_buffer; return std::nullopt;
while (file.good()) {
auto ch = file.get();
if (ch == EOF) {
break;
} else {
tmp_buffer.push_back(ch);
}
}
// 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;
}
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 " << file_path << "\n";
return new_entry;
}
}
} }
std::cerr << "MIL: failed to load message\n"; if (!o.all_of<ObjComp::Ephemeral::Backend, ObjComp::F::SingleInfo>()) {
std::cerr << "MIL error: object missing backend (?)\n";
return std::nullopt;
}
// TODO: handle collections
const auto file_size = o.get<ObjComp::F::SingleInfo>().file_size;
if (file_size > 50*1024*1024) {
std::cerr << "MIL error: image file too large\n";
return std::nullopt;
}
if (file_size == 0) {
std::cerr << "MIL warning: empty file\n";
return std::nullopt;
}
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;
}
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";
return std::nullopt; return std::nullopt;
} }

View File

@ -15,13 +15,3 @@ 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,20 +2,25 @@
#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);
@ -54,14 +59,30 @@ template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfoLoc
} }
} }
template<> void ComponentEditorWidget<Message::Components::Transfer::BytesReceived>(entt::basic_registry<Object>& registry, Object entity) { #endif
auto& c = registry.get<Message::Components::Transfer::BytesReceived>(entity);
ImGui::Text("total bytes received: %lu", c.total); 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::BytesSent>(entt::basic_registry<Object>& registry, Object entity) { template<> void ComponentEditorWidget<ObjComp::F::SingleInfoLocal>(entt::basic_registry<Object>& registry, Object entity) {
auto& c = registry.get<Message::Components::Transfer::BytesSent>(entity); auto& c = registry.get<ObjComp::F::SingleInfoLocal>(entity);
ImGui::Text("total bytes sent: %lu", c.total); 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);
} }
} // MM } // MM
@ -74,10 +95,20 @@ 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<Message::Components::Transfer::FileInfo>("Transfer::FileInfo"); _ee.registerComponent<ObjComp::Ephemeral::FilePath>("Ephemeral::FilePath");
_ee.registerComponent<Message::Components::Transfer::FileInfoLocal>("Transfer::FileInfoLocal");
_ee.registerComponent<Message::Components::Transfer::BytesReceived>("Transfer::BytesReceived"); _ee.registerComponent<ObjComp::F::SingleInfo>("File::SingleInfo");
_ee.registerComponent<Message::Components::Transfer::BytesSent>("Transfer::BytesSent"); _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");
} }
void ObjectStoreUI::render(void) { void ObjectStoreUI::render(void) {

41
src/os_comps.hpp Normal file
View File

@ -0,0 +1,41 @@
#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"

26
src/os_comps_id.inl Normal file
View File

@ -0,0 +1,26 @@
#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,10 +1,13 @@
#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/components.hpp> #include <solanaceae/tox_messages/obj_components.hpp>
#include <solanaceae/contact/components.hpp> #include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp> #include <solanaceae/tox_contacts/components.hpp>
@ -26,12 +29,13 @@ namespace Components {
}; };
ToxAvatarManager::ToxAvatarManager( ToxAvatarManager::ToxAvatarManager(
RegistryMessageModel& rmm, //RegistryMessageModel& rmm,
ObjectStore2& os,
Contact3Registry& cr, Contact3Registry& cr,
ConfigModelI& conf ConfigModelI& conf
) : _rmm(rmm), _cr(cr), _conf(conf) { ) : /*_rmm(rmm)*/ _os(os), _cr(cr), _conf(conf) {
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct); _os.subscribe(this, ObjectStore_Event::object_construct);
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated); _os.subscribe(this, ObjectStore_Event::object_update);
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/
@ -64,15 +68,15 @@ void ToxAvatarManager::iterate(void) {
// cancel queue // cancel queue
// accept queue // accept queue
for (auto& [m, path] : _accept_queue) { for (auto& [o, path] : _accept_queue) {
if (m.all_of<Message::Components::Transfer::ActionAccept>()) { if (o.any_of<ObjComp::Ephemeral::File::ActionTransferAccept, ObjComp::F::TagLocalHaveAll>()) {
continue; // already accepted continue; // already accepted / done
} }
m.emplace<Message::Components::Transfer::ActionAccept>(path, true); o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path, true);
std::cout << "TAM: auto accepted transfer\n"; std::cout << "TAM: auto accepted transfer\n";
_rmm.throwEventUpdate(m); _os.throwEventUpdate(o);
} }
_accept_queue.clear(); _accept_queue.clear();
@ -87,9 +91,6 @@ 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
@ -106,58 +107,62 @@ void ToxAvatarManager::clearAvatarFromContact(const Contact3 c) {
} }
} }
void ToxAvatarManager::checkMsg(Message3Handle h) { void ToxAvatarManager::checkObj(ObjectHandle o) {
if (h.any_of< if (o.any_of<
Message::Components::Transfer::ActionAccept, ObjComp::Ephemeral::File::ActionTransferAccept,
Components::TagAvatarImageHandled Components::TagAvatarImageHandled
>()) { >()) {
return; // already accepted or handled return; // already accepted or handled
} }
if (!h.any_of< if (!o.any_of<
Message::Components::Transfer::TagPaused, ObjComp::Ephemeral::File::TagTransferPaused,
Message::Components::Transfer::TagHaveAll ObjComp::F::TagLocalHaveAll
>()) { >()) {
// we only handle unaccepted or finished // we only handle unaccepted or finished
return; return;
} }
if (!h.all_of< if (!o.all_of<
Message::Components::Transfer::TagReceiving, ObjComp::Tox::TagIncomming,
Message::Components::Transfer::FileInfo, //ObjComp::Ephemeral::Backend,
Message::Components::Transfer::FileKind, ObjComp::F::SingleInfo,
Message::Components::ContactFrom // should always be there, just making sure ObjComp::Tox::FileKind
// 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 (h.get<Message::Components::Transfer::FileKind>().kind != 1) { if (o.get<ObjComp::Tox::FileKind>().kind != 1) {
// not an avatar // not an avatar
return; return;
} }
const auto& file_info = h.get<Message::Components::Transfer::FileInfo>(); const auto& file_info = o.get<ObjComp::F::SingleInfo>();
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.total_size > 65536ul) { if (file_info.file_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_list.empty() || file_info.file_list.front().file_name.empty() || file_info.total_size == 0) { if (file_info.file_name.empty() || file_info.file_size == 0) {
// reset // reset
clearAvatarFromContact(contact); clearAvatarFromContact(contact);
// TODO: cancel // TODO: cancel
return; return;
} }
if (!h.all_of< if (!o.all_of<
Message::Components::Transfer::FileID ObjComp::Tox::FileID
>()) { >()) {
return; return;
} }
@ -173,7 +178,7 @@ void ToxAvatarManager::checkMsg(Message3Handle h) {
return; return;
} }
if (h.all_of<Message::Components::Transfer::TagHaveAll>()) { if (o.all_of<ObjComp::F::TagLocalHaveAll>()) {
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)) {
@ -184,7 +189,7 @@ void ToxAvatarManager::checkMsg(Message3Handle h) {
std::cerr << "TAM error: cant get toxkey for contact\n"; std::cerr << "TAM error: cant get toxkey for contact\n";
} }
h.emplace_or_replace<Components::TagAvatarImageHandled>(); o.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)) {
@ -199,17 +204,18 @@ void ToxAvatarManager::checkMsg(Message3Handle h) {
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{h, file_path}); _accept_queue.push_back(AcceptEntry{o, file_path});
} }
#endif
} }
bool ToxAvatarManager::onEvent(const Message::Events::MessageConstruct& e) { bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectConstruct& e) {
checkMsg(e.e); checkObj(e.e);
return false; return false;
} }
bool ToxAvatarManager::onEvent(const Message::Events::MessageUpdated& e) { bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectUpdate& e) {
checkMsg(e.e); checkObj(e.e);
return false; return false;
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <solanaceae/message3/registry_message_model.hpp> #include <solanaceae/object_store/object_store.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 RegistryMessageModelEventI { class ToxAvatarManager : public ObjectStoreEventI {
RegistryMessageModel& _rmm; ObjectStore2& _os;
Contact3Registry& _cr; Contact3Registry& _cr;
ConfigModelI& _conf; ConfigModelI& _conf;
struct AcceptEntry { struct AcceptEntry {
Message3Handle m; ObjectHandle m;
std::string file_path; std::string file_path;
}; };
std::vector<AcceptEntry> _accept_queue; std::vector<AcceptEntry> _accept_queue;
public: public:
ToxAvatarManager( ToxAvatarManager(
RegistryMessageModel& rmm, ObjectStore2& os,
Contact3Registry& cr, Contact3Registry& cr,
ConfigModelI& conf ConfigModelI& conf
); );
@ -33,13 +33,14 @@ class ToxAvatarManager : public RegistryMessageModelEventI {
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 checkMsg(Message3Handle h); void checkObj(ObjectHandle o);
protected: // mm protected: // os
bool onEvent(const Message::Events::MessageConstruct& e) override; bool onEvent(const ObjectStore::Events::ObjectConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& e) override; bool onEvent(const ObjectStore::Events::ObjectUpdate& 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/components.hpp> #include <solanaceae/tox_messages/msg_components.hpp>
#include <limits> #include <limits>
#include <cstdint> #include <cstdint>