aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-10-01 19:12:33 +0200
committerdec05eba <dec05eba@protonmail.com>2020-10-01 19:12:33 +0200
commit30dbaeb2b175c1e67f57aba748ced1a2280fb56d (patch)
treeb84cd3b4d2eaa94c3b91fb1f1cb75e5a8733038e /src
parentd760a699bebd950644aa7a7aa006ce698a995e47 (diff)
Matrix: add room name and avatar of the current room at the top
Diffstat (limited to 'src')
-rw-r--r--src/AsyncImageLoader.cpp138
-rw-r--r--src/Body.cpp122
-rw-r--r--src/QuickMedia.cpp70
-rw-r--r--src/Storage.cpp4
-rw-r--r--src/plugins/Matrix.cpp1
5 files changed, 207 insertions, 128 deletions
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp
new file mode 100644
index 0000000..020baf1
--- /dev/null
+++ b/src/AsyncImageLoader.cpp
@@ -0,0 +1,138 @@
+#include "../include/AsyncImageLoader.hpp"
+#include "../include/base64_url.hpp"
+#include "../include/Storage.hpp"
+#include "../include/DownloadUtils.hpp"
+#include "../include/ImageUtils.hpp"
+#include "../include/Scale.hpp"
+#include <assert.h>
+
+namespace QuickMedia {
+ static sf::Vector2f to_vec2f(const sf::Vector2u &vec) {
+ return sf::Vector2f(vec.x, vec.y);
+ }
+
+ static sf::Vector2f to_vec2f(const sf::Vector2i &vec) {
+ return sf::Vector2f(vec.x, vec.y);
+ }
+
+ static sf::Vector2u to_vec2u(const sf::Vector2f &vec) {
+ return sf::Vector2u(vec.x, vec.y);
+ }
+
+ static void copy_resize(const sf::Image &source, sf::Image &destination, sf::Vector2u destination_size) {
+ const sf::Vector2u source_size = source.getSize();
+ if(source_size.x == 0 || source_size.y == 0 || destination_size.x == 0 || destination_size.y == 0)
+ return;
+
+ //float width_ratio = (float)source_size.x / (float)destination_size.x;
+ //float height_ratio = (float)source_size.y / (float)destination_size.y;
+
+ const sf::Uint8 *source_pixels = source.getPixelsPtr();
+ // TODO: Remove this somehow. Right now we need to allocate this and also allocate the same array in the destination image
+ sf::Uint32 *destination_pixels = new sf::Uint32[destination_size.x * destination_size.y];
+ sf::Uint32 *destination_pixel = destination_pixels;
+ for(unsigned int y = 0; y < destination_size.y; ++y) {
+ for(unsigned int x = 0; x < destination_size.x; ++x) {
+ int scaled_x = ((float)x / (float)destination_size.x) * source_size.x;
+ int scaled_y = ((float)y / (float)destination_size.y) * source_size.y;
+ //float scaled_x = x * width_ratio;
+ //float scaled_y = y * height_ratio;
+
+ //sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (int)(scaled_x + scaled_y * source_size.x) * 4);
+ sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (scaled_x + scaled_y * source_size.x) * 4);
+ *destination_pixel = *source_pixel;
+ ++destination_pixel;
+ }
+ }
+ destination.create(destination_size.x, destination_size.y, (sf::Uint8*)destination_pixels);
+ delete []destination_pixels;
+ }
+
+ static bool save_image_as_thumbnail_atomic(const sf::Image &image, const Path &thumbnail_path, const char *ext) {
+ Path tmp_path = thumbnail_path;
+ tmp_path.append(".tmp");
+ const char *thumbnail_path_ext = thumbnail_path.ext();
+ if(is_image_ext(ext))
+ tmp_path.append(ext);
+ else if(is_image_ext(thumbnail_path_ext))
+ tmp_path.append(thumbnail_path_ext);
+ else
+ tmp_path.append(".png");
+ return image.saveToFile(tmp_path.data) && (rename(tmp_path.data.c_str(), thumbnail_path.data.c_str()) == 0);
+ }
+
+ // Returns empty string if no extension
+ static const char* get_ext(const std::string &path) {
+ size_t index = path.rfind('.');
+ if(index == std::string::npos)
+ return "";
+ return path.c_str() + index;
+ }
+
+ bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, bool use_tor, std::shared_ptr<ThumbnailData> thumbnail_data) {
+ update();
+
+ if(loading_image)
+ return false;
+
+ loading_image = true;
+
+ assert(thumbnail_data->loading_state == LoadingState::NOT_LOADED);
+ thumbnail_data->loading_state = LoadingState::LOADING;
+
+ if(url.empty()) {
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ loading_image = false;
+ return true;
+ }
+
+ load_image_future = std::async(std::launch::async, [url, local, resize_target_size, thumbnail_data, use_tor]() mutable {
+ // TODO: Use sha256 instead of base64_url encoding
+ Path thumbnail_path = get_cache_dir().join("thumbnails").join(base64_url::encode(url));
+
+ thumbnail_data->image = std::make_unique<sf::Image>();
+ if(thumbnail_data->image->loadFromFile(thumbnail_path.data)) {
+ fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str());
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ return;
+ } else {
+ if(local) {
+ if(!thumbnail_data->image->loadFromFile(url)) {
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ return;
+ }
+ } else {
+ std::string texture_data;
+ if(download_to_string_cache(url, texture_data, {}, use_tor, true) != DownloadResult::OK || !thumbnail_data->image->loadFromMemory(texture_data.data(), texture_data.size())) {
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ return;
+ }
+ }
+ }
+
+ if(resize_target_size.x != 0 && resize_target_size.y != 0) {
+ sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(thumbnail_data->image->getSize()), to_vec2f(resize_target_size)));
+ if(new_image_size.x < thumbnail_data->image->getSize().x || new_image_size.y < thumbnail_data->image->getSize().y) {
+ auto destination_image = std::make_unique<sf::Image>();
+ copy_resize(*thumbnail_data->image, *destination_image, new_image_size);
+ thumbnail_data->image = std::move(destination_image);
+ save_image_as_thumbnail_atomic(*thumbnail_data->image, thumbnail_path, get_ext(url));
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ return;
+ }
+ }
+
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ return;
+ });
+
+ return true;
+ }
+
+ void AsyncImageLoader::update() {
+ if(loading_image && load_image_future.valid() && load_image_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+ load_image_future.get();
+ loading_image = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Body.cpp b/src/Body.cpp
index 13e3d7d..843a1b1 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -1,8 +1,6 @@
#include "../include/Body.hpp"
#include "../include/QuickMedia.hpp"
#include "../include/Scale.hpp"
-#include "../include/base64_url.hpp"
-#include "../include/ImageUtils.hpp"
#include "../plugins/Plugin.hpp"
#include <SFML/OpenGL.hpp>
#include <assert.h>
@@ -72,7 +70,6 @@ namespace QuickMedia {
wrap_around(false),
line_seperator_color(sf::Color(32, 37, 43, 255)),
program(program),
- loading_thumbnail(false),
selected_item(0),
prev_selected_item(0),
page_scroll(0.0f),
@@ -271,115 +268,6 @@ namespace QuickMedia {
//page_scroll = 0.0f;
}
- static sf::Vector2f to_vec2f(const sf::Vector2u &vec) {
- return sf::Vector2f(vec.x, vec.y);
- }
-
- static sf::Vector2f to_vec2f(const sf::Vector2i &vec) {
- return sf::Vector2f(vec.x, vec.y);
- }
-
- static sf::Vector2u to_vec2u(const sf::Vector2f &vec) {
- return sf::Vector2u(vec.x, vec.y);
- }
-
- static void copy_resize(const sf::Image &source, sf::Image &destination, sf::Vector2u destination_size) {
- const sf::Vector2u source_size = source.getSize();
- if(source_size.x == 0 || source_size.y == 0 || destination_size.x == 0 || destination_size.y == 0)
- return;
-
- //float width_ratio = (float)source_size.x / (float)destination_size.x;
- //float height_ratio = (float)source_size.y / (float)destination_size.y;
-
- const sf::Uint8 *source_pixels = source.getPixelsPtr();
- // TODO: Remove this somehow. Right now we need to allocate this and also allocate the same array in the destination image
- sf::Uint32 *destination_pixels = new sf::Uint32[destination_size.x * destination_size.y];
- sf::Uint32 *destination_pixel = destination_pixels;
- for(unsigned int y = 0; y < destination_size.y; ++y) {
- for(unsigned int x = 0; x < destination_size.x; ++x) {
- int scaled_x = ((float)x / (float)destination_size.x) * source_size.x;
- int scaled_y = ((float)y / (float)destination_size.y) * source_size.y;
- //float scaled_x = x * width_ratio;
- //float scaled_y = y * height_ratio;
-
- //sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (int)(scaled_x + scaled_y * source_size.x) * 4);
- sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (scaled_x + scaled_y * source_size.x) * 4);
- *destination_pixel = *source_pixel;
- ++destination_pixel;
- }
- }
- destination.create(destination_size.x, destination_size.y, (sf::Uint8*)destination_pixels);
- delete []destination_pixels;
- }
-
- static bool save_image_as_thumbnail_atomic(const sf::Image &image, const Path &thumbnail_path, const char *ext) {
- Path tmp_path = thumbnail_path;
- tmp_path.append(".tmp");
- const char *thumbnail_path_ext = thumbnail_path.ext();
- if(is_image_ext(ext))
- tmp_path.append(ext);
- else if(is_image_ext(thumbnail_path_ext))
- tmp_path.append(thumbnail_path_ext);
- else
- tmp_path.append(".png");
- return image.saveToFile(tmp_path.data) && (rename(tmp_path.data.c_str(), thumbnail_path.data.c_str()) == 0);
- }
-
- // Returns empty string if no extension
- static const char* get_ext(const std::string &path) {
- size_t index = path.rfind('.');
- if(index == std::string::npos)
- return "";
- return path.c_str() + index;
- }
-
- // TODO: Do not load thumbnails for images larger than 30mb.
- // TODO: Load the thumbnail embedded in the file instead.
- void Body::load_thumbnail_from_url(const std::string &url, bool local, sf::Vector2i thumbnail_resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data) {
- assert(!loading_thumbnail);
- loading_thumbnail = true;
-
- load_thumbnail_future = std::async(std::launch::async, [this, url, local, thumbnail_resize_target_size, thumbnail_data]() mutable {
- // TODO: Use sha256 instead of base64_url encoding
- Path thumbnail_path = get_cache_dir().join("thumbnails").join(base64_url::encode(url));
-
- thumbnail_data->image = std::make_unique<sf::Image>();
- if(thumbnail_data->image->loadFromFile(thumbnail_path.data)) {
- fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str());
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
- } else {
- if(local) {
- if(!thumbnail_data->image->loadFromFile(url)) {
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
- }
- } else {
- std::string texture_data;
- if(download_to_string_cache(url, texture_data, {}, program->get_current_plugin()->use_tor, true) != DownloadResult::OK || !thumbnail_data->image->loadFromMemory(texture_data.data(), texture_data.size())) {
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
- }
- }
- }
-
- if(thumbnail_resize_target_size.x != 0 && thumbnail_resize_target_size.y != 0) {
- sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(thumbnail_data->image->getSize()), to_vec2f(thumbnail_resize_target_size)));
- if(new_image_size.x < thumbnail_data->image->getSize().x || new_image_size.y < thumbnail_data->image->getSize().y) {
- auto destination_image = std::make_unique<sf::Image>();
- copy_resize(*thumbnail_data->image, *destination_image, new_image_size);
- thumbnail_data->image = std::move(destination_image);
- save_image_as_thumbnail_atomic(*thumbnail_data->image, thumbnail_path, get_ext(url));
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
- }
- }
-
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
- });
- }
-
void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) {
draw(window, pos, size, Json::nullValue);
}
@@ -404,11 +292,6 @@ namespace QuickMedia {
num_visible_items = 0;
last_item_fully_visible = true;
- if(loading_thumbnail && load_thumbnail_future.valid() && load_thumbnail_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
- load_thumbnail_future.get();
- loading_thumbnail = false;
- }
-
int num_items = items.size();
if(num_items == 0 || size.y <= 0.0f) {
for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) {
@@ -570,9 +453,8 @@ namespace QuickMedia {
item_thumbnail->referenced = true;
if(draw_thumbnails) {
- if(!loading_thumbnail && !item->thumbnail_url.empty() && item_thumbnail->loading_state == LoadingState::NOT_LOADED) {
- item_thumbnail->loading_state = LoadingState::LOADING;
- load_thumbnail_from_url(item->thumbnail_url, item->thumbnail_is_local, thumbnail_resize_target_size, item_thumbnail);
+ if(!item->thumbnail_url.empty() && item_thumbnail->loading_state == LoadingState::NOT_LOADED) {
+ async_image_loader.load_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_resize_target_size, program->get_current_plugin()->use_tor, item_thumbnail);
}
if(item_thumbnail->loading_state == LoadingState::FINISHED_LOADING) {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 709dfea..909bdd7 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3502,6 +3502,20 @@ namespace QuickMedia {
const float tab_vertical_offset = 10.0f;
+ sf::Text room_name_text("", *font, 18);
+ if(current_room_body_data)
+ room_name_text.setString(current_room_body_data->body_item->get_title());
+ const float room_name_text_height = std::floor(room_name_text.getLocalBounds().height);
+ const float room_name_text_padding_y = 10.0f;
+ const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f;
+ const float room_avatar_height = 32.0f;
+
+ sf::Sprite room_avatar_sprite;
+ auto room_avatar_thumbnail_data = std::make_shared<ThumbnailData>();
+ AsyncImageLoader async_image_loader;
+ if(current_room_body_data)
+ async_image_loader.load_thumbnail(current_room_body_data->body_item->thumbnail_url, false, sf::Vector2i(), use_tor, room_avatar_thumbnail_data);
+
auto typing_async_func = [matrix](bool new_state, std::string room_id) {
if(new_state) {
matrix->on_start_typing(room_id);
@@ -3755,7 +3769,7 @@ namespace QuickMedia {
}
//chat_input.on_event(event);
chat_input.process_event(event);
- } else if(tabs[selected_tab].type == ChatTabType::ROOMS && event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Enter) {
+ } else if(tabs[selected_tab].type == ChatTabType::ROOMS && event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Enter) {
BodyItem *selected_item = tabs[selected_tab].body->get_selected();
if(selected_item) {
current_room_id = selected_item->url;
@@ -3773,8 +3787,11 @@ namespace QuickMedia {
}
auto room_body_item_it = body_items_by_room_id.find(current_room_id);
- if(room_body_item_it != body_items_by_room_id.end())
+ if(room_body_item_it != body_items_by_room_id.end()) {
current_room_body_data = &room_body_item_it->second;
+ room_name_text.setString(current_room_body_data->body_item->get_title());
+ room_avatar_thumbnail_data = std::make_shared<ThumbnailData>();
+ }
}
}
}
@@ -3782,8 +3799,10 @@ namespace QuickMedia {
switch(new_page) {
case Page::FILE_MANAGER: {
new_page = Page::CHAT;
- if(!file_manager)
+ if(!file_manager) {
file_manager = new FileManager();
+ file_manager->set_current_directory(get_home_dir().data);
+ }
page_stack.push(Page::CHAT);
current_page = Page::FILE_MANAGER;
file_manager_page();
@@ -3839,7 +3858,28 @@ namespace QuickMedia {
++it;
}
- const float tab_shade_height = tab_spacer_height + std::floor(tab_vertical_offset) + tab_height + 10.0f;
+ async_image_loader.update();
+ if(current_room_body_data && room_avatar_thumbnail_data->loading_state == LoadingState::NOT_LOADED)
+ async_image_loader.load_thumbnail(current_room_body_data->body_item->thumbnail_url, false, sf::Vector2i(), use_tor, room_avatar_thumbnail_data);
+
+ if(room_avatar_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING) {
+ if(!room_avatar_thumbnail_data->texture.loadFromImage(*room_avatar_thumbnail_data->image))
+ fprintf(stderr, "Warning: failed to load texture for room avatar\n");
+ room_avatar_thumbnail_data->image.reset();
+ room_avatar_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE;
+ room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true);
+
+ auto texture_size = room_avatar_sprite.getTexture()->getSize();
+ if(texture_size.x > 0 && texture_size.y > 0) {
+ float width_ratio = (float)texture_size.x / (float)texture_size.y;
+ float height_scale = room_avatar_height / (float)texture_size.y;
+ float width_scale = height_scale * width_ratio;
+ room_avatar_sprite.setScale(width_scale, height_scale);
+ }
+ }
+
+ const float room_name_padding_y = (selected_tab == MESSAGES_TAB_INDEX ? room_name_total_height : 0.0f);
+ const float tab_shade_height = tab_spacer_height + std::floor(tab_vertical_offset) + tab_height + 10.0f + room_name_padding_y;
const float chat_height = chat_input.get_height();
if(std::abs(chat_height - prev_chat_height) > 1.0f) {
@@ -3883,7 +3923,7 @@ namespace QuickMedia {
if(!sync_running && sync_timer.getElapsedTime().asMilliseconds() >= sync_min_time_ms) {
fprintf(stderr, "Time since last sync: %d ms\n", sync_timer.getElapsedTime().asMilliseconds());
// TODO: Ignore matrix->sync() call the first time, its already called above for the first time
- sync_min_time_ms = 3000;
+ sync_min_time_ms = 50;
sync_running = true;
sync_timer.restart();
sync_future_room_id = current_room_id;
@@ -3949,11 +3989,25 @@ namespace QuickMedia {
url_selection_body.draw(window, body_pos, body_size);
else
tabs[selected_tab].body->draw(window, body_pos, body_size);
- const float tab_y = tab_spacer_height + std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f);
+ const float tab_y = tab_spacer_height + std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f) + room_name_padding_y;
tab_shade.setSize(sf::Vector2f(window_size.x, tab_shade_height));
window.draw(tab_shade);
+ if(tabs[selected_tab].type == ChatTabType::MESSAGES) {
+ float room_name_text_offset_x = 0.0f;
+ if(room_avatar_sprite.getTexture() && room_avatar_sprite.getTexture()->getNativeHandle() != 0) {
+ auto room_avatar_texture_size = room_avatar_sprite.getTexture()->getSize();
+ room_avatar_texture_size.x *= room_avatar_sprite.getScale().x;
+ room_avatar_texture_size.y *= room_avatar_sprite.getScale().y;
+ room_avatar_sprite.setPosition(body_pos.x, room_name_total_height * 0.5f - room_avatar_texture_size.y * 0.5f + 5.0f);
+ window.draw(room_avatar_sprite);
+ room_name_text_offset_x += room_avatar_texture_size.x + 10.0f;
+ }
+ room_name_text.setPosition(body_pos.x + room_name_text_offset_x, room_name_text_padding_y);
+ window.draw(room_name_text);
+ }
+
gradient_points[0].position.x = 0.0f;
gradient_points[0].position.y = tab_shade_height;
@@ -3969,7 +4023,7 @@ namespace QuickMedia {
int i = 0;
for(ChatTab &tab : tabs) {
if(i == selected_tab) {
- tab_background.setPosition(std::floor(i * width_per_tab + tab_margin_x), tab_spacer_height + std::floor(tab_vertical_offset));
+ tab_background.setPosition(std::floor(i * width_per_tab + tab_margin_x), tab_spacer_height + std::floor(tab_vertical_offset) + room_name_padding_y);
window.draw(tab_background);
}
const float center = (i * width_per_tab) + (width_per_tab * 0.5f);
@@ -4025,7 +4079,7 @@ namespace QuickMedia {
current_room_body_data->body_item->title_color = sf::Color::White;
current_room_body_data->last_message_read = true;
}
- } else {
+ } else if(current_room_body_data && !current_room_body_data->last_message_read) {
window.draw(more_messages_below_rect);
}
}
diff --git a/src/Storage.cpp b/src/Storage.cpp
index f82aba3..cd34b56 100644
--- a/src/Storage.cpp
+++ b/src/Storage.cpp
@@ -27,6 +27,10 @@ namespace QuickMedia {
passwd *pw = getpwuid(getuid());
homeDir = pw->pw_dir;
}
+ if(!homeDir) {
+ fprintf(stderr, "Failed to get home directory of user!\n");
+ abort();
+ }
return homeDir;
#elif OS_FAMILY == OS_FAMILY_WINDOWS
BOOL ret;
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 257feb4..6d89ff4 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -1174,6 +1174,7 @@ namespace QuickMedia {
request_data["type"] = "m.login.password";
request_data["identifier"] = std::move(identifier_json);
request_data["password"] = password;
+ request_data["initial_device_display_name"] = "QuickMedia"; // :^)
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";