aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-10-31 07:12:16 +0100
committerdec05eba <dec05eba@protonmail.com>2020-10-31 07:12:16 +0100
commit11f644afe434ce6b6d570c9da2a95590321871b3 (patch)
tree065ce7f5a9615daba7eb758bb30ccdd46bed3bc2 /src
parent928f2525c29929de0c2ab520f48c82b5cb882aa7 (diff)
Make matrix posting, deleting, replying, edit async and video check if streamable async
Diffstat (limited to 'src')
-rw-r--r--src/Program.cpp9
-rw-r--r--src/QuickMedia.cpp279
2 files changed, 175 insertions, 113 deletions
diff --git a/src/Program.cpp b/src/Program.cpp
index 136a494..d9927af 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -86,7 +86,7 @@ int exec_program_pipe(const char **args, ReadProgram *read_program) {
}
}
-int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata) {
+int exec_program(const char **args, ProgramOutputCallback output_callback, void *userdata, int buffer_size) {
ReadProgram read_program;
int res = exec_program_pipe(args, &read_program);
if(res != 0)
@@ -96,10 +96,11 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
int status;
int exit_status;
- char buffer[4097];
+ assert(buffer_size >= 1 && buffer_size <= 65536);
+ char *buffer = (char*)alloca(buffer_size + 1);
for(;;) {
- ssize_t bytes_read = read(read_program.read_fd, buffer, sizeof(buffer) - 1);
+ ssize_t bytes_read = read(read_program.read_fd, buffer, buffer_size);
if(bytes_read == 0) {
break;
} else if(bytes_read == -1) {
@@ -109,8 +110,6 @@ 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);
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 3b99d55..e16f8a4 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1086,7 +1086,6 @@ namespace QuickMedia {
redraw = true;
} else {
// TODO: Show the exact cause of error (get error message from curl).
- // TODO: Make asynchronous
show_notification("QuickMedia", std::string("Submit failed for page ") + tabs[selected_tab].page->get_title(), Urgency::CRITICAL);
}
};
@@ -1543,12 +1542,65 @@ namespace QuickMedia {
static bool video_url_is_non_streamable_mp4(const char *url) {
std::string result;
const char *args[] = { "curl", "-sLf", "-r", "0-40", "-H", useragent_str, "--", url, nullptr };
- exec_program(args, accumulate_string_limit_head, &result);
+ exec_program(args, accumulate_string_limit_head, &result, 40);
return (result.size() >= 40)
&& (memcmp(&result[4], "ftypisom", 8) == 0 || memcmp(&result[4], "ftypmp42", 8) == 0 || memcmp(&result[4], "ftymp42", 7) == 0 || memcmp(&result[4], "fty3gp5", 7) == 0)
&& (memcmp(&result[36], "moov", 4) != 0);
}
+ TaskResult Program::run_task_with_loading_screen(std::function<bool()> callback) {
+ std::promise<bool> result_promise;
+ std::future<bool> future = result_promise.get_future();
+ std::thread task_thread([](std::promise<bool> &&promise, std::function<bool()> callback) {
+ promise.set_value(callback());
+ }, std::move(result_promise), std::move(callback));
+
+ 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);
+
+ window_size.x = window.getSize().x;
+ window_size.y = window.getSize().y;
+ sf::Clock timer;
+ sf::Event event;
+ while(window.isOpen()) {
+ while(window.pollEvent(event)) {
+ if(event.type == sf::Event::Closed) {
+ program_kill_in_thread(task_thread.get_id());
+ task_thread.join();
+ current_page = PageType::EXIT;
+ window.close();
+ return TaskResult::CANCEL;
+ } 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));
+ } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
+ program_kill_in_thread(task_thread.get_id());
+ task_thread.join();
+ return TaskResult::CANCEL;
+ }
+ }
+
+ if(is_future_ready(future)) {
+ bool future_result = future.get();
+ task_thread.join();
+ if(!future_result)
+ return TaskResult::FALSE;
+ break;
+ }
+
+ 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();
+ }
+
+ return TaskResult::TRUE;
+ }
+
#define CLEANMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask|Mod5Mask))
void Program::video_content_page(Page *page, std::string video_url, std::string video_title, bool download_if_streaming_fails) {
@@ -1563,15 +1615,8 @@ namespace QuickMedia {
load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f);
bool video_url_is_local = false;
- if(download_if_streaming_fails && video_url_is_non_streamable_mp4(video_url.c_str())) {
- fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str());
+ if(download_if_streaming_fails) {
Path video_cache_dir = get_cache_dir().join("video");
- if(create_directory_recursive(video_cache_dir) != 0) {
- show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL);
- current_page = previous_page;
- return;
- }
-
Path video_path = video_cache_dir;
SHA256 sha256;
sha256.add(video_url.data(), video_url.size());
@@ -1581,55 +1626,39 @@ namespace QuickMedia {
video_url = std::move(video_path.data);
video_url_is_local = true;
} else {
- 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;
- sf::Clock timer;
- sf::Event event;
- while(window.isOpen()) {
- while(window.pollEvent(event)) {
- if(event.type == sf::Event::Closed) {
- program_kill_in_thread(download_thread.get_id());
- download_thread.join();
- current_page = previous_page;
- window.close();
- return;
- } 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));
- } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
- program_kill_in_thread(download_thread.get_id());
- download_thread.join();
- current_page = previous_page;
- return;
- }
+ TaskResult video_is_not_streamble_result = run_task_with_loading_screen([video_url]() {
+ return video_url_is_non_streamable_mp4(video_url.c_str());
+ });
+ if(video_is_not_streamble_result == TaskResult::TRUE) {
+ fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str());
+ if(create_directory_recursive(video_cache_dir) != 0) {
+ show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL);
+ current_page = previous_page;
+ return;
}
- if(is_future_ready(download_future)) {
- DownloadResult download_result = download_future.get();
- download_thread.join();
- if(download_result != DownloadResult::OK) {
+ TaskResult download_file_result = run_task_with_loading_screen([this, &video_path, video_url]() {
+ return download_to_file(video_url, video_path.data, {}, use_tor, true) == DownloadResult::OK;
+ });
+ switch(download_file_result) {
+ case TaskResult::TRUE: {
+ video_url = std::move(video_path.data);
+ video_url_is_local = true;
+ break;
+ }
+ case TaskResult::FALSE: {
show_notification("QuickMedia", "Failed to download " + video_url, Urgency::CRITICAL);
current_page = previous_page;
return;
}
- video_url = std::move(video_path.data);
- video_url_is_local = true;
- break;
+ case TaskResult::CANCEL: {
+ current_page = previous_page;
+ return;
+ }
}
-
- 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();
+ } else if(video_is_not_streamble_result == TaskResult::CANCEL) {
+ current_page = previous_page;
+ return;
}
}
}
@@ -1778,6 +1807,8 @@ namespace QuickMedia {
}
};
+ bool fullscreen = false;
+
while (current_page == PageType::VIDEO_CONTENT && window.isOpen()) {
while (window.pollEvent(event)) {
base_event_handler(event, previous_page, related_media_body.get(), nullptr, true, false);
@@ -1816,6 +1847,7 @@ namespace QuickMedia {
related_media_body->clear_cache();
} else if(event.key.code == sf::Keyboard::F && event.key.control) {
window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE);
+ fullscreen = !fullscreen;
} else if(event.key.code == sf::Keyboard::Enter) {
BodyItem *selected_item = related_media_body->get_selected();
if(!selected_item)
@@ -1844,6 +1876,7 @@ namespace QuickMedia {
current_page = previous_page;
} else if(pressed_keysym == XK_f && pressing_ctrl) {
window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE);
+ fullscreen = !fullscreen;
} else if(pressed_keysym == XK_r && pressing_ctrl && strcmp(plugin_name, "4chan") != 0) {
if(!related_media_window) {
related_media_window_size.x = window_size.x * RELATED_MEDIA_WINDOW_WIDTH;
@@ -1935,7 +1968,8 @@ namespace QuickMedia {
}
window.setMouseCursorVisible(true);
- window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET);
+ if(fullscreen)
+ window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET);
auto window_size_u = window.getSize();
window_size.x = window_size_u.x;
@@ -3244,10 +3278,11 @@ namespace QuickMedia {
tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(all_messages, matrix->get_me(current_room).get()));
modify_related_messages_in_current_room(all_messages);
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
+ all_messages.clear();
std::vector<std::string> pinned_events;
matrix->get_all_pinned_events(current_room, pinned_events);
- process_pinned_events(pinned_events);
+ process_pinned_events(std::move(pinned_events));
tabs[PINNED_TAB_INDEX].body->select_last_item();
room_name_text.setString(static_cast<BodyItem*>(current_room->userdata)->get_title());
@@ -3260,7 +3295,17 @@ namespace QuickMedia {
chat_input.draw_background = false;
chat_input.set_editable(false);
- chat_input.on_submit_callback = [this, &tabs, &chat_input, &selected_tab, &current_room, &new_page, &chat_state, &currently_operating_on_item](std::string text) mutable {
+ MessageQueue<std::function<void()>> post_task_queue;
+ std::thread post_thread([&post_task_queue]() {
+ while(true) {
+ std::optional<std::function<void()>> post_task_opt = post_task_queue.pop_wait();
+ if(!post_task_opt)
+ break;
+ post_task_opt.value()();
+ }
+ });
+
+ chat_input.on_submit_callback = [this, &chat_input, &selected_tab, &current_room, &new_page, &chat_state, &currently_operating_on_item, &post_task_queue](std::string text) mutable {
if(!current_room)
return false;
@@ -3290,41 +3335,33 @@ namespace QuickMedia {
}
if(chat_state == ChatState::TYPING_MESSAGE) {
- // TODO: Make asynchronous
- if(matrix->post_message(current_room, text, std::nullopt, std::nullopt, msgtype) == PluginResult::OK) {
- chat_input.set_editable(false);
- chat_state = ChatState::NAVIGATING;
- if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible())
- tabs[MESSAGES_TAB_INDEX].body->select_last_item();
- return true;
- } else {
- show_notification("QuickMedia", "Failed to post matrix message", Urgency::CRITICAL);
- return false;
- }
+ post_task_queue.push([this, &current_room, text, msgtype]() {
+ if(matrix->post_message(current_room, text, std::nullopt, std::nullopt, msgtype) != PluginResult::OK)
+ show_notification("QuickMedia", "Failed to post matrix message", Urgency::CRITICAL);
+ });
+ chat_input.set_editable(false);
+ chat_state = ChatState::NAVIGATING;
+ return true;
} else if(chat_state == ChatState::REPLYING) {
- // TODO: Make asynchronous
- if(matrix->post_reply(current_room, text, currently_operating_on_item->userdata) == PluginResult::OK) {
- chat_input.set_editable(false);
- chat_state = ChatState::NAVIGATING;
- currently_operating_on_item = nullptr;
- if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible())
- tabs[MESSAGES_TAB_INDEX].body->select_last_item();
- return true;
- } else {
- show_notification("QuickMedia", "Failed to post matrix reply", Urgency::CRITICAL);
- return false;
- }
+ void *related_to_message = currently_operating_on_item->userdata;
+ post_task_queue.push([this, &current_room, text, related_to_message]() {
+ if(matrix->post_reply(current_room, text, related_to_message) != PluginResult::OK)
+ show_notification("QuickMedia", "Failed to post matrix reply", Urgency::CRITICAL);
+ });
+ chat_input.set_editable(false);
+ chat_state = ChatState::NAVIGATING;
+ currently_operating_on_item = nullptr;
+ return true;
} else if(chat_state == ChatState::EDITING) {
- // TODO: Make asynchronous
- if(matrix->post_edit(current_room, text, currently_operating_on_item->userdata) == PluginResult::OK) {
- chat_input.set_editable(false);
- chat_state = ChatState::NAVIGATING;
- currently_operating_on_item = nullptr;
- return true;
- } else {
- show_notification("QuickMedia", "Failed to post matrix edit", Urgency::CRITICAL);
- return false;
- }
+ void *related_to_message = currently_operating_on_item->userdata;
+ post_task_queue.push([this, &current_room, text, related_to_message]() {
+ if(matrix->post_edit(current_room, text, related_to_message) != PluginResult::OK)
+ show_notification("QuickMedia", "Failed to post matrix edit", Urgency::CRITICAL);
+ });
+ chat_input.set_editable(false);
+ chat_state = ChatState::NAVIGATING;
+ currently_operating_on_item = nullptr;
+ return true;
}
}
return false;
@@ -3464,7 +3501,7 @@ namespace QuickMedia {
watched_videos.clear();
current_page = PageType::VIDEO_CONTENT;
// TODO: Add title
- video_content_page(video_page.get(), url, "No title", true);
+ video_content_page(video_page.get(), url, "No title", false);
redraw = true;
} else {
const char *launch_program = "xdg-open";
@@ -3526,7 +3563,7 @@ namespace QuickMedia {
bool prev_no_video = no_video;
no_video = is_audio;
// TODO: Add title
- video_content_page(video_page.get(), *selected_url, "No title", true);
+ video_content_page(video_page.get(), *selected_url, "No title", message_type == MessageType::VIDEO);
no_video = prev_no_video;
redraw = true;
return true;
@@ -3662,12 +3699,20 @@ namespace QuickMedia {
if(event.key.control && event.key.code == sf::Keyboard::V) {
frame_skip_text_entry = true;
- // TODO: Make asynchronous.
// TODO: Upload multiple files.
- std::string err_msg;
- if(matrix->post_file(current_room, sf::Clipboard::getString(), err_msg) != PluginResult::OK) {
- std::string desc = "Failed to upload media to room, error: " + err_msg;
- show_notification("QuickMedia", desc.c_str(), Urgency::CRITICAL);
+ std::string selected_file = sf::Clipboard::getString();
+ TaskResult post_file_result = run_task_with_loading_screen([this, &current_room, selected_file]() {
+ std::string err_msg;
+ if(matrix->post_file(current_room, selected_file, err_msg) == PluginResult::OK) {
+ return true;
+ } else {
+ show_notification("QuickMedia", "Failed to upload media to room, error: " + err_msg, Urgency::CRITICAL);
+ return false;
+ }
+ });
+ if(post_file_result == TaskResult::TRUE) {
+ if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible())
+ tabs[MESSAGES_TAB_INDEX].body->select_last_item();
}
}
@@ -3713,12 +3758,14 @@ namespace QuickMedia {
frame_skip_text_entry = true;
BodyItem *selected = tabs[selected_tab].body->get_selected();
if(selected) {
- // TODO: Make asynchronous
- std::string err_msg;
- if(matrix->delete_message(current_room, selected->userdata, err_msg) != PluginResult::OK) {
- // TODO: Show inline notification
- show_notification("QuickMedia", "Failed to delete message, reason: " + err_msg, Urgency::CRITICAL);
- }
+ void *selected_message = selected->userdata;
+ post_task_queue.push([this, &current_room, selected_message]() {
+ std::string err_msg;
+ if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) {
+ // TODO: Show inline notification
+ show_notification("QuickMedia", "Failed to delete message, reason: " + err_msg, Urgency::CRITICAL);
+ }
+ });
} else {
// TODO: Show inline notification
show_notification("QuickMedia", "No message selected for deletion");
@@ -3796,6 +3843,10 @@ namespace QuickMedia {
new_page = PageType::CHAT;
if(current_room) {
+ for(ChatTab &tab : tabs) {
+ tab.body->clear_cache();
+ }
+
auto file_manager_page = std::make_unique<FileManagerPage>(this);
file_manager_page->set_current_directory(get_home_dir().data);
auto file_manager_body = create_body();
@@ -3809,15 +3860,19 @@ namespace QuickMedia {
if(selected_files.empty()) {
fprintf(stderr, "No files selected!\n");
} else {
- // TODO: Make asynchronous.
// TODO: Upload multiple files.
- std::string err_msg;
- if(matrix->post_file(current_room, selected_files[0], err_msg) == PluginResult::OK) {
+ TaskResult post_file_result = run_task_with_loading_screen([this, &current_room]() {
+ std::string err_msg;
+ if(matrix->post_file(current_room, selected_files[0], err_msg) == PluginResult::OK) {
+ return true;
+ } else {
+ show_notification("QuickMedia", "Failed to upload media to room, error: " + err_msg, Urgency::CRITICAL);
+ return false;
+ }
+ });
+ if(post_file_result == TaskResult::TRUE) {
if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible())
tabs[MESSAGES_TAB_INDEX].body->select_last_item();
- } else {
- std::string desc = "Failed to upload media to room, error: " + err_msg;
- show_notification("QuickMedia", desc.c_str(), Urgency::CRITICAL);
}
}
redraw = true;
@@ -3835,10 +3890,15 @@ namespace QuickMedia {
typing_state_queue.close();
if(typing_state_thread.joinable())
typing_state_thread.join();
+ post_task_queue.close();
+ if(post_thread.joinable())
+ post_thread.join();
new_page = PageType::CHAT;
matrix->stop_sync();
matrix->logout();
- tabs[MESSAGES_TAB_INDEX].body->clear_cache();
+ for(ChatTab &tab : tabs) {
+ tab.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.
@@ -4142,6 +4202,9 @@ namespace QuickMedia {
typing_state_queue.close();
if(typing_state_thread.joinable())
typing_state_thread.join();
+ post_task_queue.close();
+ if(post_thread.joinable())
+ post_thread.join();
for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) {
delete (PinnedEventData*)body_item->userdata;