2019-04-13 00:53:46 +03:00
using System ;
using System.Collections.Generic ;
using System.Runtime.InteropServices ;
using System.Text ;
using System.Threading.Tasks ;
2019-04-16 17:00:22 +03:00
using Steamworks.Data ;
2019-04-13 00:53:46 +03:00
namespace Steamworks
{
/// <summary>
/// Interface which provides access to a range of miscellaneous utility functions
/// </summary>
2020-02-23 21:34:56 +03:00
public class SteamUtils : SteamSharedClass < SteamUtils >
2019-04-13 00:53:46 +03:00
{
2020-02-23 14:25:56 +03:00
internal static ISteamUtils Internal = > Interface as ISteamUtils ;
2019-04-13 00:53:46 +03:00
2022-10-12 08:37:19 +03:00
internal override bool InitializeInterface ( bool server )
2019-04-29 14:55:38 +03:00
{
2020-02-23 14:25:56 +03:00
SetInterface ( server , new ISteamUtils ( server ) ) ;
2022-10-12 08:37:19 +03:00
if ( Interface . Self = = IntPtr . Zero ) return false ;
2020-02-23 14:25:56 +03:00
InstallEvents ( server ) ;
2022-10-12 08:37:19 +03:00
return true ;
2019-04-29 14:55:38 +03:00
}
2020-02-23 14:25:56 +03:00
internal static void InstallEvents ( bool server )
2019-04-14 23:21:47 +03:00
{
2020-02-23 14:25:56 +03:00
Dispatch . Install < IPCountry_t > ( x = > OnIpCountryChanged ? . Invoke ( ) , server ) ;
Dispatch . Install < LowBatteryPower_t > ( x = > OnLowBatteryPower ? . Invoke ( x . MinutesBatteryLeft ) , server ) ;
Dispatch . Install < SteamShutdown_t > ( x = > SteamClosed ( ) , server ) ;
Dispatch . Install < GamepadTextInputDismissed_t > ( x = > OnGamepadTextInputDismissed ? . Invoke ( x . Submitted ) , server ) ;
2019-04-14 23:21:47 +03:00
}
2019-07-02 16:12:55 +03:00
private static void SteamClosed ( )
{
SteamClient . Cleanup ( ) ;
OnSteamShutdown ? . Invoke ( ) ;
}
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Invoked when the country of the user changed.
2019-04-14 23:21:47 +03:00
/// </summary>
public static event Action OnIpCountryChanged ;
/// <summary>
2021-10-21 10:40:56 +03:00
/// 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.
2019-04-14 23:21:47 +03:00
/// </summary>
public static event Action < int > OnLowBatteryPower ;
/// <summary>
2021-10-21 10:40:56 +03:00
/// Invoked when Steam wants to shutdown.
2019-04-14 23:21:47 +03:00
/// </summary>
public static event Action OnSteamShutdown ;
/// <summary>
2021-10-21 10:40:56 +03:00
/// Invoked when Big Picture gamepad text input has been closed. Parameter is <see langword="true"/> if text was submitted, <see langword="false"/> if cancelled etc.
2019-04-14 23:21:47 +03:00
/// </summary>
public static event Action < bool > OnGamepadTextInputDismissed ;
2019-04-14 01:04:03 +03:00
2019-04-14 23:32:48 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns the number of seconds since the application was active.
2019-04-14 23:32:48 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static uint SecondsSinceAppActive = > Internal . GetSecondsSinceAppActive ( ) ;
2019-04-14 23:21:47 +03:00
2019-04-14 23:32:48 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns the number of seconds since the user last moved the mouse and/or provided other input.
2019-04-14 23:32:48 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static uint SecondsSinceComputerActive = > Internal . GetSecondsSinceComputerActive ( ) ;
2019-04-14 01:04:03 +03:00
// the universe this client is connecting to
2019-04-15 00:05:51 +03:00
public static Universe ConnectedUniverse = > Internal . GetConnectedUniverse ( ) ;
2019-04-14 01:04:03 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time)
2019-04-14 01:04:03 +03:00
/// </summary>
2019-04-16 16:40:59 +03:00
public static DateTime SteamServerTime = > Epoch . ToDateTime ( Internal . GetServerRealTime ( ) ) ;
2019-04-14 01:04:03 +03:00
/// <summary>
/// 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".
/// </summary>
2019-04-15 00:05:51 +03:00
public static string IpCountry = > Internal . GetIPCountry ( ) ;
2019-04-14 01:04:03 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// 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).
2019-04-14 01:04:03 +03:00
/// </summary>
public static bool GetImageSize ( int image , out uint width , out uint height )
{
width = 0 ;
height = 0 ;
2019-04-15 00:05:51 +03:00
return Internal . GetImageSize ( image , ref width , ref height ) ;
2019-04-14 01:04:03 +03:00
}
2019-04-14 22:52:51 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// returns the image in RGBA format.
2019-04-14 22:52:51 +03:00
/// </summary>
2019-04-16 16:38:10 +03:00
public static Data . Image ? GetImage ( int image )
2019-04-14 22:52:51 +03:00
{
2019-05-01 15:22:06 +03:00
if ( image = = - 1 ) return null ;
2019-05-01 13:47:50 +03:00
if ( image = = 0 ) return null ;
2019-04-16 16:38:10 +03:00
var i = new Data . Image ( ) ;
2019-04-15 18:43:55 +03:00
if ( ! GetImageSize ( image , out i . Width , out i . Height ) )
2019-04-14 22:52:51 +03:00
return null ;
2019-04-15 18:43:55 +03:00
var size = i . Width * i . Height * 4 ;
var buf = Helpers . TakeBuffer ( ( int ) size ) ;
2019-04-14 22:52:51 +03:00
2019-04-15 18:43:55 +03:00
if ( ! Internal . GetImageRGBA ( image , buf , ( int ) size ) )
2019-04-14 22:52:51 +03:00
return null ;
2019-04-15 18:43:55 +03:00
i . Data = new byte [ size ] ;
Array . Copy ( buf , 0 , i . Data , 0 , size ) ;
return i ;
2019-04-14 22:52:51 +03:00
}
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns true if we're using a battery (ie, a laptop not plugged in).
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static bool UsingBatteryPower = > Internal . GetCurrentBatteryPower ( ) ! = 255 ;
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns battery power [0-1].
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static float CurrentBatteryPower = > Math . Min ( Internal . GetCurrentBatteryPower ( ) / 100 , 1.0f ) ;
2019-04-14 23:21:47 +03:00
static NotificationPosition overlayNotificationPosition = NotificationPosition . BottomRight ;
/// <summary>
/// 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.
/// </summary>
public static NotificationPosition OverlayNotificationPosition
{
get = > overlayNotificationPosition ;
set
{
overlayNotificationPosition = value ;
2019-04-15 00:05:51 +03:00
Internal . SetOverlayNotificationPosition ( value ) ;
2019-04-14 23:21:47 +03:00
}
}
/// <summary>
2019-04-14 23:32:48 +03:00
/// 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.
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static bool IsOverlayEnabled = > Internal . IsOverlayEnabled ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
/// 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.
/// </summary>
2019-04-15 00:05:51 +03:00
public static bool DoesOverlayNeedPresent = > Internal . BOverlayNeedsPresent ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
/// 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.
/// </summary>
2019-04-30 16:27:45 +03:00
public static async Task < CheckFileSignature > CheckFileSignatureAsync ( string filename )
2019-04-14 23:21:47 +03:00
{
2019-04-15 00:05:51 +03:00
var r = await Internal . CheckFileSignature ( filename ) ;
2019-04-14 23:21:47 +03:00
if ( ! r . HasValue )
{
throw new System . Exception ( "Something went wrong" ) ;
}
return r . Value . CheckFileSignature ;
}
/// <summary>
2021-10-21 10:40:56 +03:00
/// Activates the Big Picture text input dialog which only supports gamepad input.
2019-04-14 23:21:47 +03:00
/// </summary>
public static bool ShowGamepadTextInput ( GamepadTextInputMode inputMode , GamepadTextInputLineMode lineInputMode , string description , int maxChars , string existingText = "" )
{
2019-04-15 00:05:51 +03:00
return Internal . ShowGamepadTextInput ( inputMode , lineInputMode , description , ( uint ) maxChars , existingText ) ;
2019-04-14 23:21:47 +03:00
}
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns previously entered text.
2019-04-14 23:21:47 +03:00
/// </summary>
public static string GetEnteredGamepadText ( )
{
2019-04-15 00:05:51 +03:00
var len = Internal . GetEnteredGamepadTextLength ( ) ;
2019-04-14 23:21:47 +03:00
if ( len = = 0 ) return string . Empty ;
2019-08-14 16:39:04 +03:00
if ( ! Internal . GetEnteredGamepadTextInput ( out var strVal ) )
2019-04-14 23:21:47 +03:00
return string . Empty ;
2019-08-14 16:39:04 +03:00
return strVal ;
2019-04-14 23:21:47 +03:00
}
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns the language the steam client is running in. You probably want
/// <see cref="SteamApps.GameLanguage"/> instead, this is for very special usage cases.
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static string SteamUILanguage = > Internal . GetSteamUILanguage ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Returns <see langword="true"/> if Steam itself is running in VR mode.
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static bool IsSteamRunningInVR = > Internal . IsSteamRunningInVR ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition.
2019-04-14 23:21:47 +03:00
/// </summary>
public static void SetOverlayNotificationInset ( int x , int y )
{
2019-04-15 00:05:51 +03:00
Internal . SetOverlayNotificationInset ( x , y ) ;
2019-04-14 23:21:47 +03:00
}
/// <summary>
2021-10-21 10:40:56 +03:00
/// returns <see langword="true"/> if Steam and the Steam Overlay are running in Big Picture mode
2019-04-14 23:21:47 +03:00
/// Games much be launched through the Steam client to enable the Big Picture overlay. During development,
2021-10-21 10:40:56 +03:00
/// a game can be added as a non-steam game to the developers library to test this feature.
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static bool IsSteamInBigPictureMode = > Internal . IsSteamInBigPictureMode ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Ask Steam UI to create and render its OpenVR dashboard.
2019-04-14 23:21:47 +03:00
/// </summary>
2019-04-15 00:05:51 +03:00
public static void StartVRDashboard ( ) = > Internal . StartVRDashboard ( ) ;
2019-04-14 23:21:47 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Gets or sets whether the HMD content will be streamed via Steam In-Home Streaming.
/// <para>
/// If this is set to <see langword="true"/>, then the scene in the HMD headset will be streamed, and remote input will not be allowed.
/// If this is set to <see langword="false"/>, then the application window will be streamed instead, and remote input will be allowed.
/// The default is <see langword="true"/> unless "VRHeadsetStreaming" "0" is in the extended app info for a game
/// (this is useful for games that have asymmetric multiplayer gameplay).
/// </para>
2019-04-14 23:21:47 +03:00
/// </summary>
public static bool VrHeadsetStreaming
{
2019-04-15 00:05:51 +03:00
get = > Internal . IsVRHeadsetStreamingEnabled ( ) ;
2019-04-14 23:21:47 +03:00
set
{
2019-04-15 00:05:51 +03:00
Internal . SetVRHeadsetStreamingEnabled ( value ) ;
2019-04-14 23:21:47 +03:00
}
}
2019-04-13 00:53:46 +03:00
internal static bool IsCallComplete ( SteamAPICall_t call , out bool failed )
{
failed = false ;
2019-04-15 00:05:51 +03:00
return Internal . IsAPICallCompleted ( call , ref failed ) ;
2019-04-13 00:53:46 +03:00
}
2020-02-23 22:14:27 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Gets whether this steam client is a Steam China specific client (<see langword="true"/>), or the global client (<see langword="false"/>).
2020-02-23 22:14:27 +03:00
/// </summary>
public static bool IsSteamChinaLauncher = > Internal . IsSteamChinaLauncher ( ) ;
2021-01-05 20:28:45 +03:00
/// <summary>
/// Initializes text filtering, loading dictionaries for the language the game is running in.
2021-10-21 10:40:56 +03:00
/// Users can customize the text filter behavior in their Steam Account preferences.
2021-01-05 20:28:45 +03:00
/// </summary>
2021-01-09 16:05:56 +03:00
public static bool InitFilterText ( ) = > Internal . InitFilterText ( 0 ) ;
2021-01-05 20:28:45 +03:00
/// <summary>
/// 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.
/// </summary>
2021-09-24 17:47:41 +03:00
public static string FilterText ( TextFilteringContext context , SteamId sourceSteamID , string inputMessage )
2021-01-05 20:28:45 +03:00
{
2021-01-09 16:05:56 +03:00
Internal . FilterText ( context , sourceSteamID , inputMessage , out var filteredString ) ;
return filteredString ;
2021-01-05 20:28:45 +03:00
}
2021-09-24 16:29:58 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// Gets whether or not Steam itself is running on the Steam Deck.
2021-09-24 16:29:58 +03:00
/// </summary>
2021-09-24 17:47:41 +03:00
public static bool IsRunningOnSteamDeck = > Internal . IsSteamRunningOnSteamDeck ( ) ;
2021-09-24 16:36:48 +03:00
/// <summary>
2021-10-21 10:40:56 +03:00
/// In game launchers that don't have controller support: You can call this to have
2021-09-24 16:36:48 +03:00
/// Steam Input translate the controller input into mouse/kb to navigate the launcher
/// </summary>
2021-09-24 17:47:41 +03:00
public static void SetGameLauncherMode ( bool mode ) = > Internal . SetGameLauncherMode ( mode ) ;
2021-09-24 16:36:48 +03:00
//public void ShowFloatingGamepadTextInput( TextInputMode mode, int left, int top, int width, int height )
//{
// Internal.ShowFloatingGamepadTextInput( mode, left, top, width, height );
//}
2019-04-13 00:53:46 +03:00
}
2021-09-24 16:29:58 +03:00
}