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 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
|
||||||
)
|
)
|
||||||
|
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 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)
|
||||||
|
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": {
|
"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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -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, ®, 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};
|
||||||
char overlay_buf[32];
|
if (o.all_of<ObjComp::F::TagLocalHaveAll>() && fts->total_down <= 0) {
|
||||||
std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f);
|
// if have all AND no dl -> show upload progress
|
||||||
|
|
||||||
ImGui::ProgressBar(
|
|
||||||
fraction,
|
|
||||||
{-FLT_MIN, TEXT_BASE_HEIGHT},
|
|
||||||
overlay_buf
|
|
||||||
);
|
|
||||||
// TODO: numbers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (can be both)
|
|
||||||
// up progess
|
|
||||||
if (reg.all_of<Message::Components::Transfer::TagSending>(e)) {
|
|
||||||
ImGui::TextUnformatted(" up");
|
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();
|
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];
|
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(
|
ImGui::ProgressBar(
|
||||||
fraction,
|
fraction,
|
||||||
{-FLT_MIN, TEXT_BASE_HEIGHT},
|
{-FLT_MIN, TEXT_BASE_HEIGHT},
|
||||||
overlay_buf
|
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?
|
//const auto& bitest = o.get<ObjComp::F::LocalHaveBitset>().have;
|
||||||
|
// generate 1bit sdlsurface zerocopy using bitset.data() and bitset.size_bytes()
|
||||||
for (size_t i = 0; i < file_list.size(); i++) {
|
// optionally scale down filtered (would copy)
|
||||||
ImGui::PushID(i);
|
// update texture? in cache?
|
||||||
|
|
||||||
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)) {
|
if (o.all_of<ObjComp::F::FrameDims>()) {
|
||||||
const auto& local_info = reg.get<Message::Components::Transfer::FileInfoLocal>(e);
|
const auto& frame_dims = o.get<ObjComp::F::FrameDims>();
|
||||||
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;
|
||||||
|
|
||||||
|
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)) {
|
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);
|
||||||
|
@ -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,
|
||||||
|
@ -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)};
|
||||||
|
|
||||||
|
if (SDL_MUSTLOCK(conv_surf)) {
|
||||||
SDL_UnlockSurface(conv_surf);
|
SDL_UnlockSurface(conv_surf);
|
||||||
|
}
|
||||||
SDL_DestroySurface(conv_surf);
|
SDL_DestroySurface(conv_surf);
|
||||||
|
|
||||||
std::cout << "IL_SDLBMP: loaded img " << res.width << "x" << res.height << "\n";
|
std::cout << "IL_SDLBMP: loaded img " << res.width << "x" << res.height << "\n";
|
||||||
|
@ -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),
|
||||||
|
@ -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()) {
|
|
||||||
// figure out size
|
|
||||||
file.seekg(0, file.end);
|
|
||||||
if (file.tellg() > 50*1024*1024) {
|
|
||||||
// TODO: conf
|
|
||||||
// dont try load files larger 50mb
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.seekg(0, file.beg);
|
|
||||||
|
|
||||||
std::vector<uint8_t> tmp_buffer;
|
if (!o.all_of<ObjComp::F::TagLocalHaveAll>()) {
|
||||||
while (file.good()) {
|
return; // we dont have all data
|
||||||
auto ch = file.get();
|
}
|
||||||
if (ch == EOF) {
|
|
||||||
break;
|
if (!o.all_of<ObjComp::Ephemeral::Backend, ObjComp::F::SingleInfo>()) {
|
||||||
} else {
|
std::cerr << "MMIL error: object missing backend/file info (?)\n";
|
||||||
tmp_buffer.push_back(ch);
|
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
|
// try all loaders after another
|
||||||
for (auto& il : _image_loaders) {
|
for (auto& il : _image_loaders) {
|
||||||
// TODO: impl callback based load
|
// 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) {
|
if (res.height == 0 || res.width == 0) {
|
||||||
continue;
|
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 file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
|
||||||
|
|
||||||
std::cout << "MMIL loaded image info " << fil.file_list.front() << "\n";
|
|
||||||
|
|
||||||
_rmm.throwEventUpdate(m);
|
_rmm.throwEventUpdate(m);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!could_load) {
|
|
||||||
m.emplace<Message::Components::TagNotImage>();
|
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);
|
_rmm.throwEventUpdate(m);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaMetaInfoLoader::MediaMetaInfoLoader(RegistryMessageModel& rmm) : _rmm(rmm) {
|
MediaMetaInfoLoader::MediaMetaInfoLoader(RegistryMessageModel& rmm) : _rmm(rmm) {
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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,26 +39,66 @@ 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();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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
|
// try all loaders after another
|
||||||
for (auto& il : _image_loaders) {
|
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) {
|
if (res.frames.empty() || res.height == 0 || res.width == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -70,15 +115,12 @@ std::optional<TextureEntry> MessageImageLoader::load(TextureUploaderI& tu, Messa
|
|||||||
new_entry.width = res.width;
|
new_entry.width = res.width;
|
||||||
new_entry.height = res.height;
|
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;
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
@ -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
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"
|
#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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user