diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a66075..66bef13 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,9 @@ add_executable(tomato start_screen.cpp main_screen.hpp main_screen.cpp + + tox_client.hpp + tox_client.cpp ) target_compile_features(tomato PUBLIC cxx_std_17) @@ -17,7 +20,7 @@ target_link_libraries(tomato PUBLIC solanaceae_contact solanaceae_message3 - #solanaceae_plugin + solanaceae_plugin solanaceae_toxcore solanaceae_tox_contacts diff --git a/src/main_screen.cpp b/src/main_screen.cpp index 5b35205..9cc65c2 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -4,13 +4,55 @@ #include -MainScreen::MainScreen(std::string save_path) { +MainScreen::MainScreen(std::string save_path) : rmm(cr), tc(save_path) { + tel.subscribeAll(tc); + + conf.set("tox", "save_file_path", save_path); + + { // name stuff + auto name = tc.toxSelfGetName(); + if (name.empty()) { + name = "tomato"; + } + conf.set("tox", "name", name); + tc.setSelfName(name); // TODO: this is ugly + } + + // TODO: remove + std::cout << "own address: " << tc.toxSelfGetAddressStr() << "\n"; + + { // setup plugin instances + g_provideInstance("ConfigModelI", "host", &conf); + g_provideInstance("Contact3Registry", "host", &cr); + g_provideInstance("RegistryMessageModel", "host", &rmm); + + g_provideInstance("ToxI", "host", &tc); + g_provideInstance("ToxEventProviderI", "host", &tc); + + // TODO: pm? + + // graphics + g_provideInstance("ImGuiContext", "host", ImGui::GetCurrentContext()); + //g_provideInstance("TextureUploaderI", "host", &ogltu); + } + + conf.dump(); } Screen* MainScreen::poll(bool& quit) { - bool open = !quit; - ImGui::ShowDemoWindow(&open); - quit = !open; + auto new_time = std::chrono::high_resolution_clock::now(); + const float time_delta {std::chrono::duration(new_time - last_time).count()}; + last_time = new_time; + + quit = !tc.iterate(); + + pm.tick(time_delta); + + { + bool open = !quit; + ImGui::ShowDemoWindow(&open); + quit = !open; + } return nullptr; } diff --git a/src/main_screen.hpp b/src/main_screen.hpp index e4f5448..a0d8328 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -2,9 +2,33 @@ #include "./screen.hpp" +#include +#include +#include +#include +#include + +#include "./tox_client.hpp" + #include +#include +#include struct MainScreen final : public Screen { + std::chrono::high_resolution_clock::time_point last_time = std::chrono::high_resolution_clock::now(); + + SimpleConfigModel conf; + Contact3Registry cr; + RegistryMessageModel rmm; + + PluginManager pm; + + ToxEventLogger tel{std::cout}; + ToxClient tc; + + //OpenGLTextureUploader ogltu; + + MainScreen(std::string save_path); ~MainScreen(void) = default; diff --git a/src/start_screen.cpp b/src/start_screen.cpp index 35efbd5..61fa765 100644 --- a/src/start_screen.cpp +++ b/src/start_screen.cpp @@ -10,12 +10,13 @@ StartScreen::StartScreen(void) { Screen* StartScreen::poll(bool&) { // TODO: imgui tox profile selector? - // +------------------------ + // +---------------------------- + // | |*tox profile*| plugins | // | +------+ +-------- // | | ICON | | fileselector/dropdown? // | | | | password input // | +------+ +-------- - // +------------------------ + // +---------------------------- auto new_screen = std::make_unique("tomato.tox"); return new_screen.release(); diff --git a/src/tox_client.cpp b/src/tox_client.cpp new file mode 100644 index 0000000..c0172d9 --- /dev/null +++ b/src/tox_client.cpp @@ -0,0 +1,133 @@ +#include "./tox_client.hpp" + +#include + +#include +#include +#include +#include + +ToxClient::ToxClient(std::string_view save_path) : + _tox_profile_path(save_path) +{ + TOX_ERR_OPTIONS_NEW err_opt_new; + Tox_Options* options = tox_options_new(&err_opt_new); + assert(err_opt_new == TOX_ERR_OPTIONS_NEW::TOX_ERR_OPTIONS_NEW_OK); + + std::vector profile_data{}; + if (!_tox_profile_path.empty()) { + std::ifstream ifile{_tox_profile_path, std::ios::binary}; + + if (ifile.is_open()) { + std::cout << "TOX loading save " << _tox_profile_path << "\n"; + // fill savedata + while (ifile.good()) { + auto ch = ifile.get(); + if (ch == EOF) { + break; + } else { + profile_data.push_back(ch); + } + } + + if (profile_data.empty()) { + std::cerr << "empty tox save\n"; + } else { + // set options + tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE); + tox_options_set_savedata_data(options, profile_data.data(), profile_data.size()); + } + + ifile.close(); // do i need this? + } + } + + TOX_ERR_NEW err_new; + _tox = tox_new(options, &err_new); + tox_options_free(options); + if (err_new != TOX_ERR_NEW_OK) { + std::cerr << "tox_new failed with error code " << err_new << "\n"; + throw std::runtime_error{"tox failed"}; + } + + // no callbacks, use events + tox_events_init(_tox); + + // dht bootstrap + { + struct DHT_node { + const char *ip; + uint16_t port; + const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1]; // 1 for null terminator + unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; + }; + + DHT_node nodes[] = + { + // TODO: more/diff nodes + // you can change or add your own bs and tcprelays here, ideally closer to you + {"tox.plastiras.org", 443, "8E8B63299B3D520FB377FE5100E65E3322F7AE5B20A0ACED2981769FC5B43725", {}}, // LU tha14 + {"tox2.plastiras.org", 33445, "B6626D386BE7E3ACA107B46F48A5C4D522D29281750D44A0CBA6A2721E79C951", {}}, // DE tha14 + + }; + + for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) { + sodium_hex2bin( + nodes[i].key_bin, sizeof(nodes[i].key_bin), + nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, + NULL, NULL, NULL + ); + tox_bootstrap(_tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL); + // TODO: use extra tcp option to avoid error msgs + // ... this is hardcore + tox_add_tcp_relay(_tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL); + } + } +} + +ToxClient::~ToxClient(void) { + tox_kill(_tox); +} + +bool ToxClient::iterate(void) { + Tox_Err_Events_Iterate err_e_it = TOX_ERR_EVENTS_ITERATE_OK; + auto* events = tox_events_iterate(_tox, false, &err_e_it); + if (err_e_it == TOX_ERR_EVENTS_ITERATE_OK && events != nullptr) { + _subscriber_raw(events); + + // forward events to event handlers + dispatchEvents(events); + } + + tox_events_free(events); + + if (_tox_profile_dirty) { + saveToxProfile(); + } + + return true; +} + +void ToxClient::subscribeRaw(std::function fn) { + _subscriber_raw = fn; +} + +void ToxClient::saveToxProfile(void) { + if (_tox_profile_path.empty()) { + return; + } + std::cout << "TOX saving\n"; + + std::vector data{}; + data.resize(tox_get_savedata_size(_tox)); + tox_get_savedata(_tox, data.data()); + + std::ofstream ofile{_tox_profile_path, std::ios::binary}; + // TODO: improve + for (const auto& ch : data) { + ofile.put(ch); + } + + _tox_profile_dirty = false; +} + diff --git a/src/tox_client.hpp b/src/tox_client.hpp new file mode 100644 index 0000000..739d1de --- /dev/null +++ b/src/tox_client.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +struct ToxEventI; + +class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase { + private: + bool _should_stop {false}; + + std::function _subscriber_raw {[](const auto*) {}}; // only 1? + + std::string _self_name; + + std::string _tox_profile_path; + bool _tox_profile_dirty {true}; // set in callbacks + + public: + //ToxClient(/*const CommandLine& cl*/); + ToxClient(std::string_view save_path); + ~ToxClient(void); + + public: // tox stuff + Tox* getTox(void) { return _tox; } + + void setDirty(void) { _tox_profile_dirty = true; } + + // returns false when we shoul stop the program + bool iterate(void); + void stop(void); // let it know it should exit + + void setToxProfilePath(const std::string& new_path) { _tox_profile_path = new_path; } + void setSelfName(std::string_view new_name) { _self_name = new_name; toxSelfSetName(new_name); } + + public: // raw events + void subscribeRaw(std::function fn); + + private: + void saveToxProfile(void); +}; + +