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
|
|
|
|
|
2020-02-22 23:23:19 +03:00
|
|
|
|
internal override void InitializeInterface( bool server )
|
2019-04-29 14:55:38 +03:00
|
|
|
|
{
|
2020-02-23 14:25:56 +03:00
|
|
|
|
SetInterface( server, new ISteamUtils( server ) );
|
|
|
|
|
InstallEvents( server );
|
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>
|
|
|
|
|
/// The country of the user changed
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static event Action OnIpCountryChanged;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static event Action<int> OnLowBatteryPower;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Called when Steam wants to shutdown
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static event Action OnSteamShutdown;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Big Picture gamepad text input has been closed. Parameter is true if text was submitted, false if cancelled etc.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static event Action<bool> OnGamepadTextInputDismissed;
|
2019-04-14 01:04:03 +03:00
|
|
|
|
|
2019-04-14 23:32:48 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the number of seconds since the application was active
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Returns the number of seconds since the user last moved the mouse etc
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time)
|
|
|
|
|
/// </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>
|
|
|
|
|
/// 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)
|
|
|
|
|
/// </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>
|
|
|
|
|
/// returns the image in RGBA format
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Returns true if we're using a battery (ie, a laptop not plugged in)
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Returns battery power [0-1]
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Activates the Big Picture text input dialog which only supports gamepad input
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Returns previously entered text
|
|
|
|
|
/// </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>
|
|
|
|
|
/// returns the language the steam client is running in, you probably want
|
|
|
|
|
/// Apps.CurrentGameLanguage instead, this is for very special usage cases
|
|
|
|
|
/// </summary>
|
2019-04-15 00:05:51 +03:00
|
|
|
|
public static string SteamUILanguage => Internal.GetSteamUILanguage();
|
2019-04-14 23:21:47 +03:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// returns true if Steam itself is running in VR mode
|
|
|
|
|
/// </summary>
|
2019-04-15 00:05:51 +03:00
|
|
|
|
public static bool IsSteamRunningInVR => Internal.IsSteamRunningInVR();
|
2019-04-14 23:21:47 +03:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition
|
|
|
|
|
/// </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>
|
2019-04-15 13:07:55 +03:00
|
|
|
|
/// returns 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,
|
|
|
|
|
/// a game can be added as a non-steam game to the developers library to test this feature
|
|
|
|
|
/// </summary>
|
2019-04-15 00:05:51 +03:00
|
|
|
|
public static bool IsSteamInBigPictureMode => Internal.IsSteamInBigPictureMode();
|
2019-04-14 23:21:47 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ask SteamUI to create and render its OpenVR dashboard
|
|
|
|
|
/// </summary>
|
2019-04-15 00:05:51 +03:00
|
|
|
|
public static void StartVRDashboard() => Internal.StartVRDashboard();
|
2019-04-14 23:21:47 +03:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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)
|
|
|
|
|
/// </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>
|
|
|
|
|
/// Returns whether this steam client is a Steam China specific client, vs the global client
|
|
|
|
|
/// </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.
|
|
|
|
|
/// Users can customize the text filter behavior in their Steam Account preferences
|
|
|
|
|
/// </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>
|
|
|
|
|
/// returns true if Steam itself is running on the Steam Deck
|
|
|
|
|
/// </summary>
|
2021-09-24 17:47:41 +03:00
|
|
|
|
public static bool IsRunningOnSteamDeck => Internal.IsSteamRunningOnSteamDeck();
|
2021-09-24 16:36:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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
|
|
|
|
|
/// </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
|
|
|
|
}
|