Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ target_sources(
src/main.c
src/sources/gamerpic.c
src/sources/game_cover.c
src/sources/game_name.c
src/sources/gamerscore.c
src/sources/gamertag.c
src/sources/achievement_name.c
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Each text and image source exposes an **Auto show/hide** toggle in its propertie
#### Game

- **Game Cover**: image source for the currently active game's cover art
- **Game Name**: text source for the currently active game's title

#### Achievements

Expand Down
19 changes: 19 additions & 0 deletions src/common/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ typedef struct gamertag_configuration {
auto_visibility_config_t auto_visibility;
} gamertag_configuration_t;

/**
* @brief Configuration used by the active game name overlay/renderer.
*
* Ownership:
* - Strings are treated as borrowed pointers unless otherwise documented by the
* caller.
*/
typedef struct game_name_configuration {
const char *font_face;
const char *font_style;
/** Font size in pixels (height passed to FreeType). */
uint32_t font_size;
/** Top gradient color in 0xRRGGBBAA format. */
uint32_t top_color;
/** Bottom gradient color in 0xRRGGBBAA format. */
uint32_t bottom_color;
auto_visibility_config_t auto_visibility;
} game_name_configuration_t;

/**
* @brief Configuration used by the achievement name overlay/renderer.
*
Expand Down
20 changes: 20 additions & 0 deletions src/integrations/monitoring_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ static game_t *g_retro_game = NULL;
*/
static identity_source_t g_last_game_source = IDENTITY_SOURCE_XBOX;

static const game_t *get_current_active_game(void) {
if (g_last_game_source == IDENTITY_SOURCE_XBOX) {
if (g_xbox_game)
return g_xbox_game;
if (g_retro_game)
return g_retro_game;
} else {
if (g_retro_game)
return g_retro_game;
if (g_xbox_game)
return g_xbox_game;
}

return NULL;
}

static const identity_t *get_current_active_identity(void) {
/* When both sources have an active game, the one that reported a game
* most recently takes priority. */
Expand Down Expand Up @@ -647,6 +663,10 @@ const identity_t *monitoring_get_current_active_identity(void) {
return get_current_active_identity();
}

const game_t *monitoring_get_current_active_game(void) {
return get_current_active_game();
}

const achievement_t *monitoring_get_current_game_achievements(void) {
return g_current_achievements;
}
13 changes: 13 additions & 0 deletions src/integrations/monitoring_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ void monitoring_subscribe_session_ready(on_monitoring_session_ready_t callback);
*/
const identity_t *monitoring_get_current_active_identity(void);

/**
* @brief Get the currently active game, if any.
*
* Returns the same game that would be delivered to game-played subscribers
* right now.
*
* Ownership/lifetime: the returned pointer is owned by the monitoring service
* and may be replaced on the next game update. Do not free it.
*
* @return The active game, or NULL if no session is established.
*/
const game_t *monitoring_get_current_active_game(void);

/**
* @brief Get the cached generic achievements list for the current game.
*
Expand Down
79 changes: 79 additions & 0 deletions src/io/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@
#define GAMERTAG_CONFIGURATION_AUTO_VISIBILITY_HIDE_DURATION "source_gamertag_auto_visibility_hide_duration"
#define GAMERTAG_CONFIGURATION_AUTO_VISIBILITY_FADE_DURATION "source_gamertag_auto_visibility_fade_duration"

#define GAME_NAME_CONFIGURATION_TOP_COLOR "source_game_name_top_color"
#define GAME_NAME_CONFIGURATION_BOTTOM_COLOR "source_game_name_bottom_color"
#define GAME_NAME_CONFIGURATION_SIZE "source_game_name_size"
#define GAME_NAME_CONFIGURATION_FONT_FACE "source_game_name_font_face"
#define GAME_NAME_CONFIGURATION_FONT_STYLE "source_game_name_font_style"
#define GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_ENABLED "source_game_name_auto_visibility_enabled"
#define GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_SHOW_DURATION "source_game_name_auto_visibility_show_duration"
#define GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_HIDE_DURATION "source_game_name_auto_visibility_hide_duration"
#define GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_FADE_DURATION "source_game_name_auto_visibility_fade_duration"

#define ACHIEVEMENT_NAME_CONFIGURATION_ACTIVE_TOP_COLOR "source_achievement_name_active_top_color"
#define ACHIEVEMENT_NAME_CONFIGURATION_ACTIVE_BOTTOM_COLOR "source_achievement_name_active_bottom_color"
#define ACHIEVEMENT_NAME_CONFIGURATION_INACTIVE_TOP_COLOR "source_achievement_name_inactive_top_color"
Expand Down Expand Up @@ -514,6 +524,67 @@ gamertag_configuration_t *state_get_gamertag_configuration() {
return configuration;
}

void state_set_game_name_configuration(const game_name_configuration_t *configuration) {

if (!configuration) {
return;
}

obs_data_set_int(g_state, GAME_NAME_CONFIGURATION_TOP_COLOR, configuration->top_color);
obs_data_set_int(g_state, GAME_NAME_CONFIGURATION_BOTTOM_COLOR, configuration->bottom_color);
obs_data_set_int(g_state, GAME_NAME_CONFIGURATION_SIZE, configuration->font_size);
obs_data_set_string(g_state, GAME_NAME_CONFIGURATION_FONT_FACE, configuration->font_face);
obs_data_set_string(g_state, GAME_NAME_CONFIGURATION_FONT_STYLE, configuration->font_style);
obs_data_set_bool(g_state, GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_ENABLED, configuration->auto_visibility.enabled);
obs_data_set_double(g_state,
GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_SHOW_DURATION,
configuration->auto_visibility.show_duration);
obs_data_set_double(g_state,
GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_HIDE_DURATION,
configuration->auto_visibility.hide_duration);
obs_data_set_double(g_state,
GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_FADE_DURATION,
configuration->auto_visibility.fade_duration);

save_state(g_state);
}

game_name_configuration_t *state_get_game_name_configuration() {

uint32_t top_color = (uint32_t)obs_data_get_int(g_state, GAME_NAME_CONFIGURATION_TOP_COLOR);
uint32_t bottom_color = (uint32_t)obs_data_get_int(g_state, GAME_NAME_CONFIGURATION_BOTTOM_COLOR);
uint32_t size = (uint32_t)obs_data_get_int(g_state, GAME_NAME_CONFIGURATION_SIZE);
const char *font_face = obs_data_get_string(g_state, GAME_NAME_CONFIGURATION_FONT_FACE);
const char *font_style = obs_data_get_string(g_state, GAME_NAME_CONFIGURATION_FONT_STYLE);
bool auto_visibility_enabled = obs_data_get_bool(g_state, GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_ENABLED);
float auto_visibility_show_duration =
(float)obs_data_get_double(g_state, GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_SHOW_DURATION);
float auto_visibility_hide_duration =
(float)obs_data_get_double(g_state, GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_HIDE_DURATION);
float auto_visibility_fade_duration =
(float)obs_data_get_double(g_state, GAME_NAME_CONFIGURATION_AUTO_VISIBILITY_FADE_DURATION);

game_name_configuration_t *configuration = bzalloc(sizeof(game_name_configuration_t));

configuration->top_color = top_color == 0 ? 0xFFFFFFFF : top_color;
configuration->bottom_color = bottom_color == 0 ? 0xFFFFFFFF : bottom_color;
configuration->font_size = size == 0 ? 48 : size;
configuration->font_face = bstrdup(font_face);
configuration->font_style = bstrdup(font_style);
configuration->auto_visibility.enabled = auto_visibility_enabled;
configuration->auto_visibility.show_duration = auto_visibility_show_duration > 0.0f
? auto_visibility_show_duration
: AUTO_VISIBILITY_DEFAULT_SHARED_SHOW_DURATION;
configuration->auto_visibility.hide_duration = auto_visibility_hide_duration > 0.0f
? auto_visibility_hide_duration
: AUTO_VISIBILITY_DEFAULT_SHARED_HIDE_DURATION;
configuration->auto_visibility.fade_duration = auto_visibility_fade_duration > 0.0f
? auto_visibility_fade_duration
: AUTO_VISIBILITY_DEFAULT_SHARED_FADE_DURATION;

return configuration;
}

void state_set_achievement_name_configuration(const achievement_name_configuration_t *configuration) {

if (!configuration) {
Expand Down Expand Up @@ -837,6 +908,14 @@ void state_free_gamertag_configuration(gamertag_configuration_t **config) {
free_memory((void **)config);
}

void state_free_game_name_configuration(game_name_configuration_t **config) {
if (!config || !*config) {
return;
}

free_memory((void **)config);
}

void state_free_achievement_name_configuration(achievement_name_configuration_t **config) {
if (!config || !*config) {
return;
Expand Down
32 changes: 32 additions & 0 deletions src/io/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ void state_set_gamertag_configuration(const gamertag_configuration_t *configurat
*/
gamertag_configuration_t *state_get_gamertag_configuration();

/**
* @brief Set the active game name source configuration.
*
* Stores the configuration for the active game name display source, including
* font path, text size, color, and alignment. The configuration is persisted to disk.
*
* @param configuration Configuration to store (may be NULL to clear).
*/
void state_set_game_name_configuration(const game_name_configuration_t *configuration);

/**
* @brief Get the currently stored active game name source configuration.
*
* Retrieves the configuration with default values if none has been set:
* - Default color: 0xFFFFFF (white)
* - Default size: 12 pixels
*
* @return Newly allocated configuration structure. Caller must free it with
* state_free_game_name_configuration().
*/
game_name_configuration_t *state_get_game_name_configuration();

/**
* @brief Set the achievement name source configuration.
*
Expand Down Expand Up @@ -344,6 +366,16 @@ void state_free_gamerscore_configuration(gamerscore_configuration_t **config);
*/
void state_free_gamertag_configuration(gamertag_configuration_t **config);

/**
* @brief Free a game name configuration structure and its contents.
*
* Frees the font strings and the configuration structure itself.
* Safe to call with NULL.
*
* @param config Configuration structure to free. Set to NULL after freeing.
*/
void state_free_game_name_configuration(game_name_configuration_t **config);

/**
* @brief Free an achievement name configuration structure and its contents.
*
Expand Down
3 changes: 3 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "sources/game_cover.h"
#include "sources/gamerscore.h"
#include "sources/gamertag.h"
#include "sources/game_name.h"

#include "io/state.h"
#include "sources/achievement_name.h"
Expand All @@ -32,6 +33,7 @@ bool obs_module_load(void) {
game_cover_source_register();
xbox_gamerscore_source_register();
xbox_gamertag_source_register();
xbox_game_name_source_register();

/* Initialize the shared achievement display cycle before registering achievement sources */
achievement_cycle_init();
Expand Down Expand Up @@ -76,6 +78,7 @@ void obs_module_unload(void) {
xbox_gamerpic_source_cleanup();
xbox_gamerscore_source_cleanup();
xbox_gamertag_source_cleanup();
xbox_game_name_source_cleanup();

monitoring_stop();
io_cleanup();
Expand Down
Loading
Loading