using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Steamworks.Data; namespace Steamworks { /// /// Interface which provides access to a range of miscellaneous utility functions /// public class SteamUtils : SteamSharedClass { internal static ISteamUtils? Internal => Interface as ISteamUtils; internal override bool InitializeInterface( bool server ) { SetInterface( server, new ISteamUtils( server ) ); if ( Interface is null || Interface.Self == IntPtr.Zero ) return false; InstallEvents( server ); return true; } internal static void InstallEvents( bool server ) { Dispatch.Install( x => OnIpCountryChanged?.Invoke(), server ); Dispatch.Install( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ), server ); Dispatch.Install( x => SteamClosed(), server ); Dispatch.Install( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ), server ); } private static void SteamClosed() { SteamClient.Cleanup(); OnSteamShutdown?.Invoke(); } /// /// Invoked when the country of the user changed. /// public static event Action? OnIpCountryChanged; /// /// Invoked when running on a laptop and less than 10 minutes of battery is left, fires then every minute. /// The parameter is the number of minutes left. /// public static event Action? OnLowBatteryPower; /// /// Invoked when Steam wants to shutdown. /// public static event Action? OnSteamShutdown; /// /// Invoked when Big Picture gamepad text input has been closed. Parameter is if text was submitted, if cancelled etc. /// public static event Action? OnGamepadTextInputDismissed; /// /// Returns the number of seconds since the application was active. /// public static uint SecondsSinceAppActive => Internal?.GetSecondsSinceAppActive() ?? 0; /// /// Returns the number of seconds since the user last moved the mouse and/or provided other input. /// public static uint SecondsSinceComputerActive => Internal?.GetSecondsSinceComputerActive() ?? 0; // the universe this client is connecting to public static Universe ConnectedUniverse => Internal?.GetConnectedUniverse() ?? Universe.Invalid; /// /// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time) /// public static DateTime SteamServerTime => Internal != null ? Epoch.ToDateTime( Internal.GetServerRealTime() ) : default; /// /// returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database) /// e.g "US" or "UK". /// public static string? IpCountry => Internal?.GetIPCountry(); /// /// Returns true if the image exists, and the buffer was successfully filled out. /// Results are returned in RGBA format. /// The destination buffer size should be 4 * height * width * sizeof(char). /// public static bool GetImageSize( int image, out uint width, out uint height ) { width = 0; height = 0; return Internal != null && Internal.GetImageSize( image, ref width, ref height ); } /// /// returns the image in RGBA format. /// public static Data.Image? GetImage( int image ) { if ( image == -1 ) return null; if ( image == 0 ) return null; var i = new Data.Image(); if ( !GetImageSize( image, out i.Width, out i.Height ) ) return null; var size = i.Width * i.Height * 4; var buf = Helpers.TakeBuffer( (int) size ); if ( Internal is null || !Internal.GetImageRGBA( image, buf, (int)size ) ) return null; i.Data = new byte[size]; Array.Copy( buf, 0, i.Data, 0, size ); return i; } /// /// Returns true if we're using a battery (ie, a laptop not plugged in). /// public static bool UsingBatteryPower => Internal != null && Internal.GetCurrentBatteryPower() != 255; /// /// Returns battery power [0-1]. /// public static float CurrentBatteryPower => Math.Min( (Internal?.GetCurrentBatteryPower() ?? 0f) / 100, 1.0f ); static NotificationPosition overlayNotificationPosition = NotificationPosition.BottomRight; /// /// Sets the position where the overlay instance for the currently calling game should show notifications. /// This position is per-game and if this function is called from outside of a game context it will do nothing. /// public static NotificationPosition OverlayNotificationPosition { get => overlayNotificationPosition; set { overlayNotificationPosition = value; Internal?.SetOverlayNotificationPosition( value ); } } /// /// Returns true if the overlay is running and the user can access it. The overlay process could take a few seconds to /// start and hook the game process, so this function will initially return false while the overlay is loading. /// public static bool IsOverlayEnabled => Internal != null && Internal.IsOverlayEnabled(); /// /// Normally this call is unneeded if your game has a constantly running frame loop that calls the /// D3D Present API, or OGL SwapBuffers API every frame. /// /// However, if you have a game that only refreshes the screen on an event driven basis then that can break /// the overlay, as it uses your Present/SwapBuffers calls to drive it's internal frame loop and it may also /// need to Present() to the screen any time an even needing a notification happens or when the overlay is /// brought up over the game by a user. You can use this API to ask the overlay if it currently need a present /// in that case, and then you can check for this periodically (roughly 33hz is desirable) and make sure you /// refresh the screen with Present or SwapBuffers to allow the overlay to do it's work. /// public static bool DoesOverlayNeedPresent => Internal != null && Internal.BOverlayNeedsPresent(); /// /// Asynchronous call to check if an executable file has been signed using the public key set on the signing tab /// of the partner site, for example to refuse to load modified executable files. /// public static async Task CheckFileSignatureAsync( string filename ) { if (Internal is null) { throw new System.Exception( "SteamUtils not initialized" ); } var r = await Internal.CheckFileSignature( filename ); if ( !r.HasValue ) { throw new System.Exception( "Something went wrong" ); } return r.Value.CheckFileSignature; } /// /// Activates the Big Picture text input dialog which only supports gamepad input. /// public static bool ShowGamepadTextInput( GamepadTextInputMode inputMode, GamepadTextInputLineMode lineInputMode, string description, int maxChars, string existingText = "" ) { return Internal != null && Internal.ShowGamepadTextInput( inputMode, lineInputMode, description, (uint)maxChars, existingText ); } /// /// Returns previously entered text. /// public static string GetEnteredGamepadText() { if (Internal is null) { return string.Empty; } var len = Internal.GetEnteredGamepadTextLength(); if ( len == 0 ) return string.Empty; if ( !Internal.GetEnteredGamepadTextInput( out var strVal ) ) return string.Empty; return strVal; } /// /// Returns the language the steam client is running in. You probably want /// instead, this is for very special usage cases. /// public static string? SteamUILanguage => Internal?.GetSteamUILanguage(); /// /// Returns if Steam itself is running in VR mode. /// public static bool IsSteamRunningInVR => Internal != null && Internal.IsSteamRunningInVR(); /// /// Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition. /// public static void SetOverlayNotificationInset( int x, int y ) { Internal?.SetOverlayNotificationInset( x, y ); } /// /// returns if Steam and the Steam Overlay are running in Big Picture mode /// Games much be launched through the Steam client to enable the Big Picture overlay. During development, /// a game can be added as a non-steam game to the developers library to test this feature. /// public static bool IsSteamInBigPictureMode => Internal != null && Internal.IsSteamInBigPictureMode(); /// /// Ask Steam UI to create and render its OpenVR dashboard. /// public static void StartVRDashboard() => Internal?.StartVRDashboard(); /// /// Gets or sets whether the HMD content will be streamed via Steam In-Home Streaming. /// /// If this is set to , then the scene in the HMD headset will be streamed, and remote input will not be allowed. /// If this is set to , then the application window will be streamed instead, and remote input will be allowed. /// The default is unless "VRHeadsetStreaming" "0" is in the extended app info for a game /// (this is useful for games that have asymmetric multiplayer gameplay). /// /// public static bool VrHeadsetStreaming { get => Internal != null && Internal.IsVRHeadsetStreamingEnabled(); set { Internal?.SetVRHeadsetStreamingEnabled( value ); } } internal static bool IsCallComplete( SteamAPICall_t call, out bool failed ) { failed = false; return Internal != null && Internal.IsAPICallCompleted( call, ref failed ); } /// /// Gets whether this steam client is a Steam China specific client (), or the global client (). /// public static bool IsSteamChinaLauncher => Internal != null && Internal.IsSteamChinaLauncher(); /// /// Initializes text filtering, loading dictionaries for the language the game is running in. /// Users can customize the text filter behavior in their Steam Account preferences. /// public static bool InitFilterText() => Internal != null && Internal.InitFilterText( 0 ); /// /// Filters the provided input message and places the filtered result into pchOutFilteredText, /// using legally required filtering and additional filtering based on the context and user settings. /// public static string FilterText( TextFilteringContext context, SteamId sourceSteamID, string inputMessage ) { if ( Internal is null ) { return inputMessage; } Internal.FilterText( context, sourceSteamID, inputMessage, out var filteredString ); return filteredString; } /// /// Gets whether or not Steam itself is running on the Steam Deck. /// public static bool IsRunningOnSteamDeck => Internal != null && Internal.IsSteamRunningOnSteamDeck(); /// /// In game launchers that don't have controller support: You can call this to have /// Steam Input translate the controller input into mouse/kb to navigate the launcher /// public static void SetGameLauncherMode( bool mode ) => Internal?.SetGameLauncherMode( mode ); //public void ShowFloatingGamepadTextInput( TextInputMode mode, int left, int top, int width, int height ) //{ // Internal.ShowFloatingGamepadTextInput( mode, left, top, width, height ); //} } }