diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4e9c9d..19779bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -* **[Detanup01]** added a new command line option for the tool `generate_emu_config` to disable the generation of `disable_xxx.txt` files +* **[Detanup01]** added a new command line option for the tool `generate_emu_config` to disable the generation of `disable_xxx.txt` files, + suggested by **[Vlxst]** +* added new settings to the overlay which allow specifying the notifications positions, check the example file `overlay_appearance.EXAMPLE.txt` +* fixed a mistake when discarding the utf8 bom marker + +--- + +## 2024/1/25 + * added new options to the overlay to allow copying a friend's ID, plus current player ID, suggested by **[Vlxst]** * added a new option to the overlay to invite all friends playing the same game, suggested by **[Vlxst]** * added new `auto_accept_invite.txt` setting to automatically accept game/lobby invites from this list, each SteamID64 on a separate line diff --git a/dll/dll/common_includes.h b/dll/dll/common_includes.h index 5aa0d72d..a1903900 100644 --- a/dll/dll/common_includes.h +++ b/dll/dll/common_includes.h @@ -166,6 +166,8 @@ static inline void reset_LastError() #include "utfcpp/utf8.h" #include "controller/gamepad.h" +constexpr const char * const whitespaces = " \t\r\n"; + // common includes #include "common_helpers/common_helpers.hpp" @@ -247,12 +249,15 @@ static std::string uint8_vector_to_hex_string(std::vector& v) static inline void consume_bom(std::ifstream &input) { - int bom[3]; + if (!input.is_open()) return; + + auto pos = input.tellg(); + int bom[3]{}; bom[0] = input.get(); bom[1] = input.get(); bom[2] = input.get(); if (bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) { - input.seekg(-3, std::ios::cur); + input.seekg(pos, std::ios::beg); } } @@ -281,6 +286,4 @@ static inline void consume_bom(std::ifstream &input) #define LOBBY_CONNECT_APPID ((uint32)-2) -constexpr const char * const whitespaces = " \t\r\n"; - #endif//__INCLUDED_COMMON_INCLUDES__ diff --git a/dll/dll/settings.h b/dll/dll/settings.h index 9579fc0a..c3d56e34 100644 --- a/dll/dll/settings.h +++ b/dll/dll/settings.h @@ -111,6 +111,13 @@ struct Group_Clans { }; struct Overlay_Appearance { + enum NotificationPosition { + top_left, top_center, top_right, + bot_left, bot_center, bot_right, + }; + + constexpr const static NotificationPosition default_pos = NotificationPosition::top_right; + float font_size = 16.0; float icon_size = 64.0; float notification_r = 0.16; @@ -133,6 +140,23 @@ struct Overlay_Appearance { float element_active_g = -1.0; float element_active_b = -1.0; float element_active_a = -1.0; + + NotificationPosition ach_earned_pos = default_pos; // achievement earned + NotificationPosition invite_pos = default_pos; // lobby/game invitation + NotificationPosition chat_msg_pos = default_pos; // chat message from a friend + + static NotificationPosition translate_notification_position(const std::string &str) + { + if (str == "top_left") return NotificationPosition::top_left; + else if (str == "top_center") return NotificationPosition::top_center; + else if (str == "top_right") return NotificationPosition::top_right; + else if (str == "bot_left") return NotificationPosition::bot_left; + else if (str == "bot_center") return NotificationPosition::bot_center; + else if (str == "bot_right") return NotificationPosition::bot_right; + + PRINT_DEBUG("Invalid position '%s'\n", str.c_str()); + return default_pos; + } }; class Settings { @@ -261,7 +285,7 @@ public: bool disable_overlay_warning_bad_appid = false; // disable all overlay warnings bool disable_overlay_warning_any = false; - Overlay_Appearance overlay_appearance; + Overlay_Appearance overlay_appearance{}; //app build id int build_id = 10; diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index 63dffbfb..23d301ba 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -35,8 +35,8 @@ static void load_subscribed_groups_clans(std::string clans_filepath, Settings *s { PRINT_DEBUG("Group clans file path: %s\n", clans_filepath.c_str()); std::ifstream clans_file(utf8_decode(clans_filepath)); - consume_bom(clans_file); if (clans_file.is_open()) { + consume_bom(clans_file); std::string line; while (std::getline(clans_file, line)) { if (line.length() < 0) continue; @@ -73,22 +73,35 @@ static void load_subscribed_groups_clans(std::string clans_filepath, Settings *s static void load_overlay_appearance(std::string appearance_filepath, Settings *settings_client, Settings *settings_server) { - PRINT_DEBUG("Overlay appearance file path: %s\n", appearance_filepath.c_str()); std::ifstream appearance_file(utf8_decode(appearance_filepath)); - consume_bom(appearance_file); if (appearance_file.is_open()) { - std::string line; + PRINT_DEBUG("Parsing overlay appearance file: '%s'\n", appearance_filepath.c_str()); + consume_bom(appearance_file); + std::string line{}; while (std::getline(appearance_file, line)) { - if (line.length() < 0) continue; + if (line.length() <= 0) continue; std::size_t seperator = line.find(" "); - std::string name; - std::string value; + std::string name{}; + std::string value{}; if (seperator != std::string::npos) { name = line.substr(0, seperator); + name.erase(0, name.find_first_not_of(whitespaces)); + name.erase(name.find_last_not_of(whitespaces) + 1); + value = line.substr(seperator); + value.erase(0, value.find_first_not_of(whitespaces)); + value.erase(value.find_last_not_of(whitespaces) + 1); } + // comments + if (name.size() && ( + name[0] == '#' || name[0] == ';' || name.compare(0, 2, "//") == 0 + )) { + continue; + } + + PRINT_DEBUG(" Overlay appearance line '%s' = '%s'\n", name.c_str(), value.c_str()); try { if (name.compare("Font_Size") == 0) { float nfont_size = std::stof(value, NULL); @@ -178,9 +191,21 @@ static void load_overlay_appearance(std::string appearance_filepath, Settings *s float nelement_active_a = std::stof(value, NULL); settings_client->overlay_appearance.element_active_a = nelement_active_a; settings_server->overlay_appearance.element_active_a = nelement_active_a; + } else if (name.compare("PosAchievement") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.ach_earned_pos = pos; + settings_server->overlay_appearance.ach_earned_pos = pos; + } else if (name.compare("PosInvitation") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.invite_pos = pos; + settings_server->overlay_appearance.invite_pos = pos; + } else if (name.compare("PosChatMsg") == 0) { + auto pos = Overlay_Appearance::translate_notification_position(value); + settings_client->overlay_appearance.chat_msg_pos = pos; + settings_server->overlay_appearance.chat_msg_pos = pos; } - PRINT_DEBUG("Overlay appearance %s %s\n", name.c_str(), value.c_str()); - } catch (...) {} + + } catch (...) { } } } } diff --git a/overlay_experimental/overlay/steam_overlay.h b/overlay_experimental/overlay/steam_overlay.h index 0dfdc8da..a66d54db 100644 --- a/overlay_experimental/overlay/steam_overlay.h +++ b/overlay_experimental/overlay/steam_overlay.h @@ -53,8 +53,8 @@ enum notification_type struct Notification { - static constexpr float width = 0.25; - static constexpr float height = 5.0; + static constexpr float width_percent = 0.25f; // percentage from total width + static constexpr float height = 5.0f; static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000); static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000); static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out; @@ -85,6 +85,12 @@ struct Overlay_Achievement #ifdef EMU_OVERLAY #include #include "Renderer_Hook.h" + +struct NotificationsIndexes { + int top_left = 0, top_center = 0, top_right = 0; + int bot_left = 0, bot_center = 0, bot_right = 0; +}; + class Steam_Overlay { Settings* settings; @@ -153,8 +159,8 @@ class Steam_Overlay bool FriendJoinable(std::pair &f); bool IHaveLobby(); - void NotifyUser(friend_window_state& friend_state); - void NotifyUserAchievement(); + void NotifySoundUserInvite(friend_window_state& friend_state); + void NotifySoundUserAchievement(); void NotifySoundAutoAcceptFriendInvite(); // Right click on friend @@ -162,6 +168,7 @@ class Steam_Overlay // Double click on friend void BuildFriendWindow(Friend const& frd, friend_window_state &state); // Notifications like achievements, chat and invitations + void SetNextNotificationPos(float width, float height, float font_size, notification_type type, struct NotificationsIndexes &idx); void BuildNotifications(int width, int height); // invite a single friend void InviteFriend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking); diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index 8c5b08b8..a0f1c6c7 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -336,7 +336,7 @@ void Steam_Overlay::ShowOverlay(bool state) overlay_state_changed = true; } -void Steam_Overlay::NotifyUser(friend_window_state& friend_state) +void Steam_Overlay::NotifySoundUserInvite(friend_window_state& friend_state) { if (settings->disable_overlay_friend_notification) return; if (!(friend_state.window_state & window_state_show) || !show_overlay) @@ -352,7 +352,7 @@ void Steam_Overlay::NotifyUser(friend_window_state& friend_state) } } -void Steam_Overlay::NotifyUserAchievement() +void Steam_Overlay::NotifySoundUserAchievement() { if (settings->disable_overlay_achievement_notification) return; if (!show_overlay) @@ -391,7 +391,7 @@ void Steam_Overlay::SetLobbyInvite(Friend friendId, uint64 lobbyId) // Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows) frd.window_state &= ~window_state_rich_invite; AddInviteNotification(*i); - NotifyUser(i->second); + NotifySoundUserInvite(i->second); } } @@ -410,7 +410,7 @@ void Steam_Overlay::SetRichInvite(Friend friendId, const char* connect_str) // Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows) frd.window_state &= ~window_state_lobby_invite; AddInviteNotification(*i); - NotifyUser(i->second); + NotifySoundUserInvite(i->second); } } @@ -487,7 +487,7 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach) notif.message = ach["displayName"].get() + "\n" + ach["description"].get(); notif.start_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); - NotifyUserAchievement(); + NotifySoundUserAchievement(); have_notifications = true; } else @@ -744,85 +744,142 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st ImFont *font_default; ImFont *font_notif; +// set the position of the next notification +void Steam_Overlay::SetNextNotificationPos(float width, float height, float font_size, notification_type type, struct NotificationsIndexes &idx) +{ + // 0 on the y-axis is top, 0 on the x-axis is left + + // get the required position + Overlay_Appearance::NotificationPosition pos = Overlay_Appearance::default_pos; + switch (type) { + case notification_type::notification_type_achievement: pos = settings->overlay_appearance.ach_earned_pos; break; + case notification_type::notification_type_invite: pos = settings->overlay_appearance.invite_pos; break; + case notification_type::notification_type_message: pos = settings->overlay_appearance.chat_msg_pos; break; + default: /* satisfy compiler warning */ break; + } + + float x = 0.0f; + float y = 0.0f; + const float noti_width = width * Notification::width_percent; + const float noti_height = Notification::height * font_size; + switch (pos) { + // top + case Overlay_Appearance::NotificationPosition::top_left: + x = 0.0f; + y = noti_height * idx.top_left; + ++idx.top_left; + break; + case Overlay_Appearance::NotificationPosition::top_center: + x = (width / 2) - (noti_width / 2); + y = noti_height * idx.top_center; + ++idx.top_center; + break; + case Overlay_Appearance::NotificationPosition::top_right: + x = width - noti_width; + y = noti_height * idx.top_right; + ++idx.top_right; + break; + + // bot + case Overlay_Appearance::NotificationPosition::bot_left: + x = 0.0f; + y = height - noti_height * (idx.bot_left + 1); + ++idx.bot_left; + break; + case Overlay_Appearance::NotificationPosition::bot_center: + x = (width / 2) - (noti_width / 2); + y = height - noti_height * (idx.bot_center + 1); + ++idx.bot_center; + break; + case Overlay_Appearance::NotificationPosition::bot_right: + x = width - noti_width; + y = height - noti_height * (idx.bot_right + 1); + ++idx.bot_right; + break; + + default: /* satisfy compiler warning */ break; + } + + ImGui::SetNextWindowPos(ImVec2( x, y )); +} + void Steam_Overlay::BuildNotifications(int width, int height) { auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); - int i = 0; - int font_size = ImGui::GetFontSize(); + float font_size = ImGui::GetFontSize(); - std::queue friend_actions_temp; + std::queue friend_actions_temp{}; { std::lock_guard lock(notifications_mutex); - for (auto it = notifications.begin(); it != notifications.end(); ++it, ++i) + NotificationsIndexes idx{}; + for (auto it = notifications.begin(); it != notifications.end(); ++it) { auto elapsed_notif = now - it->start_time; - if ( elapsed_notif < Notification::fade_in) - { + if ( elapsed_notif < Notification::fade_in) { float alpha = settings->overlay_appearance.notification_a * (elapsed_notif.count() / static_cast(Notification::fade_in.count())); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2)); } - else if ( elapsed_notif > Notification::fade_out_start) - { + else if ( elapsed_notif > Notification::fade_out_start) { float alpha = settings->overlay_appearance.notification_a * ((Notification::show_time - elapsed_notif).count() / static_cast(Notification::fade_out.count())); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2)); - } - else - { + } else { ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, settings->overlay_appearance.notification_a)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, settings->overlay_appearance.notification_a)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, settings->overlay_appearance.notification_a*2)); } - ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i )); - ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size )); - ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | + SetNextNotificationPos(width, height, font_size, (notification_type)it->type, idx); + ImGui::SetNextWindowSize(ImVec2( width * Notification::width_percent, Notification::height * font_size )); + std::string wnd_name = "NotiPopupShow" + std::to_string(it->id); + ImGui::Begin(wnd_name.c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration); - switch (it->type) - { + switch (it->type) { case notification_type_achievement: - { - if (!it->icon.expired()) { - ImGui::BeginTable("imgui_table", 2); - ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size); - ImGui::TableSetupColumn("imgui_table_text"); - ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size); + { + if (!it->icon.expired()) { + ImGui::BeginTable("imgui_table", 2); + ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size); + ImGui::TableSetupColumn("imgui_table_text"); + ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size); - ImGui::TableSetColumnIndex(0); - ImGui::Image((ImU64)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size)); + ImGui::TableSetColumnIndex(0); + ImGui::Image((ImU64)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size)); - ImGui::TableSetColumnIndex(1); - ImGui::TextWrapped("%s", it->message.c_str()); - - ImGui::EndTable(); - } else { - ImGui::TextWrapped("%s", it->message.c_str()); - } - } - break; - case notification_type_invite: - { + ImGui::TableSetColumnIndex(1); + ImGui::TextWrapped("%s", it->message.c_str()); + + ImGui::EndTable(); + } else { ImGui::TextWrapped("%s", it->message.c_str()); - if (ImGui::Button(translationJoin[current_language])) - { - it->frd->second.window_state |= window_state_join; - friend_actions_temp.push(it->frd->first); - it->start_time = std::chrono::seconds(0); - } } - break; + } + break; + case notification_type_invite: + { + ImGui::TextWrapped("%s", it->message.c_str()); + if (ImGui::Button(translationJoin[current_language])) + { + it->frd->second.window_state |= window_state_join; + friend_actions_temp.push(it->frd->first); + it->start_time = std::chrono::seconds(0); + } + } + break; case notification_type_message: - ImGui::TextWrapped("%s", it->message.c_str()); break; + ImGui::TextWrapped("%s", it->message.c_str()); + break; case notification_type_auto_accept_invite: - ImGui::TextWrapped("%s", it->message.c_str()); break; + ImGui::TextWrapped("%s", it->message.c_str()); + break; } ImGui::End(); @@ -1308,7 +1365,7 @@ void Steam_Overlay::Callback(Common_Message *msg) } AddMessageNotification(friend_info->first.name() + ": " + steam_message.message()); - NotifyUser(friend_info->second); + NotifySoundUserInvite(friend_info->second); } } } diff --git a/post_build/README.release.md b/post_build/README.release.md index b23cca8e..b2cf3aab 100644 --- a/post_build/README.release.md +++ b/post_build/README.release.md @@ -422,6 +422,7 @@ Second, the project is not a malware, if your antivirus software complains, be s --- ## Overlay warnings: +**Note: at the moment this feature is only enabled in the windows experimental builds** These configuration files allow disabling various overlay warnings: * `disable_overlay_warning_forced_setting.txt`: @@ -435,6 +436,22 @@ Check the exaxmple files in the `steam_settings` folder --- +## Overlay appearance: +**Note: at the moment this feature is only enabled in the windows experimental builds** + +These configuration file `overlay_appearance.txt` has various options to set for the overlay appearance. +The notifications positions could be set to one of these values: +* `top_left` +* `top_center` +* `top_right` +* `bot_left` +* `bot_center` +* `bot_right` + +Check the exaxmple files in the `steam_settings` folder + +--- + ## Auto accept game/lobby invites: When the overlay is enabled and working, you can bypass it and auto-accept invites (lobby or game) from a list of Steam IDs (SteamID64 format). diff --git a/post_build/steam_settings.EXAMPLE/overlay_appearance.EXAMPLE.txt b/post_build/steam_settings.EXAMPLE/overlay_appearance.EXAMPLE.txt index ff06aaf3..bd8daed0 100644 --- a/post_build/steam_settings.EXAMPLE/overlay_appearance.EXAMPLE.txt +++ b/post_build/steam_settings.EXAMPLE/overlay_appearance.EXAMPLE.txt @@ -1,22 +1,46 @@ + Font_Size 16.0 Icon_Size 64.0 + Notification_R 0.16 Notification_G 0.29 Notification_B 0.48 Notification_A 1.0 -Background_R 0.0 -Background_G 0.0 -Background_B 0.1 -Background_A 0.5 -Element_R 0.0 -Element_G 0.1 -Element_B 0.0 -Element_A 1.0 -ElementHovered_R 0.0 -ElementHovered_G 0.5 -ElementHovered_B 0.0 -ElementHovered_A 1.0 -ElementActive_R 0.0 -ElementActive_G 0.75 -ElementActive_B 0.0 -ElementActive_A 1.0 \ No newline at end of file + +Background_R -1.0 +Background_G -1.0 +Background_B -1.0 +Background_A -1.0 + +Element_R -1.0 +Element_G -1.0 +Element_B -1.0 +Element_A -1.0 + +ElementHovered_R -1.0 +ElementHovered_G -1.0 +ElementHovered_B -1.0 +ElementHovered_A -1.0 + +ElementActive_R -1.0 +ElementActive_G -1.0 +ElementActive_B -1.0 +ElementActive_A -1.0 + + +; available options: +; top_left +; top_center +; top_right +; bot_left +; bot_center +; bot_right + +; position of achievements +PosAchievement top_right + +; position of invitations +PosInvitation top_right + +; position of chat messages +PosChatMsg top_right