diff --git a/CMakeLists.txt b/CMakeLists.txt index 36c07e49cc..435325884e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,11 @@ else (WIN32) PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp) endif (WIN32) +# Include libdl for dlopen +if (UNIX) + set(DLLIB dl) +endif (UNIX) + INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS}) LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS}) @@ -93,4 +98,4 @@ endif (WIN32) # libopenrct2.dll -> openrct2.dll set_target_properties(${PROJECT} PROPERTIES PREFIX "") -TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2_LIBRARIES} ${ORCTLIBS_LIB} ${JANSSON_LIBRARIES} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES}) +TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2_LIBRARIES} ${ORCTLIBS_LIB} ${JANSSON_LIBRARIES} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB}) diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 2dc461a654..b125cef62f 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3891,6 +3891,7 @@ STR_5549 :Year/Month/Day STR_5550 :{POP16}{POP16}Year {COMMA16}, {PUSH16}{PUSH16}{MONTH} {PUSH16}{PUSH16}{STRINGID} STR_5551 :Year/Day/Month STR_5552 :{POP16}{POP16}Year {COMMA16}, {PUSH16}{PUSH16}{PUSH16}{STRINGID} {MONTH} +STR_5553 :Pause game when Steam overlay is open ##################### # Rides/attractions # diff --git a/src/config.c b/src/config.c index c979af93ea..3f45c323b9 100644 --- a/src/config.c +++ b/src/config.c @@ -192,6 +192,7 @@ config_property_definition _generalDefinitions[] = { { offsetof(general_configuration, day_night_cycle), "day_night_cycle", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, { offsetof(general_configuration, upper_case_banners), "upper_case_banners", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, { offsetof(general_configuration, allow_loading_with_incorrect_checksum),"allow_loading_with_incorrect_checksum", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, + { offsetof(general_configuration, steam_overlay_pause), "steam_overlay_pause", CONFIG_VALUE_TYPE_BOOLEAN, true, NULL }, }; config_property_definition _interfaceDefinitions[] = { diff --git a/src/config.h b/src/config.h index 127e7602e4..7708042d0a 100644 --- a/src/config.h +++ b/src/config.h @@ -166,6 +166,7 @@ typedef struct { uint8 day_night_cycle; uint8 upper_case_banners; uint8 allow_loading_with_incorrect_checksum; + uint8 steam_overlay_pause; } general_configuration; typedef struct { diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 779e3dac99..75d5d689eb 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2126,6 +2126,8 @@ enum { STR_DATE_FORMAT_YEAR_DAY_MONTH = 5551, STR_DATE_FORMAT_YDM = 5552, + STR_STEAM_OVERLAY_PAUSE = 5553, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/platform/platform.h b/src/platform/platform.h index 545cac11cd..0929c215e3 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -90,6 +90,8 @@ extern int gNumResolutions; extern resolution *gResolutions; extern SDL_Window *gWindow; +extern bool gSteamOverlayActive; + // Platform shared definitions void platform_update_fullscreen_resolutions(); void platform_get_closest_resolution(int inWidth, int inHeight, int *outWidth, int *outHeight); @@ -143,6 +145,8 @@ uint16 platform_get_locale_language(); uint8 platform_get_locale_measurement_format(); uint8 platform_get_locale_temperature_format(); +bool platform_check_steam_overlay_attached(); + // Windows specific definitions #ifdef _WIN32 // Defining WIN32_LEAN_AND_MEAN breaks dsound.h in audio.h (uncomment when dsound is finally removed) diff --git a/src/platform/shared.c b/src/platform/shared.c index 65594368fe..cdf9ecf854 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -59,6 +59,8 @@ SDL_PixelFormat *gBufferTextureFormat = NULL; SDL_Color gPalette[256]; uint32 gPaletteHWMapped[256]; +bool gSteamOverlayActive = false; + static SDL_Surface *_surface; static SDL_Palette *_palette; @@ -73,6 +75,9 @@ static const int _fullscreen_modes[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW_FU static unsigned int _lastGestureTimestamp; static float _gestureRadius; +static uint32 _pixelBeforeOverlay; +static uint32 _pixelAfterOverlay; + static void platform_create_window(); static void platform_load_cursors(); static void platform_unload_cursors(); @@ -179,6 +184,39 @@ void platform_get_closest_resolution(int inWidth, int inHeight, int *outWidth, i } } +static void read_center_pixel(int width, int height, uint32 *pixel) { + SDL_Rect centerPixelRegion = {width / 2, height / 2, 1, 1}; + SDL_RenderReadPixels(gRenderer, ¢erPixelRegion, SDL_PIXELFORMAT_RGBA8888, pixel, sizeof(uint32)); +} + +// Should be called before SDL_RenderPresent to capture frame buffer before Steam overlay is drawn. +static void overlay_pre_render_check(int width, int height) { + read_center_pixel(width, height, &_pixelBeforeOverlay); +} + +// Should be called after SDL_RenderPresent, when Steam overlay has had the chance to be drawn. +static void overlay_post_render_check(int width, int height) { + static bool overlayActive = false; + static bool pausedBeforeOverlay = false; + + read_center_pixel(width, height, &_pixelAfterOverlay); + + // Detect an active Steam overlay by checking if the center pixel is changed by the gray fade. + // Will not be triggered by applications rendering to corners, like FRAPS, MSI Afterburner and Friends popups. + bool newOverlayActive = _pixelBeforeOverlay != _pixelAfterOverlay; + + // Toggle game pause state consistently with base pause state + if (!overlayActive && newOverlayActive) { + pausedBeforeOverlay = RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint32) & 1; + + if (!pausedBeforeOverlay) pause_toggle(); + } else if (overlayActive && !newOverlayActive && !pausedBeforeOverlay) { + pause_toggle(); + } + + overlayActive = newOverlayActive; +} + void platform_draw() { int width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); @@ -215,7 +253,16 @@ void platform_draw() } SDL_RenderCopy(gRenderer, gBufferTexture, NULL, NULL); + + if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) { + overlay_pre_render_check(width, height); + } + SDL_RenderPresent(gRenderer); + + if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) { + overlay_post_render_check(width, height); + } } else { // Lock the surface before setting its pixels @@ -643,6 +690,9 @@ static void platform_create_window() platform_update_fullscreen_resolutions(); platform_set_fullscreen_mode(gConfigGeneral.fullscreen_mode); + + // Check if steam overlay renderer is loaded into the process + gSteamOverlayActive = platform_check_steam_overlay_attached(); } int platform_scancode_to_rct_keycode(int sdl_key) diff --git a/src/platform/unix.c b/src/platform/unix.c index 602709bb63..956828364e 100644 --- a/src/platform/unix.c +++ b/src/platform/unix.c @@ -23,6 +23,7 @@ #include "../cmdline.h" #include "../openrct2.h" +#include /** * Unix, linux and fallback entry point to OpenRCT2. @@ -42,5 +43,36 @@ char platform_get_path_separator() } */ +// See http://syprog.blogspot.ru/2011/12/listing-loaded-shared-objects-in-linux.html +struct lmap { + void* base_address; + char* path; + void* unused; + struct lmap *next, *prev; +}; + +struct dummy { + void* pointers[3]; + struct dummy* ptr; +}; + +bool platform_check_steam_overlay_attached() { + void* processHandle = dlopen(NULL, RTLD_NOW); + + struct dummy* p = (struct dummy*) processHandle; + p = p->ptr; + + struct lmap* pl = (struct lmap*) p->ptr; + + while (pl != NULL) { + if (strstr(pl->path, "gameoverlayrenderer.so") != NULL) { + return true; + } + pl = pl->next; + } + + return false; +} + #endif #endif diff --git a/src/platform/windows.c b/src/platform/windows.c index 911d42475f..5856582f0b 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -21,6 +21,7 @@ #ifdef _WIN32 #include +#include #include #include #include @@ -839,4 +840,9 @@ uint8 platform_get_locale_temperature_format() return TEMPERATURE_FORMAT_C; } } + +bool platform_check_steam_overlay_attached() +{ + return GetModuleHandle("GameOverlayRenderer.dll") != NULL; +} #endif diff --git a/src/windows/options.c b/src/windows/options.c index 76a1f079ae..301843995c 100644 --- a/src/windows/options.c +++ b/src/windows/options.c @@ -79,6 +79,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_HARDWARE_DISPLAY_CHECKBOX, WIDX_UNCAP_FPS_CHECKBOX, WIDX_MINIMIZE_FOCUS_LOSS, + WIDX_STEAM_OVERLAY_PAUSE, WIDX_RENDERING_GROUP, WIDX_TILE_SMOOTHING_CHECKBOX, WIDX_GRIDLINES_CHECKBOX, @@ -168,7 +169,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { static rct_widget window_options_display_widgets[] = { MAIN_OPTIONS_WIDGETS, - { WWT_GROUPBOX, 1, 5, 304, 53, 145, STR_HARDWARE_GROUP, STR_NONE }, // Hardware group + { WWT_GROUPBOX, 1, 5, 304, 53, 160, STR_HARDWARE_GROUP, STR_NONE }, // Hardware group { WWT_DROPDOWN, 1, 155, 299, 68, 79, STR_RESOLUTION_X_BY_Y, STR_NONE }, // resolution { WWT_DROPDOWN_BUTTON, 1, 288, 298, 69, 78, STR_DROPDOWN_GLYPH, STR_NONE }, { WWT_DROPDOWN, 1, 155, 299, 83, 94, 871, STR_NONE }, // fullscreen @@ -176,14 +177,15 @@ static rct_widget window_options_display_widgets[] = { { WWT_CHECKBOX, 1, 10, 290, 99, 110, STR_HARDWARE_DISPLAY, STR_NONE }, // hardware display { WWT_CHECKBOX, 1, 10, 290, 114, 125, STR_UNCAP_FPS, STR_NONE }, // uncap fps { WWT_CHECKBOX, 1, 10, 290, 129, 140, STR_MININISE_FULL_SCREEN_ON_FOCUS_LOSS, STR_NONE }, // minimise fullscreen focus loss + { WWT_CHECKBOX, 1, 10, 290, 144, 155, STR_STEAM_OVERLAY_PAUSE, STR_NONE }, // minimise fullscreen focus loss - { WWT_GROUPBOX, 1, 5, 304, 149, 240, STR_RENDERING_GROUP, STR_NONE }, // Rendering group - { WWT_CHECKBOX, 1, 10, 290, 164, 175, STR_TILE_SMOOTHING, STR_TILE_SMOOTHING_TIP }, // landscape smoothing - { WWT_CHECKBOX, 1, 10, 290, 179, 190, STR_GRIDLINES, STR_GRIDLINES_TIP }, // gridlines - { WWT_DROPDOWN, 1, 155, 299, 193, 204, STR_NONE, STR_NONE }, // construction marker - { WWT_DROPDOWN_BUTTON, 1, 288, 298, 194, 203, STR_DROPDOWN_GLYPH, STR_NONE }, - { WWT_CHECKBOX, 1, 10, 290, 209, 220, STR_CYCLE_DAY_NIGHT, STR_NONE }, // cycle day-night - { WWT_CHECKBOX, 1, 10, 290, 224, 235, STR_UPPER_CASE_BANNERS, STR_NONE }, // upper case banners + { WWT_GROUPBOX, 1, 5, 304, 164, 255, STR_RENDERING_GROUP, STR_NONE }, // Rendering group + { WWT_CHECKBOX, 1, 10, 290, 179, 190, STR_TILE_SMOOTHING, STR_TILE_SMOOTHING_TIP }, // landscape smoothing + { WWT_CHECKBOX, 1, 10, 290, 194, 205, STR_GRIDLINES, STR_GRIDLINES_TIP }, // gridlines + { WWT_DROPDOWN, 1, 155, 299, 208, 219, STR_NONE, STR_NONE }, // construction marker + { WWT_DROPDOWN_BUTTON, 1, 288, 298, 209, 218, STR_DROPDOWN_GLYPH, STR_NONE }, + { WWT_CHECKBOX, 1, 10, 290, 224, 235, STR_CYCLE_DAY_NIGHT, STR_NONE }, // cycle day-night + { WWT_CHECKBOX, 1, 10, 290, 239, 250, STR_UPPER_CASE_BANNERS, STR_NONE }, // upper case banners { WIDGETS_END }, }; @@ -355,6 +357,7 @@ static uint32 window_options_page_enabled_widgets[] = { (1 << WIDX_HARDWARE_DISPLAY_CHECKBOX) | (1 << WIDX_UNCAP_FPS_CHECKBOX) | (1 << WIDX_MINIMIZE_FOCUS_LOSS) | + (1 << WIDX_STEAM_OVERLAY_PAUSE) | (1 << WIDX_CONSTRUCTION_MARKER) | (1 << WIDX_CONSTRUCTION_MARKER_DROPDOWN) | (1 << WIDX_DAY_NIGHT_CHECKBOX) | @@ -502,6 +505,11 @@ static void window_options_mouseup(rct_window *w, int widgetIndex) config_save_default(); window_invalidate(w); break; + case WIDX_STEAM_OVERLAY_PAUSE: + gConfigGeneral.steam_overlay_pause ^= 1; + config_save_default(); + window_invalidate(w); + break; case WIDX_DAY_NIGHT_CHECKBOX: gConfigGeneral.day_night_cycle ^= 1; config_save_default(); @@ -1114,6 +1122,7 @@ static void window_options_invalidate(rct_window *w) widget_set_checkbox_value(w, WIDX_HARDWARE_DISPLAY_CHECKBOX, gConfigGeneral.hardware_display); widget_set_checkbox_value(w, WIDX_UNCAP_FPS_CHECKBOX, gConfigGeneral.uncap_fps); widget_set_checkbox_value(w, WIDX_MINIMIZE_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss); + widget_set_checkbox_value(w, WIDX_STEAM_OVERLAY_PAUSE, gConfigGeneral.steam_overlay_pause); widget_set_checkbox_value(w, WIDX_DAY_NIGHT_CHECKBOX, gConfigGeneral.day_night_cycle); widget_set_checkbox_value(w, WIDX_UPPER_CASE_BANNERS_CHECKBOX, gConfigGeneral.upper_case_banners); @@ -1131,6 +1140,7 @@ static void window_options_invalidate(rct_window *w) window_options_display_widgets[WIDX_HARDWARE_DISPLAY_CHECKBOX].type = WWT_CHECKBOX; window_options_display_widgets[WIDX_UNCAP_FPS_CHECKBOX].type = WWT_CHECKBOX; window_options_display_widgets[WIDX_MINIMIZE_FOCUS_LOSS].type = WWT_CHECKBOX; + window_options_display_widgets[WIDX_STEAM_OVERLAY_PAUSE].type = WWT_CHECKBOX; window_options_display_widgets[WIDX_DAY_NIGHT_CHECKBOX].type = WWT_CHECKBOX; window_options_display_widgets[WIDX_UPPER_CASE_BANNERS_CHECKBOX].type = WWT_CHECKBOX; break;