diff --git a/src/single_window/connection_status_window.cpp b/src/single_window/connection_status_window.cpp index 87f4983..172bcf9 100644 --- a/src/single_window/connection_status_window.cpp +++ b/src/single_window/connection_status_window.cpp @@ -104,8 +104,8 @@ int Render::ConnectionStatusWindow( focus_on_input_widget_ = false; } - ImGui::InputText("##password", remote_password_, - IM_ARRAYSIZE(remote_password_), + ImGui::InputText("##password", props->remote_password_, + IM_ARRAYSIZE(props->remote_password_), ImGuiInputTextFlags_CharsNoBlank); ImGui::SetWindowFontScale(0.4f); @@ -138,7 +138,7 @@ int Render::ConnectionStatusWindow( if (ImGui::Button( localization::cancel[localization_language_index_].c_str()) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { - memset(remote_password_, 0, sizeof(remote_password_)); + memset(props->remote_password_, 0, sizeof(props->remote_password_)); show_connection_status_window_ = false; focus_on_input_widget_ = true; } diff --git a/src/single_window/recent_connections.cpp b/src/single_window/recent_connections.cpp index 56b7cac..43e9344 100644 --- a/src/single_window/recent_connections.cpp +++ b/src/single_window/recent_connections.cpp @@ -56,12 +56,12 @@ int Render::ShowRecentConnections() { ImGuiWindowFlags_NoScrollWithMouse); ImGui::PopStyleVar(); ImGui::PopStyleColor(); - size_t recent_connections_count = recent_connection_textures_.size(); + size_t recent_connections_count = recent_connections_.size(); int count = 0; float button_width = 22; float button_height = 22; - for (auto it = recent_connection_textures_.begin(); - it != recent_connection_textures_.end(); ++it) { + for (auto it = recent_connections_.begin(); it != recent_connections_.end(); + ++it) { sub_containers_pos[it->first] = ImGui::GetCursorPos(); std::string recent_connection_sub_window_name = "RecentConnectionsSubContainer" + it->first; @@ -77,23 +77,23 @@ int Render::ShowRecentConnections() { ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoScrollbar); std::string connection_info = it->first; - std::string remote_id; - std::string password; - std::string host_name; // remote id length is 9 // password length is 6 // connection_info -> remote_id + 'Y' + password + host_name // -> remote_id + 'N' + host_name if ('Y' == connection_info[9] && connection_info.size() >= 16) { - remote_id = connection_info.substr(0, 9); - password = connection_info.substr(10, 6); - host_name = connection_info.substr(16); + it->second.remote_id = connection_info.substr(0, 9); + it->second.password = connection_info.substr(10, 6); + it->second.remote_host_name = connection_info.substr(16); + it->second.remember_password = true; } else if ('N' == connection_info[9] && connection_info.size() >= 10) { - remote_id = connection_info.substr(0, 9); - host_name = connection_info.substr(10); + it->second.remote_id = connection_info.substr(0, 9); + it->second.remote_host_name = connection_info.substr(10); + it->second.password = ""; + it->second.remember_password = false; } else { - host_name = "unknown"; + it->second.remote_host_name = "unknown"; } ImVec2 image_screen_pos = ImVec2(ImGui::GetCursorScreenPos().x + 5.0f, @@ -101,7 +101,7 @@ int Render::ShowRecentConnections() { ImVec2 image_pos = ImVec2(ImGui::GetCursorPosX() + 5.0f, ImGui::GetCursorPosY() + 5.0f); ImGui::SetCursorPos(image_pos); - ImGui::Image((ImTextureID)(intptr_t)it->second, + ImGui::Image((ImTextureID)(intptr_t)it->second.texture, ImVec2((float)recent_connection_image_width_, (float)recent_connection_image_height_)); @@ -113,7 +113,7 @@ int Render::ShowRecentConnections() { ImVec2 dummy_button_pos = ImVec2(image_pos.x, image_pos.y + recent_connection_image_height_); - std::string dummy_button_name = "##DummyButton" + remote_id; + std::string dummy_button_name = "##DummyButton" + it->second.remote_id; ImGui::SetCursorPos(dummy_button_pos); ImGui::SetWindowFontScale(0.6f); ImGui::Button(dummy_button_name.c_str(), @@ -123,14 +123,14 @@ int Render::ShowRecentConnections() { ImGui::SetCursorPos( ImVec2(dummy_button_pos.x + 2.0f, dummy_button_pos.y + 1.0f)); ImGui::SetWindowFontScale(0.65f); - ImGui::Text("%s", remote_id.c_str()); + ImGui::Text("%s", it->second.remote_id.c_str()); ImGui::SetWindowFontScale(1.0f); ImGui::PopStyleColor(3); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::SetWindowFontScale(0.5f); - ImGui::Text("%s", host_name.c_str()); + ImGui::Text("%s", it->second.remote_host_name.c_str()); ImGui::SetWindowFontScale(1.0f); ImGui::EndTooltip(); } @@ -176,15 +176,8 @@ int Render::ShowRecentConnections() { connect + "##ConnectionTo" + it->first; if (ImGui::Button(connect_to_this_connection_button_name.c_str(), ImVec2(button_width, button_height))) { - remote_id_ = remote_id; - - bool remember_password_flag = false; - if (!password.empty() && password.size() == 6) { - remember_password_flag = true; - memcpy(remote_password_, password.c_str(), 6); - } - - ConnectTo(remote_id, remote_password_, remember_password_flag); + ConnectTo(it->second.remote_id, it->second.password.c_str(), + it->second.remember_password); } } ImGui::SetWindowFontScale(1.0f); diff --git a/src/single_window/remote_peer_window.cpp b/src/single_window/remote_peer_window.cpp index 9dce011..ec1ac6b 100644 --- a/src/single_window/remote_peer_window.cpp +++ b/src/single_window/remote_peer_window.cpp @@ -68,21 +68,44 @@ int Render::RemoteWindow() { ImGui::PopStyleVar(); ImGui::SameLine(); + + std::string remote_id = remote_id_display_; + remote_id.erase(remove_if(remote_id.begin(), remote_id.end(), + static_cast(&isspace)), + remote_id.end()); if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG, ImVec2(55, 38)) || enter_pressed) { connect_button_pressed_ = true; - remote_id_ = remote_id_display_; - remote_id_.erase(remove_if(remote_id_.begin(), remote_id_.end(), - static_cast(&isspace)), - remote_id_.end()); - ConnectTo(remote_id_, remote_password_, false); + bool found = false; + for (auto &[id, props] : recent_connections_) { + if (id.find(remote_id) != std::string::npos) { + found = true; + if (client_properties_.find(remote_id) != + client_properties_.end()) { + if (!client_properties_[remote_id]->connection_established_) { + ConnectTo(props.remote_id, props.password.c_str(), false); + } else { + // todo: show warning message + LOG_INFO("Already connected to [{}]", remote_id); + } + } else { + ConnectTo(props.remote_id, props.password.c_str(), false); + } + } + } + + if (!found) { + ConnectTo(remote_id, "", false); + } } - if (client_properties_.find(remote_id_) != client_properties_.end()) { - auto props = client_properties_[remote_id_]; - if (props->rejoin_) { - ConnectTo(remote_id_, remote_password_, - client_properties_[remote_id_]->remember_password_); + if (need_to_rejoin_) { + need_to_rejoin_ = false; + for (const auto &[_, props] : client_properties_) { + if (props->rejoin_) { + ConnectTo(props->remote_id_, props->remote_password_, + props->remember_password_); + } } } } @@ -133,13 +156,14 @@ int Render::ConnectTo(const std::string &remote_id, const char *password, int ret = -1; auto props = client_properties_[remote_id]; if (!props->connection_established_) { + props->remember_password_ = remember_password; + memcpy(props->remote_password_, password, 6); ret = JoinConnection(props->peer_, remote_id.c_str(), password); if (0 == ret) { props->rejoin_ = false; - props->remember_password_ = remember_password; - memcpy(props->remote_password_, password, 6); } else { props->rejoin_ = true; + need_to_rejoin_ = true; } } diff --git a/src/single_window/render.cpp b/src/single_window/render.cpp index 7394882..e34766a 100644 --- a/src/single_window/render.cpp +++ b/src/single_window/render.cpp @@ -527,6 +527,7 @@ int Render::CreateStreamWindow() { SDL_PushEvent(&event); stream_window_created_ = true; + just_created_ = true; return 0; } @@ -841,9 +842,9 @@ void Render::HandleRecentConnections() { if (reload_recent_connections_ && main_renderer_) { uint32_t now_time = SDL_GetTicks(); if (now_time - recent_connection_image_save_time_ >= 50) { - int ret = thumbnail_->LoadThumbnail( - main_renderer_, recent_connection_textures_, - &recent_connection_image_width_, &recent_connection_image_height_); + int ret = thumbnail_->LoadThumbnail(main_renderer_, recent_connections_, + &recent_connection_image_width_, + &recent_connection_image_height_); if (!ret) { LOG_INFO("Load recent connection thumbnails"); } @@ -1029,51 +1030,55 @@ void Render::ProcessSdlEvent() { } else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED && stream_window_created_ && event.window.windowID == SDL_GetWindowID(stream_window_)) { - for (auto& [_, props] : client_properties_) { - if (!props->reset_control_bar_pos_) { - props->mouse_diff_control_bar_pos_x_ = 0; - props->mouse_diff_control_bar_pos_y_ = 0; - } + if (just_created_) { + just_created_ = false; + } else { + for (auto& [_, props] : client_properties_) { + if (!props->reset_control_bar_pos_) { + props->mouse_diff_control_bar_pos_x_ = 0; + props->mouse_diff_control_bar_pos_y_ = 0; + } - props->reset_control_bar_pos_ = true; - int stream_window_width, stream_window_height; - SDL_GetWindowSize(stream_window_, &stream_window_width, - &stream_window_height); - stream_window_width_ = (float)stream_window_width; - stream_window_height_ = (float)stream_window_height; + props->reset_control_bar_pos_ = true; + int stream_window_width, stream_window_height; + SDL_GetWindowSize(stream_window_, &stream_window_width, + &stream_window_height); + stream_window_width_ = (float)stream_window_width; + stream_window_height_ = (float)stream_window_height; - float video_ratio = - (float)props->video_width_ / (float)props->video_height_; - float video_ratio_reverse = - (float)props->video_height_ / (float)props->video_width_; + float video_ratio = + (float)props->video_width_ / (float)props->video_height_; + float video_ratio_reverse = + (float)props->video_height_ / (float)props->video_width_; - float render_area_width = stream_window_width_; - float render_area_height = - stream_window_height_ - - (fullscreen_button_pressed_ ? 0 : title_bar_height_); + float render_area_width = stream_window_width_; + float render_area_height = + stream_window_height_ - + (fullscreen_button_pressed_ ? 0 : title_bar_height_); - props->stream_render_rect_last_ = props->stream_render_rect_; - if (render_area_width < render_area_height * video_ratio) { - props->stream_render_rect_ = { - 0, - (int)(abs(render_area_height - - render_area_width * video_ratio_reverse) / - 2 + - (fullscreen_button_pressed_ ? 0 : title_bar_height_)), - (int)render_area_width, - (int)(render_area_width * video_ratio_reverse)}; - } else if (render_area_width > render_area_height * video_ratio) { - props->stream_render_rect_ = { - (int)abs(render_area_width - - render_area_height * video_ratio) / - 2, - fullscreen_button_pressed_ ? 0 : (int)title_bar_height_, - (int)(render_area_height * video_ratio), - (int)render_area_height}; - } else { - props->stream_render_rect_ = { - 0, fullscreen_button_pressed_ ? 0 : (int)title_bar_height_, - (int)render_area_width, (int)render_area_height}; + props->stream_render_rect_last_ = props->stream_render_rect_; + if (render_area_width < render_area_height * video_ratio) { + props->stream_render_rect_ = { + 0, + (int)(abs(render_area_height - + render_area_width * video_ratio_reverse) / + 2 + + (fullscreen_button_pressed_ ? 0 : title_bar_height_)), + (int)render_area_width, + (int)(render_area_width * video_ratio_reverse)}; + } else if (render_area_width > render_area_height * video_ratio) { + props->stream_render_rect_ = { + (int)abs(render_area_width - + render_area_height * video_ratio) / + 2, + fullscreen_button_pressed_ ? 0 : (int)title_bar_height_, + (int)(render_area_height * video_ratio), + (int)render_area_height}; + } else { + props->stream_render_rect_ = { + 0, fullscreen_button_pressed_ ? 0 : (int)title_bar_height_, + (int)render_area_width, (int)render_area_height}; + } } } } else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED || diff --git a/src/single_window/render.h b/src/single_window/render.h index ea8fb8d..20fb1bd 100644 --- a/src/single_window/render.h +++ b/src/single_window/render.h @@ -240,7 +240,7 @@ class Render { std::unique_ptr thumbnail_; // recent connections - std::map recent_connection_textures_; + std::map recent_connections_; int recent_connection_image_width_ = 160; int recent_connection_image_height_ = 90; uint32_t recent_connection_image_save_time_ = 0; @@ -296,13 +296,13 @@ class Render { char input_password_tmp_[7] = ""; char input_password_[7] = ""; std::string random_password_ = ""; - char remote_password_[7] = ""; char new_password_[7] = ""; char remote_id_display_[12] = ""; - std::string remote_id_ = ""; unsigned char audio_buffer_[720]; int audio_len_ = 0; bool audio_buffer_fresh_ = false; + bool need_to_rejoin_ = false; + bool just_created_ = false; // stream window render SDL_Window *stream_window_ = nullptr; diff --git a/src/single_window/render_callback_func.cpp b/src/single_window/render_callback_func.cpp index 8397c85..57cd5c6 100644 --- a/src/single_window/render_callback_func.cpp +++ b/src/single_window/render_callback_func.cpp @@ -359,9 +359,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id, render->StopSpeakerCapturer(); render->audio_capture_ = false; } - if (!render->rejoin_) { - memset(render->remote_password_, 0, sizeof(render->remote_password_)); - } + if (props->dst_buffer_) { memset(props->dst_buffer_, 0, props->dst_buffer_capacity_); SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_, diff --git a/src/single_window/thumbnail.cpp b/src/single_window/thumbnail.cpp index 9abfb28..a2db5e5 100644 --- a/src/single_window/thumbnail.cpp +++ b/src/single_window/thumbnail.cpp @@ -23,6 +23,65 @@ static std::string test; +bool LoadTextureFromMemory(const void* data, size_t data_size, + SDL_Renderer* renderer, SDL_Texture** out_texture, + int* out_width, int* out_height) { + int image_width = 0; + int image_height = 0; + int channels = 4; + unsigned char* image_data = + stbi_load_from_memory((const unsigned char*)data, (int)data_size, + &image_width, &image_height, NULL, 4); + if (image_data == nullptr) { + LOG_ERROR("Failed to load image: [{}]", stbi_failure_reason()); + return false; + } + + // ABGR + SDL_Surface* surface = SDL_CreateRGBSurfaceFrom( + (void*)image_data, image_width, image_height, channels * 8, + channels * image_width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + if (surface == nullptr) { + LOG_ERROR("Failed to create SDL surface: [{}]", SDL_GetError()); + return false; + } + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + if (texture == nullptr) { + LOG_ERROR("Failed to create SDL texture: [{}]", SDL_GetError()); + } + + *out_texture = texture; + *out_width = image_width; + *out_height = image_height; + + SDL_FreeSurface(surface); + stbi_image_free(image_data); + + return true; +} + +bool LoadTextureFromFile(const char* file_name, SDL_Renderer* renderer, + SDL_Texture** out_texture, int* out_width, + int* out_height) { + std::filesystem::path file_path(file_name); + if (!std::filesystem::exists(file_path)) return false; + std::ifstream file(file_path, std::ios::binary); + if (!file) return false; + file.seekg(0, std::ios::end); + size_t file_size = file.tellg(); + file.seekg(0, std::ios::beg); + if (file_size == -1) return false; + char* file_data = new char[file_size]; + if (!file_data) return false; + file.read(file_data, file_size); + bool ret = LoadTextureFromMemory(file_data, file_size, renderer, out_texture, + out_width, out_height); + delete[] file_data; + + return ret; +} + void ScaleYUV420pToABGR(char* dst_buffer_, int video_width_, int video_height_, int scaled_video_width_, int scaled_video_height_, char* rgba_buffer_) { @@ -87,7 +146,7 @@ int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height, std::string image_name; if (password.empty()) { - image_name = remote_id + 'N' + host_name; + return 0; } else { // delete the file which has no password in its name std::string filename_without_password = remote_id + "N" + host_name; @@ -104,63 +163,49 @@ int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height, return 0; } -bool LoadTextureFromMemory(const void* data, size_t data_size, - SDL_Renderer* renderer, SDL_Texture** out_texture, - int* out_width, int* out_height) { - int image_width = 0; - int image_height = 0; - int channels = 4; - unsigned char* image_data = - stbi_load_from_memory((const unsigned char*)data, (int)data_size, - &image_width, &image_height, NULL, 4); - if (image_data == nullptr) { - LOG_ERROR("Failed to load image: [{}]", stbi_failure_reason()); - return false; +int Thumbnail::LoadThumbnail( + SDL_Renderer* renderer, + std::map& recent_connections, int* width, + int* height) { + for (auto& it : recent_connections) { + if (it.second.texture != nullptr) { + SDL_DestroyTexture(it.second.texture); + it.second.texture = nullptr; + } } + recent_connections.clear(); - // ABGR - SDL_Surface* surface = SDL_CreateRGBSurfaceFrom( - (void*)image_data, image_width, image_height, channels * 8, - channels * image_width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); - if (surface == nullptr) { - LOG_ERROR("Failed to create SDL surface: [{}]", SDL_GetError()); - return false; + std::vector image_paths = + FindThumbnailPath(image_path_); + + if (image_paths.size() == 0) { + return -1; + } else { + for (int i = 0; i < image_paths.size(); i++) { + size_t pos1 = image_paths[i].string().find('/') + 1; + std::string cipher_image_name = image_paths[i].string().substr(pos1); + std::string original_image_name = + AES_decrypt(cipher_image_name, aes128_key_, aes128_iv_); + std::string image_path = image_path_ + cipher_image_name; + recent_connections[original_image_name].texture = nullptr; + LoadTextureFromFile(image_path.c_str(), renderer, + &(recent_connections[original_image_name].texture), + width, height); + } + return 0; } - - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); - if (texture == nullptr) { - LOG_ERROR("Failed to create SDL texture: [{}]", SDL_GetError()); - } - - *out_texture = texture; - *out_width = image_width; - *out_height = image_height; - - SDL_FreeSurface(surface); - stbi_image_free(image_data); - - return true; + return 0; } -bool LoadTextureFromFile(const char* file_name, SDL_Renderer* renderer, - SDL_Texture** out_texture, int* out_width, - int* out_height) { - std::filesystem::path file_path(file_name); - if (!std::filesystem::exists(file_path)) return false; - std::ifstream file(file_path, std::ios::binary); - if (!file) return false; - file.seekg(0, std::ios::end); - size_t file_size = file.tellg(); - file.seekg(0, std::ios::beg); - if (file_size == -1) return false; - char* file_data = new char[file_size]; - if (!file_data) return false; - file.read(file_data, file_size); - bool ret = LoadTextureFromMemory(file_data, file_size, renderer, out_texture, - out_width, out_height); - delete[] file_data; - - return ret; +int Thumbnail::DeleteThumbnail(const std::string& file_name) { + std::string ciphertext = AES_encrypt(file_name, aes128_key_, aes128_iv_); + std::string file_path = image_path_ + ciphertext; + if (std::filesystem::exists(file_path)) { + std::filesystem::remove(file_path); + return 0; + } else { + return -1; + } } std::vector Thumbnail::FindThumbnailPath( @@ -194,49 +239,6 @@ std::vector Thumbnail::FindThumbnailPath( return thumbnails_path; } -int Thumbnail::LoadThumbnail(SDL_Renderer* renderer, - std::map& textures, - int* width, int* height) { - for (auto& it : textures) { - if (it.second != nullptr) { - SDL_DestroyTexture(it.second); - it.second = nullptr; - } - } - textures.clear(); - - std::vector image_paths = - FindThumbnailPath(image_path_); - - if (image_paths.size() == 0) { - return -1; - } else { - for (int i = 0; i < image_paths.size(); i++) { - size_t pos1 = image_paths[i].string().find('/') + 1; - std::string cipher_image_name = image_paths[i].string().substr(pos1); - std::string original_image_name = - AES_decrypt(cipher_image_name, aes128_key_, aes128_iv_); - std::string image_path = image_path_ + cipher_image_name; - textures[original_image_name] = nullptr; - LoadTextureFromFile(image_path.c_str(), renderer, - &(textures[original_image_name]), width, height); - } - return 0; - } - return 0; -} - -int Thumbnail::DeleteThumbnail(const std::string& file_name) { - std::string ciphertext = AES_encrypt(file_name, aes128_key_, aes128_iv_); - std::string file_path = image_path_ + ciphertext; - if (std::filesystem::exists(file_path)) { - std::filesystem::remove(file_path); - return 0; - } else { - return -1; - } -} - int Thumbnail::DeleteAllFilesInDirectory() { if (std::filesystem::exists(image_path_) && std::filesystem::is_directory(image_path_)) { diff --git a/src/single_window/thumbnail.h b/src/single_window/thumbnail.h index 95d95e2..c5e7528 100644 --- a/src/single_window/thumbnail.h +++ b/src/single_window/thumbnail.h @@ -13,6 +13,15 @@ #include class Thumbnail { + public: + struct RecentConnection { + SDL_Texture* texture = nullptr; + std::string remote_id; + std::string remote_host_name; + std::string password; + bool remember_password = false; + }; + public: Thumbnail(); explicit Thumbnail(unsigned char* aes128_key, unsigned char* aes128_iv); @@ -25,8 +34,8 @@ class Thumbnail { const std::string& password); int LoadThumbnail(SDL_Renderer* renderer, - std::map& textures, int* width, - int* height); + std::map& recent_connections, + int* width, int* height); int DeleteThumbnail(const std::string& file_name);