Compare commits
2 Commits
f27d178b78
...
60f2c07a6a
Author | SHA1 | Date | |
---|---|---|---|
60f2c07a6a | |||
|
2abf09ac06 |
3
external/sdl/CMakeLists.txt
vendored
3
external/sdl/CMakeLists.txt
vendored
@ -20,7 +20,8 @@ if (NOT TARGET SDL3::SDL3)
|
||||
#GIT_TAG 9651ca59187c16079846918483c40d6b5c2f454c # tip 09-06-2024
|
||||
#GIT_TAG 657c0135b1ff1685afa1bad63b0417d92f4bcb46 # tip 09-06-2024
|
||||
#GIT_TAG d65a8146b950abe31b4fbf779f3e2fea731af9bd # tip 16-07-2024 - before string policy changes breaking imgui
|
||||
GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024
|
||||
#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
|
||||
)
|
||||
|
3
external/sdl_image/CMakeLists.txt
vendored
3
external/sdl_image/CMakeLists.txt
vendored
@ -19,7 +19,8 @@ if (NOT TARGET SDL3_image::SDL3_image)
|
||||
#GIT_TAG a34ccf16f961e6d5a480045eb650fc3dddb4bfaa # tip 14-05-2024
|
||||
#GIT_TAG 2fc5310a9a2700fc856663200f94edebeb5e554a # tip 28-05-2024
|
||||
#GIT_TAG 8eff782fa33d795c9ea1ac42dbe7e17cc9874c78 # tip 09-06-2024
|
||||
GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
|
||||
#GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
|
||||
GIT_TAG 2a27018eda394a4e005cd8ba6bb3bfd0298809c7 # tip 29-07-2024
|
||||
FIND_PACKAGE_ARGS # for the future
|
||||
)
|
||||
FetchContent_MakeAvailable(SDL3_image)
|
||||
|
2
external/solanaceae_message3
vendored
2
external/solanaceae_message3
vendored
@ -1 +1 @@
|
||||
Subproject commit f1dd5107f820fe86cb6b6b9ca22d42e0a3a3cf30
|
||||
Subproject commit 9728f71c9833baa65995e19e993d3450da750c20
|
2
external/solanaceae_object_store
vendored
2
external/solanaceae_object_store
vendored
@ -1 +1 @@
|
||||
Subproject commit 4605d64df28c45096cef7748d5c143e942aefeb1
|
||||
Subproject commit 2801fc21fbd6f69479f6638ab1725d00238698f8
|
2
external/solanaceae_tox
vendored
2
external/solanaceae_tox
vendored
@ -1 +1 @@
|
||||
Subproject commit 676e50c61aa7dd816dca846fd06493d2e3ae4aab
|
||||
Subproject commit 1a3d9dd1870b1f45e252ff636adfd0c1f0ccf521
|
16
flake.lock
generated
16
flake.lock
generated
@ -63,34 +63,34 @@
|
||||
"sdl3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1721156595,
|
||||
"narHash": "sha256-BamJ5xK2uEHv1Q5AM2aNYk+ZDsrKCVsyZ4ium7pTTis=",
|
||||
"lastModified": 1722282731,
|
||||
"narHash": "sha256-02y7JB23xAehjqBp6hj7ExNkD06XkFE8Odxnu0hYx8k=",
|
||||
"owner": "libsdl-org",
|
||||
"repo": "SDL",
|
||||
"rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
|
||||
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libsdl-org",
|
||||
"repo": "SDL",
|
||||
"rev": "d65a8146b950abe31b4fbf779f3e2fea731af9bd",
|
||||
"rev": "67b973b5fad633b3be76d4daf4fd9fece292c25f",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"sdl3_image": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1721253495,
|
||||
"narHash": "sha256-mZjO5Tfu6TyGGC/2AduzOqKHW9NdH4oSzyVGjipW5bQ=",
|
||||
"lastModified": 1722000822,
|
||||
"narHash": "sha256-h2vyWcGdLPbg42M6bgJAzskXJfeq9UYTMHi1XdP/otk=",
|
||||
"owner": "libsdl-org",
|
||||
"repo": "SDL_image",
|
||||
"rev": "8abc07df88cc035997e797967ac2f479b0e50981",
|
||||
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libsdl-org",
|
||||
"repo": "SDL_image",
|
||||
"rev": "8abc07df88cc035997e797967ac2f479b0e50981",
|
||||
"rev": "2a27018eda394a4e005cd8ba6bb3bfd0298809c7",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
@ -11,11 +11,11 @@
|
||||
flake = false;
|
||||
};
|
||||
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;
|
||||
};
|
||||
sdl3_image = {
|
||||
url = "github:libsdl-org/SDL_image/8abc07df88cc035997e797967ac2f479b0e50981";
|
||||
url = "github:libsdl-org/SDL_image/2a27018eda394a4e005cd8ba6bb3bfd0298809c7";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
@ -1,7 +1,11 @@
|
||||
#include "./chat_gui4.hpp"
|
||||
|
||||
#include <solanaceae/object_store/object_store.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/util/utils.hpp>
|
||||
|
||||
@ -191,13 +195,14 @@ void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared
|
||||
|
||||
ChatGui4::ChatGui4(
|
||||
ConfigModelI& conf,
|
||||
ObjectStore2& os,
|
||||
RegistryMessageModel& rmm,
|
||||
Contact3Registry& cr,
|
||||
TextureUploaderI& tu,
|
||||
ContactTextureCache& contact_tc,
|
||||
MessageTextureCache& msg_tc,
|
||||
Theme& theme
|
||||
) : _conf(conf), _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) {
|
||||
@ -511,7 +516,7 @@ float ChatGui4::render(float time_delta) {
|
||||
ImGui::TableNextColumn();
|
||||
if (msg_reg.all_of<Message::Components::MessageText>(e)) {
|
||||
renderMessageBodyText(msg_reg, e);
|
||||
} else if (msg_reg.any_of<Message::Components::Transfer::FileInfo>(e)) { // add more comps?
|
||||
} else if (msg_reg.any_of<Message::Components::MessageFileObject>(e)) {
|
||||
renderMessageBodyFile(msg_reg, e);
|
||||
} else {
|
||||
ImGui::TextDisabled("---");
|
||||
@ -829,8 +834,9 @@ float ChatGui4::render(float time_delta) {
|
||||
}
|
||||
|
||||
void ChatGui4::sendFilePath(const char* file_path) {
|
||||
if (_selected_contact && std::filesystem::is_regular_file(file_path)) {
|
||||
_rmm.sendFilePath(*_selected_contact, std::filesystem::path(file_path).filename().generic_u8string(), file_path);
|
||||
const auto path = std::filesystem::path(file_path);
|
||||
if (_selected_contact && std::filesystem::is_regular_file(path)) {
|
||||
_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -940,11 +946,12 @@ void ChatGui4::renderMessageBodyText(Message3Registry& reg, const Message3 e) {
|
||||
}
|
||||
|
||||
void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
auto o = reg.get<Message::Components::MessageFileObject>(e).o;
|
||||
if (
|
||||
!_show_chat_avatar_tf
|
||||
&& (
|
||||
reg.all_of<Message::Components::Transfer::FileKind>(e)
|
||||
&& reg.get<Message::Components::Transfer::FileKind>(e).kind == 1
|
||||
o.all_of<ObjComp::Tox::FileKind>()
|
||||
&& o.get<ObjComp::Tox::FileKind>().kind == 1
|
||||
)
|
||||
) {
|
||||
// TODO: this looks ugly
|
||||
@ -952,6 +959,8 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
#if 0
|
||||
if (msg_reg.all_of<Components::TransferState>(e)) {
|
||||
switch (msg_reg.get<Components::TransferState>(e).state) {
|
||||
@ -965,51 +974,24 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
}
|
||||
#endif
|
||||
// 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");
|
||||
} else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) {
|
||||
ImGui::TextUnformatted("done");
|
||||
//} else if (reg.all_of<Message::Components::Transfer::TagReceiving, Message::Components::Transfer::TagHaveAll>(e)) {
|
||||
// ImGui::TextUnformatted("done");
|
||||
} else {
|
||||
// TODO: missing other states
|
||||
ImGui::TextUnformatted("running");
|
||||
}
|
||||
|
||||
if (reg.all_of<Message::Components::Transfer::TagHaveAll, Message::Components::Transfer::FileInfoLocal>(e)) {
|
||||
// hack lul
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("forward")) {
|
||||
ImGui::OpenPopup("forward to contact");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("forward to contact")) {
|
||||
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
|
||||
// filter
|
||||
if (_cr.any_of<Contact::Components::RequestIncoming, Contact::Components::TagRequestOutgoing>(c)) {
|
||||
continue;
|
||||
}
|
||||
// TODO: check for contact capability
|
||||
|
||||
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 1, false, true, false)) {
|
||||
//if (renderContactListContactSmall(c, false)) {
|
||||
//_rmm.sendFilePath(*_selected_contact, path.filename().generic_u8string(), path.generic_u8string());
|
||||
const auto& fil = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
|
||||
for (const auto& path : fil.file_list) {
|
||||
_rmm.sendFilePath(c, std::filesystem::path{path}.filename().generic_u8string(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
// if in offered state
|
||||
// paused, never started
|
||||
if (
|
||||
reg.all_of<Message::Components::Transfer::TagReceiving>(e) &&
|
||||
reg.all_of<Message::Components::Transfer::TagPaused>(e) &&
|
||||
!o.all_of<ObjComp::F::TagLocalHaveAll>() &&
|
||||
//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?
|
||||
!reg.all_of<Message::Components::Transfer::FileInfoLocal>(e) &&
|
||||
!reg.all_of<Message::Components::Transfer::ActionAccept>(e)
|
||||
!o.all_of<ObjComp::F::SingleInfoLocal>() &&
|
||||
!o.all_of<ObjComp::Ephemeral::File::ActionTransferAccept>()
|
||||
) {
|
||||
if (ImGui::Button("save to")) {
|
||||
_fss.requestFile(
|
||||
@ -1018,11 +1000,13 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
path.remove_filename();
|
||||
return std::filesystem::is_directory(path);
|
||||
},
|
||||
[this, ®, e](const auto& path) {
|
||||
if (reg.valid(e)) { // still valid
|
||||
[this, o](const auto& path) {
|
||||
if (static_cast<bool>(o)) { // still valid
|
||||
// TODO: trim file?
|
||||
reg.emplace<Message::Components::Transfer::ActionAccept>(e, path.string());
|
||||
_rmm.throwEventUpdate(reg, e);
|
||||
o.emplace<ObjComp::Ephemeral::File::ActionTransferAccept>(path.generic_u8string());
|
||||
//_rmm.throwEventUpdate(reg, e);
|
||||
// TODO: block recursion
|
||||
_os.throwEventUpdate(o);
|
||||
}
|
||||
},
|
||||
[](){}
|
||||
@ -1030,97 +1014,75 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
}
|
||||
}
|
||||
|
||||
// down progress
|
||||
if (reg.all_of<Message::Components::Transfer::TagReceiving>(e)) {
|
||||
ImGui::TextUnformatted("down");
|
||||
if (reg.all_of<Message::Components::Transfer::BytesReceived>(e)) {
|
||||
ImGui::SameLine();
|
||||
// hacky
|
||||
const auto* fts = o.try_get<ObjComp::Ephemeral::File::TransferStats>();
|
||||
if (fts != nullptr && o.any_of<ObjComp::F::SingleInfo, ObjComp::F::CollectionInfo>()) {
|
||||
const auto total_size =
|
||||
o.all_of<ObjComp::F::SingleInfo>() ?
|
||||
o.get<ObjComp::F::SingleInfo>().file_size :
|
||||
o.get<ObjComp::F::CollectionInfo>().total_size
|
||||
;
|
||||
|
||||
float fraction = float(reg.get<Message::Components::Transfer::BytesReceived>(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
|
||||
}
|
||||
}
|
||||
|
||||
// (can be both)
|
||||
// up progess
|
||||
if (reg.all_of<Message::Components::Transfer::TagSending>(e)) {
|
||||
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");
|
||||
if (reg.all_of<Message::Components::Transfer::BytesSent>(e)) {
|
||||
total = fts->total_up;
|
||||
rate = fts->rate_up;
|
||||
} else {
|
||||
// else show download progress
|
||||
ImGui::TextUnformatted("down");
|
||||
total = fts->total_down;
|
||||
rate = fts->rate_down;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
float fraction = float(reg.get<Message::Components::Transfer::BytesSent>(e).total) / reg.get<Message::Components::Transfer::FileInfo>(e).total_size;
|
||||
float fraction = float(total) / total_size;
|
||||
|
||||
char overlay_buf[32];
|
||||
if (rate > 0.000001f) {
|
||||
const char* byte_suffix = "???";
|
||||
int64_t byte_divider = sizeToHumanReadable(rate, byte_suffix);
|
||||
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%% @ %.1f%s/s", fraction * 100 + 0.01f, rate/byte_divider, byte_suffix);
|
||||
} else {
|
||||
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f);
|
||||
}
|
||||
|
||||
ImGui::ProgressBar(
|
||||
fraction,
|
||||
{-FLT_MIN, TEXT_BASE_HEIGHT},
|
||||
overlay_buf
|
||||
);
|
||||
// TODO: numbers
|
||||
}
|
||||
} else {
|
||||
// infinite scrolling progressbar fallback
|
||||
ImGui::TextUnformatted(" ??");
|
||||
ImGui::SameLine();
|
||||
ImGui::ProgressBar(
|
||||
-0.333f * ImGui::GetTime(),
|
||||
{-FLT_MIN, TEXT_BASE_HEIGHT},
|
||||
"?%"
|
||||
);
|
||||
}
|
||||
|
||||
const auto file_list = reg.get<Message::Components::Transfer::FileInfo>(e).file_list;
|
||||
if (!o.all_of<ObjComp::F::TagLocalHaveAll>() && o.all_of<ObjComp::F::LocalHaveBitset>()) {
|
||||
// texture based on have bitset
|
||||
// TODO: missing have chunks/chunksize to get the correct size
|
||||
|
||||
// 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();
|
||||
//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 (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);
|
||||
if (o.all_of<ObjComp::F::FrameDims>()) {
|
||||
const auto& frame_dims = o.get<ObjComp::F::FrameDims>();
|
||||
|
||||
// TODO: config
|
||||
const auto max_inline_height = 10*TEXT_BASE_HEIGHT;
|
||||
|
||||
float height = frame_dims.height;
|
||||
float width = frame_dims.width;
|
||||
float width = frame_dims.w;
|
||||
float height = frame_dims.h;
|
||||
|
||||
if (height > max_inline_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();
|
||||
ImGui::Dummy(ImVec2{width, height});
|
||||
|
||||
// deploy dummy of framedim size and check visibility
|
||||
bool image_preview_visible = ImGui::IsItemVisible();
|
||||
|
||||
|
||||
if (image_preview_visible && file_list.size() == 1 && reg.all_of<Message::Components::Transfer::FileInfoLocal>(e)) {
|
||||
// +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>()) {
|
||||
ImGui::SetCursorPos(orig_curser_pos); // reset for actual img
|
||||
|
||||
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;
|
||||
//}
|
||||
|
||||
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
|
||||
}
|
||||
} else if (o.all_of<ObjComp::F::SingleInfo>()) { // only show info if not inlined image
|
||||
// just filename
|
||||
const auto& si = o.get<ObjComp::F::SingleInfo>();
|
||||
const char* byte_suffix = "???";
|
||||
int64_t byte_divider = sizeToHumanReadable(si.file_size, byte_suffix);
|
||||
ImGui::Text("%s (%.2lf %s)", si.file_name.c_str(), double(si.file_size)/byte_divider, byte_suffix);
|
||||
} else if (o.all_of<ObjComp::F::CollectionInfo>()) {
|
||||
// same old bulletpoint list
|
||||
const auto& file_list = o.get<ObjComp::F::CollectionInfo>().file_list;
|
||||
|
||||
// if has local, display save base path?, do we have base save path?
|
||||
|
||||
for (size_t i = 0; i < file_list.size(); i++) {
|
||||
ImGui::PushID(i);
|
||||
|
||||
const char* byte_suffix = "???";
|
||||
int64_t byte_divider = sizeToHumanReadable(file_list[i].file_size, byte_suffix);
|
||||
|
||||
// TODO: selectable text widget ?
|
||||
ImGui::Bullet(); ImGui::Text("%s (%.2lf %s)", file_list[i].file_name.c_str(), double(file_list[i].file_size)/byte_divider, byte_suffix);
|
||||
|
||||
if (o.all_of<ObjComp::F::CollectionInfoLocal>()) {
|
||||
const auto& local_info = o.get<ObjComp::F::CollectionInfoLocal>();
|
||||
if (local_info.file_list.size() > i && ImGui::BeginPopupContextItem("##file_c")) {
|
||||
|
||||
if (ImGui::MenuItem("open")) {
|
||||
const std::string url {file_path_to_file_url(local_info.file_list.at(i).file_path)};
|
||||
std::cout << "opening file '" << url << "'\n";
|
||||
SDL_OpenURL(url.c_str());
|
||||
}
|
||||
if (ImGui::MenuItem("copy file")) {
|
||||
const std::string url {file_path_to_file_url(local_info.file_list.at(i).file_path)};
|
||||
//ImGui::SetClipboardText(url.c_str());
|
||||
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
|
||||
}
|
||||
if (ImGui::MenuItem("copy filepath")) {
|
||||
const auto file_path = std::filesystem::canonical(local_info.file_list.at(i).file_path).u8string(); //TODO: use generic over native?
|
||||
ImGui::SetClipboardText(file_path.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("neither info available");
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
if (o.all_of<ObjComp::F::SingleInfoLocal>()) {
|
||||
const auto& local_info = o.get<ObjComp::F::SingleInfoLocal>();
|
||||
if (!local_info.file_path.empty() && ImGui::BeginPopupContextItem("##file_c")) {
|
||||
if (o.all_of<ObjComp::F::TagLocalHaveAll>()) {
|
||||
if (ImGui::BeginMenu("forward")) {
|
||||
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
|
||||
// filter
|
||||
if (_cr.any_of<Contact::Components::RequestIncoming, Contact::Components::TagRequestOutgoing>(c)) {
|
||||
continue;
|
||||
}
|
||||
// TODO: check for contact capability
|
||||
// or just error popup?/noti/toast
|
||||
|
||||
if (renderContactBig(_theme, _contact_tc, {_cr, c}, 1, false, true, false)) {
|
||||
// TODO: try object interface first instead, then fall back to send with SingleInfoLocal
|
||||
//_rmm.sendFileObj(c, o);
|
||||
std::filesystem::path path = o.get<ObjComp::F::SingleInfoLocal>().file_path;
|
||||
_rmm.sendFilePath(c, path.filename().generic_u8string(), path.generic_u8string());
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("open")) {
|
||||
const std::string url {file_path_to_file_url(local_info.file_path)};
|
||||
std::cout << "opening file '" << url << "'\n";
|
||||
SDL_OpenURL(url.c_str());
|
||||
}
|
||||
if (ImGui::MenuItem("copy file")) {
|
||||
const std::string url {file_path_to_file_url(local_info.file_path)};
|
||||
//ImGui::SetClipboardText(url.c_str());
|
||||
setClipboardData({"text/uri-list", "text/x-moz-url"}, std::make_shared<std::vector<uint8_t>>(url.begin(), url.end()));
|
||||
}
|
||||
if (ImGui::MenuItem("copy filepath")) {
|
||||
const auto file_path = std::filesystem::canonical(local_info.file_path).u8string(); //TODO: use generic over native?
|
||||
ImGui::SetClipboardText(file_path.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
// TODO: how to collections?
|
||||
|
||||
if (ImGui::BeginItemTooltip()) {
|
||||
if (o.all_of<ObjComp::F::SingleInfo>()) {
|
||||
ImGui::SeparatorText("single info");
|
||||
const auto& si = o.get<ObjComp::F::SingleInfo>();
|
||||
ImGui::Text("file name: '%s'", si.file_name.c_str());
|
||||
ImGui::Text("file size: %lu Bytes", si.file_size);
|
||||
if (o.all_of<ObjComp::F::SingleInfoLocal>()) {
|
||||
ImGui::Text("local path: '%s'", o.get<ObjComp::F::SingleInfoLocal>().file_path.c_str());
|
||||
}
|
||||
} else if (o.all_of<ObjComp::F::CollectionInfo>()) {
|
||||
ImGui::SeparatorText("collection info");
|
||||
const auto& ci = o.get<ObjComp::F::CollectionInfo>();
|
||||
ImGui::Text("total size: %lu Bytes", ci.total_size);
|
||||
}
|
||||
|
||||
if (fts != nullptr) {
|
||||
ImGui::SeparatorText("transfer stats");
|
||||
ImGui::Text("rate up : %.1f Bytes/s", fts->rate_up);
|
||||
ImGui::Text("rate down : %.1f Bytes/s", fts->rate_down);
|
||||
ImGui::Text("total up : %lu Bytes", fts->total_up);
|
||||
ImGui::Text("total down: %lu Bytes", fts->total_down);
|
||||
}
|
||||
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) {
|
||||
if (reg.all_of<Message::Components::Transfer::FileKind>(e)) {
|
||||
ImGui::TextDisabled("fk:%lu", reg.get<Message::Components::Transfer::FileKind>(e).kind);
|
||||
if (reg.all_of<Message::Components::MessageFileObject>(e)) {
|
||||
const auto o = reg.get<Message::Components::MessageFileObject>(e).o;
|
||||
|
||||
ImGui::TextDisabled("o:%u", entt::to_integral(o.entity()));
|
||||
|
||||
if (o.all_of<ObjComp::Tox::FileKind>()) {
|
||||
ImGui::TextDisabled("fk:%lu", o.get<ObjComp::Tox::FileKind>().kind);
|
||||
}
|
||||
if (o.all_of<ObjComp::Ephemeral::ToxTransferFriend>()) {
|
||||
ImGui::TextDisabled("ttf:%u", o.get<ObjComp::Ephemeral::ToxTransferFriend>().transfer_number);
|
||||
}
|
||||
if (reg.all_of<Message::Components::Transfer::ToxTransferFriend>(e)) {
|
||||
ImGui::TextDisabled("ttf:%u", reg.get<Message::Components::Transfer::ToxTransferFriend>(e).transfer_number);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool ChatGui4::renderSubContactListContact(const Contact3 c, const bool selected) const {
|
||||
std::string label;
|
||||
|
||||
if (_cr.all_of<Contact::Components::ConnectionState>(c)) {
|
||||
const auto c_state = _cr.get<Contact::Components::ConnectionState>(c).state;
|
||||
if (c_state == Contact::Components::ConnectionState::State::direct) {
|
||||
label += "[D] ";
|
||||
} else if (c_state == Contact::Components::ConnectionState::State::cloud) {
|
||||
label += "[C] ";
|
||||
} else {
|
||||
label += "[-] ";
|
||||
}
|
||||
} else {
|
||||
label += "[?] ";
|
||||
}
|
||||
|
||||
label += (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name.c_str() : "<unk>");
|
||||
label += "###";
|
||||
label += std::to_string(entt::to_integral(c));
|
||||
|
||||
return ImGui::Selectable(label.c_str(), selected);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ChatGui4::pasteFile(const char* mime_type) {
|
||||
size_t data_size = 0;
|
||||
void* data = SDL_GetClipboardData(mime_type, &data_size);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <solanaceae/object_store/fwd.hpp>
|
||||
#include <solanaceae/message3/registry_message_model.hpp>
|
||||
#include <solanaceae/util/config_model.hpp>
|
||||
|
||||
@ -25,6 +26,7 @@ using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoad
|
||||
|
||||
class ChatGui4 {
|
||||
ConfigModelI& _conf;
|
||||
ObjectStore2& _os;
|
||||
RegistryMessageModel& _rmm;
|
||||
Contact3Registry& _cr;
|
||||
|
||||
@ -57,6 +59,7 @@ class ChatGui4 {
|
||||
public:
|
||||
ChatGui4(
|
||||
ConfigModelI& conf,
|
||||
ObjectStore2& os,
|
||||
RegistryMessageModel& rmm,
|
||||
Contact3Registry& cr,
|
||||
TextureUploaderI& tu,
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
|
||||
ImageInfo res;
|
||||
@ -14,6 +15,9 @@ ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t
|
||||
return res;
|
||||
}
|
||||
|
||||
assert(surf->w >= 0);
|
||||
assert(surf->h >= 0);
|
||||
|
||||
res.width = surf->w;
|
||||
res.height = surf->h;
|
||||
res.file_ext = "bmp";
|
||||
@ -24,32 +28,48 @@ ImageLoaderSDLBMP::ImageInfo ImageLoaderSDLBMP::loadInfoFromMemory(const uint8_t
|
||||
}
|
||||
|
||||
ImageLoaderSDLBMP::ImageResult ImageLoaderSDLBMP::loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) {
|
||||
ImageResult res;
|
||||
|
||||
auto* ios = SDL_IOFromConstMem(data, data_size);
|
||||
|
||||
SDL_Surface* surf = SDL_LoadBMP_IO(ios, SDL_TRUE);
|
||||
if (surf == nullptr) {
|
||||
return res;
|
||||
return {};
|
||||
}
|
||||
|
||||
SDL_Surface* conv_surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
|
||||
SDL_DestroySurface(surf);
|
||||
if (conv_surf == nullptr) {
|
||||
return res;
|
||||
return {};
|
||||
}
|
||||
|
||||
res.width = surf->w;
|
||||
res.height = surf->h;
|
||||
res.file_ext = "bmp";
|
||||
assert(conv_surf->w >= 0);
|
||||
assert(conv_surf->h >= 0);
|
||||
|
||||
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();
|
||||
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)};
|
||||
|
||||
if (SDL_MUSTLOCK(conv_surf)) {
|
||||
SDL_UnlockSurface(conv_surf);
|
||||
}
|
||||
SDL_DestroySurface(conv_surf);
|
||||
|
||||
std::cout << "IL_SDLBMP: loaded img " << res.width << "x" << res.height << "\n";
|
||||
|
@ -26,17 +26,17 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme
|
||||
#endif
|
||||
tcm(cr, tc, tc),
|
||||
tmm(rmm, cr, tcm, tc, tc),
|
||||
ttm(rmm, cr, tcm, tc, tc),
|
||||
ttm(rmm, cr, tcm, tc, tc, os),
|
||||
tffom(cr, rmm, tcm, tc, tc),
|
||||
theme(theme_),
|
||||
mmil(rmm),
|
||||
tam(rmm, cr, conf),
|
||||
tam(/*rmm, */ os, cr, conf),
|
||||
sdlrtu(renderer_),
|
||||
tal(cr),
|
||||
contact_tc(tal, sdlrtu),
|
||||
mil(),
|
||||
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),
|
||||
osui(os),
|
||||
tuiu(tc, conf),
|
||||
|
@ -7,72 +7,118 @@
|
||||
|
||||
#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>
|
||||
|
||||
void MediaMetaInfoLoader::handleMessage(const Message3Handle& m) {
|
||||
if (m.any_of<Message::Components::TagNotImage, Message::Components::FrameDims>()) {
|
||||
if (!static_cast<bool>(m)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m.all_of<Message::Components::Transfer::FileInfoLocal, Message::Components::Transfer::TagHaveAll>()) {
|
||||
// move to obj
|
||||
if (m.any_of<Message::Components::TagNotImage>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& fil = m.get<Message::Components::Transfer::FileInfoLocal>();
|
||||
if (fil.file_list.size() != 1) {
|
||||
if (!m.all_of<Message::Components::MessageFileObject>()) {
|
||||
// not a file message
|
||||
return;
|
||||
}
|
||||
const auto& o = m.get<Message::Components::MessageFileObject>().o;
|
||||
|
||||
if (!static_cast<bool>(o)) {
|
||||
std::cerr << "MMIL error: invalid object in file message\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(fil.file_list.front(), std::ios::binary);
|
||||
if (file.is_open()) {
|
||||
// figure out size
|
||||
file.seekg(0, file.end);
|
||||
if (file.tellg() > 50*1024*1024) {
|
||||
// TODO: conf
|
||||
// dont try load files larger 50mb
|
||||
if (o.any_of<ObjComp::F::FrameDims>()) {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
auto res = il->loadInfoFromMemory(read_data.ptr, read_data.size);
|
||||
if (res.height == 0 || res.width == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m.emplace<Message::Components::FrameDims>(res.width, res.height);
|
||||
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()))
|
||||
);
|
||||
|
||||
could_load = true;
|
||||
|
||||
std::cout << "MMIL loaded image info " << fil.file_list.front() << "\n";
|
||||
std::cout << "MMIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
|
||||
|
||||
_rmm.throwEventUpdate(m);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!could_load) {
|
||||
m.emplace<Message::Components::TagNotImage>();
|
||||
|
||||
std::cout << "MMIL loading failed image info " << fil.file_list.front() << "\n";
|
||||
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) {
|
||||
// HACK: make them be added externally?
|
||||
|
@ -6,11 +6,6 @@
|
||||
|
||||
namespace Message::Components {
|
||||
|
||||
struct FrameDims {
|
||||
uint32_t width {0};
|
||||
uint32_t height {0};
|
||||
};
|
||||
|
||||
struct TagNotImage {};
|
||||
|
||||
} // Message::Components
|
||||
@ -31,5 +26,10 @@ class MediaMetaInfoLoader : public RegistryMessageModelEventI {
|
||||
protected: // rmm
|
||||
bool onEvent(const Message::Events::MessageConstruct& e) override;
|
||||
bool onEvent(const Message::Events::MessageUpdated& e) override;
|
||||
|
||||
//protected: // os
|
||||
// bool onEvent(const ObjectStore::Events::ObjectConstruct& e) override;
|
||||
// should listen on update
|
||||
// bool onEvent(const ObjectStore::Events::ObjectUpdate& e) override;
|
||||
};
|
||||
|
||||
|
@ -8,10 +8,15 @@
|
||||
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
|
||||
#include "./os_comps.hpp"
|
||||
|
||||
#include <solanaceae/object_store/object_store.hpp>
|
||||
|
||||
#include <solanaceae/file/file2.hpp>
|
||||
|
||||
#include <entt/entity/entity.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
// fwd
|
||||
namespace Message {
|
||||
@ -34,26 +39,66 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (m.all_of<Message::Components::Transfer::FileInfoLocal>()) {
|
||||
const auto& file_list = m.get<Message::Components::Transfer::FileInfoLocal>().file_list;
|
||||
assert(!file_list.empty());
|
||||
const auto& file_path = file_list.front();
|
||||
|
||||
std::ifstream file(file_path, std::ios::binary);
|
||||
if (file.is_open()) {
|
||||
std::vector<uint8_t> tmp_buffer;
|
||||
while (file.good()) {
|
||||
auto ch = file.get();
|
||||
if (ch == EOF) {
|
||||
break;
|
||||
} else {
|
||||
tmp_buffer.push_back(ch);
|
||||
if (!m.all_of<Message::Components::MessageFileObject>()) {
|
||||
// not a file message
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto& o = m.get<Message::Components::MessageFileObject>().o;
|
||||
|
||||
if (!static_cast<bool>(o)) {
|
||||
std::cerr << "MIL error: invalid object in file message\n";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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(tmp_buffer.data(), tmp_buffer.size());
|
||||
auto res = il->loadFromMemoryRGBA(read_data.ptr, read_data.size);
|
||||
if (res.frames.empty() || res.height == 0 || res.width == 0) {
|
||||
continue;
|
||||
}
|
||||
@ -70,15 +115,12 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
|
||||
new_entry.width = res.width;
|
||||
new_entry.height = res.height;
|
||||
|
||||
std::cout << "MIL: loaded image file " << file_path << "\n";
|
||||
std::cout << "MIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
|
||||
|
||||
return new_entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "MIL: failed to load message\n";
|
||||
|
||||
std::cerr << "MIL error: failed to load message (unhandled format)\n";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,3 @@ class MessageImageLoader {
|
||||
std::optional<TextureEntry> load(TextureUploaderI& tu, Message3Handle c);
|
||||
};
|
||||
|
||||
// TODO: move to rmm
|
||||
template<>
|
||||
struct std::hash<Message3Handle> {
|
||||
std::size_t operator()(Message3Handle const& m) const noexcept {
|
||||
const std::size_t h1 = reinterpret_cast<std::size_t>(m.registry());
|
||||
const std::size_t h2 = entt::to_integral(m.entity());
|
||||
return (h1 << 3) ^ (h2 * 11400714819323198485llu);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,20 +2,25 @@
|
||||
|
||||
#include <solanaceae/util/utils.hpp>
|
||||
#include <solanaceae/object_store/meta_components.hpp>
|
||||
#include <solanaceae/object_store/meta_components_file.hpp>
|
||||
#include "./os_comps.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
|
||||
namespace MM {
|
||||
|
||||
template<> void ComponentEditorWidget<ObjectStore::Components::ID>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<ObjectStore::Components::ID>(entity);
|
||||
|
||||
const auto str = bin2hex(c.v);
|
||||
if (ImGui::SmallButton("copy")) {
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(str.c_str());
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<> void ComponentEditorWidget<Message::Components::Transfer::FileInfo>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<Message::Components::Transfer::FileInfo>(entity);
|
||||
|
||||
@ -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) {
|
||||
auto& c = registry.get<Message::Components::Transfer::BytesReceived>(entity);
|
||||
ImGui::Text("total bytes received: %lu", c.total);
|
||||
#endif
|
||||
|
||||
template<> void ComponentEditorWidget<ObjComp::F::SingleInfo>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<ObjComp::F::SingleInfo>(entity);
|
||||
if (!c.file_name.empty()) {
|
||||
ImGui::Text("file name: %s", c.file_name.c_str());
|
||||
}
|
||||
|
||||
template<> void ComponentEditorWidget<Message::Components::Transfer::BytesSent>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<Message::Components::Transfer::BytesSent>(entity);
|
||||
ImGui::Text("total bytes sent: %lu", c.total);
|
||||
ImGui::Text("file size: %lu", c.file_size);
|
||||
}
|
||||
|
||||
template<> void ComponentEditorWidget<ObjComp::F::SingleInfoLocal>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<ObjComp::F::SingleInfoLocal>(entity);
|
||||
if (!c.file_path.empty()) {
|
||||
ImGui::Text("file path: %s", c.file_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
template<> void ComponentEditorWidget<ObjComp::Ephemeral::File::TransferStats>(entt::basic_registry<Object>& registry, Object entity) {
|
||||
auto& c = registry.get<ObjComp::Ephemeral::File::TransferStats>(entity);
|
||||
ImGui::Text("upload rate : %.1f bytes/s", c.rate_up);
|
||||
ImGui::Text("download rate: %.1f bytes/s", c.rate_down);
|
||||
ImGui::Text("total bytes uploaded : %lu bytes", c.total_up);
|
||||
ImGui::Text("total bytes downloaded: %lu bytes", c.total_down);
|
||||
}
|
||||
|
||||
} // MM
|
||||
@ -74,10 +95,20 @@ ObjectStoreUI::ObjectStoreUI(
|
||||
_ee.registerComponent<ObjectStore::Components::ID>("ID");
|
||||
_ee.registerComponent<ObjectStore::Components::DataCompressionType>("DataCompressionType");
|
||||
|
||||
_ee.registerComponent<Message::Components::Transfer::FileInfo>("Transfer::FileInfo");
|
||||
_ee.registerComponent<Message::Components::Transfer::FileInfoLocal>("Transfer::FileInfoLocal");
|
||||
_ee.registerComponent<Message::Components::Transfer::BytesReceived>("Transfer::BytesReceived");
|
||||
_ee.registerComponent<Message::Components::Transfer::BytesSent>("Transfer::BytesSent");
|
||||
_ee.registerComponent<ObjComp::Ephemeral::FilePath>("Ephemeral::FilePath");
|
||||
|
||||
_ee.registerComponent<ObjComp::F::SingleInfo>("File::SingleInfo");
|
||||
_ee.registerComponent<ObjComp::F::SingleInfoLocal>("File::SingleInfoLocal");
|
||||
_ee.registerComponent<ObjComp::F::Collection>("File::Collection");
|
||||
_ee.registerComponent<ObjComp::F::CollectionInfo>("File::CollectionInfo");
|
||||
_ee.registerComponent<ObjComp::F::CollectionInfoLocal>("File::CollectionInfoLocal");
|
||||
_ee.registerComponent<ObjComp::F::LocalHaveBitset>("File::LocalHaveBitset");
|
||||
_ee.registerComponent<ObjComp::F::RemoteHaveBitset>("File::RemoteHaveBitset");
|
||||
|
||||
_ee.registerComponent<ObjComp::Ephemeral::File::DownloadPriority>("Ephemeral::File::DownloadPriority");
|
||||
_ee.registerComponent<ObjComp::Ephemeral::File::ReadHeadHint>("Ephemeral::File::ReadHeadHint");
|
||||
_ee.registerComponent<ObjComp::Ephemeral::File::TransferStats>("Ephemeral::File::TransferStats");
|
||||
_ee.registerComponent<ObjComp::Ephemeral::File::TransferStatsSeparated>("Ephemeral::File::TransferStatsSeparated");
|
||||
}
|
||||
|
||||
void ObjectStoreUI::render(void) {
|
||||
|
41
src/os_comps.hpp
Normal file
41
src/os_comps.hpp
Normal 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
26
src/os_comps_id.inl
Normal 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
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "./tox_avatar_manager.hpp"
|
||||
|
||||
// TODO: this whole thing needs a rewrite and is currently disabled
|
||||
|
||||
#include <solanaceae/util/config_model.hpp>
|
||||
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
//#include <solanaceae/message3/components.hpp>
|
||||
#include <solanaceae/object_store/meta_components_file.hpp>
|
||||
// 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/tox_contacts/components.hpp>
|
||||
@ -26,12 +29,13 @@ namespace Components {
|
||||
};
|
||||
|
||||
ToxAvatarManager::ToxAvatarManager(
|
||||
RegistryMessageModel& rmm,
|
||||
//RegistryMessageModel& rmm,
|
||||
ObjectStore2& os,
|
||||
Contact3Registry& cr,
|
||||
ConfigModelI& conf
|
||||
) : _rmm(rmm), _cr(cr), _conf(conf) {
|
||||
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
|
||||
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
|
||||
) : /*_rmm(rmm)*/ _os(os), _cr(cr), _conf(conf) {
|
||||
_os.subscribe(this, ObjectStore_Event::object_construct);
|
||||
_os.subscribe(this, ObjectStore_Event::object_update);
|
||||
|
||||
if (!_conf.has_string("ToxAvatarManager", "save_path")) {
|
||||
// or on linux: $HOME/.config/tox/avatars/
|
||||
@ -64,15 +68,15 @@ void ToxAvatarManager::iterate(void) {
|
||||
// cancel queue
|
||||
|
||||
// accept queue
|
||||
for (auto& [m, path] : _accept_queue) {
|
||||
if (m.all_of<Message::Components::Transfer::ActionAccept>()) {
|
||||
continue; // already accepted
|
||||
for (auto& [o, path] : _accept_queue) {
|
||||
if (o.any_of<ObjComp::Ephemeral::File::ActionTransferAccept, ObjComp::F::TagLocalHaveAll>()) {
|
||||
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";
|
||||
|
||||
_rmm.throwEventUpdate(m);
|
||||
_os.throwEventUpdate(o);
|
||||
}
|
||||
_accept_queue.clear();
|
||||
|
||||
@ -87,9 +91,6 @@ std::string ToxAvatarManager::getAvatarPath(const ToxKey& key) const {
|
||||
}
|
||||
|
||||
void ToxAvatarManager::addAvatarFileToContact(const Contact3 c, const ToxKey& key) {
|
||||
//const std::string_view avatar_save_path {_conf.get_string("ToxAvatarManager", "save_path").value()};
|
||||
//const auto pub_key_string = bin2hex({key.data.cbegin(), key.data.cend()});
|
||||
//const auto file_path = std::filesystem::path(avatar_save_path) / (pub_key_string + ".png");
|
||||
const auto file_path = getAvatarPath(key);
|
||||
if (std::filesystem::is_regular_file(file_path)) {
|
||||
// avatar file png file exists
|
||||
@ -106,58 +107,62 @@ void ToxAvatarManager::clearAvatarFromContact(const Contact3 c) {
|
||||
}
|
||||
}
|
||||
|
||||
void ToxAvatarManager::checkMsg(Message3Handle h) {
|
||||
if (h.any_of<
|
||||
Message::Components::Transfer::ActionAccept,
|
||||
void ToxAvatarManager::checkObj(ObjectHandle o) {
|
||||
if (o.any_of<
|
||||
ObjComp::Ephemeral::File::ActionTransferAccept,
|
||||
Components::TagAvatarImageHandled
|
||||
>()) {
|
||||
return; // already accepted or handled
|
||||
}
|
||||
|
||||
if (!h.any_of<
|
||||
Message::Components::Transfer::TagPaused,
|
||||
Message::Components::Transfer::TagHaveAll
|
||||
if (!o.any_of<
|
||||
ObjComp::Ephemeral::File::TagTransferPaused,
|
||||
ObjComp::F::TagLocalHaveAll
|
||||
>()) {
|
||||
// we only handle unaccepted or finished
|
||||
return;
|
||||
}
|
||||
|
||||
if (!h.all_of<
|
||||
Message::Components::Transfer::TagReceiving,
|
||||
Message::Components::Transfer::FileInfo,
|
||||
Message::Components::Transfer::FileKind,
|
||||
Message::Components::ContactFrom // should always be there, just making sure
|
||||
if (!o.all_of<
|
||||
ObjComp::Tox::TagIncomming,
|
||||
//ObjComp::Ephemeral::Backend,
|
||||
ObjComp::F::SingleInfo,
|
||||
ObjComp::Tox::FileKind
|
||||
// TODO: mesage? how do we know where a file is from??
|
||||
//Message::Components::ContactFrom // should always be there, just making sure
|
||||
>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
if (file_info.total_size > 65536ul) {
|
||||
if (file_info.file_size > 65536ul) {
|
||||
// TODO: mark handled?
|
||||
return; // too large
|
||||
}
|
||||
|
||||
#if 0 // TODO: make avatars work again !!!!!
|
||||
|
||||
// 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
|
||||
clearAvatarFromContact(contact);
|
||||
// TODO: cancel
|
||||
return;
|
||||
}
|
||||
|
||||
if (!h.all_of<
|
||||
Message::Components::Transfer::FileID
|
||||
if (!o.all_of<
|
||||
ObjComp::Tox::FileID
|
||||
>()) {
|
||||
return;
|
||||
}
|
||||
@ -173,7 +178,7 @@ void ToxAvatarManager::checkMsg(Message3Handle h) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (h.all_of<Message::Components::Transfer::TagHaveAll>()) {
|
||||
if (o.all_of<ObjComp::F::TagLocalHaveAll>()) {
|
||||
std::cout << "TAM: full avatar received\n";
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
h.emplace_or_replace<Components::TagAvatarImageHandled>();
|
||||
o.emplace_or_replace<Components::TagAvatarImageHandled>();
|
||||
} else {
|
||||
// check file id for existing hash
|
||||
if (std::filesystem::is_regular_file(file_path)) {
|
||||
@ -199,17 +204,18 @@ void ToxAvatarManager::checkMsg(Message3Handle h) {
|
||||
std::cout << "TAM: accepted avatar ft\n";
|
||||
|
||||
// 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) {
|
||||
checkMsg(e.e);
|
||||
bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectConstruct& e) {
|
||||
checkObj(e.e);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ToxAvatarManager::onEvent(const Message::Events::MessageUpdated& e) {
|
||||
checkMsg(e.e);
|
||||
bool ToxAvatarManager::onEvent(const ObjectStore::Events::ObjectUpdate& e) {
|
||||
checkObj(e.e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <solanaceae/message3/registry_message_model.hpp>
|
||||
#include <solanaceae/object_store/object_store.hpp>
|
||||
#include <solanaceae/contact/contact_model3.hpp>
|
||||
|
||||
#include <string>
|
||||
@ -12,20 +12,20 @@
|
||||
struct ConfigModelI;
|
||||
struct ToxKey;
|
||||
|
||||
class ToxAvatarManager : public RegistryMessageModelEventI {
|
||||
RegistryMessageModel& _rmm;
|
||||
class ToxAvatarManager : public ObjectStoreEventI {
|
||||
ObjectStore2& _os;
|
||||
Contact3Registry& _cr;
|
||||
ConfigModelI& _conf;
|
||||
|
||||
struct AcceptEntry {
|
||||
Message3Handle m;
|
||||
ObjectHandle m;
|
||||
std::string file_path;
|
||||
};
|
||||
std::vector<AcceptEntry> _accept_queue;
|
||||
|
||||
public:
|
||||
ToxAvatarManager(
|
||||
RegistryMessageModel& rmm,
|
||||
ObjectStore2& os,
|
||||
Contact3Registry& cr,
|
||||
ConfigModelI& conf
|
||||
);
|
||||
@ -33,13 +33,14 @@ class ToxAvatarManager : public RegistryMessageModelEventI {
|
||||
void iterate(void);
|
||||
|
||||
protected:
|
||||
// TODO: become backend and work in objects instead
|
||||
std::string getAvatarPath(const ToxKey& key) const;
|
||||
void addAvatarFileToContact(const Contact3 c, const ToxKey& key);
|
||||
void clearAvatarFromContact(const Contact3 c);
|
||||
void checkMsg(Message3Handle h);
|
||||
void checkObj(ObjectHandle o);
|
||||
|
||||
protected: // mm
|
||||
bool onEvent(const Message::Events::MessageConstruct& e) override;
|
||||
bool onEvent(const Message::Events::MessageUpdated& e) override;
|
||||
protected: // os
|
||||
bool onEvent(const ObjectStore::Events::ObjectConstruct& e) override;
|
||||
bool onEvent(const ObjectStore::Events::ObjectUpdate& e) override;
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
#include <solanaceae/tox_contacts/components.hpp>
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
#include <solanaceae/tox_messages/components.hpp>
|
||||
#include <solanaceae/tox_messages/msg_components.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
|
Loading…
Reference in New Issue
Block a user