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 static class SteamUtils { static ISteamUtils _internal; internal static ISteamUtils Internal { get { if ( _internal == null ) { _internal = new ISteamUtils(); _internal.Init(); } return _internal; } } internal static void Shutdown() { _internal = null; } internal static void InstallEvents() { IPCountry_t.Install( x => OnIpCountryChanged?.Invoke() ); LowBatteryPower_t.Install( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ) ); SteamShutdown_t.Install( x => OnSteamShutdown?.Invoke() ); GamepadTextInputDismissed_t.Install( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ) ); } /// /// The country of the user changed /// public static event Action OnIpCountryChanged; /// /// Fired 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; /// /// Called when Steam wants to shutdown /// public static event Action OnSteamShutdown; /// /// Big Picture gamepad text input has been closed. Parameter is true if text was submitted, false if cancelled etc. /// public static event Action OnGamepadTextInputDismissed; /// /// Returns the number of seconds since the application was active /// public static uint SecondsSinceAppActive => Internal.GetSecondsSinceAppActive(); /// /// Returns the number of seconds since the user last moved the mouse etc /// public static uint SecondsSinceComputerActive => Internal.GetSecondsSinceComputerActive(); // the universe this client is connecting to public static Universe ConnectedUniverse => Internal.GetConnectedUniverse(); /// /// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time) /// public static DateTime SteamServerTime => Epoch.ToDateTime( Internal.GetServerRealTime() ); /// /// 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.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.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.GetCurrentBatteryPower() != 255; /// /// Returns battery power [0-1] /// public static float CurrentBatteryPower => Math.Min( Internal.GetCurrentBatteryPower() / 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.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.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 ) { 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.ShowGamepadTextInput( inputMode, lineInputMode, description, (uint)maxChars, existingText ); } /// /// Returns previously entered text /// public static string GetEnteredGamepadText() { var len = Internal.GetEnteredGamepadTextLength(); if ( len == 0 ) return string.Empty; var sb = Helpers.TakeStringBuilder(); if ( !Internal.GetEnteredGamepadTextInput( sb, len ) ) return string.Empty; return sb.ToString(); } /// /// returns the language the steam client is running in, you probably want /// Apps.CurrentGameLanguage instead, this is for very special usage cases /// public static string SteamUILanguage => Internal.GetSteamUILanguage(); /// /// returns true if Steam itself is running in VR mode /// public static bool IsSteamRunningInVR => 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 true 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.IsSteamInBigPictureMode(); /// /// ask SteamUI to create and render its OpenVR dashboard /// public static void StartVRDashboard() => Internal.StartVRDashboard(); /// /// Set whether the HMD content will be streamed via Steam In-Home Streaming /// If this is set to true, then the scene in the HMD headset will be streamed, and remote input will not be allowed. /// If this is set to false, then the application window will be streamed instead, and remote input will be allowed. /// The default is true unless "VRHeadsetStreaming" "0" is in the extended appinfo for a game. /// (this is useful for games that have asymmetric multiplayer gameplay) /// public static bool VrHeadsetStreaming { get => Internal.IsVRHeadsetStreamingEnabled(); set { Internal.SetVRHeadsetStreamingEnabled( value ); } } internal static bool IsCallComplete( SteamAPICall_t call, out bool failed ) { failed = false; return Internal.IsAPICallCompleted( call, ref failed ); } } }