Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
ff5dbaffc0 | |||
b56d581e4b | |||
aa661aaaa7 | |||
cc3f430bab | |||
139db5b03b | |||
7d0e5c80bd | |||
f716ad9dd1 | |||
671772a20e | |||
b0173f6d68 | |||
3da5872df8 | |||
3deb6e8469 | |||
0c674e0137 |
2
external/solanaceae_contact
vendored
2
external/solanaceae_contact
vendored
Submodule external/solanaceae_contact updated: 2d73c7272c...9ca6adee4f
2
external/solanaceae_message3
vendored
2
external/solanaceae_message3
vendored
Submodule external/solanaceae_message3 updated: ab282235b5...a1f5add8d3
2
external/solanaceae_plugin
vendored
2
external/solanaceae_plugin
vendored
Submodule external/solanaceae_plugin updated: 17ffaee013...82cfb6d492
2
external/solanaceae_util
vendored
2
external/solanaceae_util
vendored
Submodule external/solanaceae_util updated: 390b123fb7...d304d719e9
@ -61,6 +61,9 @@ add_executable(tomato
|
|||||||
./tox_dht_cap_histo.hpp
|
./tox_dht_cap_histo.hpp
|
||||||
./tox_dht_cap_histo.cpp
|
./tox_dht_cap_histo.cpp
|
||||||
|
|
||||||
|
./tox_friend_faux_offline_messaging.hpp
|
||||||
|
./tox_friend_faux_offline_messaging.cpp
|
||||||
|
|
||||||
./chat_gui4.hpp
|
./chat_gui4.hpp
|
||||||
./chat_gui4.cpp
|
./chat_gui4.cpp
|
||||||
)
|
)
|
||||||
|
@ -37,7 +37,7 @@ namespace Components {
|
|||||||
|
|
||||||
} // Components
|
} // Components
|
||||||
|
|
||||||
static float lerp(float a, float b, float t) {
|
static constexpr float lerp(float a, float b, float t) {
|
||||||
return a + t * (b - a);
|
return a + t * (b - a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +96,21 @@ const void* clipboard_callback(void* userdata, const char* mime_type, size_t* si
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared_ptr<std::vector<uint8_t>>&& data) {
|
void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared_ptr<std::vector<uint8_t>>&& data) {
|
||||||
std::vector<const char*> tmp_mimetype_list;
|
if (!static_cast<bool>(data)) {
|
||||||
for (const auto& mime_type : mime_types) {
|
std::cerr << "CG error: tried to set clipboard with empty shp\n";
|
||||||
tmp_mimetype_list.push_back(mime_type.data());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->empty()) {
|
||||||
|
std::cerr << "CG error: tried to set clipboard with empty data\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char*> tmp_mimetype_list;
|
||||||
|
|
||||||
std::lock_guard lg{_set_clipboard_data_mutex};
|
std::lock_guard lg{_set_clipboard_data_mutex};
|
||||||
for (const auto& mime_type : mime_types) {
|
for (const auto& mime_type : mime_types) {
|
||||||
|
tmp_mimetype_list.push_back(mime_type.data());
|
||||||
_set_clipboard_data[mime_type] = data;
|
_set_clipboard_data[mime_type] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +121,10 @@ ChatGui4::ChatGui4(
|
|||||||
ConfigModelI& conf,
|
ConfigModelI& conf,
|
||||||
RegistryMessageModel& rmm,
|
RegistryMessageModel& rmm,
|
||||||
Contact3Registry& cr,
|
Contact3Registry& cr,
|
||||||
TextureUploaderI& tu
|
TextureUploaderI& tu,
|
||||||
) : _conf(conf), _rmm(rmm), _cr(cr), _tal(_cr), _contact_tc(_tal, tu), _msg_tc(_mil, tu), _sip(tu) {
|
ContactTextureCache& contact_tc,
|
||||||
|
MessageTextureCache& msg_tc
|
||||||
|
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _sip(tu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatGui4::~ChatGui4(void) {
|
ChatGui4::~ChatGui4(void) {
|
||||||
@ -128,20 +138,7 @@ ChatGui4::~ChatGui4(void) {
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatGui4::render(float time_delta) {
|
float ChatGui4::render(float time_delta) {
|
||||||
if (!_cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars
|
|
||||||
std::vector<Contact3> to_purge;
|
|
||||||
_cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) {
|
|
||||||
to_purge.push_back(c);
|
|
||||||
});
|
|
||||||
_cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend());
|
|
||||||
_contact_tc.invalidate(to_purge);
|
|
||||||
}
|
|
||||||
// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE
|
|
||||||
// it might unload textures, so it needs to be done before rendering
|
|
||||||
_contact_tc.update();
|
|
||||||
_msg_tc.update();
|
|
||||||
|
|
||||||
_fss.render();
|
_fss.render();
|
||||||
_sip.render(time_delta);
|
_sip.render(time_delta);
|
||||||
|
|
||||||
@ -619,8 +616,7 @@ void ChatGui4::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
_contact_tc.workLoadQueue();
|
return 1000.f; // TODO: higher min fps?
|
||||||
_msg_tc.workLoadQueue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatGui4::sendFilePath(const char* file_path) {
|
void ChatGui4::sendFilePath(const char* file_path) {
|
||||||
@ -741,7 +737,11 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
|||||||
) {
|
) {
|
||||||
if (ImGui::Button("save to")) {
|
if (ImGui::Button("save to")) {
|
||||||
_fss.requestFile(
|
_fss.requestFile(
|
||||||
[](const auto& path) -> bool { return std::filesystem::is_directory(path); },
|
[](std::filesystem::path& path) -> bool {
|
||||||
|
// remove file path
|
||||||
|
path.remove_filename();
|
||||||
|
return std::filesystem::is_directory(path);
|
||||||
|
},
|
||||||
[this, ®, e](const auto& path) {
|
[this, ®, e](const auto& path) {
|
||||||
if (reg.valid(e)) { // still valid
|
if (reg.valid(e)) { // still valid
|
||||||
// TODO: trim file?
|
// TODO: trim file?
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
#include "./message_image_loader.hpp"
|
#include "./message_image_loader.hpp"
|
||||||
#include "./file_selector.hpp"
|
#include "./file_selector.hpp"
|
||||||
#include "./send_image_popup.hpp"
|
#include "./send_image_popup.hpp"
|
||||||
#include "entt/container/dense_map.hpp"
|
|
||||||
|
#include <entt/container/dense_map.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -17,15 +18,16 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
using ContactTextureCache = TextureCache<void*, Contact3, ToxAvatarLoader>;
|
||||||
|
using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoader>;
|
||||||
|
|
||||||
class ChatGui4 {
|
class ChatGui4 {
|
||||||
ConfigModelI& _conf;
|
ConfigModelI& _conf;
|
||||||
RegistryMessageModel& _rmm;
|
RegistryMessageModel& _rmm;
|
||||||
Contact3Registry& _cr;
|
Contact3Registry& _cr;
|
||||||
|
|
||||||
ToxAvatarLoader _tal;
|
ContactTextureCache& _contact_tc;
|
||||||
TextureCache<void*, Contact3, ToxAvatarLoader> _contact_tc;
|
MessageTextureCache& _msg_tc;
|
||||||
MessageImageLoader _mil;
|
|
||||||
TextureCache<void*, Message3Handle, MessageImageLoader> _msg_tc;
|
|
||||||
|
|
||||||
FileSelector _fss;
|
FileSelector _fss;
|
||||||
SendImagePopup _sip;
|
SendImagePopup _sip;
|
||||||
@ -52,12 +54,14 @@ class ChatGui4 {
|
|||||||
ConfigModelI& conf,
|
ConfigModelI& conf,
|
||||||
RegistryMessageModel& rmm,
|
RegistryMessageModel& rmm,
|
||||||
Contact3Registry& cr,
|
Contact3Registry& cr,
|
||||||
TextureUploaderI& tu
|
TextureUploaderI& tu,
|
||||||
|
ContactTextureCache& contact_tc,
|
||||||
|
MessageTextureCache& msg_tc
|
||||||
);
|
);
|
||||||
~ChatGui4(void);
|
~ChatGui4(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void render(float time_delta);
|
float render(float time_delta);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool any_unread {false};
|
bool any_unread {false};
|
||||||
|
@ -18,7 +18,7 @@ FileSelector::FileSelector(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FileSelector::requestFile(
|
void FileSelector::requestFile(
|
||||||
std::function<bool(const std::filesystem::path& path)>&& is_valid,
|
std::function<bool(std::filesystem::path& path)>&& is_valid,
|
||||||
std::function<void(const std::filesystem::path& path)>&& on_choose,
|
std::function<void(const std::filesystem::path& path)>&& on_choose,
|
||||||
std::function<void(void)>&& on_cancel
|
std::function<void(void)>&& on_cancel
|
||||||
) {
|
) {
|
||||||
@ -77,7 +77,9 @@ void FileSelector::render(void) {
|
|||||||
if (current_path.has_parent_path()) {
|
if (current_path.has_parent_path()) {
|
||||||
if (ImGui::TableNextColumn()) {
|
if (ImGui::TableNextColumn()) {
|
||||||
if (ImGui::Selectable("D##..", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
if (ImGui::Selectable("D##..", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||||
_current_file_path = _current_file_path.parent_path();
|
// the first "parent_path()" only removes the filename and the ending "/"
|
||||||
|
_current_file_path = _current_file_path.parent_path().parent_path() / "";
|
||||||
|
//_current_file_path = _current_file_path.remove_filename().parent_path() / "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ struct FileSelector {
|
|||||||
|
|
||||||
bool _open_popup {false};
|
bool _open_popup {false};
|
||||||
|
|
||||||
std::function<bool(const std::filesystem::path& path)> _is_valid = [](auto){ return true; };
|
std::function<bool(std::filesystem::path& path)> _is_valid = [](auto){ return true; };
|
||||||
std::function<void(const std::filesystem::path& path)> _on_choose = [](auto){};
|
std::function<void(const std::filesystem::path& path)> _on_choose = [](auto){};
|
||||||
std::function<void(void)> _on_cancel = [](){};
|
std::function<void(void)> _on_cancel = [](){};
|
||||||
|
|
||||||
@ -18,8 +18,9 @@ struct FileSelector {
|
|||||||
FileSelector(void);
|
FileSelector(void);
|
||||||
|
|
||||||
// TODO: supply hints
|
// TODO: supply hints
|
||||||
|
// HACK: until we supply hints, is_valid can modify
|
||||||
void requestFile(
|
void requestFile(
|
||||||
std::function<bool(const std::filesystem::path& path)>&& is_valid,
|
std::function<bool(std::filesystem::path& path)>&& is_valid,
|
||||||
std::function<void(const std::filesystem::path& path)>&& on_choose,
|
std::function<void(const std::filesystem::path& path)>&& on_choose,
|
||||||
std::function<void(void)>&& on_cancel
|
std::function<void(void)>&& on_cancel
|
||||||
);
|
);
|
||||||
|
@ -178,9 +178,13 @@ int main(int argc, char** argv) {
|
|||||||
//)
|
//)
|
||||||
//));
|
//));
|
||||||
|
|
||||||
const float min_delay = std::min<float>(
|
const float min_delay =
|
||||||
|
std::min<float>(
|
||||||
|
std::min<float>(
|
||||||
screen->nextTick() - time_delta_tick,
|
screen->nextTick() - time_delta_tick,
|
||||||
screen->nextRender() - time_delta_render
|
screen->nextRender() - time_delta_render
|
||||||
|
),
|
||||||
|
0.25f // dont sleep too long
|
||||||
) * 1000.f;
|
) * 1000.f;
|
||||||
|
|
||||||
if (min_delay > 0.f) {
|
if (min_delay > 0.f) {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#include "./main_screen.hpp"
|
#include "./main_screen.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) :
|
MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) :
|
||||||
renderer(renderer_),
|
renderer(renderer_),
|
||||||
@ -16,10 +19,15 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
|||||||
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),
|
||||||
|
tffom(cr, rmm, tcm, tc, tc),
|
||||||
mmil(rmm),
|
mmil(rmm),
|
||||||
tam(rmm, cr, conf),
|
tam(rmm, cr, conf),
|
||||||
sdlrtu(renderer_),
|
sdlrtu(renderer_),
|
||||||
cg(conf, rmm, cr, sdlrtu),
|
tal(cr),
|
||||||
|
contact_tc(tal, sdlrtu),
|
||||||
|
mil(),
|
||||||
|
msg_tc(mil, sdlrtu),
|
||||||
|
cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc),
|
||||||
sw(conf),
|
sw(conf),
|
||||||
tuiu(tc, conf),
|
tuiu(tc, conf),
|
||||||
tdch(tpi)
|
tdch(tpi)
|
||||||
@ -166,7 +174,22 @@ Screen* MainScreen::render(float time_delta, bool&) {
|
|||||||
|
|
||||||
const float pm_interval = pm.render(time_delta); // render
|
const float pm_interval = pm.render(time_delta); // render
|
||||||
|
|
||||||
cg.render(time_delta); // render
|
// TODO: move this somewhere else!!!
|
||||||
|
// needs both tal and tc <.<
|
||||||
|
if (!cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars
|
||||||
|
std::vector<Contact3> to_purge;
|
||||||
|
cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) {
|
||||||
|
to_purge.push_back(c);
|
||||||
|
});
|
||||||
|
cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend());
|
||||||
|
contact_tc.invalidate(to_purge);
|
||||||
|
}
|
||||||
|
// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE
|
||||||
|
// it might unload textures, so it needs to be done before rendering
|
||||||
|
const float ctc_interval = contact_tc.update();
|
||||||
|
const float msgtc_interval = msg_tc.update();
|
||||||
|
|
||||||
|
const float cg_interval = cg.render(time_delta); // render
|
||||||
sw.render(); // render
|
sw.render(); // render
|
||||||
tuiu.render(); // render
|
tuiu.render(); // render
|
||||||
tdch.render(); // render
|
tdch.render(); // render
|
||||||
@ -215,20 +238,136 @@ Screen* MainScreen::render(float time_delta, bool&) {
|
|||||||
ImGui::ShowDemoWindow();
|
ImGui::ShowDemoWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
float tc_unfinished_queue_interval;
|
||||||
_fps_perf_mode > 1 // TODO: magic
|
{ // load rendered but not loaded textures
|
||||||
) {
|
bool unfinished_work_queue = contact_tc.workLoadQueue();
|
||||||
// powersave forces 250ms
|
unfinished_work_queue = unfinished_work_queue || msg_tc.workLoadQueue();
|
||||||
_render_interval = 1.f/4.f;
|
|
||||||
} else if (
|
if (unfinished_work_queue) {
|
||||||
_time_since_event > 1.f && ( // 1sec cool down
|
tc_unfinished_queue_interval = 0.1f; // so we can get images loaded faster
|
||||||
_fps_perf_mode == 1 || // TODO: magic
|
|
||||||
_window_hidden
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
_render_interval = std::min<float>(1.f/4.f, pm_interval);
|
|
||||||
} else {
|
} else {
|
||||||
_render_interval = std::min<float>(1.f/60.f, pm_interval);
|
tc_unfinished_queue_interval = 1.f; // TODO: higher min fps?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate interval for next frame
|
||||||
|
// normal:
|
||||||
|
// - if < 1.5sec since last event
|
||||||
|
// - min all and clamp(1/60, 1/1)
|
||||||
|
// - if < 30sec since last event
|
||||||
|
// - min all (anim + everything else) clamp(1/60, 1/1) (maybe less?)
|
||||||
|
// - else
|
||||||
|
// - min without anim and clamp(1/60, 1/1) (maybe more?)
|
||||||
|
// reduced:
|
||||||
|
// - if < 1sec since last event
|
||||||
|
// - min all and clamp(1/60, 1/1)
|
||||||
|
// - if < 10sec since last event
|
||||||
|
// - min all (anim + everything else) clamp(1/10, 1/1)
|
||||||
|
// - else
|
||||||
|
// - min without anim and max clamp(1/10, 1/1)
|
||||||
|
// powersave:
|
||||||
|
// - if < 0sec since last event
|
||||||
|
// - (ignored)
|
||||||
|
// - if < 1sec since last event
|
||||||
|
// - min all (anim + everything else) clamp(1/8, 1/1)
|
||||||
|
// - else
|
||||||
|
// - min without anim and clamp(1/1, 1/1)
|
||||||
|
struct PerfProfileRender {
|
||||||
|
float low_delay_window {1.5f};
|
||||||
|
float low_delay_min {1.f/60.f};
|
||||||
|
float low_delay_max {1.f/60.f};
|
||||||
|
|
||||||
|
float mid_delay_window {30.f};
|
||||||
|
float mid_delay_min {1.f/60.f};
|
||||||
|
float mid_delay_max {1.f/2.f};
|
||||||
|
|
||||||
|
// also when main window hidden
|
||||||
|
float else_delay_min {1.f/60.f};
|
||||||
|
float else_delay_max {1.f/2.f};
|
||||||
|
};
|
||||||
|
|
||||||
|
const static PerfProfileRender normalPerfProfile{
|
||||||
|
//1.5f, // low_delay_window
|
||||||
|
//1.f/60.f, // low_delay_min
|
||||||
|
//1.f/60.f, // low_delay_max
|
||||||
|
|
||||||
|
//30.f, // mid_delay_window
|
||||||
|
//1.f/60.f, // mid_delay_min
|
||||||
|
//1.f/2.f, // mid_delay_max
|
||||||
|
|
||||||
|
//1.f/60.f, // else_delay_min
|
||||||
|
//1.f/2.f, // else_delay_max
|
||||||
|
};
|
||||||
|
const static PerfProfileRender reducedPerfProfile{
|
||||||
|
1.f, // low_delay_window
|
||||||
|
1.f/60.f, // low_delay_min
|
||||||
|
1.f/30.f, // low_delay_max
|
||||||
|
|
||||||
|
10.f, // mid_delay_window
|
||||||
|
1.f/10.f, // mid_delay_min
|
||||||
|
1.f/4.f, // mid_delay_max
|
||||||
|
|
||||||
|
1.f/10.f, // else_delay_min
|
||||||
|
1.f, // else_delay_max
|
||||||
|
};
|
||||||
|
// TODO: fix powersave by adjusting it in the events handler (make ppr member)
|
||||||
|
const static PerfProfileRender powersavePerfProfile{
|
||||||
|
// no window -> ignore first case
|
||||||
|
0.f, // low_delay_window
|
||||||
|
1.f, // low_delay_min
|
||||||
|
1.f, // low_delay_max
|
||||||
|
|
||||||
|
1.f, // mid_delay_window
|
||||||
|
1.f/8.f, // mid_delay_min
|
||||||
|
1.f/4.f, // mid_delay_max
|
||||||
|
|
||||||
|
1.f, // else_delay_min
|
||||||
|
1.f, // else_delay_max
|
||||||
|
};
|
||||||
|
|
||||||
|
const PerfProfileRender& curr_profile =
|
||||||
|
// TODO: magic
|
||||||
|
_fps_perf_mode > 1
|
||||||
|
? powersavePerfProfile
|
||||||
|
: (
|
||||||
|
_fps_perf_mode == 1
|
||||||
|
? reducedPerfProfile
|
||||||
|
: normalPerfProfile
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
// min over non animations in all cases
|
||||||
|
_render_interval = std::min<float>(pm_interval, cg_interval);
|
||||||
|
_render_interval = std::min<float>(_render_interval, tc_unfinished_queue_interval);
|
||||||
|
|
||||||
|
// low delay time window
|
||||||
|
if (!_window_hidden && _time_since_event < curr_profile.low_delay_window) {
|
||||||
|
_render_interval = std::min<float>(_render_interval, ctc_interval);
|
||||||
|
_render_interval = std::min<float>(_render_interval, msgtc_interval);
|
||||||
|
|
||||||
|
_render_interval = std::clamp(
|
||||||
|
_render_interval,
|
||||||
|
curr_profile.low_delay_min,
|
||||||
|
curr_profile.low_delay_max
|
||||||
|
);
|
||||||
|
// mid delay time window
|
||||||
|
} else if (!_window_hidden && _time_since_event < curr_profile.mid_delay_window) {
|
||||||
|
_render_interval = std::min<float>(_render_interval, ctc_interval);
|
||||||
|
_render_interval = std::min<float>(_render_interval, msgtc_interval);
|
||||||
|
|
||||||
|
_render_interval = std::clamp(
|
||||||
|
_render_interval,
|
||||||
|
curr_profile.mid_delay_min,
|
||||||
|
curr_profile.mid_delay_max
|
||||||
|
);
|
||||||
|
// timed out or window hidden
|
||||||
|
} else {
|
||||||
|
// no animation timing here
|
||||||
|
_render_interval = std::clamp(
|
||||||
|
_render_interval,
|
||||||
|
curr_profile.else_delay_min,
|
||||||
|
curr_profile.else_delay_max
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_time_since_event += time_delta;
|
_time_since_event += time_delta;
|
||||||
@ -241,6 +380,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
|||||||
|
|
||||||
tcm.iterate(time_delta); // compute
|
tcm.iterate(time_delta); // compute
|
||||||
|
|
||||||
|
const float fo_interval = tffom.tick(time_delta);
|
||||||
|
|
||||||
tam.iterate(); // compute
|
tam.iterate(); // compute
|
||||||
|
|
||||||
const float pm_interval = pm.tick(time_delta); // compute
|
const float pm_interval = pm.tick(time_delta); // compute
|
||||||
@ -250,9 +391,17 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
|||||||
mts.iterate(); // compute
|
mts.iterate(); // compute
|
||||||
|
|
||||||
_min_tick_interval = std::min<float>(
|
_min_tick_interval = std::min<float>(
|
||||||
tc.toxIterationInterval()/1000.f,
|
// HACK: pow by 1.6 to increase 50 -> ~500 (~522)
|
||||||
|
// and it does not change 1
|
||||||
|
std::pow(tc.toxIterationInterval(), 1.6f)/1000.f,
|
||||||
pm_interval
|
pm_interval
|
||||||
);
|
);
|
||||||
|
_min_tick_interval = std::min<float>(
|
||||||
|
_min_tick_interval,
|
||||||
|
fo_interval
|
||||||
|
);
|
||||||
|
|
||||||
|
//std::cout << "MS: min tick interval: " << _min_tick_interval << "\n";
|
||||||
|
|
||||||
switch (_compute_perf_mode) {
|
switch (_compute_perf_mode) {
|
||||||
// normal 1ms lower bound
|
// normal 1ms lower bound
|
||||||
|
@ -21,10 +21,15 @@
|
|||||||
#include "./tox_avatar_manager.hpp"
|
#include "./tox_avatar_manager.hpp"
|
||||||
|
|
||||||
#include "./sdlrenderer_texture_uploader.hpp"
|
#include "./sdlrenderer_texture_uploader.hpp"
|
||||||
|
#include "./texture_cache.hpp"
|
||||||
|
#include "./tox_avatar_loader.hpp"
|
||||||
|
#include "./message_image_loader.hpp"
|
||||||
|
|
||||||
#include "./chat_gui4.hpp"
|
#include "./chat_gui4.hpp"
|
||||||
#include "./settings_window.hpp"
|
#include "./settings_window.hpp"
|
||||||
#include "./tox_ui_utils.hpp"
|
#include "./tox_ui_utils.hpp"
|
||||||
#include "./tox_dht_cap_histo.hpp"
|
#include "./tox_dht_cap_histo.hpp"
|
||||||
|
#include "./tox_friend_faux_offline_messaging.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -52,6 +57,7 @@ struct MainScreen final : public Screen {
|
|||||||
ToxContactModel2 tcm;
|
ToxContactModel2 tcm;
|
||||||
ToxMessageManager tmm;
|
ToxMessageManager tmm;
|
||||||
ToxTransferManager ttm;
|
ToxTransferManager ttm;
|
||||||
|
ToxFriendFauxOfflineMessaging tffom;
|
||||||
|
|
||||||
MediaMetaInfoLoader mmil;
|
MediaMetaInfoLoader mmil;
|
||||||
ToxAvatarManager tam;
|
ToxAvatarManager tam;
|
||||||
@ -59,6 +65,11 @@ struct MainScreen final : public Screen {
|
|||||||
SDLRendererTextureUploader sdlrtu;
|
SDLRendererTextureUploader sdlrtu;
|
||||||
//OpenGLTextureUploader ogltu;
|
//OpenGLTextureUploader ogltu;
|
||||||
|
|
||||||
|
ToxAvatarLoader tal;
|
||||||
|
TextureCache<void*, Contact3, ToxAvatarLoader> contact_tc;
|
||||||
|
MessageImageLoader mil;
|
||||||
|
TextureCache<void*, Message3Handle, MessageImageLoader> msg_tc;
|
||||||
|
|
||||||
ChatGui4 cg;
|
ChatGui4 cg;
|
||||||
SettingsWindow sw;
|
SettingsWindow sw;
|
||||||
ToxUIUtils tuiu;
|
ToxUIUtils tuiu;
|
||||||
@ -67,7 +78,7 @@ struct MainScreen final : public Screen {
|
|||||||
bool _show_tool_style_editor {false};
|
bool _show_tool_style_editor {false};
|
||||||
|
|
||||||
bool _window_hidden {false};
|
bool _window_hidden {false};
|
||||||
bool _window_hidden_ts {0};
|
uint64_t _window_hidden_ts {0};
|
||||||
float _time_since_event {0.f};
|
float _time_since_event {0.f};
|
||||||
|
|
||||||
MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins);
|
MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins);
|
||||||
@ -83,7 +94,7 @@ struct MainScreen final : public Screen {
|
|||||||
// 0 - normal
|
// 0 - normal
|
||||||
// 1 - reduced
|
// 1 - reduced
|
||||||
// 2 - power save
|
// 2 - power save
|
||||||
int _fps_perf_mode {1};
|
int _fps_perf_mode {0};
|
||||||
// 0 - normal
|
// 0 - normal
|
||||||
// 1 - power save
|
// 1 - power save
|
||||||
int _compute_perf_mode {0};
|
int _compute_perf_mode {0};
|
||||||
|
@ -32,7 +32,7 @@ struct SendImagePopup {
|
|||||||
Rect crop_rect;
|
Rect crop_rect;
|
||||||
Rect crop_before_drag;
|
Rect crop_before_drag;
|
||||||
|
|
||||||
bool cropping {false};
|
bool cropping {true};
|
||||||
bool dragging_last_frame_ul {false};
|
bool dragging_last_frame_ul {false};
|
||||||
bool dragging_last_frame_lr {false};
|
bool dragging_last_frame_lr {false};
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
void TextureEntry::doAnimation(const int64_t ts_now) {
|
int64_t TextureEntry::doAnimation(const int64_t ts_now) {
|
||||||
if (frame_duration.size() > 1) { // is animation
|
if (frame_duration.size() > 1) { // is animation
|
||||||
do { // why is this loop so ugly
|
do { // why is this loop so ugly
|
||||||
const int64_t duration = getDuration();
|
const int64_t duration = getDuration();
|
||||||
@ -11,11 +12,13 @@ void TextureEntry::doAnimation(const int64_t ts_now) {
|
|||||||
timestamp_last_rendered += duration;
|
timestamp_last_rendered += duration;
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
break;
|
// return ts for next frame
|
||||||
|
return timestamp_last_rendered + duration;
|
||||||
}
|
}
|
||||||
} while(true);
|
} while (true);
|
||||||
} else {
|
} else {
|
||||||
timestamp_last_rendered = ts_now;
|
timestamp_last_rendered = ts_now;
|
||||||
|
return std::numeric_limits<int64_t>::max(); // static image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@ struct TextureEntry {
|
|||||||
current_texture = (current_texture + 1) % frame_duration.size();
|
current_texture = (current_texture + 1) % frame_duration.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void doAnimation(const int64_t ts_now);
|
// returns ts for next frame
|
||||||
|
int64_t doAnimation(const int64_t ts_now);
|
||||||
|
|
||||||
template<typename TextureType>
|
template<typename TextureType>
|
||||||
TextureType getID(void) {
|
TextureType getID(void) {
|
||||||
@ -133,14 +134,16 @@ struct TextureCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(void) {
|
float update(void) {
|
||||||
const uint64_t ts_now = Message::getTimeMS();
|
const uint64_t ts_now = Message::getTimeMS();
|
||||||
|
uint64_t ts_min_next = ts_now + ms_before_purge;
|
||||||
|
|
||||||
std::vector<KeyType> to_purge;
|
std::vector<KeyType> to_purge;
|
||||||
for (auto&& [key, te] : _cache) {
|
for (auto&& [key, te] : _cache) {
|
||||||
if (te.rendered_this_frame) {
|
if (te.rendered_this_frame) {
|
||||||
te.doAnimation(ts_now);
|
const uint64_t ts_next = te.doAnimation(ts_now);
|
||||||
te.rendered_this_frame = false;
|
te.rendered_this_frame = false;
|
||||||
|
ts_min_next = std::min(ts_min_next, ts_next);
|
||||||
} else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) {
|
} else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) {
|
||||||
to_purge.push_back(key);
|
to_purge.push_back(key);
|
||||||
}
|
}
|
||||||
@ -148,7 +151,10 @@ struct TextureCache {
|
|||||||
|
|
||||||
invalidate(to_purge);
|
invalidate(to_purge);
|
||||||
|
|
||||||
|
// we ignore the default texture ts :)
|
||||||
_default_texture.doAnimation(ts_now);
|
_default_texture.doAnimation(ts_now);
|
||||||
|
|
||||||
|
return (ts_min_next - ts_now) / 1000.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate(const std::vector<KeyType>& to_purge) {
|
void invalidate(const std::vector<KeyType>& to_purge) {
|
||||||
@ -162,16 +168,22 @@ struct TextureCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void workLoadQueue(void) {
|
// returns true if there is still work queued up
|
||||||
for (auto it = _to_load.begin(); it != _to_load.end(); it++) {
|
bool workLoadQueue(void) {
|
||||||
|
auto it = _to_load.begin();
|
||||||
|
for (; it != _to_load.end(); it++) {
|
||||||
auto new_entry_opt = _l.load(_tu, *it);
|
auto new_entry_opt = _l.load(_tu, *it);
|
||||||
if (new_entry_opt.has_value()) {
|
if (new_entry_opt.has_value()) {
|
||||||
_cache.emplace(*it, new_entry_opt.value());
|
_cache.emplace(*it, new_entry_opt.value());
|
||||||
_to_load.erase(it);
|
it = _to_load.erase(it);
|
||||||
// TODO: not a good idea
|
|
||||||
|
// TODO: not a good idea?
|
||||||
break; // end load from queue/onlyload 1 per update
|
break; // end load from queue/onlyload 1 per update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// peak
|
||||||
|
return it != _to_load.end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
213
src/tox_friend_faux_offline_messaging.cpp
Normal file
213
src/tox_friend_faux_offline_messaging.cpp
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
#include "./tox_friend_faux_offline_messaging.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/components.hpp>
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include <solanaceae/tox_messages/components.hpp>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
//#include <iostream>
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
struct LastSendAttempt {
|
||||||
|
uint64_t ts {0};
|
||||||
|
};
|
||||||
|
} // Message::Components
|
||||||
|
|
||||||
|
namespace Contact::Components {
|
||||||
|
struct NextSendAttempt {
|
||||||
|
uint64_t ts {0};
|
||||||
|
};
|
||||||
|
} // Contact::Components
|
||||||
|
|
||||||
|
ToxFriendFauxOfflineMessaging::ToxFriendFauxOfflineMessaging(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
ToxContactModel2& tcm,
|
||||||
|
ToxI& t,
|
||||||
|
ToxEventProviderI& tep
|
||||||
|
) : _cr(cr), _rmm(rmm), _tcm(tcm), _t(t), _tep(tep) {
|
||||||
|
_tep.subscribe(this, Tox_Event_Type::TOX_EVENT_FRIEND_CONNECTION_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ToxFriendFauxOfflineMessaging::tick(float time_delta) {
|
||||||
|
_interval_timer -= time_delta;
|
||||||
|
if (_interval_timer > 0.f) {
|
||||||
|
return std::max(_interval_timer, 0.001f); // TODO: min next timer
|
||||||
|
}
|
||||||
|
// interval ~ once per minute
|
||||||
|
_interval_timer = 60.f;
|
||||||
|
|
||||||
|
|
||||||
|
const uint64_t ts_now = Message::getTimeMS();
|
||||||
|
|
||||||
|
// check ALL
|
||||||
|
// for each online tox friend
|
||||||
|
uint64_t min_next_attempt_ts {std::numeric_limits<uint64_t>::max()};
|
||||||
|
_cr.view<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()
|
||||||
|
.each([this, &min_next_attempt_ts, ts_now](const Contact3 c, const auto& tfe, const auto& cs) {
|
||||||
|
if (cs.state == Contact::Components::ConnectionState::disconnected) {
|
||||||
|
// cleanup
|
||||||
|
if (_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
|
||||||
|
_cr.remove<Contact::Components::NextSendAttempt>(c);
|
||||||
|
auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c);
|
||||||
|
if (mr != nullptr) {
|
||||||
|
mr->storage<Message::Components::LastSendAttempt>().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
|
||||||
|
if (false) { // has unsent messages
|
||||||
|
const auto& nsa = _cr.emplace<Contact::Components::NextSendAttempt>(c, ts_now + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
|
||||||
|
min_next_attempt_ts = std::min(min_next_attempt_ts, nsa.ts);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto ret = doFriendMessageCheck(c, tfe);
|
||||||
|
if (ret == dfmc_Ret::SENT_THIS_TICK) {
|
||||||
|
const auto ts = _cr.get<Contact::Components::NextSendAttempt>(c).ts = ts_now + uint64_t(_delay_inbetween*1000);
|
||||||
|
min_next_attempt_ts = std::min(min_next_attempt_ts, ts);
|
||||||
|
} else if (ret == dfmc_Ret::TOO_SOON) {
|
||||||
|
// TODO: set to _delay_inbetween? prob expensive for no good reason
|
||||||
|
min_next_attempt_ts = std::min(min_next_attempt_ts, _cr.get<Contact::Components::NextSendAttempt>(c).ts);
|
||||||
|
} else {
|
||||||
|
_cr.remove<Contact::Components::NextSendAttempt>(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (min_next_attempt_ts <= ts_now) {
|
||||||
|
// we (probably) sent this iterate
|
||||||
|
_interval_timer = 0.1f; // TODO: ugly magic
|
||||||
|
} else if (min_next_attempt_ts == std::numeric_limits<uint64_t>::max()) {
|
||||||
|
// nothing to sync or all offline that need syncing
|
||||||
|
} else {
|
||||||
|
_interval_timer = std::min(_interval_timer, (min_next_attempt_ts - ts_now) / 1000.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << "TFFOM: iterate (i:" << _interval_timer << ")\n";
|
||||||
|
|
||||||
|
return _interval_timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxFriendFauxOfflineMessaging::dfmc_Ret ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe) {
|
||||||
|
// walk all messages and check if
|
||||||
|
// unacked message
|
||||||
|
// timeouts for exising unacked messages expired (send)
|
||||||
|
|
||||||
|
auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c);
|
||||||
|
if (mr == nullptr) {
|
||||||
|
// no messages
|
||||||
|
return dfmc_Ret::NO_MSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t ts_now = Message::getTimeMS();
|
||||||
|
|
||||||
|
// filter for unconfirmed messages
|
||||||
|
|
||||||
|
// we assume sorted
|
||||||
|
// ("reverse" iteration <.<)
|
||||||
|
auto msg_view = mr->view<Message::Components::Timestamp>();
|
||||||
|
bool valid_unsent {false};
|
||||||
|
// we search for the oldest, not too recently sent, unconfirmed message
|
||||||
|
for (auto it = msg_view.rbegin(), view_end = msg_view.rend(); it != view_end; it++) {
|
||||||
|
const Message3 msg = *it;
|
||||||
|
|
||||||
|
// require
|
||||||
|
if (!mr->all_of<
|
||||||
|
Message::Components::MessageText, // text only for now
|
||||||
|
Message::Components::ContactTo
|
||||||
|
>(msg)
|
||||||
|
) {
|
||||||
|
continue; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
// exclude
|
||||||
|
if (mr->any_of<
|
||||||
|
Message::Components::Remote::TimestampReceived // this acts like a tag, which is wrong in groups
|
||||||
|
>(msg)
|
||||||
|
) {
|
||||||
|
continue; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mr->get<Message::Components::ContactTo>(msg).c != c) {
|
||||||
|
continue; // not outbound (in private)
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_unsent = true;
|
||||||
|
|
||||||
|
uint64_t msg_ts = msg_view.get<Message::Components::Timestamp>(msg).ts;
|
||||||
|
if (mr->all_of<Message::Components::TimestampWritten>(msg)) {
|
||||||
|
msg_ts = mr->get<Message::Components::TimestampWritten>(msg).ts;
|
||||||
|
}
|
||||||
|
if (mr->all_of<Message::Components::LastSendAttempt>(msg)) {
|
||||||
|
const auto lsa = mr->get<Message::Components::LastSendAttempt>(msg).ts;
|
||||||
|
if (lsa > msg_ts) {
|
||||||
|
msg_ts = lsa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts_now < (msg_ts + uint64_t(_delay_retry * 1000))) {
|
||||||
|
// not time yet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is time
|
||||||
|
const auto [msg_id, _] = _t.toxFriendSendMessage(
|
||||||
|
tfe.friend_number,
|
||||||
|
(
|
||||||
|
mr->all_of<Message::Components::TagMessageIsAction>(msg)
|
||||||
|
? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION
|
||||||
|
: Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL
|
||||||
|
),
|
||||||
|
mr->get<Message::Components::MessageText>(msg).text
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: this is ugly
|
||||||
|
mr->emplace_or_replace<Message::Components::LastSendAttempt>(msg, ts_now);
|
||||||
|
|
||||||
|
if (msg_id.has_value()) {
|
||||||
|
// tmm will pick this up for us
|
||||||
|
mr->emplace_or_replace<Message::Components::ToxFriendMessageID>(msg, msg_id.value());
|
||||||
|
} // else error
|
||||||
|
|
||||||
|
// we sent our message, no point further iterating
|
||||||
|
return dfmc_Ret::SENT_THIS_TICK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid_unsent) {
|
||||||
|
// somehow cleanup lsa
|
||||||
|
mr->storage<Message::Components::LastSendAttempt>().clear();
|
||||||
|
//std::cout << "TFFOM: all sent, deleting lsa\n";
|
||||||
|
return dfmc_Ret::NO_MSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dfmc_Ret::TOO_SOON;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToxFriendFauxOfflineMessaging::onToxEvent(const Tox_Event_Friend_Connection_Status* e) {
|
||||||
|
const auto friend_number = tox_event_friend_connection_status_get_friend_number(e);
|
||||||
|
const auto friend_status = tox_event_friend_connection_status_get_connection_status(e);
|
||||||
|
|
||||||
|
if (friend_status == Tox_Connection::TOX_CONNECTION_NONE) {
|
||||||
|
return false; // skip
|
||||||
|
// maybe cleanup?
|
||||||
|
}
|
||||||
|
|
||||||
|
auto c = _tcm.getContactFriend(friend_number);
|
||||||
|
if (!static_cast<bool>(c) || !c.all_of<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()) {
|
||||||
|
// UH error??
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cr.emplace_or_replace<Contact::Components::NextSendAttempt>(c, Message::getTimeMS() + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
|
||||||
|
|
||||||
|
_interval_timer = 0.f;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
55
src/tox_friend_faux_offline_messaging.hpp
Normal file
55
src/tox_friend_faux_offline_messaging.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||||
|
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct ToxI;
|
||||||
|
namespace Contact::Components {
|
||||||
|
struct ToxFriendEphemeral;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resends unconfirmed messages.
|
||||||
|
// timers get reset on connection changes, and send order is preserved.
|
||||||
|
class ToxFriendFauxOfflineMessaging : public ToxEventI {
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
RegistryMessageModel& _rmm;
|
||||||
|
ToxContactModel2& _tcm;
|
||||||
|
ToxI& _t;
|
||||||
|
ToxEventProviderI& _tep;
|
||||||
|
|
||||||
|
float _interval_timer{0.f};
|
||||||
|
|
||||||
|
// TODO: increase timer?
|
||||||
|
const float _delay_after_cc {4.5f};
|
||||||
|
const float _delay_inbetween {0.3f};
|
||||||
|
const float _delay_retry {10.f}; // retry sending after 10s
|
||||||
|
|
||||||
|
public:
|
||||||
|
ToxFriendFauxOfflineMessaging(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
ToxContactModel2& tcm,
|
||||||
|
ToxI& t,
|
||||||
|
ToxEventProviderI& tep
|
||||||
|
);
|
||||||
|
|
||||||
|
float tick(float time_delta);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class dfmc_Ret {
|
||||||
|
TOO_SOON,
|
||||||
|
SENT_THIS_TICK,
|
||||||
|
NO_MSG,
|
||||||
|
};
|
||||||
|
// only called for online friends
|
||||||
|
// returns true if a message was sent
|
||||||
|
// dont call this too often
|
||||||
|
dfmc_Ret doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override;
|
||||||
|
};
|
||||||
|
|
Reference in New Issue
Block a user