From 928f2525c29929de0c2ab520f48c82b5cb882aa7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 30 Oct 2020 16:47:40 +0100 Subject: Matrix: re-add /logout, cancel task immediately Cancel video download when pressing escape, other fixes.. --- src/Body.cpp | 10 +- src/DownloadUtils.cpp | 3 +- src/FileAnalyzer.cpp | 2 +- src/Notification.cpp | 2 +- src/Program.c | 203 ---------------------------------------- src/Program.cpp | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ src/QuickMedia.cpp | 222 ++++++++++++++++++++++++++------------------ src/VideoPlayer.cpp | 2 +- src/plugins/Manga.cpp | 5 +- src/plugins/Matrix.cpp | 11 ++- src/plugins/NyaaSi.cpp | 2 +- 11 files changed, 398 insertions(+), 310 deletions(-) delete mode 100644 src/Program.c create mode 100644 src/Program.cpp (limited to 'src') diff --git a/src/Body.cpp b/src/Body.cpp index 7f96263..3f5c755 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -186,10 +186,11 @@ namespace QuickMedia { return true; } - void Body::set_selected_item(int item) { + void Body::set_selected_item(int item, bool reset_prev_selected_item) { //assert(item >= 0 && item < (int)items.size()); selected_item = item; - prev_selected_item = selected_item; + if(reset_prev_selected_item) + prev_selected_item = selected_item; clamp_selection(); //page_scroll = 0.0f; } @@ -216,10 +217,6 @@ namespace QuickMedia { clamp_selection(); } - void Body::reset_selected() { - select_first_item(); - } - void Body::clear_items() { items.clear(); selected_item = 0; @@ -235,6 +232,7 @@ namespace QuickMedia { items.insert(items.end(), std::make_move_iterator(new_items.begin()), std::make_move_iterator(new_items.end())); } + // TODO: Binary search and use hint to start search from start or end (for example when adding "previous" items or "next" items) void Body::insert_item_by_timestamp(std::shared_ptr body_item) { for(size_t i = 0; i < items.size(); ++i) { if(body_item->get_timestamp() < items[i]->get_timestamp()) { diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp index fd7e7d1..cbdded6 100644 --- a/src/DownloadUtils.cpp +++ b/src/DownloadUtils.cpp @@ -1,5 +1,5 @@ #include "../include/DownloadUtils.hpp" -#include "../include/Program.h" +#include "../include/Program.hpp" #include "../include/Storage.hpp" #include "../include/base64_url.hpp" #include @@ -141,6 +141,7 @@ namespace QuickMedia { char read_buffer[8192]; rapidjson::FileReadStream is(file, read_buffer, sizeof(read_buffer)); rapidjson::ParseResult parse_result = result.ParseStream(is); + program_clear_current_thread(); fclose(file); wait_program(read_program.pid); diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp index 82d2c06..8d5eae8 100644 --- a/src/FileAnalyzer.cpp +++ b/src/FileAnalyzer.cpp @@ -1,5 +1,5 @@ #include "../include/FileAnalyzer.hpp" -#include "../include/Program.h" +#include "../include/Program.hpp" #include #include #include diff --git a/src/Notification.cpp b/src/Notification.cpp index 1201557..9ffbaac 100644 --- a/src/Notification.cpp +++ b/src/Notification.cpp @@ -1,5 +1,5 @@ #include "../include/Notification.hpp" -#include "../include/Program.h" +#include "../include/Program.hpp" #include #include diff --git a/src/Program.c b/src/Program.c deleted file mode 100644 index a82bcd2..0000000 --- a/src/Program.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "../include/Program.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define READ_END 0 -#define WRITE_END 1 - -int exec_program_pipe(const char **args, ReadProgram *read_program) { - /* 1 arguments */ - if(args[0] == NULL) - return -1; - - int fd[2]; - if(pipe(fd) == -1) { - perror("Failed to open pipe"); - return -2; - } - - pid_t parent_pid = getpid(); - - pid_t pid = fork(); - if(pid == -1) { - perror("Failed to fork"); - close(fd[READ_END]); - close(fd[WRITE_END]); - return -3; - } else if(pid == 0) { /* child */ - if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { - perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); - _exit(127); - } - - /* Test if the parent died before the above call to prctl */ - if(getppid() != parent_pid) - _exit(127); - - dup2(fd[WRITE_END], STDOUT_FILENO); - close(fd[READ_END]); - close(fd[WRITE_END]); - - execvp(args[0], (char* const*)args); - perror("execvp"); - _exit(127); - } else { /* parent */ - close(fd[WRITE_END]); - read_program->pid = pid; - read_program->read_fd = fd[READ_END]; - return 0; - } -} - -int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata) { - ReadProgram read_program; - int res = exec_program_pipe(args, &read_program); - if(res != 0) - return res; - - int result = 0; - int status; - - char buffer[4097]; - - for(;;) { - ssize_t bytes_read = read(read_program.read_fd, buffer, sizeof(buffer) - 1); - if(bytes_read == 0) { - break; - } else if(bytes_read == -1) { - int err = errno; - fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err)); - result = -err; - break; - } - - buffer[bytes_read] = '\0'; - if(output_callback) { - result = output_callback(buffer, bytes_read, userdata); - if(result != 0) - break; - } - } - - if(result != 0) - kill(read_program.pid, SIGTERM); - - if(waitpid(read_program.pid, &status, 0) == -1) { - perror("waitpid failed"); - result = -5; - goto cleanup; - } - - if(!WIFEXITED(status)) { - result = -4; - goto cleanup; - } - - int exit_status = WEXITSTATUS(status); - if(exit_status != 0) { - fprintf(stderr, "Failed to execute program ("); - const char **arg = args; - while(*arg) { - if(arg != args) - fputc(' ', stderr); - fprintf(stderr, "'%s'", *arg); - ++arg; - } - fprintf(stderr, "), exit status %d\n", exit_status); - result = -exit_status; - } - - cleanup: - close(read_program.read_fd); - return result; -} - -int wait_program(pid_t process_id) { - int status; - if(waitpid(process_id, &status, 0) == -1) { - perror("waitpid failed"); - return -errno; - } - - if(!WIFEXITED(status)) - return -4; - - return WEXITSTATUS(status); -} - -int wait_program_non_blocking(pid_t process_id, int *status) { - int s; - int wait_result = waitpid(process_id, &s, WNOHANG); - if(wait_result == -1) { - perror("waitpid failed"); - *status = -errno; - return 0; - } else if(wait_result == 0) { - /* the child process is still running */ - *status = 0; - return 0; - } - - if(!WIFEXITED(s)) { - *status = -4; - return 0; - } - - *status = WEXITSTATUS(s); - return 1; -} - -int exec_program_async(const char **args, pid_t *result_process_id) { - /* 1 arguments */ - if(args[0] == NULL) - return -1; - - pid_t parent_pid = getpid(); - - pid_t pid = fork(); - if(pid == -1) { - int err = errno; - perror("Failed to fork"); - return -err; - } else if(pid == 0) { /* child */ - if(result_process_id) { - if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { - perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); - _exit(127); - } - - /* Test if the parent died before the above call to prctl */ - if(getppid() != parent_pid) - _exit(127); - - execvp(args[0], (char* const*)args); - perror("execvp"); - _exit(127); - } else { - setsid(); - signal(SIGHUP, SIG_IGN); - - // TODO: Still creates zombie??? find a fix! - - // Daemonize child to make the parent the init process which will reap the zombie child - pid_t second_child = fork(); - if(second_child == 0) { // child - execvp(args[0], (char* const*)args); - perror("execvp"); - _exit(127); - } else if(second_child != -1) { - _exit(0); - } - } - } else { /* parent */ - if(result_process_id) - *result_process_id = pid; - } - return 0; -} diff --git a/src/Program.cpp b/src/Program.cpp new file mode 100644 index 0000000..136a494 --- /dev/null +++ b/src/Program.cpp @@ -0,0 +1,246 @@ +#include "../include/Program.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define READ_END 0 +#define WRITE_END 1 + +class CurrentThreadProgram { +public: + void set(ReadProgram read_program) { + std::lock_guard lock(mutex); + thread_current_program[std::this_thread::get_id()] = read_program; + } + + void clear() { + std::lock_guard lock(mutex); + thread_current_program.erase(std::this_thread::get_id()); + } + + void kill_in_thread(const std::thread::id &thread_id) { + std::lock_guard lock(mutex); + auto it = thread_current_program.find(thread_id); + if(it != thread_current_program.end()) { + close(it->second.read_fd); + kill(it->second.pid, SIGTERM); + } + } +private: + std::unordered_map thread_current_program; + std::mutex mutex; +}; + +static CurrentThreadProgram current_thread_program; + +int exec_program_pipe(const char **args, ReadProgram *read_program) { + /* 1 arguments */ + if(args[0] == NULL) + return -1; + + int fd[2]; + if(pipe(fd) == -1) { + perror("Failed to open pipe"); + return -2; + } + + pid_t parent_pid = getpid(); + + pid_t pid = fork(); + if(pid == -1) { + perror("Failed to fork"); + close(fd[READ_END]); + close(fd[WRITE_END]); + return -3; + } else if(pid == 0) { /* child */ + if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); + _exit(127); + } + + /* Test if the parent died before the above call to prctl */ + if(getppid() != parent_pid) + _exit(127); + + dup2(fd[WRITE_END], STDOUT_FILENO); + close(fd[READ_END]); + close(fd[WRITE_END]); + + execvp(args[0], (char* const*)args); + perror("execvp"); + _exit(127); + } else { /* parent */ + close(fd[WRITE_END]); + read_program->pid = pid; + read_program->read_fd = fd[READ_END]; + current_thread_program.set(*read_program); + return 0; + } +} + +int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata) { + ReadProgram read_program; + int res = exec_program_pipe(args, &read_program); + if(res != 0) + return res; + + int result = 0; + int status; + int exit_status; + + char buffer[4097]; + + for(;;) { + ssize_t bytes_read = read(read_program.read_fd, buffer, sizeof(buffer) - 1); + if(bytes_read == 0) { + break; + } else if(bytes_read == -1) { + int err = errno; + fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err)); + result = -err; + break; + } + + //check if running. Also do the same in download_to_json + + buffer[bytes_read] = '\0'; + if(output_callback) { + result = output_callback(buffer, bytes_read, userdata); + if(result != 0) + break; + } + } + program_clear_current_thread(); + + if(result != 0) + kill(read_program.pid, SIGTERM); + + if(waitpid(read_program.pid, &status, 0) == -1) { + perror("waitpid failed"); + result = -5; + goto cleanup; + } + + if(!WIFEXITED(status)) { + result = -4; + goto cleanup; + } + + exit_status = WEXITSTATUS(status); + if(exit_status != 0) { + fprintf(stderr, "Failed to execute program ("); + const char **arg = args; + while(*arg) { + if(arg != args) + fputc(' ', stderr); + fprintf(stderr, "'%s'", *arg); + ++arg; + } + fprintf(stderr, "), exit status %d\n", exit_status); + result = -exit_status; + } + + cleanup: + close(read_program.read_fd); + return result; +} + +int wait_program(pid_t process_id) { + int status; + if(waitpid(process_id, &status, 0) == -1) { + perror("waitpid failed"); + return -errno; + } + + if(!WIFEXITED(status)) + return -4; + + return WEXITSTATUS(status); +} + +int wait_program_non_blocking(pid_t process_id, int *status) { + int s; + int wait_result = waitpid(process_id, &s, WNOHANG); + if(wait_result == -1) { + perror("waitpid failed"); + *status = -errno; + return 0; + } else if(wait_result == 0) { + /* the child process is still running */ + *status = 0; + return 0; + } + + if(!WIFEXITED(s)) { + *status = -4; + return 0; + } + + *status = WEXITSTATUS(s); + return 1; +} + +int exec_program_async(const char **args, pid_t *result_process_id) { + /* 1 arguments */ + if(args[0] == NULL) + return -1; + + pid_t parent_pid = getpid(); + + pid_t pid = fork(); + if(pid == -1) { + int err = errno; + perror("Failed to fork"); + return -err; + } else if(pid == 0) { /* child */ + if(result_process_id) { + if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); + _exit(127); + } + + /* Test if the parent died before the above call to prctl */ + if(getppid() != parent_pid) + _exit(127); + + execvp(args[0], (char* const*)args); + perror("execvp"); + _exit(127); + } else { + setsid(); + signal(SIGHUP, SIG_IGN); + + // TODO: Still creates zombie??? find a fix! + + // Daemonize child to make the parent the init process which will reap the zombie child + pid_t second_child = fork(); + if(second_child == 0) { // child + execvp(args[0], (char* const*)args); + perror("execvp"); + _exit(127); + } else if(second_child != -1) { + _exit(0); + } + } + } else { /* parent */ + if(result_process_id) + *result_process_id = pid; + } + return 0; +} + +void program_clear_current_thread() { + current_thread_program.clear(); +} + +void program_kill_in_thread(const std::thread::id &thread_id) { + current_thread_program.kill_in_thread(thread_id); +} diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index ba9a680..3b99d55 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -9,7 +9,7 @@ #include "../plugins/Matrix.hpp" #include "../plugins/FileManager.hpp" #include "../include/Scale.hpp" -#include "../include/Program.h" +#include "../include/Program.hpp" #include "../include/VideoPlayer.hpp" #include "../include/StringUtils.hpp" #include "../include/GoogleCaptcha.hpp" @@ -641,51 +641,7 @@ namespace QuickMedia { chat_login_page(); } - if(!window.isOpen()) - return exit_code; - - auto rooms_body = create_body(); - rooms_body->thumbnail_mask_shader = &circle_mask_shader; - auto matrix_rooms_page = std::make_unique(this, rooms_body.get(), "All rooms"); - - auto rooms_tags_body = create_body(); - rooms_tags_body->thumbnail_mask_shader = &circle_mask_shader; - auto matrix_rooms_tag_page = std::make_unique(this, rooms_tags_body.get()); - - MatrixQuickMedia matrix_handler(this, matrix, matrix_rooms_page.get(), matrix_rooms_tag_page.get()); - matrix->start_sync(&matrix_handler); - - tabs.push_back(Tab{std::move(rooms_body), std::move(matrix_rooms_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); - tabs.push_back(Tab{std::move(rooms_tags_body), std::move(matrix_rooms_tag_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); - - sf::Sprite load_sprite(loading_icon); - sf::Vector2u loading_icon_size = loading_icon.getSize(); - load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); - - sf::Clock timer; - sf::Event event; - while(window.isOpen() && !matrix->is_initial_sync_finished()) { - while(window.pollEvent(event)) { - if(event.type == sf::Event::Closed) - window.close(); - else if(event.type == sf::Event::Resized) { - window_size.x = event.size.width; - window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } - } - window.clear(back_color); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(timer.getElapsedTime().asSeconds() * 400.0); - window.draw(load_sprite); - window.display(); - } - - while(window.isOpen()) { - page_loop(tabs); - } - + after_matrix_login_page(); exit(exit_code); // Exit immediately without waiting for anything to finish //matrix->stop_sync(); } @@ -975,6 +931,16 @@ namespace QuickMedia { return current_chat_room; } + static void select_body_item_by_room(Body *body, RoomData *room) { + for(size_t i = 0; i < body->items.size(); ++i) { + auto &body_item = body->items[i]; + if(body_item->userdata == room) { + body->set_selected_item(i, false); + return; + } + } + } + void Program::page_loop(std::vector &tabs) { if(tabs.empty()) { show_notification("QuickMedia", "No tabs provided!", Urgency::CRITICAL); @@ -983,7 +949,7 @@ namespace QuickMedia { for(Tab &tab : tabs) { tab.body->thumbnail_max_size = tab.page->get_thumbnail_max_size(); - tab.page->on_navigate_to_page(); + tab.page->on_navigate_to_page(tab.body->items); } const Json::Value *json_chapters = &Json::Value::nullSingleton(); @@ -1106,11 +1072,12 @@ namespace QuickMedia { current_page = PageType::CHAT; current_chat_room = matrix->get_room_by_id(selected_item->url); chat_page(static_cast(new_tabs[0].page.get()), current_chat_room); + select_body_item_by_room(tabs[selected_tab].body.get(), current_chat_room); current_chat_room = nullptr; } else { page_loop(new_tabs); } - tabs[selected_tab].page->on_navigate_to_page(); + tabs[selected_tab].page->on_navigate_to_page(tabs[selected_tab].body->items); if(content_storage_json.isObject()) { const Json::Value &chapters_json = content_storage_json["chapters"]; if(chapters_json.isObject()) @@ -1614,9 +1581,11 @@ namespace QuickMedia { video_url = std::move(video_path.data); video_url_is_local = true; } else { - std::future download_future = std::async(std::launch::async, [this, &video_path, video_url]() { - return download_to_file(video_url, video_path.data, {}, use_tor, true); - }); + std::promise download_result_promise; + std::future download_future = download_result_promise.get_future(); + std::thread download_thread([this, &video_path, video_url](std::promise &&promise) { + promise.set_value(download_to_file(video_url, video_path.data, {}, use_tor, true)); + }, std::move(download_result_promise)); window_size.x = window.getSize().x; window_size.y = window.getSize().y; @@ -1625,9 +1594,8 @@ namespace QuickMedia { while(window.isOpen()) { while(window.pollEvent(event)) { if(event.type == sf::Event::Closed) { - // TODO: Remove this - if(download_future.valid()) - download_future.get(); + program_kill_in_thread(download_thread.get_id()); + download_thread.join(); current_page = previous_page; window.close(); return; @@ -1637,16 +1605,17 @@ namespace QuickMedia { sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); window.setView(sf::View(visible_area)); } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { - // TODO: Remove this - if(download_future.valid()) - download_future.get(); + program_kill_in_thread(download_thread.get_id()); + download_thread.join(); current_page = previous_page; return; } } if(is_future_ready(download_future)) { - if(download_future.get() != DownloadResult::OK) { + DownloadResult download_result = download_future.get(); + download_thread.join(); + if(download_result != DownloadResult::OK) { show_notification("QuickMedia", "Failed to download " + video_url, Urgency::CRITICAL); current_page = previous_page; return; @@ -3307,7 +3276,9 @@ namespace QuickMedia { chat_state = ChatState::NAVIGATING; return true; } else if(text == "/logout") { - show_notification("QuickMedia", "/logout command is temporary disabled. Delete " + get_storage_dir().join("matrix").join("session.json").data + " and restart QuickMedia to logout", Urgency::CRITICAL); + new_page = PageType::CHAT_LOGIN; + chat_input.set_editable(false); + chat_state = ChatState::NAVIGATING; return true; } else if(strncmp(text.c_str(), "/me ", 4) == 0) { msgtype = "m.emote"; @@ -3460,14 +3431,20 @@ namespace QuickMedia { float tab_vertical_offset = 0.0f; - auto typing_async_func = [this](bool new_state, RoomData *room) { - if(new_state) { - matrix->on_start_typing(room); - } else { - matrix->on_stop_typing(room); + MessageQueue typing_state_queue; + std::thread typing_state_thread([this, ¤t_room, &typing_state_queue]() { + while(true) { + std::optional state_opt = typing_state_queue.pop_wait(); + if(!state_opt) + break; + + bool state = state_opt.value(); + if(state) + matrix->on_start_typing(current_room); + else + matrix->on_stop_typing(current_room); } - }; - std::vector> typing_futures; + }); sf::Clock frame_timer; @@ -3638,7 +3615,7 @@ namespace QuickMedia { if(typing && current_room) { fprintf(stderr, "Stopped typing\n"); typing = false; - typing_futures.push_back(std::async(typing_async_func, false, current_room)); + typing_state_queue.push(false); } } else if((event.key.code == sf::Keyboard::Right) && selected_tab < (int)tabs.size() - 1) { tabs[selected_tab].body->clear_cache(); @@ -3648,7 +3625,7 @@ namespace QuickMedia { if(typing && current_room) { fprintf(stderr, "Stopped typing\n"); typing = false; - typing_futures.push_back(std::async(typing_async_func, false, current_room)); + typing_state_queue.push(false); } } else if(event.key.code == sf::Keyboard::Escape) { goto chat_page_end; @@ -3662,6 +3639,14 @@ namespace QuickMedia { } } + if(current_room) { + if(event.key.control && event.key.code == sf::Keyboard::C) { + BodyItem *selected = tabs[selected_tab].body->get_selected(); + if(selected) + sf::Clipboard::setString(selected->get_description()); + } + } + if(selected_tab == MESSAGES_TAB_INDEX && current_room) { if(event.key.code == sf::Keyboard::U) { frame_skip_text_entry = true; @@ -3783,7 +3768,7 @@ namespace QuickMedia { start_typing_timer.restart(); if(!typing && current_room) { fprintf(stderr, "Started typing\n"); - typing_futures.push_back(std::async(typing_async_func, true, current_room)); + typing_state_queue.push(true); } typing = true; } @@ -3795,7 +3780,7 @@ namespace QuickMedia { if(typing && current_room) { fprintf(stderr, "Stopped typing\n"); typing = false; - typing_futures.push_back(std::async(typing_async_func, false, current_room)); + typing_state_queue.push(false); } } //chat_input.on_event(event); @@ -3840,7 +3825,30 @@ namespace QuickMedia { break; } case PageType::CHAT_LOGIN: { - abort(); + // TODO: Cancel these instead + if(set_read_marker_future.valid()) + set_read_marker_future.get(); + if(previous_messages_future.valid()) + previous_messages_future.get(); + if(fetch_message_future.valid()) + fetch_message_future.get(); + typing_state_queue.close(); + if(typing_state_thread.joinable()) + typing_state_thread.join(); + new_page = PageType::CHAT; + matrix->stop_sync(); + matrix->logout(); + tabs[MESSAGES_TAB_INDEX].body->clear_cache(); + // TODO: Instead of doing this, exit this current function and navigate to chat login page instead. + // This doesn't currently work because at the end of this function there are futures that need to wait + // and one of them is /sync, which has a timeout of 30 seconds. That timeout has to be killed somehow. + //delete current_plugin; + //current_plugin = new Matrix(); + current_page = PageType::CHAT_LOGIN; + chat_login_page(); + if(current_page == PageType::CHAT) + after_matrix_login_page(); + exit(0); break; } default: @@ -3850,22 +3858,7 @@ namespace QuickMedia { if(typing && start_typing_timer.getElapsedTime().asSeconds() >= typing_timeout_seconds && current_room) { fprintf(stderr, "Stopped typing\n"); typing = false; - typing_futures.push_back(std::async(typing_async_func, false, current_room)); - } - - for(auto it = typing_futures.begin(); it != typing_futures.end(); ) { - if(!it->valid()) { - it = typing_futures.erase(it); - continue; - } - - if(it->wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - it->get(); - it = typing_futures.erase(it); - continue; - } - - ++it; + typing_state_queue.push(false); } if(current_room && current_room->userdata && room_avatar_thumbnail_data->loading_state == LoadingState::NOT_LOADED) @@ -4146,13 +4139,60 @@ namespace QuickMedia { previous_messages_future.get(); if(fetch_message_future.valid()) fetch_message_future.get(); - for(auto &typing_future : typing_futures) { - if(typing_future.valid()) - typing_future.get(); - } + typing_state_queue.close(); + if(typing_state_thread.joinable()) + typing_state_thread.join(); for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) { delete (PinnedEventData*)body_item->userdata; } } + + void Program::after_matrix_login_page() { + if(!window.isOpen()) + exit(exit_code); + + auto rooms_body = create_body(); + rooms_body->thumbnail_mask_shader = &circle_mask_shader; + auto matrix_rooms_page = std::make_unique(this, rooms_body.get(), "All rooms"); + + auto rooms_tags_body = create_body(); + rooms_tags_body->thumbnail_mask_shader = &circle_mask_shader; + auto matrix_rooms_tag_page = std::make_unique(this, rooms_tags_body.get()); + + MatrixQuickMedia matrix_handler(this, matrix, matrix_rooms_page.get(), matrix_rooms_tag_page.get()); + matrix->start_sync(&matrix_handler); + + std::vector tabs; + tabs.push_back(Tab{std::move(rooms_body), std::move(matrix_rooms_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + tabs.push_back(Tab{std::move(rooms_tags_body), std::move(matrix_rooms_tag_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + + sf::Sprite load_sprite(loading_icon); + sf::Vector2u loading_icon_size = loading_icon.getSize(); + load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); + + sf::Clock timer; + sf::Event event; + while(window.isOpen() && !matrix->is_initial_sync_finished()) { + while(window.pollEvent(event)) { + if(event.type == sf::Event::Closed) + window.close(); + else if(event.type == sf::Event::Resized) { + window_size.x = event.size.width; + window_size.y = event.size.height; + sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); + window.setView(sf::View(visible_area)); + } + } + window.clear(back_color); + load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); + load_sprite.setRotation(timer.getElapsedTime().asSeconds() * 400.0); + window.draw(load_sprite); + window.display(); + } + + while(window.isOpen()) { + page_loop(tabs); + } + } } diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index cbb9634..c7d2697 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -1,6 +1,6 @@ #include "../include/VideoPlayer.hpp" #include "../include/Storage.hpp" -#include "../include/Program.h" +#include "../include/Program.hpp" #include #include #include diff --git a/src/plugins/Manga.cpp b/src/plugins/Manga.cpp index 323794c..4685cec 100644 --- a/src/plugins/Manga.cpp +++ b/src/plugins/Manga.cpp @@ -1,5 +1,5 @@ #include "../../plugins/Manga.hpp" -#include "../../include/Program.h" +#include "../../include/Program.hpp" namespace QuickMedia { TrackResult MangaChaptersPage::track(const std::string &str) { @@ -10,7 +10,8 @@ namespace QuickMedia { return TrackResult::ERR; } - void MangaChaptersPage::on_navigate_to_page() { + void MangaChaptersPage::on_navigate_to_page(BodyItems &body_items) { + (void)body_items; std::string manga_id; if(extract_id_from_url(content_url, manga_id)) load_manga_content_storage(get_service_name(), content_title, manga_id); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index a702841..93eb1d6 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -3,6 +3,7 @@ #include "../../include/StringUtils.hpp" #include "../../include/NetUtils.hpp" #include "../../include/Notification.hpp" +#include "../../include/Program.hpp" #include #include #include @@ -521,8 +522,8 @@ namespace QuickMedia { } void Matrix::stop_sync() { - // TODO: Kill the running download in |sync_thread| instead of waiting until sync returns (which can be up to 30 seconds) sync_running = false; + program_kill_in_thread(sync_thread.get_id()); if(sync_thread.joinable()) sync_thread.join(); } @@ -534,8 +535,12 @@ namespace QuickMedia { void Matrix::get_room_sync_data(RoomData *room, SyncData &sync_data) { room->acquire_room_lock(); auto &room_messages = room->get_messages_thread_unsafe(); - sync_data.messages.insert(sync_data.messages.end(), room_messages.begin() + room->messages_read_index, room_messages.end()); - room->messages_read_index = room_messages.size(); + if(room->messages_read_index <= room_messages.size()) { + sync_data.messages.insert(sync_data.messages.end(), room_messages.begin() + room->messages_read_index, room_messages.end()); + room->messages_read_index = room_messages.size(); + } else { + fprintf(stderr, "Unexpected behavior!!!! get_room_sync_data said read index is %zu but we only have %zu messages\n", room->messages_read_index, room_messages.size()); + } if(room->pinned_events_updated) { sync_data.pinned_events = room->get_pinned_events_unsafe(); room->pinned_events_updated = false; diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp index 8b1efc7..082aaa8 100644 --- a/src/plugins/NyaaSi.cpp +++ b/src/plugins/NyaaSi.cpp @@ -1,5 +1,5 @@ #include "../../plugins/NyaaSi.hpp" -#include "../../include/Program.h" +#include "../../include/Program.hpp" #include "../../include/Storage.hpp" #include "../../include/Notification.hpp" #include "../../include/StringUtils.hpp" -- cgit v1.2.3