diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Body.cpp | 10 | ||||
-rw-r--r-- | src/DownloadUtils.cpp | 3 | ||||
-rw-r--r-- | src/FileAnalyzer.cpp | 2 | ||||
-rw-r--r-- | src/Notification.cpp | 2 | ||||
-rw-r--r-- | src/Program.cpp (renamed from src/Program.c) | 47 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 222 | ||||
-rw-r--r-- | src/VideoPlayer.cpp | 2 | ||||
-rw-r--r-- | src/plugins/Manga.cpp | 5 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 11 | ||||
-rw-r--r-- | src/plugins/NyaaSi.cpp | 2 |
10 files changed, 197 insertions, 109 deletions
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<BodyItem> 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 <SFML/System/Clock.hpp> @@ -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 <sys/stat.h> #include <stdio.h> #include <array> 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 <assert.h> #include <stdio.h> diff --git a/src/Program.c b/src/Program.cpp index a82bcd2..136a494 100644 --- a/src/Program.c +++ b/src/Program.cpp @@ -1,4 +1,4 @@ -#include "../include/Program.h" +#include "../include/Program.hpp" #include <unistd.h> #include <sys/wait.h> #include <sys/prctl.h> @@ -7,10 +7,40 @@ #include <stdio.h> #include <stdlib.h> #include <assert.h> +#include <unordered_map> +#include <mutex> +#include <signal.h> #define READ_END 0 #define WRITE_END 1 +class CurrentThreadProgram { +public: + void set(ReadProgram read_program) { + std::lock_guard<std::mutex> lock(mutex); + thread_current_program[std::this_thread::get_id()] = read_program; + } + + void clear() { + std::lock_guard<std::mutex> lock(mutex); + thread_current_program.erase(std::this_thread::get_id()); + } + + void kill_in_thread(const std::thread::id &thread_id) { + std::lock_guard<std::mutex> 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<std::thread::id, ReadProgram> 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) @@ -51,6 +81,7 @@ int exec_program_pipe(const char **args, ReadProgram *read_program) { close(fd[WRITE_END]); read_program->pid = pid; read_program->read_fd = fd[READ_END]; + current_thread_program.set(*read_program); return 0; } } @@ -63,6 +94,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void int result = 0; int status; + int exit_status; char buffer[4097]; @@ -77,6 +109,8 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void 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); @@ -84,6 +118,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void break; } } + program_clear_current_thread(); if(result != 0) kill(read_program.pid, SIGTERM); @@ -99,7 +134,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void goto cleanup; } - int exit_status = WEXITSTATUS(status); + exit_status = WEXITSTATUS(status); if(exit_status != 0) { fprintf(stderr, "Failed to execute program ("); const char **arg = args; @@ -201,3 +236,11 @@ int exec_program_async(const char **args, pid_t *result_process_id) { } 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<MatrixRoomsPage>(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<MatrixRoomTagsPage>(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<Tab> &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<MatrixChatPage*>(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<DownloadResult> 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<DownloadResult> download_result_promise; + std::future<DownloadResult> download_future = download_result_promise.get_future(); + std::thread download_thread([this, &video_path, video_url](std::promise<DownloadResult> &&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<bool> typing_state_queue; + std::thread typing_state_thread([this, ¤t_room, &typing_state_queue]() { + while(true) { + std::optional<bool> 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<std::future<void>> 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<MatrixRoomsPage>(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<MatrixRoomTagsPage>(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<Tab> 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 <string> #include <json/reader.h> #include <json/writer.h> 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 <rapidjson/document.h> #include <rapidjson/writer.h> #include <rapidjson/stringbuffer.h> @@ -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" |