From 55b5118dc0f17cb6e548f49b0b93a4df79838495 Mon Sep 17 00:00:00 2001 From: redpolline <11156324-redpolline@users.noreply.gitlab.com> Date: Sat, 21 Dec 2024 05:34:29 -0500 Subject: [PATCH] Add Overlay UI functions. Too many things to list. --- overlay_experimental/steam_overlay.cpp | 2260 +++++++++++++++++++++--- overlay_experimental/steam_overlay.h | 153 +- 2 files changed, 2203 insertions(+), 210 deletions(-) diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index fcbbf47..66e5dc9 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -50,8 +50,26 @@ static constexpr char *valid_languages[] = { "vietnamese" }; +static constexpr char *valid_ui_notification_position_labels[] = { + "top left", + "top right", + "bottom left", + "bottom right" +}; + #define URL_WINDOW_NAME "URL Window" +class Steam_Overlay_CCallback +{ + private: + GOLDBERG_CALLBACK_INTERNAL(Steam_Overlay_CCallback, OnAvatarImageLoaded, AvatarImageLoaded_t); + + public: + bool is_ready() { + return (AvatarImageLoaded_t_is_registered()); + } +}; + int find_free_id(std::vector & ids, int base) { std::sort(ids.begin(), ids.end()); @@ -94,12 +112,273 @@ int find_free_notification_id(std::vector const& notifications) return find_free_id(ids, base_friend_window_id); } +bool operator<(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return ((a.id.ConvertToUint64() < b.id.ConvertToUint64()) && (a.eAvatarSize < b.eAvatarSize)); +} + +bool operator>(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return (b < a); +} + +bool operator<=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a > b); +} + +bool operator>=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a < b); +} + +bool operator==(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return ((a.id.ConvertToUint64() == b.id.ConvertToUint64()) && (a.eAvatarSize == b.eAvatarSize)); +} + +bool operator!=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a == b); +} + +void Steam_Overlay::populate_initial_profile_images(CSteamID id = k_steamIDNil) { + bool found = false; + if (id == k_steamIDNil) { + id = settings->get_local_steam_id().ConvertToUint64(); + } + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + found = true; + break; + } + } + if (!found) { + profile_images[id] = Profile_Image_Set(); + } + return; +} + #ifdef __WINDOWS__ #include "windows/Windows_Hook.h" #endif #include "notification.h" +bool Steam_Overlay::LoadProfileImage(const CSteamID & id, const int eAvatarSize) { + bool ret = false; + Profile_Image_Set new_images; + Profile_Image_Set old_images; + + ret = LoadProfileImage(id, eAvatarSize, new_images); + if (ret == true) { + std::lock_guard lock(overlay_mutex); + auto entry = profile_images.find(id); + if (entry != profile_images.end()) { + if (eAvatarSize == k_EAvatarSize32x32) { + profile_images[id].small = new_images.small; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + profile_images[id].medium = new_images.medium; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + profile_images[id].large = new_images.large; + } + } + } + } else { + profile_images[id] = new_images; + } + } + + return ret; +} + +bool Steam_Overlay::LoadProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %"PRIu64" size %d.\n", id.ConvertToUint64(), eAvatarSize); + + bool ret = false; + Profile_Image * image = NULL; + uint32 width = 0; + uint32 height = 0; + Steam_Utils* steamUtils = get_steam_client()->steam_utils; + Steam_Friends* steamFriends = get_steam_client()->steam_friends; + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (image != NULL) { + int image_handle = steamFriends->GetFriendAvatar(id, eAvatarSize); + if (image_handle != 0) { + if (steamUtils->GetImageSize(image_handle, &width, &height) == true && + width > 0 && + height > 0) { + + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %"PRIu64" size %d image_handle %d width %d height %d.\n", + id.ConvertToUint64(), eAvatarSize, image_handle, width, height); + + if (image->raw_image != NULL) { + delete image->raw_image; + image->raw_image = NULL; + } + image->width = 0; + image->height = 0; + DestroyProfileImageResource(id, eAvatarSize); + + uint8 * raw_image = new uint8[(width * height * sizeof(uint32))]; + if (raw_image != NULL) { + if (steamUtils->GetImageRGBA(image_handle, raw_image, (width * height * sizeof(uint32))) == true) { + image->raw_image = raw_image; + image->width = width; + image->height = height; + + ret = true; + } else { + delete raw_image; + raw_image = NULL; + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %"PRIu64" size %d could not get pixel data.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + } else { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %"PRIu64" size %d pixel data has invalid size.\n", + id.ConvertToUint64(), eAvatarSize); + } + } else { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %"PRIu64" size %d profile pixel data not loaded.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + + return ret; +} + +void Steam_Overlay::DestroyProfileImage(const CSteamID & id, const int eAvatarSize) { + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + DestroyProfileImage(id, eAvatarSize, x.second); + break; + } + } + + return; +} + +void Steam_Overlay::DestroyProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::DestroyProfileImage() %"PRIu64" size %d.\n", id.ConvertToUint64(), eAvatarSize); + + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (image != NULL) { + if (image->raw_image != NULL) { + delete image->raw_image; + image->raw_image = NULL; + } + image->width = 0; + image->height = 0; + } + + return; +} + +bool Steam_Overlay::CreateProfileImageResource(const CSteamID & id, const int eAvatarSize) { + bool ret = false; + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + ret = CreateProfileImageResource(id, eAvatarSize, x.second); + break; + } + } + + return ret; +} + +bool Steam_Overlay::CreateProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() %"PRIu64" size %d.\n", id.ConvertToUint64(), eAvatarSize); + + bool ret = false; + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (_renderer) { + if (image->raw_image != NULL && image->width > 0 && image->height > 0 && + image->image_resource.expired() == true) { + std::weak_ptr test; + test = _renderer->CreateImageResource(image->raw_image, + image->width, + image->height); + std::shared_ptr test2; + test2 = test.lock(); + if (!test2) { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() Unable to create resource for profile id %"PRIu64" size %d.\n", + id.ConvertToUint64(), eAvatarSize); + } else { + image->image_resource = test; + ret = true; + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() created resource for profile id %"PRIu64" size %d -> %"PRIu64".\n", + id.ConvertToUint64(), eAvatarSize, *test2); + } + } else { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() invalid raw data for profile id %"PRIu64" size %d.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + + return ret; +} + +void Steam_Overlay::DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize) { + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + DestroyProfileImageResource(id, eAvatarSize, x.second); + break; + } + } + + return; +} + +void Steam_Overlay::DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) +{ + PRINT_DEBUG("Steam_Overlay::DestroyProfileImageResource() %"PRIu64" size %d.\n", id.ConvertToUint64(), eAvatarSize); + + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (_renderer != NULL && image != NULL && image->image_resource.expired() == false) { + _renderer->ReleaseImageResource(image->image_resource); + image->image_resource.reset(); + } + + return; +} + void Steam_Overlay::LoadAchievementImage(Overlay_Achievement & ach) { PRINT_DEBUG("LoadAchievementImage() %s.\n", ach.name.c_str()); @@ -116,32 +395,41 @@ void Steam_Overlay::LoadAchievementImage(Overlay_Achievement & ach) PRINT_DEBUG("LoadAchievementImage() %d %d %d.\n", image_handle, width, height); - if (ach.raw_image != NULL) { - delete ach.raw_image; - ach.raw_image = NULL; + { + std::lock_guard lock(overlay_mutex); + if (ach.raw_image != NULL) { + delete ach.raw_image; + ach.raw_image = NULL; + } ach.raw_image_width = 0; ach.raw_image_height = 0; + DestroyAchievementImageResource(ach); } - DestroyAchievementImageResource(ach); uint8 * raw_image = new uint8[(width * height * sizeof(uint32))]; - if ((raw_image != NULL) && - (steamUtils->GetImageRGBA(image_handle, - raw_image, - (width * height * sizeof(uint32))) == true)) { - PRINT_DEBUG("LoadAchievementImage() %d -> %p.\n", image_handle, raw_image); - ach.raw_image = raw_image; - ach.raw_image_width = width; - ach.raw_image_height = height; + if (raw_image != NULL) { + if (steamUtils->GetImageRGBA(image_handle, + raw_image, + (width * height * sizeof(uint32))) == true) { + PRINT_DEBUG("LoadAchievementImage() %d -> %p.\n", image_handle, raw_image); + { + std::lock_guard lock(overlay_mutex); + ach.raw_image = raw_image; + ach.raw_image_width = width; + ach.raw_image_height = height; + } + } else { + delete raw_image; + PRINT_DEBUG("LoadAchievementImage() Achievement %s could not get pixel data.\n", ach.name.c_str()); + } } else { - delete ach.raw_image; - PRINT_DEBUG("Image for achievement %s could not get pixel data.\n", ach.name.c_str()); + PRINT_DEBUG("LoadAchievementImage() Achievement %s could not allocate memory for pixel data.\n", ach.name.c_str()); } } else { - PRINT_DEBUG("Image for achievement %s has an invalid size.\n", ach.name.c_str()); + PRINT_DEBUG("LoadAchievementImage() Achievement %s image pixel data has an invalid size.\n", ach.name.c_str()); } } else { - PRINT_DEBUG("Image for achievement %s is not loaded.\n", ach.name.c_str()); + PRINT_DEBUG("LoadAchievementImage() Achievement %s is not loaded.\n", ach.name.c_str()); } } @@ -150,6 +438,7 @@ void Steam_Overlay::CreateAchievementImageResource(Overlay_Achievement & ach) PRINT_DEBUG("CreateAchievementImageResource() %s. %d x %d -> %p\n", ach.name.c_str(), ach.raw_image_width, ach.raw_image_height, ach.raw_image); if (_renderer) { + std::lock_guard lock(overlay_mutex); if (ach.raw_image != NULL && ach.raw_image_width > 0 && ach.raw_image_height > 0 && ach.image_resource.expired() == true) { std::weak_ptr test; test = _renderer->CreateImageResource(ach.raw_image, @@ -174,8 +463,12 @@ void Steam_Overlay::DestroyAchievementImageResource(Overlay_Achievement & ach) { PRINT_DEBUG("DestroyAchievementImageResource() %s.\n", ach.name.c_str()); - if (_renderer && ach.image_resource.expired() == false) { - _renderer->ReleaseImageResource(ach.image_resource); + if (_renderer) { + std::lock_guard lock(overlay_mutex); + if (ach.image_resource.expired() == false) { + _renderer->ReleaseImageResource(ach.image_resource); + ach.image_resource.reset(); + } } } @@ -200,13 +493,18 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu setup_overlay_called(false), show_overlay(false), is_ready(false), - notif_position(ENotificationPosition::k_EPositionBottomLeft), + notif_position(ENotificationPosition::k_EPositionTopRight), + current_ui_notification_position_selection(1), // valid_ui_notification_position_labels[1] == "top right" h_inset(0), v_inset(0), overlay_state_changed(false), i_have_lobby(false), show_achievements(false), show_settings(false), + show_profile_image_select(false), + show_drive_list(false), + tried_load_new_profile_image(false), + cleared_new_profile_images_struct(true), _renderer(nullptr), fonts_atlas(nullptr), earned_achievement_count(0) @@ -216,6 +514,10 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu show_achievement_desc_on_unlock = settings->get_show_achievement_desc_on_unlock(); show_achievement_hidden_unearned = settings->get_show_achievement_hidden_unearned(); + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[1] = false; + radio_btn_new_profile_image_size[2] = true; + if (settings->warn_forced) { this->disable_forced = true; this->warning_forced = true; @@ -230,10 +532,53 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu this->local_save = false; } + int i = 0; + std::string ui_notif_pos = settings->get_ui_notification_position(); + if (ui_notif_pos.length() > 0) { + for (auto n : valid_ui_notification_position_labels) { + if (strcmp(n, ui_notif_pos.c_str()) == 0) { + this->current_ui_notification_position_selection = i; + break; + } + ++i; + } + switch (i) { + case 0: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionTopLeft"); + this->notif_position = k_EPositionTopLeft; + break; + case 1: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionTopRight"); + this->notif_position = k_EPositionTopRight; + break; + case 2: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionBottomLeft"); + this->notif_position = k_EPositionBottomLeft; + break; + case 3: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionBottomRight"); + this->notif_position = k_EPositionBottomRight; + break; + default: + PRINT_DEBUG("%s %s. %s.\n", + "Steam_Overlay::Steam_Overlay Unrecognized ui notification position received from Settings: ", + ui_notif_pos.c_str(), + "Defaulting to k_EPositionTopRight"); + break; + }; + } else { + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Settings does not have a ui notification position defined. Defaulting to k_EPositionTopRight"); + } + current_language = 0; const char *language = settings->get_language(); - int i = 0; + i = 0; for (auto l : valid_languages) { if (strcmp(l, language) == 0) { current_language = i; @@ -243,13 +588,22 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu ++i; } + populate_initial_profile_images(); + run_every_runcb->add(&Steam_Overlay::steam_overlay_run_every_runcb, this); this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::steam_overlay_callback, this); + this->overlay_CCallback = new Steam_Overlay_CCallback(); } Steam_Overlay::~Steam_Overlay() { + std::lock_guard lock(overlay_mutex); run_every_runcb->remove(&Steam_Overlay::steam_overlay_run_every_runcb, this); + if (this->overlay_CCallback != NULL) { + delete this->overlay_CCallback; + this->overlay_CCallback = NULL; + } + if (achievements.size()) { for (auto & x : achievements) { if (x.raw_image != NULL) { @@ -261,6 +615,40 @@ Steam_Overlay::~Steam_Overlay() DestroyAchievementImageResource(x); } } + + if (profile_images.size()) { + for (auto & x : profile_images) { + if (x.second.small.raw_image != NULL) { + delete x.second.small.raw_image; + x.second.small.raw_image = NULL; + } + x.second.small.width = 0; + x.second.small.height = 0; + + if (x.second.medium.raw_image != NULL) { + delete x.second.medium.raw_image; + x.second.medium.raw_image = NULL; + } + x.second.medium.width = 0; + x.second.medium.height = 0; + + if (x.second.large.raw_image != NULL) { + delete x.second.large.raw_image; + x.second.large.raw_image = NULL; + } + x.second.large.width = 0; + x.second.large.height = 0; + } + } + + DestroyProfileImageResources(); + + new_profile_image_handles.small.raw_image = NULL; // Handle + new_profile_image_handles.medium.raw_image = NULL; // Handle + new_profile_image_handles.large.raw_image = NULL; // Handle + + DestroyTemporaryImageResources(); + DestroyTemporaryImages(); } bool Steam_Overlay::Ready() const @@ -275,7 +663,37 @@ bool Steam_Overlay::NeedPresent() const void Steam_Overlay::SetNotificationPosition(ENotificationPosition eNotificationPosition) { - notif_position = eNotificationPosition; + switch (eNotificationPosition) { + case k_EPositionTopLeft: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionTopLeft"); + this->notif_position = k_EPositionTopLeft; + this->current_ui_notification_position_selection = 0; + break; + case k_EPositionTopRight: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionTopRight"); + this->notif_position = k_EPositionTopRight; + this->current_ui_notification_position_selection = 1; + break; + case k_EPositionBottomLeft: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionBottomLeft"); + this->notif_position = k_EPositionBottomLeft; + this->current_ui_notification_position_selection = 2; + break; + case k_EPositionBottomRight: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionBottomRight"); + this->notif_position = k_EPositionBottomRight; + this->current_ui_notification_position_selection = 3; + break; + default: + PRINT_DEBUG("%s %d.\n", + "Steam_Overlay::SetNotificationPosition Unrecognized ui notification position received: ", + eNotificationPosition); + break; + }; } void Steam_Overlay::SetNotificationInset(int nHorizontalInset, int nVerticalInset) @@ -489,7 +907,7 @@ void Steam_Overlay::FriendDisconnect(Friend _friend) friends.erase(it); } -void Steam_Overlay::AddMessageNotification(std::string const& message) +void Steam_Overlay::AddMessageNotification(std::string const& message, CSteamID frd = k_steamIDNil) { std::lock_guard lock(notifications_mutex); int id = find_free_notification_id(notifications); @@ -499,6 +917,7 @@ void Steam_Overlay::AddMessageNotification(std::string const& message) notif.id = id; notif.type = notification_type_message; notif.message = message; + notif.steam_id = frd; notif.start_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); have_notifications = true; @@ -568,6 +987,7 @@ void Steam_Overlay::AddInviteNotification(std::pair(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); have_notifications = true; @@ -660,6 +1080,35 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st std::string friend_window_id = std::move("###" + std::to_string(state.id)); if (ImGui::Begin((state.window_title + friend_window_id).c_str(), &show)) { + ImGuiStyle currentStyle = ImGui::GetStyle(); + ImVec2 image_offset(((ImGui::GetWindowWidth() * 0.04f) + currentStyle.FramePadding.x), + ((ImGui::GetWindowHeight() * 0.04f) + currentStyle.FramePadding.y + + ImGui::GetFontSize() + currentStyle.ItemSpacing.y)); // calc border. + ImVec2 image_max_resolution((ImGui::GetWindowWidth() - image_offset.x), (ImGui::GetWindowHeight() - image_offset.y)); // calc total space for image. + if (image_max_resolution.x > image_max_resolution.y) { // fix image aspect ratio. (square) + image_max_resolution.x = image_max_resolution.x - (image_max_resolution.x - image_max_resolution.y); + } else { + image_max_resolution.y = image_max_resolution.y - (image_max_resolution.y - image_max_resolution.x); + } + ImVec2 image_scale(image_max_resolution.x * 0.3f, + image_max_resolution.y * 0.3f); + ImVec2 text_offset(image_offset.x + image_scale.x + currentStyle.ItemSpacing.x, + image_offset.y + (image_scale.y * 0.2f) + currentStyle.ItemSpacing.y); + ImVec2 next_line_offset(currentStyle.FramePadding.x + currentStyle.ItemInnerSpacing.x, + image_offset.y + image_scale.y + (currentStyle.ItemSpacing.y * 2.0f)); + ImGui::SetCursorPos(image_offset); + + display_imgui_avatar(image_scale.x, + image_scale.y, + 1.0f, 1.0f, 1.0f, 1.0f, + CSteamID((uint64)frd.id()), + k_EAvatarSize184x184, + 0x1); + ImGui::SetCursorPos(text_offset); + + ImGui::TextWrapped(std::string(frd.name() + " (" + std::to_string((uint64)frd.id()) + ")").c_str()); + ImGui::SetCursorPos(next_line_offset); + if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused()) { state.window_state &= ~window_state_need_attention; @@ -704,8 +1153,7 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st // | [__chat line__] [send] | // |------------------------------| float wnd_width = ImGui::GetWindowContentRegionWidth(); - ImGuiStyle &style = ImGui::GetStyle(); - wnd_width -= ImGui::CalcTextSize("Send").x + style.FramePadding.x * 2 + style.ItemSpacing.x + 1; + wnd_width -= ImGui::CalcTextSize("Send").x + currentStyle.FramePadding.x * 2 + currentStyle.ItemSpacing.x + 1; ImGui::PushItemWidth(wnd_width); if (ImGui::InputText("##chat_line", state.chat_input, max_chat_len, ImGuiInputTextFlags_EnterReturnsTrue)) @@ -783,7 +1231,29 @@ void Steam_Overlay::BuildNotifications(int width, int height) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, Notification::max_alpha*2)); } - ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i )); + ImVec2 window_pos((float)width - width * Notification::width, Notification::height * font_size * i); + + ImGuiViewport * viewport = ImGui::GetMainViewport(); + if (viewport != NULL) { + if (notif_position == ENotificationPosition::k_EPositionBottomLeft || + notif_position == ENotificationPosition::k_EPositionBottomRight) { + window_pos.y = ((viewport->Pos.y + viewport->Size.y) - Notification::height * font_size) - window_pos.y; + } + } + if (notif_position == ENotificationPosition::k_EPositionTopLeft || + notif_position == ENotificationPosition::k_EPositionBottomLeft) { + window_pos.x = 0; + } + + // Avoid overlay titlebar. + if (show_overlay == true && + (notif_position == ENotificationPosition::k_EPositionTopLeft || + notif_position == ENotificationPosition::k_EPositionTopRight)) { + window_pos.x += currentStyle.FramePadding.x + currentStyle.ItemSpacing.x; + window_pos.y += currentStyle.FramePadding.y + ImGui::GetFontSize() + currentStyle.ItemSpacing.y; + } + + ImGui::SetNextWindowPos(window_pos); ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size )); ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration); @@ -797,7 +1267,9 @@ void Steam_Overlay::BuildNotifications(int width, int height) } else { image_max_resolution.y = image_max_resolution.y - (image_max_resolution.y - image_max_resolution.x); } - ImVec2 text_offset(image_offset.x, image_offset.y); + ImVec2 image_scale(image_max_resolution.x * 0.4f, + image_max_resolution.y * 0.4f); + ImVec2 text_offset(image_offset.x + currentStyle.ItemSpacing.x + image_scale.x, image_offset.y); ImGui::SetCursorPos(image_offset); switch (it->type) @@ -809,12 +1281,6 @@ void Steam_Overlay::BuildNotifications(int width, int height) if ((a.image_resource.expired() == false) && (a.raw_image_width > 0) && (a.raw_image_height > 0)) { - ImVec2 image_scale(image_max_resolution.x * 0.4f, - image_max_resolution.y * 0.4f); - - // Fix text offset. - text_offset.x = image_offset.x + currentStyle.ItemSpacing.x + image_scale.x; - std::shared_ptr s_ptr = a.image_resource.lock(); ImGui::Image((ImTextureID)(intptr_t)*s_ptr, image_scale, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), image_color_multipler); } @@ -828,6 +1294,15 @@ void Steam_Overlay::BuildNotifications(int width, int height) break; case notification_type_invite: { + display_imgui_avatar(image_scale.x, + image_scale.y, + image_color_multipler.x, // r + image_color_multipler.y, // g + image_color_multipler.z, // b + image_color_multipler.w, // a + it->steam_id, + k_EAvatarSize184x184, + 0x1); ImGui::SetCursorPos(text_offset); ImGui::TextWrapped("%s", it->message.c_str()); if (ImGui::Button("Join")) @@ -839,8 +1314,18 @@ void Steam_Overlay::BuildNotifications(int width, int height) } break; case notification_type_message: + display_imgui_avatar(image_scale.x, + image_scale.y, + image_color_multipler.x, // r + image_color_multipler.y, // g + image_color_multipler.z, // b + image_color_multipler.w, // a + it->steam_id, + k_EAvatarSize184x184, + 0x1); ImGui::SetCursorPos(text_offset); - ImGui::TextWrapped("%s", it->message.c_str()); break; + ImGui::TextWrapped("%s", it->message.c_str()); + break; } ImGui::End(); @@ -872,6 +1357,7 @@ struct cb_font_str ImFont * defaultFont; HDC hDevice; int foundBits; + std::recursive_mutex mutex; }; static cb_font_str CBSTR; @@ -909,10 +1395,13 @@ int LoadWindowsFontFromMem(const LOGFONT *lf) oldFont = SelectObject(CBSTR.hDevice, hFont); uint8_t metsize = GetOutlineTextMetrics(CBSTR.hDevice, 0, NULL); if (metsize > 0) { - OUTLINETEXTMETRIC * metric = (OUTLINETEXTMETRIC*)calloc(metsize, 1); + OUTLINETEXTMETRIC * metric = (OUTLINETEXTMETRIC*)malloc(metsize); if (metric != NULL) { + memset(metric, '\0', metsize); if (GetOutlineTextMetrics(CBSTR.hDevice, metsize, metric) != 0) { - if ((metric->otmfsType & 0x1) == 0) { + // otmfsType: Bit 1 (May not be embedded if set.) Bit 2 (Read-Only embedding.) + // See also: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-outlinetextmetrica + if ((((UINT)(metric->otmfsType)) & ((UINT)0x1) << 1) == 0) { DWORD type = 0; DWORD fontDataSize = LoadWindowsFontFromMem_GetSize_Helper(lf, &type); if (fontDataSize != GDI_ERROR && fontDataSize > 4 && type != ~((DWORD)0)) { @@ -974,7 +1463,7 @@ int LoadWindowsFontFromMem(const LOGFONT *lf) PRINT_DEBUG("%s %d.\n", "GetFontData() failed. Unable to get initial size of font data. Ret: ", fontDataSize); } } else { - PRINT_DEBUG("%s %s.\n", "Licensing failure. Cannot use font", lf->lfFaceName); + PRINT_DEBUG("%s %s. otmfsType data: %d.\n", "Licensing failure. Cannot use font", lf->lfFaceName, metric->otmfsType); } } @@ -993,6 +1482,7 @@ int CALLBACK cb_enumfonts(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD fontTyp { int ret = 1; // Continue iteration. cb_font_str * cbStr = &CBSTR; + std::lock_guard lock(cbStr->mutex); if (CBSTR.atlas != NULL && lf != NULL && fontType == TRUETYPE_FONTTYPE) { /* foundBits: @@ -1119,7 +1609,6 @@ void Steam_Overlay::CreateFonts() HWND oWND = WindowFromDC(oDC); int caps = GetDeviceCaps(oDC, RASTERCAPS); if (caps != 0) { - int width = GetDeviceCaps(oDC, HORZRES); int height = GetDeviceCaps(oDC, VERTRES); HBITMAP hBitmap = CreateCompatibleBitmap(oDC, width, height); @@ -1129,50 +1618,82 @@ void Steam_Overlay::CreateFonts() lf.lfCharSet = DEFAULT_CHARSET; lf.lfHeight = font_size; lf.lfQuality = ANTIALIASED_QUALITY; - memset(&CBSTR, '\0', sizeof(cb_font_str)); - CBSTR.font_size = font_size; - CBSTR.fontcfg = fontcfg; - CBSTR.atlas = Fonts; - CBSTR.hDevice = CreateCompatibleDC(oDC); - HGDIOBJ hOldBitmap = SelectObject(CBSTR.hDevice, hBitmap); - DeleteObject(hOldBitmap); + HGDIOBJ hOldBitmap; + { + std::lock_guard lock(CBSTR.mutex); + CBSTR.font_size = font_size; + CBSTR.fontcfg = fontcfg; + CBSTR.atlas = Fonts; + CBSTR.defaultFont = NULL; + CBSTR.hDevice = CreateCompatibleDC(oDC); + CBSTR.foundBits = 0; + hOldBitmap = SelectObject(CBSTR.hDevice, hBitmap); + DeleteObject(hOldBitmap); + } PRINT_DEBUG("%s\n", "Atempting to load preferred ANSI font from Win32 API."); EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); - if ((CBSTR.foundBits & 0x1) != 0x1) { - CBSTR.foundBits = CBSTR.foundBits | 0x80; + bool resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0x1) != 0x1) { + CBSTR.foundBits = CBSTR.foundBits | 0x80; + resume_search = true; + } + } + if (resume_search) { PRINT_DEBUG("%s\n", "Atempting to load generic ANSI font from Win32 API."); EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); - if ((CBSTR.foundBits & 0x1) != 0x1) { - PRINT_DEBUG("%s\n", "Falling back to built in ImGUI ANSI font."); - CBSTR.defaultFont = Fonts->AddFontDefault(&fontcfg); - if (CBSTR.defaultFont == NULL) { - PRINT_DEBUG("%s\n", "Built in ImGUI ANSI font failed to load. Weird text will probably happen."); + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0x1) != 0x1) { + PRINT_DEBUG("%s\n", "Falling back to built in ImGUI ANSI font."); + CBSTR.defaultFont = Fonts->AddFontDefault(&fontcfg); + if (CBSTR.defaultFont == NULL) { + PRINT_DEBUG("%s\n", "Built in ImGUI ANSI font failed to load. Weird text will probably happen."); + } + CBSTR.foundBits = CBSTR.foundBits | 0x1; } - CBSTR.foundBits = CBSTR.foundBits | 0x1; + CBSTR.foundBits = CBSTR.foundBits ^ 0x80; } - CBSTR.foundBits = CBSTR.foundBits ^ 0x80; } - if ((CBSTR.foundBits & 0xE) != 0xE) { + resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0xE) != 0xE) { + resume_search = true; + } + } + if (resume_search) { PRINT_DEBUG("%s\n", "Atempting to load preferred CJK fonts from Win32 API."); EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); } - CBSTR.foundBits = CBSTR.foundBits | 0x80; - if ((CBSTR.foundBits & 0xF) != 0xF) { + resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + CBSTR.foundBits = CBSTR.foundBits | 0x80; + if ((CBSTR.foundBits & 0xF) != 0xF) { + resume_search = true; + } + } + if (resume_search) { PRINT_DEBUG("%s\n", "Loading generic CJK fonts from Win32 API."); EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); } - if (CBSTR.defaultFont != NULL) { - font = CBSTR.defaultFont; - } - if (need_extra_fonts == true) { - if ((CBSTR.foundBits & 0xF) == 0xF) { - need_extra_fonts = false; + { + std::lock_guard lock(CBSTR.mutex); + if (CBSTR.defaultFont != NULL) { + font = CBSTR.defaultFont; + } + if (need_extra_fonts == true) { + if ((CBSTR.foundBits & 0xF) == 0xF) { + need_extra_fonts = false; + } } - } - DeleteDC(CBSTR.hDevice); // Order is important. - DeleteObject(hBitmap); + DeleteDC(CBSTR.hDevice); // Order is important. + DeleteObject(hBitmap); + } } ReleaseDC(oWND, oDC); #else @@ -1203,10 +1724,188 @@ void Steam_Overlay::CreateFonts() reset_LastError(); } +void Steam_Overlay::ReturnTemporaryImage(Profile_Image & imageData) +{ + if (imageData.raw_image != NULL) { + delete imageData.raw_image; + imageData.raw_image = NULL; + } + imageData.width = 0; + imageData.height = 0; + return; +} + +Profile_Image Steam_Overlay::GetTemporaryImage(uint8 * imageData) +{ + Profile_Image ret; + + if (imageData != NULL) { + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.first == imageData && + x.second.image_data.raw_image != NULL && + x.second.image_data.width > 0 && + x.second.image_data.height > 0) { + size_t buffer_size = x.second.image_data.width * x.second.image_data.height * sizeof(uint32_t); + ret.raw_image = new uint8[buffer_size]; + if (ret.raw_image != NULL) { + for (size_t y = 0; y < buffer_size; y++) { + uint8 a = x.second.image_data.raw_image[y]; + ret.raw_image[y] = a; + } + ret.width = x.second.image_data.width; + ret.height = x.second.image_data.height; + x.second.last_display_time = std::chrono::steady_clock::now(); + } + break; + } + } + } + + return ret; +} + +void Steam_Overlay::PruneTemporaryImages() +{ + std::lock_guard lock(overlay_mutex); + if (temp_display_images.size() > 0) { + for (auto & x : temp_display_images) { + if (x.second.last_display_time < (std::chrono::steady_clock::now() - std::chrono::minutes(1))) { + if (x.second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x.second.image_data.image_resource); + x.second.image_data.image_resource.reset(); + } + } + if (x.second.image_data.raw_image != NULL) { + delete x.second.image_data.raw_image; + x.second.image_data.raw_image = NULL; + } + x.second.image_data.width = 0; + x.second.image_data.height = 0; + } + } + + bool done = false; + do { + if (temp_display_images.size() > 0) { + for (std::map::iterator x = temp_display_images.begin(); + x != temp_display_images.end(); + x++) { + std::map::iterator y = x; + y++; + if (y == temp_display_images.end()) { + done = true; + } + if ((x->second.image_data.image_resource.expired() == true) && + x->second.image_data.raw_image == NULL && + x->second.image_data.width <= 0 && + x->second.image_data.height <= 0) { + temp_display_images.erase(x); + break; + } + } + } else { + done = true; + } + } while (!done); + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImageResources() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x.second.image_data.image_resource); + x.second.image_data.image_resource.reset(); + } + } + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImageResource(uint8 * imageData) +{ + std::lock_guard lock(overlay_mutex); + auto x = temp_display_images.find(imageData); + if (x != temp_display_images.end()) { + if (x->second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x->second.image_data.image_resource); + x->second.image_data.image_resource.reset(); + } + } + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImages() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.second.image_data.raw_image != NULL) { + delete x.second.image_data.raw_image; + x.second.image_data.raw_image = NULL; + } + x.second.image_data.width = 0; + x.second.image_data.height = 0; + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImage(uint8 * imageData) +{ + std::lock_guard lock(overlay_mutex); + DestroyTemporaryImageResource(imageData); + auto x = temp_display_images.find(imageData); + if (x != temp_display_images.end()) { + if (x->second.image_data.raw_image != NULL) { + delete x->second.image_data.raw_image; + x->second.image_data.raw_image = NULL; + } + x->second.image_data.width = 0; + x->second.image_data.height = 0; + temp_display_images.erase(x); + } + + return; +} + +void Steam_Overlay::DestroyProfileImageResources() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : profile_images) { + if (x.second.small.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize32x32, x.second); + } + } + if (x.second.medium.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize64x64, x.second); + } + } + if (x.second.large.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize184x184, x.second); + } + } + } + + return; +} + void Steam_Overlay::DestroyAchievementImageResources() { for (auto & x : achievements) { - if (x.image_resource.expired()) { + if (x.image_resource.expired() == false) { DestroyAchievementImageResource(x); } } @@ -1214,6 +1913,455 @@ void Steam_Overlay::DestroyAchievementImageResources() return; } +int Steam_Overlay::display_imgui_achievement(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + uint32_t loadType = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeAchievement, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + achName, + k_steamIDNil, + k_EAvatarSizeMAX, + NULL, + 0x0, + 0x0, + 0x0, + loadType); +} + +int Steam_Overlay::display_imgui_avatar(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + CSteamID userID = k_steamIDNil, + int eAvatarSize = k_EAvatarSizeMAX, + uint32_t loadType = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeAvatar, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + "", + userID, + eAvatarSize, + NULL, + 0x0, + 0x0, + 0x0, + loadType); +} + +int Steam_Overlay::display_imgui_custom_image(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + uint8 * imageData = NULL, + uint32_t imageDataLength = 0x0, + uint32_t imageDataWidth = 0x0, + uint32_t imageDataHeight = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeCustom, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + "", + k_steamIDNil, + k_EAvatarSizeMAX, + imageData, + imageDataLength, + imageDataWidth, + imageDataHeight, + 0x0); +} + +int Steam_Overlay::display_imgui_image(uint32_t displayImageType, + float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName = "", + CSteamID userID = k_steamIDNil, + int eAvatarSize = k_EAvatarSizeMAX, + uint8 * imageData = NULL, + uint32_t imageDataLength = 0x0, + uint32_t imageDataWidth = 0x0, + uint32_t imageDataHeight = 0x0, + uint32_t loadType = 0x0) +{ + int ret = 0; + bool valid_args = false; + ImVec2 image_size(xSize, ySize); + ImVec4 image_color_multipler(image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a); + + switch (displayImageType) { + case displayImageTypeAchievement: + // Achievements + if (achName != "") { + valid_args = true; + for (auto & x : achievements) { + if (x.name == achName) { + if (loadType == 0x0) { + if (x.raw_image == NULL) { + LoadAchievementImage(x); + } + } + + if (x.image_resource.expired() == true) { + CreateAchievementImageResource(x); + } + + if ((x.image_resource.expired() == false) && + (x.raw_image_width > 0) && + (x.raw_image_height > 0)) { + std::shared_ptr s_ptr = x.image_resource.lock(); + ImGui::Image((ImTextureID)(intptr_t)*s_ptr, + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + break; + } + } + } + break; + case displayImageTypeAvatar: + // User Avatars + if (userID != k_steamIDNil) { + bool found_profile_images = false; + Profile_Image image; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + + if (found_profile_images == false) { + populate_initial_profile_images(userID); + } else { + if (valid_args) { + bool reload = false; + if (image.raw_image == NULL || + image.width == 0 || + image.height == 0) { + if (loadType == 0x0) { + if (LoadProfileImage(userID, eAvatarSize) == true) { + reload = true; + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Got avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Load OK."); + } else{ + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Load Failure."); + } + } else { + uint32_t type = 0x0; + switch (eAvatarSize) { + case k_EAvatarSize32x32: + type |= 0x1; + break; + case k_EAvatarSize64x64: + type |= 0x2; + break; + case k_EAvatarSize184x184: + type |= 0x4; + break; + default: + type = 0x0; + break; + }; + + if (type != 0x0) { + PRINT_DEBUG("%s %"PRIu64" %s %d.\n", + "Steam_Overlay::display_imgui_image Queuing Lazy Load avatar image for", + userID.ConvertToUint64(), + "size", + eAvatarSize); + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + auto ld = lazy_load_avatar_images.find(userID); + if (ld == lazy_load_avatar_images.end()) { + type |= lazy_load_avatar_images[userID] & 0x7; + lazy_load_avatar_images[userID] = type; + } else { + lazy_load_avatar_images[userID] = type; + } + } + } + } + + // Re-get list in case the map buffer was reallocated. + if (reload) { + found_profile_images = false; + valid_args = false; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + } + + reload = false; + if (found_profile_images && valid_args && image.raw_image != NULL && image.width > 0 && image.height > 0) { + //if (!image.image_resource) { + if (image.image_resource.expired() == true) { + + if (CreateProfileImageResource(userID, eAvatarSize) == true) { + reload = true; + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Got profile image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource OK."); + } else { + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource creation failure."); + } + } + + // Re-get list in case the image resource was reallocated. + if (reload) { + found_profile_images = false; + valid_args = false; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + } + + if (valid_args && found_profile_images && image.image_resource.expired() == false) { + std::shared_ptr test2; + test2 = image.image_resource.lock(); + if (test2) { + ImGui::Image((ImTextureID)(intptr_t)*(test2), + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + } else { + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource Invalid."); + } + } else { + if (loadType == 0x0) { + PRINT_DEBUG("%s %d %s %"PRIu64" %s\n", + "Steam_Overlay::display_imgui_image Unable to display avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Could not find buffer after pixel data load."); + } + } + } + } + } + break; + case displayImageTypeCustom: + // Custom image. + if (imageData != NULL) { + valid_args = true; + { + Temporary_Image new_temp_image; + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end()) { + result->second.last_display_time = std::chrono::steady_clock::now(); + } else { + if (imageDataLength > 0) { + uint8 * new_pixels = new uint8[(imageDataWidth * imageDataHeight * sizeof(uint32_t))]; + if (new_pixels != NULL) { + for (size_t x = 0; x < (imageDataWidth * imageDataHeight * sizeof(uint32_t)) && x < imageDataLength; x++) { + new_pixels[x] = imageData[x]; + } + new_temp_image.image_data.raw_image = new_pixels; + new_temp_image.image_data.width = imageDataWidth; + new_temp_image.image_data.height = imageDataHeight; + new_temp_image.last_display_time = std::chrono::steady_clock::now(); + temp_display_images[imageData] = new_temp_image; + } else { + valid_args = false; + PRINT_DEBUG("%s %p. %s.\n", + "Steam_Overlay::display_imgui_image Unable to display custom image", + imageData, + "Could not allocate memory"); + } + } else { + valid_args = false; + PRINT_DEBUG("%s %p. %s.\n", + "Steam_Overlay::display_imgui_image Unable to display custom image", + imageData, + "Image length not given, and image is not registered in temporary mappings"); + } + } + } + if (valid_args == true) { + { + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end()) { + if (result->second.image_data.image_resource.expired() == true) { + if (_renderer && + result->second.image_data.raw_image != NULL && + result->second.image_data.width > 0 && + result->second.image_data.height > 0) { + std::weak_ptr test; + test = _renderer->CreateImageResource(result->second.image_data.raw_image, + result->second.image_data.width, + result->second.image_data.height); + std::shared_ptr test2; + test2 = test.lock(); + if (!test2) { + PRINT_DEBUG("Steam_Overlay::display_imgui_image Unable to create resource for custom image %p.\n", + imageData); + _renderer->ReleaseImageResource(test); + } else { + PRINT_DEBUG("Steam_Overlay::display_imgui_image created resource for custom image %p -> %"PRIu64".\n", + imageData, + *test2); + result->second.image_data.image_resource = test; + } + } + } + } + } + { + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end() && + result->second.image_data.raw_image != NULL && + result->second.image_data.width > 0 && + result->second.image_data.height > 0 && + result->second.image_data.image_resource.expired() == false) { + std::shared_ptr test2; + test2 = result->second.image_data.image_resource.lock(); + if (test2) { + ImGui::Image((ImTextureID)(intptr_t)*(test2), + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + } + } + } + } + break; + case displayImageTypeEND: + default: + PRINT_DEBUG("%s %d.\n", + "Steam_Overlay::display_imgui_image Unrecognized displayImageType: ", + displayImageType); + break; + }; + + return ret; +} + // Try to make this function as short as possible or it might affect game's fps. void Steam_Overlay::OverlayProc() { @@ -1244,128 +2392,147 @@ void Steam_Overlay::OverlayProc() if (ImGui::Begin("SteamOverlay", &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus)) { - ImGui::LabelText("##label", "Username: %s(%llu) playing %u", - settings->get_local_name(), - settings->get_local_steam_id().ConvertToUint64(), - settings->get_local_game_id().AppID()); - ImGui::SameLine(); + bool user_image_displayed = false; + CSteamID local_user_id = settings->get_local_steam_id(); + ImGuiStyle currentStyle = ImGui::GetStyle(); - ImGui::LabelText("##label", "Renderer: %s", (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str())); + user_image_displayed = (display_imgui_avatar(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize184x184) == 1); - ImGui::Text("Achievements earned: %d / %d", earned_achievement_count, total_achievement_count); - ImGui::SameLine(); - ImGui::ProgressBar((earned_achievement_count / total_achievement_count), ImVec2((io.DisplaySize.x * 0.20f),0)); - - ImGui::Spacing(); - if (ImGui::Button("Show Achievements")) { - show_achievements = true; - } - - ImGui::SameLine(); - - if (ImGui::Button("Settings")) { - show_settings = true; - } - - ImGui::Spacing(); - ImGui::Spacing(); - - ImGui::LabelText("##label", "Friends"); - - std::lock_guard lock(overlay_mutex); - if (!friends.empty()) { - if (ImGui::ListBoxHeader("##label", friends.size())) + std::lock_guard lock(overlay_mutex); + + ImGui::LabelText("##label", "Username: %s(%llu) playing %u", + settings->get_local_name(), + local_user_id.ConvertToUint64(), + settings->get_local_game_id().AppID()); + ImGui::SameLine(); + + ImGui::LabelText("##label", "Renderer: %s", (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str())); + + ImGui::Text("Achievements earned: %d / %d", earned_achievement_count, total_achievement_count); + ImGui::SameLine(); + ImGui::ProgressBar((earned_achievement_count / total_achievement_count), ImVec2((io.DisplaySize.x * 0.20f),0)); + + ImGui::Spacing(); + if (ImGui::Button("Show Achievements")) { + show_achievements = true; + } + + ImGui::SameLine(); + + if (ImGui::Button("Settings")) { + show_settings = true; + } + + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::LabelText("##label", "Friends"); + + if (!friends.empty()) { - std::for_each(friends.begin(), friends.end(), [this](std::pair &i) + if (ImGui::ListBoxHeader("##label", friends.size())) { - ImGui::PushID(i.second.id-base_friend_window_id+base_friend_item_id); - - ImGui::Selectable(i.second.window_title.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick); - BuildContextMenu(i.first, i.second); - if (ImGui::IsItemClicked() && ImGui::IsMouseDoubleClicked(0)) + std::for_each(friends.begin(), friends.end(), [this](std::pair &i) { - i.second.window_state |= window_state_show; - } - ImGui::PopID(); + ImGui::PushID(i.second.id-base_friend_window_id+base_friend_item_id); - BuildFriendWindow(i.first, i.second); - }); - ImGui::ListBoxFooter(); - } - } - - if (show_achievements && achievements.size()) { - ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 32, ImGui::GetFontSize() * 32), ImVec2(8192, 8192)); - bool show = show_achievements; - if (ImGui::Begin("Achievement Window", &show)) { - ImGui::Text("List of achievements"); - ImGui::BeginChild("Achievements"); - ImGuiStyle currentStyle = ImGui::GetStyle(); - float window_x_offset = (ImGui::GetWindowWidth() > currentStyle.ScrollbarSize) ? (ImGui::GetWindowWidth() - currentStyle.ScrollbarSize) : 0; - for (auto & x : achievements) { - bool achieved = x.achieved; - bool hidden = x.hidden && !achieved; - - if (!hidden || show_achievement_hidden_unearned) { - - if (x.raw_image == NULL) { - LoadAchievementImage(x); + // Selectable "" + SameLine + Image + Text == all highlighted. + // See also: https://github.com/ocornut/imgui/issues/1658#issuecomment-373731255 + ImGui::Selectable("", false, ImGuiSelectableFlags_AllowDoubleClick); + BuildContextMenu(i.first, i.second); + if (ImGui::IsItemClicked() && ImGui::IsMouseDoubleClicked(0)) + { + i.second.window_state |= window_state_show; } + ImGui::PopID(); - if (x.image_resource.expired() == true) { - CreateAchievementImageResource(x); + ImGui::SameLine(); + + if (display_imgui_avatar(32 * 0.4f, 32 * 0.4f, + 1.0f, 1.0f, 1.0f, 1.0f, + CSteamID((uint64)i.first.id()), + k_EAvatarSize32x32, + 0x1) != 1) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(32 * 0.4f, 32 * 0.4f)); + PRINT_DEBUG("%s %"PRIu64".\n", + "SteamOverlay::OverlayProc Failed to display friend avatar for", + (uint64)i.first.id()); } + ImGui::SameLine(); - ImGui::Separator(); + ImGui::Text(i.second.window_title.c_str()); - // Anchor the image to the right side of the list. - ImVec2 target = ImGui::GetCursorPos(); - target.x = window_x_offset; - if (target.x > (x.raw_image_width * 0.4f)) { - target.x = target.x - (x.raw_image_width * 0.4f); - } else { - target.x = 0; - } - - ImGui::PushTextWrapPos(target.x); - ImGui::Text("%s", x.title.c_str()); - if (hidden) { - ImGui::Text("Hidden Achievement"); - } else { - ImGui::Text("%s", x.description.c_str()); - } - ImGui::PopTextWrapPos(); - - if (achieved) { - char buffer[80] = {}; - time_t unlock_time = (time_t)x.unlock_time; - std::strftime(buffer, 80, "%Y-%m-%d at %H:%M:%S", std::localtime(&unlock_time)); - - ImGui::TextColored(ImVec4(0, 255, 0, 255), "achieved on %s", buffer); - } else { - ImGui::TextColored(ImVec4(255, 0, 0, 255), "not achieved"); - } - - // Set cursor for image output. - if (target.x != 0) { - ImGui::SetCursorPos(target); - } - - if ((x.image_resource.expired() == false) && - (x.raw_image_width > 0) && - (x.raw_image_height > 0)) { - std::shared_ptr s_ptr = x.image_resource.lock(); - ImGui::Image((ImTextureID)(intptr_t)*s_ptr, ImVec2(x.raw_image_width * 0.4f, x.raw_image_height * 0.4f)); - } - - ImGui::Separator(); - } + BuildFriendWindow(i.first, i.second); + }); + ImGui::ListBoxFooter(); } - ImGui::EndChild(); } - ImGui::End(); - show_achievements = show; + + if (show_achievements && achievements.size()) { + ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 32, ImGui::GetFontSize() * 32), ImVec2(8192, 8192)); + bool show = show_achievements; + if (ImGui::Begin("Achievement Window", &show)) { + ImGui::Text("List of achievements"); + ImGui::BeginChild("Achievements"); + float window_x_offset = (ImGui::GetWindowWidth() > currentStyle.ScrollbarSize) ? (ImGui::GetWindowWidth() - currentStyle.ScrollbarSize) : 0; + for (auto & x : achievements) { + bool achieved = x.achieved; + bool hidden = x.hidden && !achieved; + + if (!hidden || show_achievement_hidden_unearned) { + + ImGui::Separator(); + + // Anchor the image to the right side of the list. + ImVec2 target = ImGui::GetCursorPos(); + target.x = window_x_offset; + if (target.x > (256 * 0.4f)) { + target.x = target.x - (256 * 0.4f); + } else { + target.x = 0; + } + + ImGui::PushTextWrapPos(target.x); + ImGui::Text("%s", x.title.c_str()); + if (hidden) { + ImGui::Text("Hidden Achievement"); + } else { + ImGui::Text("%s", x.description.c_str()); + } + ImGui::PopTextWrapPos(); + + if (achieved) { + char buffer[80] = {}; + time_t unlock_time = (time_t)x.unlock_time; + std::strftime(buffer, 80, "%Y-%m-%d at %H:%M:%S", std::localtime(&unlock_time)); + + ImGui::TextColored(ImVec4(0, 255, 0, 255), "achieved on %s", buffer); + } else { + ImGui::TextColored(ImVec4(255, 0, 0, 255), "not achieved"); + } + + // Set cursor for image output. + if (target.x != 0) { + ImGui::SetCursorPos(target); + } + + display_imgui_achievement(256 * 0.4f, 256 * 0.4f, + 1.0f, 1.0f, 1.0f, 1.0f, + x.name); + + ImGui::Separator(); + } + } + ImGui::EndChild(); + } + ImGui::End(); + show_achievements = show; + } + } if (show_settings) { @@ -1374,41 +2541,530 @@ void Steam_Overlay::OverlayProc() ImGui::Separator(); - ImGui::Text("Username:"); - ImGui::SameLine(); - ImGui::InputText("##username", username_text, sizeof(username_text), disable_forced ? ImGuiInputTextFlags_ReadOnly : 0); + { + std::lock_guard lock(overlay_mutex); - ImGui::Separator(); + ImGui::Text("Username:"); + ImGui::SameLine(); + ImGui::InputText("##username", username_text, sizeof(username_text), disable_forced ? ImGuiInputTextFlags_ReadOnly : 0); - ImGui::Text("Language:"); + ImGui::Separator(); - if (ImGui::ListBox("##language", ¤t_language, valid_languages, sizeof(valid_languages) / sizeof(char *), 7)) { + ImGui::Text("Language:"); - } + if (ImGui::ListBox("##language", ¤t_language, valid_languages, sizeof(valid_languages) / sizeof(char *), 7)) { - ImGui::Text("Selected Language: %s", valid_languages[current_language]); - - ImGui::Separator(); - - ImGui::Checkbox("Show achievement descriptions on unlock", &show_achievement_desc_on_unlock); - ImGui::Checkbox("Show unearned hidden achievements", &show_achievement_hidden_unearned); - - ImGui::Separator(); - - if (!disable_forced) { - ImGui::Text("You may have to restart the game for these to apply."); - if (ImGui::Button("Save")) { - save_settings = true; - show_settings = false; } - } else { - ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); - ImGui::TextWrapped("Some steam_settings/force_*.txt files have been detected. Please delete them if you want this menu to work."); - ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + + ImGui::Text("Selected Language: %s", valid_languages[current_language]); + + ImGui::Separator(); + + ImGui::LabelText("##notificationpositions", "Notification Positions"); + + if (ImGui::BeginListBox("##notificationpositions", ImVec2(-0.1f, ImGui::GetTextLineHeightWithSpacing() * 4.25f + currentStyle.FramePadding.y * 2.0f))) { + for (size_t x = 0; x < (sizeof(valid_ui_notification_position_labels) / sizeof(char*)); x++) { + bool sel = false; + sel = ImGui::Selectable(valid_ui_notification_position_labels[x], + (current_ui_notification_position_selection == x), + ImGuiSelectableFlags_AllowDoubleClick); + if (sel) { + PRINT_DEBUG("%s %s.\n", + "SteamOverlay::OverlayProc click on notification positions item", + valid_ui_notification_position_labels[x]); + switch (x) { + case 0: + notif_position = k_EPositionTopLeft; + current_ui_notification_position_selection = 0; + break; + case 1: + notif_position = k_EPositionTopRight; + current_ui_notification_position_selection = 1; + break; + case 2: + notif_position = k_EPositionBottomLeft; + current_ui_notification_position_selection = 2; + break; + case 3: + notif_position = k_EPositionBottomRight; + current_ui_notification_position_selection = 3; + break; + default: + break; + }; + } + } + + ImGui::EndListBox(); + } + + ImGui::Text("Selected notification position: %s", valid_ui_notification_position_labels[current_ui_notification_position_selection]); + + ImGui::Separator(); + + ImGui::Checkbox("Show achievement descriptions on unlock", &show_achievement_desc_on_unlock); + ImGui::Checkbox("Show unearned hidden achievements", &show_achievement_hidden_unearned); + + ImGui::Separator(); + + if (ImGui::Button("Set User Profile Image")) { + show_profile_image_select = true; + } + + ImGui::Separator(); + + if (!disable_forced) { + ImGui::Text("You may have to restart the game for these to apply."); + if (ImGui::Button("Save")) { + save_settings = true; + show_settings = false; + show_profile_image_select = false; + } + } else { + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + ImGui::TextWrapped("Some steam_settings/force_*.txt files have been detected. Please delete them if you want this menu to work."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + } } } ImGui::End(); + + if (show_profile_image_select) { + bool shown_drive_list = false; + { + std::lock_guard lock(overlay_mutex); + + cleared_new_profile_images_struct = false; + shown_drive_list = show_drive_list; + + if (shown_drive_list) { + std::vector temp = Local_Storage::get_drive_list(); + for (auto x : temp) { + filesystem_list[x] = false; + } + } else { + if (current_path.length() <= 0) { + filesystem_list.clear(); + if (future_path.length() > 0) { + current_path = future_path; + future_path.clear(); + } else { + current_path = Local_Storage::get_user_pictures_path(); + } + } + + if (filesystem_list.size() <= 0) { + std::vector temp = Local_Storage::get_filenames_path(current_path); + for (auto x : temp) { + filesystem_list[x] = false; + } + filesystem_list[".."] = false; + } + } + } + + if (ImGui::Begin("Set User Profile Image", &show_profile_image_select, ImGuiWindowFlags_AlwaysAutoResize)) { + + std::string status_message = ""; + bool small_image_displayed = false; + bool medium_image_displayed = false; + bool large_image_displayed = false; + user_image_displayed = false; + bool found_profile_images = false; + + ImGui::Text("Currently Used Profile Images:"); + small_image_displayed = (display_imgui_avatar(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize32x32) == 1); + if (small_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(50.0f, 50.0f)); + } + ImGui::SameLine(); + + medium_image_displayed = (display_imgui_avatar(70.0f, 70.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize64x64) == 1); + if (medium_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(70.0f, 70.0f)); + } + ImGui::SameLine(); + + large_image_displayed = (display_imgui_avatar(90.0f, 90.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize184x184) == 1); + if (large_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(90.0f, 90.0f)); + } + ImGui::SameLine(); + user_image_displayed = (small_image_displayed && medium_image_displayed && large_image_displayed); + ImGui::Separator(); + + ImGui::TextWrapped("Select image size to configure:"); + ImGui::SameLine(); + + { + std::lock_guard lock(overlay_mutex); + if (ImGui::RadioButton("32x32 (Small)", (radio_btn_new_profile_image_size[0] == true)) == true) { + radio_btn_new_profile_image_size[0] = true; + radio_btn_new_profile_image_size[1] = false; + radio_btn_new_profile_image_size[2] = false; + } + ImGui::SameLine(); + if (ImGui::RadioButton("64x64 (Medium)", (radio_btn_new_profile_image_size[1] == true)) == true) { + radio_btn_new_profile_image_size[1] = true; + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[2] = false; + } + ImGui::SameLine(); + if (ImGui::RadioButton("184x184 (Large)", (radio_btn_new_profile_image_size[2] == true)) == true) { + radio_btn_new_profile_image_size[2] = true; + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[1] = false; + } + } + ImGui::TextWrapped("Note: Selected image must have a resolution bigger than the previous option and less than or equal to the selected option."); + ImGui::Separator(); + + bool load_image_file = false; + ImGui::TextWrapped("Please select a JPG or PNG file"); + { + std::lock_guard lock(overlay_mutex); + if (new_profile_image_path.length() > 0) { + load_image_file = true; + } + std::string path_message = "Current Path: " + current_path; + ImGui::TextWrapped(path_message.c_str()); + } + if (ImGui::Button("Up")) { + std::string temp = Local_Storage::get_parent_directory(current_path); + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Image Select Up button."); + if (Local_Storage::is_directory(temp) == true) { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", temp.c_str(), " is a directory"); + std::lock_guard lock(overlay_mutex); + current_path.clear(); + future_path = temp; + show_drive_list = false; + } else { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", temp.c_str(), " is NOT a directory"); + } + } + ImGui::SameLine(); + if (ImGui::Button("Home")) { + PRINT_DEBUG("%s.\n", "SteamOverlay::OverlayProc Image Select Home button"); + std::lock_guard lock(overlay_mutex); + future_path = Local_Storage::get_user_pictures_path(); + if (future_path.length() > 0) { + current_path.clear(); + show_drive_list = false; + } + } + ImGui::SameLine(); + if (ImGui::Button("Drives")) { + PRINT_DEBUG("%s.\n", "SteamOverlay::OverlayProc Image Select Drives button"); + std::lock_guard lock(overlay_mutex); + show_drive_list = true; + current_path.clear(); + future_path.clear(); + filesystem_list.clear(); + } + if (ImGui::BeginListBox("##FileSelect")) { + { + std::lock_guard lock(overlay_mutex); + for (auto x : filesystem_list) { + if (x.first.length() > 0) { + x.second = ImGui::Selectable(x.first.c_str(), x.second, ImGuiSelectableFlags_AllowDoubleClick); + + if (x.second) { + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc click on filesystem list item", x.first.c_str()); + if (shown_drive_list) { + future_path = ((x.first.find_last_of((char)PATH_SEPARATOR) == (x.first.length())) ? (x.first) : (x.first + PATH_SEPARATOR)); + if (Local_Storage::is_directory(future_path) == true) { + current_path.clear(); + show_drive_list = false; + PRINT_DEBUG("%s %s %"PRI_ZU" %"PRI_ZU".\n", "SteamOverlay::OverlayProc Next directory will be", future_path.c_str(), x.first.find_last_of((char)PATH_SEPARATOR), x.first.length()); + } else { + future_path.clear(); + } + } else { + if (x.first == "..") { + future_path = Local_Storage::get_parent_directory(current_path); + PRINT_DEBUG("%s %s is %s.\n", "SteamOverlay::OverlayProc parent directory of", current_path.c_str(), future_path.c_str()); + if (Local_Storage::is_directory(future_path) == true) { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", future_path.c_str(), "is a directory"); + current_path.clear(); + } else { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", future_path.c_str(), "is NOT a directory"); + future_path.clear(); + } + } else { + std::string temp_path = ((x.first.find_last_of((char)PATH_SEPARATOR) == (x.first.length())) ? (current_path + x.first) : (current_path + PATH_SEPARATOR + x.first)); + if (Local_Storage::is_directory(temp_path) == true) { + future_path = temp_path; + current_path.clear(); + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Next directory will be", future_path.c_str()); + } else { + // Load file. + new_profile_image_path = temp_path; + status_message.clear(); + tried_load_new_profile_image = false; + load_image_file = true; + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Load file at", new_profile_image_path.c_str()); + } + } + } + } + } + } + } + + ImGui::EndListBox(); + + ImGui::Separator(); + } + + if (load_image_file == true) { + Profile_Image image; + bool loaded_new_profile_image = false; + int new_image_size = k_EAvatarSizeMAX; + int new_image_pix_size_max = 0; + int new_image_pix_size_min = 0; + uint32_t new_profile_image_width = 0; + uint32_t new_profile_image_height = 0; + std::string image_path = ""; + { + std::lock_guard lock(overlay_mutex); + if (radio_btn_new_profile_image_size[0]) { + new_image_size = k_EAvatarSize32x32; + new_image_pix_size_max = 32; + new_image_pix_size_min = 0; + image = new_profile_image_handles.small; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_image_size = k_EAvatarSize64x64; + new_image_pix_size_max = 64; + new_image_pix_size_min = 32; + image = new_profile_image_handles.medium; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_image_size = k_EAvatarSize184x184; + new_image_pix_size_max = 184; + new_image_pix_size_min = 64; + image = new_profile_image_handles.large; + } else { + new_image_size = k_EAvatarSizeMAX; + new_image_pix_size_max = 0; + new_image_pix_size_min = 0; + image = Profile_Image(); + } + } + } + image_path = new_profile_image_path; + loaded_new_profile_image = tried_load_new_profile_image; + } + + if (image_path.length() > 0 && new_image_size != k_EAvatarSizeMAX) { + if (loaded_new_profile_image == false) { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc New image load stage 1."); + { + std::lock_guard lock(overlay_mutex); + DestroyTemporaryImage(image.raw_image); + image.width = 0; + image.height = 0; + if (radio_btn_new_profile_image_size[0]) { + new_profile_image_handles.small = image; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_profile_image_handles.medium = image; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_profile_image_handles.large = image; + } + } + } + } + + std::string new_profile_image_str = ""; + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Loading new image pixel data from", image_path.c_str()); + new_profile_image_str = convert_vector_image_pixel_t_to_std_string(get_steam_client()->local_storage->load_image(image_path, &new_profile_image_width, &new_profile_image_height)); + if ((new_profile_image_str.length() > 0) && + (new_profile_image_width > new_image_pix_size_min) && + (new_profile_image_width <= new_image_pix_size_max) && + (new_profile_image_height > new_image_pix_size_min) && + (new_profile_image_height <= new_image_pix_size_max)) { + uint8 * temp_raw_image = NULL; + temp_raw_image = new uint8[(new_profile_image_width * new_profile_image_height * sizeof(uint32_t))]; + if (temp_raw_image != NULL) { + size_t y = 0; + for (auto & x : new_profile_image_str) { + if (y < new_profile_image_str.length() && y < (new_profile_image_width * new_profile_image_height * sizeof(uint32_t))) { + temp_raw_image[y] = x; + y++; + } + } + image.width = new_profile_image_width; + image.height = new_profile_image_height; + image.raw_image = temp_raw_image; + + status_message = "Loaded selected image.\n"; + ImGui::TextWrapped("Selected new avatar image:"); + if (display_imgui_custom_image(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + image.raw_image, + (image.width * image.height * sizeof(uint32_t)), + image.width, + image.height) == 1) { + std::lock_guard lock(overlay_mutex); + if (radio_btn_new_profile_image_size[0]) { + new_profile_image_handles.small = image; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_profile_image_handles.medium = image; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_profile_image_handles.large = image; + } + } + } + } + + delete temp_raw_image; + temp_raw_image = NULL; + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Unable to allocate memory for new image."); + new_profile_image_str.clear(); + new_profile_image_width = 0; + new_profile_image_height = 0; + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Unable to load new image. Invalid size, or invalid image."); + new_profile_image_str.clear(); + new_profile_image_width = 0; + new_profile_image_height = 0; + status_message = "Failed to load selected image. Invalid size, or invalid image.\n"; + } + { + std::lock_guard lock(overlay_mutex); + tried_load_new_profile_image = true; + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc New image load stage 2."); + if (image.raw_image != NULL && image.width > 0 && image.height > 0) { + status_message = "Loaded selected image.\n"; + ImGui::TextWrapped("Selected new avatar image:"); + display_imgui_custom_image(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + image.raw_image, + 0, + image.width, + image.height); + if (ImGui::Button("Apply")) { + Profile_Image temp_pi = GetTemporaryImage(image.raw_image); + if (temp_pi.raw_image != NULL && temp_pi.width > 0 && temp_pi.height > 0) { + Image_Data id; + //id.data = (char*)temp_pi.raw_image; Not a deep copy.... + id.data.clear(); + for (size_t x = 0; x < (temp_pi.width * temp_pi.height * sizeof(uint32_t)); x++) { + char a = (char)(temp_pi.raw_image[x]); + id.data += a; + } + id.width = temp_pi.width; + id.height = temp_pi.height; + PRINT_DEBUG("%s %d %s %"PRIu64".\n", + "SteamOverlay::OverlayProc Attempting to set new avatar image size", + new_image_size, + "for profile", + local_user_id.ConvertToUint64()); + if (new_image_size == k_EAvatarSize32x32) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize32x32, &id)) { + new_profile_image_path.clear(); + save_small_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + if (new_image_size == k_EAvatarSize64x64) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize64x64, &id)) { + new_profile_image_path.clear(); + save_medium_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + if (new_image_size == k_EAvatarSize184x184) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize184x184, &id)) { + new_profile_image_path.clear(); + save_large_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + DestroyProfileImageResource(local_user_id, new_image_size); + DestroyProfileImage(local_user_id, new_image_size); + PRINT_DEBUG("%s\n", "Steam_Overlay::OverlayProc End apply new avatar image."); + } else { + PRINT_DEBUG("%s.\n", + "Steam_Overlay::OverlayProc Unable to set new avatar image. Image data expired"); + status_message = "Loaded image data expired. Please reselect the image file in the file chooser above."; + } + ReturnTemporaryImage(temp_pi); + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Invalid image data after load cannot display."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + ImGui::TextWrapped("Could not load file as image. Image file must be either a PNG or JPG and must be less than or equal to selected resolution."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + status_message = "Selected file is not an image, or image has an invalid resolution."; + } + } + } + } + if (ImGui::Button("Cancel")) { + std::lock_guard lock(overlay_mutex); + show_profile_image_select = false; + } + ImGui::Separator(); + + ImGui::TextWrapped("Status"); + ImGui::TextWrapped(status_message.c_str()); + ImGui::Separator(); + + { + std::lock_guard lock(overlay_mutex); + current_status_message = status_message; + } + } + + ImGui::End(); + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Begin new avatar image select cleanup."); + std::lock_guard lock(overlay_mutex); + new_profile_image_path.clear(); + new_profile_image_handles.small.raw_image = NULL; // Handle + new_profile_image_handles.small.width = 0; + new_profile_image_handles.small.height = 0; + new_profile_image_handles.medium.raw_image = NULL; // Handle + new_profile_image_handles.medium.width = 0; + new_profile_image_handles.medium.height = 0; + new_profile_image_handles.large.raw_image = NULL; // Handle + new_profile_image_handles.large.width = 0; + new_profile_image_handles.large.height = 0; + current_status_message.clear(); + cleared_new_profile_images_struct = true; + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc End new avatar image select cleanup."); + } } std::string url = show_url; @@ -1484,7 +3140,8 @@ void Steam_Overlay::Callback(Common_Message *msg) friend_info->second.window_state |= window_state_need_attention; } - AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message()); + AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message(), + CSteamID((uint64)friend_info->first.id())); NotifyUser(friend_info->second); } } @@ -1554,6 +3211,83 @@ void Steam_Overlay::RunCallbacks() PRINT_DEBUG("tried to start renderer %u\n", started); } + if (Ready() && _renderer) { + bool done = false; + std::map temp_lazy; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + temp_lazy = lazy_load_avatar_images; + } + if (temp_lazy.size() > 0) { + for (auto x : temp_lazy) { + uint32_t types = x.second & 0x7; + uint32_t attempts = x.second & 0x70; + if (x.first != k_steamIDNil && attempts < 0x70 && types > 0x0) { + switch (attempts) { + case 0x0: + attempts |= 0x10; + break; + case 0x10: + attempts |= 0x20; + break; + case 0x20: + attempts |= 0x40; + break; + default: + attempts = 0x70; + break; + }; + uint32_t retry = 0x0; + PRINT_DEBUG("%s %"PRIu64".\n", + "Steam_Overlay::RunCallbacks Lazy loading Friend Avatars for", + x.first.ConvertToUint64()); + if (x.second & 0x1) { + if (LoadProfileImage(x.first, k_EAvatarSize32x32) == false) { + retry |= 0x1; + } + } + if (x.second & 0x2) { + if (LoadProfileImage(x.first, k_EAvatarSize64x64) == false) { + retry |= 0x2; + } + } + if (x.second & 0x4) { + if (LoadProfileImage(x.first, k_EAvatarSize184x184) == false) { + retry |= 0x4; + } + } + + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + if ((lazy_load_avatar_images[x.first] & 0x7) != types) { + // New type requested during load. + retry = ((lazy_load_avatar_images[x.first] & 0x7) ^ types) | retry; + attempts = 0x0; + } + if (retry != 0x0) { + lazy_load_avatar_images[x.first] = retry | attempts; + } else { + auto found = lazy_load_avatar_images.find(x.first); + if (found != lazy_load_avatar_images.end()) { + lazy_load_avatar_images.erase(found); + } + } + } + } else { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + auto found = lazy_load_avatar_images.find(x.first); + if (found != lazy_load_avatar_images.end()) { + lazy_load_avatar_images.erase(found); + } + } + } + } + } + + if (!show_overlay) { + PruneTemporaryImages(); + } + if (overlay_state_changed) { GameOverlayActivated_t data = { 0 }; @@ -1561,7 +3295,6 @@ void Steam_Overlay::RunCallbacks() data.m_bUserInitiated = true; data.m_nAppID = settings->get_local_game_id().AppID(); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - DestroyAchievementImageResources(); // Don't recreate them here. OpenGL won't display them if we do. overlay_state_changed = false; } @@ -1569,16 +3302,92 @@ void Steam_Overlay::RunCallbacks() Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking; if (save_settings) { - char *language_text = valid_languages[current_language]; + bool temp_show_achievement_desc_on_unlock = false; + bool temp_show_achievement_hidden_unearned = false; + char *language_text = NULL; + char *notification_position_text = NULL; + int temp_notif_position = 0; + { + std::lock_guard lock(overlay_mutex); + temp_show_achievement_desc_on_unlock = show_achievement_desc_on_unlock; + temp_show_achievement_hidden_unearned = show_achievement_hidden_unearned; + temp_notif_position = notif_position; + language_text = valid_languages[current_language]; + } + switch (temp_notif_position) { + case k_EPositionTopLeft: + notification_position_text = valid_ui_notification_position_labels[0]; + break; + case k_EPositionTopRight: + notification_position_text = valid_ui_notification_position_labels[1]; + break; + case k_EPositionBottomLeft: + notification_position_text = valid_ui_notification_position_labels[2]; + break; + case k_EPositionBottomRight: + notification_position_text = valid_ui_notification_position_labels[3]; + break; + default: + notification_position_text = NULL; + break; + }; get_steam_client()->settings_client->set_local_name(username_text); get_steam_client()->settings_client->set_language(language_text); - get_steam_client()->settings_client->set_show_achievement_desc_on_unlock(show_achievement_desc_on_unlock); - get_steam_client()->settings_client->set_show_achievement_hidden_unearned(show_achievement_hidden_unearned); + if (notification_position_text != NULL) { + get_steam_client()->settings_client->set_ui_notification_position(notification_position_text); + } + get_steam_client()->settings_client->set_show_achievement_desc_on_unlock(temp_show_achievement_desc_on_unlock); + get_steam_client()->settings_client->set_show_achievement_hidden_unearned(temp_show_achievement_hidden_unearned); get_steam_client()->settings_server->set_local_name(username_text); get_steam_client()->settings_server->set_language(language_text); - get_steam_client()->settings_server->set_show_achievement_desc_on_unlock(show_achievement_desc_on_unlock); - get_steam_client()->settings_server->set_show_achievement_hidden_unearned(show_achievement_hidden_unearned); + if (notification_position_text != NULL) { + get_steam_client()->settings_server->set_ui_notification_position(notification_position_text); + } + get_steam_client()->settings_server->set_show_achievement_desc_on_unlock(temp_show_achievement_desc_on_unlock); + get_steam_client()->settings_server->set_show_achievement_hidden_unearned(temp_show_achievement_hidden_unearned); save_global_settings(get_steam_client()->local_storage, get_steam_client()->settings_client); + bool save_small = false; + bool save_medium = false; + bool save_large = false; + { + std::lock_guard lock(overlay_mutex); + if (save_small_profile_image) save_small = true; + if (save_medium_profile_image) save_medium = true; + if (save_large_profile_image) save_large = true; + } + if (save_small || save_medium || save_large) { + CSteamID local_steam_id = settings->get_local_steam_id(); + if (save_small) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize32x32) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize32x32, + profile_images[local_steam_id].small.width, + profile_images[local_steam_id].small.height, + profile_images[local_steam_id].small.raw_image); + save_small_profile_image = false; + } + } + if (save_medium) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize64x64) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize64x64, + profile_images[local_steam_id].medium.width, + profile_images[local_steam_id].medium.height, + profile_images[local_steam_id].medium.raw_image); + save_medium_profile_image = false; + } + } + if (save_large) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize184x184) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize184x184, + profile_images[local_steam_id].large.width, + profile_images[local_steam_id].large.height, + profile_images[local_steam_id].large.raw_image); + save_large_profile_image = false; + } + } + } steamFriends->resend_friend_data(); save_settings = false; } @@ -1685,4 +3494,43 @@ void Steam_Overlay::RunCallbacks() } } +bool Steam_Overlay::RegisteredInternalCallbacks() +{ + return (overlay_CCallback != NULL && + overlay_CCallback->is_ready() == true); +} + +void Steam_Overlay::OnAvatarImageLoaded(AvatarImageLoaded_t *pParam) +{ + if (pParam != NULL) { + if (pParam->m_steamID != k_steamIDNil) { + PRINT_DEBUG("%s %"PRIu64". %s %d.\n", + "Steam_Overlay::OnAvatarImageLoaded Destroy avatar images for", + pParam->m_steamID.ConvertToUint64(), + "New reference", + pParam->m_iImage); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize32x32); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize32x32); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize64x64); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize64x64); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize184x184); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize184x184); + } + } + return; +} + +void Steam_Overlay_CCallback::OnAvatarImageLoaded(AvatarImageLoaded_t * pParam) +{ + auto client = try_get_steam_client(); + if (client != NULL && client->steam_overlay != NULL) { + PRINT_DEBUG("%s 0x%p sent to overlay 0x%p.\n", + "Steam_Overlay_CCallback::OnAvatarImageLoaded", + pParam, + client->steam_overlay); + client->steam_overlay->OnAvatarImageLoaded(pParam); + } + return; +} + #endif diff --git a/overlay_experimental/steam_overlay.h b/overlay_experimental/steam_overlay.h index 853c9c1..721a8c1 100644 --- a/overlay_experimental/steam_overlay.h +++ b/overlay_experimental/steam_overlay.h @@ -69,6 +69,7 @@ struct Notification std::string message; std::pair* frd; std::string ach_name; + CSteamID steam_id; }; struct Overlay_Achievement @@ -85,9 +86,61 @@ struct Overlay_Achievement std::weak_ptr image_resource; }; +struct Profile_Image_ID +{ + CSteamID id; + int eAvatarSize; +}; + +bool operator<(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator>(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator<=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator>=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator==(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator!=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +struct Profile_Image +{ + uint32 width; + uint32 height; + uint8 * raw_image; + std::weak_ptr image_resource; +}; + +#ifdef small +#undef small +#endif + +struct Profile_Image_Set +{ + Profile_Image small; + Profile_Image medium; + Profile_Image large; +}; + +struct Temporary_Image +{ + Profile_Image image_data; + std::chrono::steady_clock::time_point last_display_time; +}; + +enum DisplayImageType { + displayImageTypeAchievement = 0, + displayImageTypeAvatar = 1, + displayImageTypeCustom = 2, + displayImageTypeEND = 3 +}; + #ifdef EMU_OVERLAY #include #include "Renderer_Hook.h" +class Steam_Overlay_CCallback; + class Steam_Overlay { Settings* settings; @@ -95,6 +148,7 @@ class Steam_Overlay SteamCallBacks* callbacks; RunEveryRunCB* run_every_runcb; Networking* network; + Steam_Overlay_CCallback* overlay_CCallback; // friend id, show client window (to chat and accept invite maybe) std::map friends; @@ -106,16 +160,33 @@ class Steam_Overlay int h_inset, v_inset; std::string show_url; std::vector achievements; - bool show_achievements, show_settings; + bool show_achievements, show_settings, show_profile_image_select; void *fonts_atlas; bool disable_forced, local_save, warning_forced, show_achievement_desc_on_unlock, show_achievement_hidden_unearned; uint32_t appid, total_achievement_count, earned_achievement_count; char username_text[256]; - std::atomic_bool save_settings; + std::atomic_bool save_settings, save_small_profile_image, save_medium_profile_image, save_large_profile_image; + + // Set Avatar Image filesystem chooser + bool show_drive_list; + std::map filesystem_list; + std::string current_path; + std::string future_path; + + // Avatar Images + std::map profile_images; + std::maplazy_load_avatar_images; + + std::string new_profile_image_path; + std::string current_status_message; + bool tried_load_new_profile_image, cleared_new_profile_images_struct; + Profile_Image_Set new_profile_image_handles; + bool radio_btn_new_profile_image_size[3]; int current_language; + int current_ui_notification_position_selection; std::string warning_message; @@ -155,11 +226,81 @@ class Steam_Overlay // Notifications like achievements, chat and invitations void BuildNotifications(int width, int height); + // ImGui image. + std::map temp_display_images; + + int display_imgui_achievement(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + uint32_t loadType); + + int display_imgui_avatar(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + CSteamID userID, + int eAvatarSize, + uint32_t loadType); + + int display_imgui_custom_image(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + uint8 * imageData, + uint32_t imageDataLength, + uint32_t imageDataWidth, + uint32_t imageDataHeight); + + int display_imgui_image(uint32_t displayImageType, + float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + CSteamID userID, + int eAvatarSize, + uint8 * imageData, + uint32_t imageDataLength, + uint32_t imageDataWidth, + uint32_t imageDataHeight, + uint32_t loadType); + void LoadAchievementImage(Overlay_Achievement & ach); void CreateAchievementImageResource(Overlay_Achievement & ach); void DestroyAchievementImageResource(Overlay_Achievement & ach); void DestroyAchievementImageResources(); + // Profile images + void populate_initial_profile_images(CSteamID id); + bool LoadProfileImage(const CSteamID & id, const int eAvatarSize); + bool LoadProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + bool CreateProfileImageResource(const CSteamID & id, const int eAvatarSize); + bool CreateProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImage(const CSteamID & id, const int eAvatarSize); + void DestroyProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize); + void DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImageResources(); + + // Temporary images + void ReturnTemporaryImage(Profile_Image & imageData); + Profile_Image GetTemporaryImage(uint8 * imageData); + void DestroyTemporaryImageResource(uint8 * imageData); + void DestroyTemporaryImageResources(); + void DestroyTemporaryImage(uint8 * imageData); + void DestroyTemporaryImages(); + void PruneTemporaryImages(); + public: Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network); @@ -174,6 +315,7 @@ public: void SetNotificationInset(int nHorizontalInset, int nVerticalInset); void SetupOverlay(); void UnSetupOverlay(); + bool RegisteredInternalCallbacks(); void HookReady(bool ready); @@ -194,9 +336,10 @@ public: void FriendConnect(Friend _friend); void FriendDisconnect(Friend _friend); - void AddMessageNotification(std::string const& message); + void AddMessageNotification(std::string const& message, CSteamID frd); void AddAchievementNotification(nlohmann::json const& ach); void AddInviteNotification(std::pair &wnd_state); + void OnAvatarImageLoaded(AvatarImageLoaded_t *pParam); }; #else @@ -216,6 +359,7 @@ public: void SetNotificationInset(int nHorizontalInset, int nVerticalInset) {} void SetupOverlay() {} void UnSetupOverlay() {} + bool RegisteredInternalCallbacks() {} void HookReady(bool ready) {} @@ -236,9 +380,10 @@ public: void FriendConnect(Friend _friend) {} void FriendDisconnect(Friend _friend) {} - void AddMessageNotification(std::string const& message) {} + void AddMessageNotification(std::string const& message, CSteamID frd) {} void AddAchievementNotification(nlohmann::json const& ach) {} void AddInviteNotification(std::pair &wnd_state) {} + void OnAvatarImageLoaded(AvatarImageLoaded_t *pParam) {} }; #endif