diff options
author | dec05eba <dec05eba@protonmail.com> | 2020-10-01 19:12:33 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2020-10-01 19:12:33 +0200 |
commit | 30dbaeb2b175c1e67f57aba748ced1a2280fb56d (patch) | |
tree | b84cd3b4d2eaa94c3b91fb1f1cb75e5a8733038e | |
parent | d760a699bebd950644aa7a7aa006ce698a995e47 (diff) |
Matrix: add room name and avatar of the current room at the top
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | include/AsyncImageLoader.hpp | 36 | ||||
-rw-r--r-- | include/Body.hpp | 17 | ||||
-rw-r--r-- | src/AsyncImageLoader.cpp | 138 | ||||
-rw-r--r-- | src/Body.cpp | 122 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 70 | ||||
-rw-r--r-- | src/Storage.cpp | 4 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 1 |
8 files changed, 252 insertions, 144 deletions
@@ -60,4 +60,10 @@ Sleep when idle, to reduce cpu usage from 1-2% to 0%, important for mobile devic Show a notification when somebody replies to you on matrix and also provide a way to notify when notifications should be received (using matrix api) and also read the notification config from matrix. Also provide a way to disable notifications globally. Also send read markers to not notify every time we restart QuickMedia. Direct message room name should always be the name of the other peer, or in an anonymous room the room name should be some of their names and "X others". Add room search. -Use quickmedia to show image in matrix rooms, instead of mpv.
\ No newline at end of file +Use quickmedia to show image in matrix rooms, instead of mpv. +Respect ~/.Xresources dpi (read the file, loop lines and look for Xft.dpi). Fallback to 96 dpi. +Merge body items in matrix if they are posted by the same author (there is a git stash for this). +Add joining/leaving room in matrix, and also show invites and add command to ban users. Also add joining by invite, and show invites in the rooms list. +Support peertube (works with mpv, but need to implement search and related videos). +Scroll to bottom when receiving a new message even if the selected message is not the last one. It should instead school if the last message is visible on the screen. +Add ".." directory to file-manager, to go up one directory. Also add a tab for common directories and recently accessed files/directories (the directories would be the directory of used files).
\ No newline at end of file diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp new file mode 100644 index 0000000..27e46e3 --- /dev/null +++ b/include/AsyncImageLoader.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <string> +#include <SFML/System/Vector2.hpp> +#include <SFML/Graphics/Texture.hpp> +#include <memory> +#include <future> + +namespace QuickMedia { + enum class LoadingState { + NOT_LOADED, + LOADING, + FINISHED_LOADING, + APPLIED_TO_TEXTURE + }; + + struct ThumbnailData { + bool referenced = false; + LoadingState loading_state = LoadingState::NOT_LOADED; + sf::Texture texture; + std::unique_ptr<sf::Image> image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory + }; + + class AsyncImageLoader { + public: + // Returns false if the image loader is already loading an image. In that case, this function should be called again later. + // set |resize_target_size| to {0, 0} to disable resizing. + // |thumbnail_data.loading_state| has to be LoadingState::NOT_LOADED when calling this! + // Note: this method is not thread-safe + bool load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, bool use_tor, std::shared_ptr<ThumbnailData> thumbnail_data); + void update(); + private: + bool loading_image = false; + std::future<void> load_image_future; + }; +}
\ No newline at end of file diff --git a/include/Body.hpp b/include/Body.hpp index 60b976d..d2a3424 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -1,6 +1,7 @@ #pragma once #include "Text.hpp" +#include "AsyncImageLoader.hpp" #include <SFML/Graphics/Text.hpp> #include <SFML/Graphics/Texture.hpp> #include <SFML/Graphics/RectangleShape.hpp> @@ -154,23 +155,9 @@ namespace QuickMedia { private: void draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress); private: - enum class LoadingState { - NOT_LOADED, - LOADING, - FINISHED_LOADING, - APPLIED_TO_TEXTURE - }; - - struct ThumbnailData { - bool referenced = false; - LoadingState loading_state = LoadingState::NOT_LOADED; - sf::Texture texture; - std::unique_ptr<sf::Image> image; // Set in another thread, and then reset after loading it into |texture| - }; Program *program; - void load_thumbnail_from_url(const std::string &url, bool local, sf::Vector2i thumbnail_resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data); std::unordered_map<std::string, std::shared_ptr<ThumbnailData>> item_thumbnail_textures; - bool loading_thumbnail; + AsyncImageLoader async_image_loader; int selected_item; int prev_selected_item; float page_scroll; 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"; |