* revert the previous change and force-display a mouse cursor inside the overlay, Truberbrook and others won't show a cursor in the overlay by default

* attempt to fix the crash when closing the app, use global object with bigger life time and avoid local states

* attempt to fix usage of invalid/outdated window size when displaying notifications

* lock overlay mutex everywhere to avoid sync problems

* lock global emu mutex in the relevant places when using client APIs

* only change the ImGui ini and round corner settings once
This commit is contained in:
otavepto 2024-03-04 02:39:50 +02:00 committed by otavepto
parent 66d1fab36f
commit c19fc81e84
2 changed files with 224 additions and 211 deletions

View File

@ -88,6 +88,7 @@ struct Overlay_Achievement
#ifdef EMU_OVERLAY #ifdef EMU_OVERLAY
#include <future> #include <future>
#include <atomic>
#include "InGameOverlay/RendererHook.h" #include "InGameOverlay/RendererHook.h"
struct NotificationsIndexes { struct NotificationsIndexes {
@ -108,8 +109,7 @@ class Steam_Overlay
// avoids spam loading on failure // avoids spam loading on failure
std::atomic<int32_t> load_achievements_trials = 3; std::atomic<int32_t> load_achievements_trials = 3;
bool setup_overlay_called; bool is_ready = false;
bool is_ready;
bool show_overlay; bool show_overlay;
ENotificationPosition notif_position; ENotificationPosition notif_position;
int h_inset, v_inset; int h_inset, v_inset;
@ -127,7 +127,7 @@ class Steam_Overlay
bool warn_bad_appid; bool warn_bad_appid;
char username_text[256]; char username_text[256];
std::atomic_bool save_settings; std::atomic<bool> save_settings;
int current_language; int current_language;
@ -136,17 +136,14 @@ class Steam_Overlay
// Callback infos // Callback infos
std::queue<Friend> has_friend_action; std::queue<Friend> has_friend_action;
std::vector<Notification> notifications; std::vector<Notification> notifications;
std::recursive_mutex notifications_mutex;
std::atomic<bool> have_notifications;
// used when the button "Invite all" is clicked // used when the button "Invite all" is clicked
std::atomic<bool> invite_all_friends_clicked = false; std::atomic<bool> invite_all_friends_clicked = false;
bool overlay_state_changed; bool overlay_state_changed;
std::recursive_mutex overlay_mutex;
std::atomic<bool> i_have_lobby; std::atomic<bool> i_have_lobby;
std::future<InGameOverlay::RendererHook_t*> future_renderer; std::future<InGameOverlay::RendererHook_t*> future_renderer;
InGameOverlay::RendererHook_t* _renderer; InGameOverlay::RendererHook_t *_renderer;
Steam_Overlay(Steam_Overlay const&) = delete; Steam_Overlay(Steam_Overlay const&) = delete;
Steam_Overlay(Steam_Overlay&&) = delete; Steam_Overlay(Steam_Overlay&&) = delete;
@ -162,6 +159,8 @@ class Steam_Overlay
bool FriendJoinable(std::pair<const Friend, friend_window_state> &f); bool FriendJoinable(std::pair<const Friend, friend_window_state> &f);
bool IHaveLobby(); bool IHaveLobby();
bool submit_notification(notification_type type, const std::string &msg, std::pair<const Friend, friend_window_state> *frd = nullptr, const std::weak_ptr<uint64_t> &icon = {});
void NotifySoundUserInvite(friend_window_state& friend_state); void NotifySoundUserInvite(friend_window_state& friend_state);
void NotifySoundUserAchievement(); void NotifySoundUserAchievement();
void NotifySoundAutoAcceptFriendInvite(); void NotifySoundAutoAcceptFriendInvite();
@ -211,7 +210,7 @@ public:
void FriendConnect(Friend _friend); void FriendConnect(Friend _friend);
void FriendDisconnect(Friend _friend); void FriendDisconnect(Friend _friend);
void AddMessageNotification(std::string const& message); void AddChatMessageNotification(std::string const& message);
void AddAchievementNotification(nlohmann::json const& ach); void AddAchievementNotification(nlohmann::json const& ach);
void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state); void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state);
void AddAutoAcceptInviteNotification(); void AddAutoAcceptInviteNotification();
@ -254,7 +253,7 @@ public:
void FriendConnect(Friend _friend) {} void FriendConnect(Friend _friend) {}
void FriendDisconnect(Friend _friend) {} void FriendDisconnect(Friend _friend) {}
void AddMessageNotification(std::string const& message) {} void AddChatMessageNotification(std::string const& message) {}
void AddAchievementNotification(nlohmann::json const& ach) {} void AddAchievementNotification(nlohmann::json const& ach) {}
void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state) {} void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state) {}
void AddAutoAcceptInviteNotification() {} void AddAutoAcceptInviteNotification() {}

View File

@ -64,6 +64,10 @@ ImFontAtlas fonts_atlas{};
ImFont *font_default{}; ImFont *font_default{};
ImFont *font_notif{}; ImFont *font_notif{};
std::recursive_mutex overlay_mutex{};
std::atomic<bool> setup_overlay_called = false;
int find_free_id(std::vector<int> & ids, int base) int find_free_id(std::vector<int> & ids, int base)
{ {
std::sort(ids.begin(), ids.end()); std::sort(ids.begin(), ids.end());
@ -129,7 +133,6 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu
callbacks(callbacks), callbacks(callbacks),
run_every_runcb(run_every_runcb), run_every_runcb(run_every_runcb),
network(network), network(network),
setup_overlay_called(false),
show_overlay(false), show_overlay(false),
is_ready(false), is_ready(false),
notif_position(ENotificationPosition::k_EPositionBottomLeft), notif_position(ENotificationPosition::k_EPositionBottomLeft),
@ -183,7 +186,7 @@ bool Steam_Overlay::Ready() const
bool Steam_Overlay::NeedPresent() const bool Steam_Overlay::NeedPresent() const
{ {
return true; return !settings->disable_overlay;
} }
void Steam_Overlay::SetNotificationPosition(ENotificationPosition eNotificationPosition) void Steam_Overlay::SetNotificationPosition(ENotificationPosition eNotificationPosition)
@ -200,10 +203,10 @@ void Steam_Overlay::SetNotificationInset(int nHorizontalInset, int nVerticalInse
void Steam_Overlay::SetupOverlay() void Steam_Overlay::SetupOverlay()
{ {
PRINT_DEBUG("Steam_Overlay::SetupOverlay\n");
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (!setup_overlay_called) { PRINT_DEBUG("Steam_Overlay::SetupOverlay\n");
setup_overlay_called = true; bool not_called = false;
if (setup_overlay_called.compare_exchange_weak(not_called, true)) {
future_renderer = InGameOverlay::DetectRenderer(); future_renderer = InGameOverlay::DetectRenderer();
PRINT_DEBUG("Steam_Overlay::SetupOverlay requested renderer detector/hook\n"); PRINT_DEBUG("Steam_Overlay::SetupOverlay requested renderer detector/hook\n");
} }
@ -212,47 +215,63 @@ void Steam_Overlay::SetupOverlay()
void Steam_Overlay::UnSetupOverlay() void Steam_Overlay::UnSetupOverlay()
{ {
PRINT_DEBUG("Steam_Overlay::UnSetupOverlay\n");
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
InGameOverlay::StopRendererDetection(); PRINT_DEBUG("Steam_Overlay::UnSetupOverlay\n");
if (!Ready() && future_renderer.valid()) { bool called = true;
if (future_renderer.wait_for(std::chrono::milliseconds{500}) == std::future_status::ready) { if (setup_overlay_called.compare_exchange_weak(called, false)) {
future_renderer.get(); // to invalidate the future object InGameOverlay::StopRendererDetection();
InGameOverlay::FreeDetector(); if (!Ready() && future_renderer.valid()) {
if (future_renderer.wait_for(std::chrono::milliseconds{500}) == std::future_status::ready) {
future_renderer.get(); // to invalidate the future object
InGameOverlay::FreeDetector();
}
} }
}
if (_renderer) {
if (_renderer) { for (auto &ach : achievements) {
for (auto &ach : achievements) { if (!ach.icon.expired()) _renderer->ReleaseImageResource(ach.icon);
if (!ach.icon.expired()) _renderer->ReleaseImageResource(ach.icon); if (!ach.icon_gray.expired()) _renderer->ReleaseImageResource(ach.icon_gray);
if (!ach.icon_gray.expired()) _renderer->ReleaseImageResource(ach.icon_gray); }
_renderer = nullptr;
PRINT_DEBUG("Steam_Overlay::UnSetupOverlay freed all images\n");
} }
_renderer = nullptr;
PRINT_DEBUG("Steam_Overlay::UnSetupOverlay freed all images\n");
} }
} }
// called initially and when window size is updated
void Steam_Overlay::HookReady(bool ready) void Steam_Overlay::HookReady(bool ready)
{ {
PRINT_DEBUG("Steam_Overlay::HookReady %i\n", (int)ready); // NOTE usage of local objects here cause an exception when this is called with false state
// the reason is that by the time this hook is called, the object would've been already destructed
// this is why we check this global state
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (!setup_overlay_called) return;
is_ready = ready; is_ready = ready;
ImGuiIO &io = ImGui::GetIO(); PRINT_DEBUG("Steam_Overlay::HookReady %i\n", (int)ready);
ImGuiStyle &style = ImGui::GetStyle();
// disable loading the default ini file static bool initialized_imgui = false;
io.IniFilename = NULL; if (ready && !initialized_imgui) {
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
initialized_imgui = true;
// Disable round window ImGuiIO &io = ImGui::GetIO();
style.WindowRounding = 0.0; ImGuiStyle &style = ImGui::GetStyle();
// disable loading the default ini file
io.IniFilename = NULL;
// Disable round window
style.WindowRounding = 0.0;
// TODO: Uncomment this and draw our own cursor (cosmetics)
//io.WantSetMousePos = false;
//io.MouseDrawCursor = false;
//io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
}
// TODO: Uncomment this and draw our own cursor (cosmetics)
//io.WantSetMousePos = false;
//io.MouseDrawCursor = false;
//io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
} }
void Steam_Overlay::OpenOverlayInvite(CSteamID lobbyId) void Steam_Overlay::OpenOverlayInvite(CSteamID lobbyId)
@ -285,6 +304,7 @@ bool Steam_Overlay::ShowOverlay() const
return show_overlay; return show_overlay;
} }
// called when the user presses SHIFT + TAB
bool Steam_Overlay::OpenOverlayHook(bool toggle) bool Steam_Overlay::OpenOverlayHook(bool toggle)
{ {
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
@ -301,19 +321,30 @@ void Steam_Overlay::ShowOverlay(bool state)
if (!Ready() || show_overlay == state) if (!Ready() || show_overlay == state)
return; return;
show_overlay = state;
overlay_state_changed = true;
ImGuiIO &io = ImGui::GetIO();
// this is very important, it doesn't just prevent input confusion between game's window // this is very important, it doesn't just prevent input confusion between game's window
// and overlay's window, but internally it calls the necessary fuctions to properly update // and overlay's window, but internally it calls the necessary fuctions to properly update
// ImGui window size // ImGui window size
if (state) { if (state) {
// force draw the cursor, otherwise games like Truberbrook will not have an overlay cursor
io.MouseDrawCursor = true;
// clip the cursor
_renderer->HideAppInputs(true); _renderer->HideAppInputs(true);
// allow internal frmae processing
_renderer->HideOverlayInputs(false); _renderer->HideOverlayInputs(false);
} else { } else {
io.MouseDrawCursor = false;
// don't clip the cursor
_renderer->HideAppInputs(false); _renderer->HideAppInputs(false);
_renderer->HideOverlayInputs(true); // only stop internal frame processing when our state flag == false, and we don't have notifications
if (notifications.empty()) {
_renderer->HideOverlayInputs(true);
}
} }
show_overlay = state;
overlay_state_changed = true;
} }
void Steam_Overlay::NotifySoundUserInvite(friend_window_state& friend_state) void Steam_Overlay::NotifySoundUserInvite(friend_window_state& friend_state)
@ -419,38 +450,51 @@ void Steam_Overlay::FriendDisconnect(Friend _friend)
friends.erase(it); friends.erase(it);
} }
void Steam_Overlay::AddMessageNotification(std::string const& message) bool Steam_Overlay::submit_notification(notification_type type, const std::string &msg, std::pair<const Friend, friend_window_state> *frd, const std::weak_ptr<uint64_t> &icon)
{ {
std::lock_guard<std::recursive_mutex> lock(notifications_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (settings->disable_overlay_friend_notification) return; PRINT_DEBUG("Steam_Overlay::submit_notification %i, '%s'\n", (int)type, msg.c_str());
int id = find_free_notification_id(notifications); int id = find_free_notification_id(notifications);
if (id != 0) if (id == 0) {
{ PRINT_DEBUG("Steam_Overlay::submit_notification error no free id to create a notification window\n");
Notification notif; return false;
notif.id = id;
notif.type = notification_type_message;
notif.message = message;
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
have_notifications = true;
} }
else
PRINT_DEBUG("Steam_Overlay error no free id to create a notification window\n"); // this is very important, internally it calls the necessary fuctions to properly update
// ImGui window size, change it here since we want the next OverlayProc to have a full window size
// otherwise notification position will relative to an outdated window size
_renderer->HideOverlayInputs(false);
Notification notif{};
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notif.id = id;
notif.type = type;
notif.message = msg;
notif.frd = frd;
notif.icon = icon;
notifications.emplace_back(notif);
return true;
}
void Steam_Overlay::AddChatMessageNotification(std::string const &message)
{
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (settings->disable_overlay_friend_notification) return;
submit_notification(notification_type_message, message);
} }
// show a notification when the user unlocks an achievement // show a notification when the user unlocks an achievement
void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach) void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
{ {
std::lock_guard<std::recursive_mutex> lock(notifications_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
std::lock_guard<std::recursive_mutex> lock2(global_mutex);
if (!settings->disable_overlay_achievement_notification) { if (!settings->disable_overlay_achievement_notification) {
int id = find_free_notification_id(notifications); // Load achievement image
if (id != 0) std::weak_ptr<uint64_t> icon_rsrc{};
{ if (_renderer) {
Notification notif;
notif.id = id;
notif.type = notification_type_achievement;
// Load achievement image
std::string file_path = Local_Storage::get_game_settings_path() + ach["icon"].get<std::string>(); std::string file_path = Local_Storage::get_game_settings_path() + ach["icon"].get<std::string>();
unsigned long long file_size = file_size_(file_path); unsigned long long file_size = file_size_(file_path);
if (!file_size) { if (!file_size) {
@ -460,18 +504,18 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
if (file_size) { if (file_size) {
std::string img = Local_Storage::load_image_resized(file_path, "", settings->overlay_appearance.icon_size); std::string img = Local_Storage::load_image_resized(file_path, "", settings->overlay_appearance.icon_size);
if (img.length() > 0) { if (img.length() > 0) {
if (_renderer) notif.icon = _renderer->CreateImageResource((void*)img.c_str(), settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size); icon_rsrc = _renderer->CreateImageResource((void*)img.c_str(), settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size);
} }
} }
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
NotifySoundUserAchievement();
have_notifications = true;
} }
else
PRINT_DEBUG("Steam_Overlay error no free id to create a notification window\n"); submit_notification(
notification_type_achievement,
ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>(),
{},
icon_rsrc
);
NotifySoundUserAchievement();
} }
std::string ach_name = ach.value("name", ""); std::string ach_name = ach.value("name", "");
@ -488,59 +532,31 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
void Steam_Overlay::AddInviteNotification(std::pair<const Friend, friend_window_state>& wnd_state) void Steam_Overlay::AddInviteNotification(std::pair<const Friend, friend_window_state>& wnd_state)
{ {
std::lock_guard<std::recursive_mutex> lock(notifications_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (settings->disable_overlay_friend_notification) return; if (settings->disable_overlay_friend_notification) return;
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_invite;
notif.frd = &wnd_state;
{ char tmp[TRANSLATION_BUFFER_SIZE]{};
char tmp[TRANSLATION_BUFFER_SIZE]{}; auto &first_friend = wnd_state.first;
auto &first_friend = wnd_state.first; auto &name = first_friend.name();
auto &name = first_friend.name(); snprintf(tmp, sizeof(tmp), translationInvitedYouToJoinTheGame[current_language], name.c_str(), (uint64)first_friend.id());
snprintf(tmp, sizeof(tmp), translationInvitedYouToJoinTheGame[current_language], name.c_str(), (uint64)first_friend.id());
notif.message = tmp; submit_notification(notification_type_invite, tmp, &wnd_state);
}
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
have_notifications = true;
}
else
PRINT_DEBUG("Steam_Overlay error no free id to create a notification window\n");
} }
void Steam_Overlay::AddAutoAcceptInviteNotification() void Steam_Overlay::AddAutoAcceptInviteNotification()
{ {
std::lock_guard<std::recursive_mutex> lock(notifications_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
int id = find_free_notification_id(notifications);
if (id != 0) char tmp[TRANSLATION_BUFFER_SIZE]{};
{ snprintf(tmp, sizeof(tmp), "%s", translationAutoAcceptFriendInvite[current_language]);
Notification notif{};
notif.id = id; submit_notification(notification_type_auto_accept_invite, tmp);
notif.type = notification_type_auto_accept_invite; NotifySoundAutoAcceptFriendInvite();
{
char tmp[TRANSLATION_BUFFER_SIZE]{};
snprintf(tmp, sizeof(tmp), "%s", translationAutoAcceptFriendInvite[current_language]);
notif.message = tmp;
}
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
NotifySoundAutoAcceptFriendInvite();
have_notifications = true;
} else {
PRINT_DEBUG("Steam_Overlay error no free id to create an auto-accept notification window\n");
}
} }
bool Steam_Overlay::FriendJoinable(std::pair<const Friend, friend_window_state> &f) bool Steam_Overlay::FriendJoinable(std::pair<const Friend, friend_window_state> &f)
{ {
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_Friends* steamFriends = get_steam_client()->steam_friends; Steam_Friends* steamFriends = get_steam_client()->steam_friends;
if( std::string(steamFriends->GetFriendRichPresence((uint64)f.first.id(), "connect")).length() > 0 ) if( std::string(steamFriends->GetFriendRichPresence((uint64)f.first.id(), "connect")).length() > 0 )
@ -556,6 +572,7 @@ bool Steam_Overlay::FriendJoinable(std::pair<const Friend, friend_window_state>
bool Steam_Overlay::IHaveLobby() bool Steam_Overlay::IHaveLobby()
{ {
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_Friends* steamFriends = get_steam_client()->steam_friends; Steam_Friends* steamFriends = get_steam_client()->steam_friends;
if (std::string(steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect")).length() > 0) if (std::string(steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect")).length() > 0)
return true; return true;
@ -782,96 +799,90 @@ void Steam_Overlay::SetNextNotificationPos(float width, float height, float font
void Steam_Overlay::BuildNotifications(int width, int height) void Steam_Overlay::BuildNotifications(int width, int height)
{ {
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()); auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
float font_size = ImGui::GetFontSize(); float font_size = ImGui::GetFontSize();
std::queue<Friend> friend_actions_temp{}; std::queue<Friend> friend_actions_temp{};
{ NotificationsIndexes idx{};
std::lock_guard<std::recursive_mutex> lock(notifications_mutex); for (auto it = notifications.begin(); it != notifications.end(); ++it) {
auto elapsed_notif = now - it->start_time;
NotificationsIndexes idx{}; if ( elapsed_notif < Notification::fade_in) { // still appearing (fading in)
for (auto it = notifications.begin(); it != notifications.end(); ++it) float alpha = settings->overlay_appearance.notification_a * (elapsed_notif.count() / static_cast<float>(Notification::fade_in.count()));
{ ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha));
auto elapsed_notif = now - it->start_time; 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));
if ( elapsed_notif < Notification::fade_in) { } else if ( elapsed_notif > Notification::fade_out_start) { // fading out
float alpha = settings->overlay_appearance.notification_a * (elapsed_notif.count() / static_cast<float>(Notification::fade_in.count())); float alpha = settings->overlay_appearance.notification_a * ((Notification::show_time - elapsed_notif).count() / static_cast<float>(Notification::fade_out.count()));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha)); 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_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)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2));
} } else { // still in the visible time limit
else if ( elapsed_notif > Notification::fade_out_start) { ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, settings->overlay_appearance.notification_a));
float alpha = settings->overlay_appearance.notification_a * ((Notification::show_time - elapsed_notif).count() / static_cast<float>(Notification::fade_out.count())); 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_Border, ImVec4(0, 0, 0, alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, settings->overlay_appearance.notification_a*2));
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 {
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));
}
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) {
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);
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::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;
case notification_type_auto_accept_invite:
ImGui::TextWrapped("%s", it->message.c_str());
break;
}
ImGui::End();
ImGui::PopStyleColor(3);
} }
notifications.erase(std::remove_if(notifications.begin(), notifications.end(), [&now](Notification &item) {
return (now - item.start_time) > Notification::show_time; SetNextNotificationPos(width, height, font_size, (notification_type)it->type, idx);
}), notifications.end()); 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);
have_notifications = !notifications.empty(); 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);
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::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;
case notification_type_auto_accept_invite:
ImGui::TextWrapped("%s", it->message.c_str());
break;
}
ImGui::End();
ImGui::PopStyleColor(3);
} }
// erase all notifications whose visible time exceeded the max
notifications.erase(std::remove_if(notifications.begin(), notifications.end(), [&now](Notification &item) {
return (now - item.start_time) > Notification::show_time;
}), notifications.end());
if (!friend_actions_temp.empty()) { if (!friend_actions_temp.empty()) {
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
while (!friend_actions_temp.empty()) { while (!friend_actions_temp.empty()) {
has_friend_action.push(friend_actions_temp.front()); has_friend_action.push(friend_actions_temp.front());
friend_actions_temp.pop(); friend_actions_temp.pop();
@ -967,20 +978,24 @@ void Steam_Overlay::OverlayProc()
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (show_overlay) { // Set the overlay windows to the size of the game window
// Set the overlay windows to the size of the game window // only if we have a reason (full overlay or just some notification)
if (show_overlay || notifications.size()) {
ImGui::SetNextWindowPos({ 0,0 }); ImGui::SetNextWindowPos({ 0,0 });
ImGui::SetNextWindowSize({ io.DisplaySize.x, io.DisplaySize.y }); ImGui::SetNextWindowSize({ io.DisplaySize.x, io.DisplaySize.y });
ImGui::SetNextWindowBgAlpha(0.60);
ImGui::SetNextWindowBgAlpha(0.50);
ImGui::PushFont(font_default);
} }
if (have_notifications) { if (notifications.size()) {
ImGui::PushFont(font_notif); ImGui::PushFont(font_notif);
BuildNotifications(io.DisplaySize.x, io.DisplaySize.y); BuildNotifications(io.DisplaySize.x, io.DisplaySize.y);
ImGui::PopFont(); ImGui::PopFont();
// after showing all notifications, and if we won't show the overlay
// then disable frame rendering
if (notifications.empty() && !show_overlay) {
_renderer->HideOverlayInputs(true);
}
} }
// ******************** exit early if we shouldn't show the overlay // ******************** exit early if we shouldn't show the overlay
@ -989,6 +1004,8 @@ void Steam_Overlay::OverlayProc()
} }
// ******************** // ********************
//ImGui::SetNextWindowFocus();
ImGui::PushFont(font_default);
bool show = true; bool show = true;
char tmp[TRANSLATION_BUFFER_SIZE]{}; char tmp[TRANSLATION_BUFFER_SIZE]{};
@ -1057,7 +1074,6 @@ void Steam_Overlay::OverlayProc()
ImGui::LabelText("##label", "%s", translationFriends[current_language]); ImGui::LabelText("##label", "%s", translationFriends[current_language]);
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (!friends.empty()) { if (!friends.empty()) {
if (i_have_lobby) { if (i_have_lobby) {
std::string inviteAll(translationInviteAll[current_language]); std::string inviteAll(translationInviteAll[current_language]);
@ -1275,7 +1291,7 @@ void Steam_Overlay::Callback(Common_Message *msg)
friend_info->second.window_state |= window_state_need_attention; friend_info->second.window_state |= window_state_need_attention;
} }
AddMessageNotification(friend_info->first.name() + ": " + steam_message.message()); AddChatMessageNotification(friend_info->first.name() + ": " + steam_message.message());
NotifySoundUserInvite(friend_info->second); NotifySoundUserInvite(friend_info->second);
} }
} }
@ -1284,6 +1300,7 @@ void Steam_Overlay::Callback(Common_Message *msg)
void Steam_Overlay::RunCallbacks() void Steam_Overlay::RunCallbacks()
{ {
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
std::lock_guard<std::recursive_mutex> lock2(global_mutex);
if (Ready() && !achievements.size() && load_achievements_trials > 0) { if (Ready() && !achievements.size() && load_achievements_trials > 0) {
--load_achievements_trials; --load_achievements_trials;
Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats; Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats;
@ -1348,9 +1365,6 @@ void Steam_Overlay::RunCallbacks()
_renderer->OverlayProc = [this]() { OverlayProc(); }; _renderer->OverlayProc = [this]() { OverlayProc(); };
_renderer->OverlayHookReady = [this](InGameOverlay::OverlayHookState state) { _renderer->OverlayHookReady = [this](InGameOverlay::OverlayHookState state) {
PRINT_DEBUG("Steam_Overlay hook state changed %i\n", (int)state); PRINT_DEBUG("Steam_Overlay hook state changed %i\n", (int)state);
if (state == InGameOverlay::OverlayHookState::Removing) {
_renderer = nullptr;
}
HookReady(state == InGameOverlay::OverlayHookState::Ready || state == InGameOverlay::OverlayHookState::Reset); HookReady(state == InGameOverlay::OverlayHookState::Ready || state == InGameOverlay::OverlayHookState::Reset);
}; };
bool started = _renderer->StartHook(overlay_toggle_callback, overlay_toggle_keys, &fonts_atlas); bool started = _renderer->StartHook(overlay_toggle_callback, overlay_toggle_keys, &fonts_atlas);