Skip to content

Commit

Permalink
Merge pull request #178 from PounceLight/basic-screenshots-api
Browse files Browse the repository at this point in the history
Basic screenshots API
  • Loading branch information
Noxime authored Jun 28, 2024
2 parents 08050e8 + 5262af0 commit 07cf098
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extern crate bitflags;
#[macro_use]
extern crate lazy_static;

use screenshots::Screenshots;
#[cfg(feature = "raw-bindings")]
pub use steamworks_sys as sys;
#[cfg(not(feature = "raw-bindings"))]
Expand Down Expand Up @@ -51,6 +52,7 @@ pub mod networking_types;
pub mod networking_utils;
mod remote_play;
mod remote_storage;
pub mod screenshots;
mod server;
mod ugc;
mod user;
Expand Down Expand Up @@ -435,6 +437,18 @@ impl<Manager> Client<Manager> {
}
}

/// Returns an accessor to the steam screenshots interface
pub fn screenshots(&self) -> Screenshots<Manager> {
unsafe {
let screenshots = sys::SteamAPI_SteamScreenshots_v003();
debug_assert!(!screenshots.is_null());
Screenshots {
screenshots,
_inner: self.inner.clone(),
}
}
}

/// Returns an accessor to the steam UGC interface (steam workshop)
pub fn ugc(&self) -> UGC<Manager> {
unsafe {
Expand Down
154 changes: 154 additions & 0 deletions src/screenshots.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::path::Path;

pub use sys::ScreenshotHandle;

use super::*;

/// Access to the steam screenshots interface
pub struct Screenshots<Manager> {
pub(crate) screenshots: *mut sys::ISteamScreenshots,
pub(crate) _inner: Arc<Inner<Manager>>,
}

impl<Manager> Screenshots<Manager> {
/// Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or if the game handles them.
///
/// Hooking is disabled by default, and only ever enabled if you do so with this function.
///
/// If the hooking is enabled, then the [`ScreenshotRequested`] callback will be sent if the user presses the hotkey or when [`Self::trigger_screenshot`] is called,
/// and then the game is expected to call `WriteScreenshot` or [`Self::add_screenshot_to_library`] in response.
///
/// You can check if hooking is enabled with [`Self::is_screenshots_hooked`].
pub fn hook_screenshots(&self, hook: bool) {
unsafe {
sys::SteamAPI_ISteamScreenshots_HookScreenshots(self.screenshots, hook);
}
}

/// Checks if the app is hooking screenshots, or if the Steam Overlay is handling them.
///
/// This can be toggled with [`Self::hook_screenshots`].
///
/// Returns
/// - `true` if the game is hooking screenshots and is expected to handle them; otherwise, `false`.
pub fn is_screenshots_hooked(&self) -> bool {
unsafe { sys::SteamAPI_ISteamScreenshots_IsScreenshotsHooked(self.screenshots) }
}

/// Either causes the Steam Overlay to take a screenshot, or tells your screenshot manager that a screenshot needs to be taken.
/// Depending on the value of [`Self::is_screenshots_hooked`].
///
/// - Triggers a [`ScreenshotRequested`] callback.
/// - Triggers a [`ScreenshotReady`] callback.
/// - Only causes [`ScreenshotRequested`] if hooking has been enabled with [`Self::hook_screenshots`].
/// - Otherwise [`ScreenshotReady`] will be called when the screenshot has been saved and added to the library.
pub fn trigger_screenshot(&self) {
unsafe {
sys::SteamAPI_ISteamScreenshots_TriggerScreenshot(self.screenshots);
}
}

/// Adds a screenshot to the user's Steam screenshot library from disk.
///
/// Triggers a [`ScreenshotReady`] callback.
/// The handle to this screenshot that is valid for the duration of the game process and can be used to apply tags.
///
/// This call is asynchronous, a [`ScreenshotReady`] callback will be sent when the screenshot has finished writing to disk.
pub fn add_screenshot_to_library(
&self,
filename: &Path,
thumbnail_filename: Option<&Path>,
width: i32,
height: i32,
) -> Result<ScreenshotHandle, ScreenshotLibraryAddError> {
let filename =
path_to_absolute_cstring(filename).ok_or(ScreenshotLibraryAddError::InvalidPath)?;

let thumbnail_filename = if let Some(thumbnail_filename) = thumbnail_filename {
Some(
path_to_absolute_cstring(thumbnail_filename)
.ok_or(ScreenshotLibraryAddError::InvalidPath)?,
)
} else {
None
};

unsafe {
let handle = sys::SteamAPI_ISteamScreenshots_AddScreenshotToLibrary(
self.screenshots,
filename.as_ptr(),
thumbnail_filename.map_or(std::ptr::null(), |s| s.as_ptr()),
width,
height,
);

if handle != sys::INVALID_SCREENSHOT_HANDLE {
Ok(handle)
} else {
Err(ScreenshotLibraryAddError::SavingFailed)
}
}
}
}

#[derive(Debug, Error)]
pub enum ScreenshotLibraryAddError {
/// Steam failed to save the file for an unspecified reason.
#[error("The screenshot file could not be saved")]
SavingFailed,
/// One of the paths provided was invalid.
#[error("Invalid path")]
InvalidPath,
}

/// A screenshot has been requested by the user from the Steam screenshot hotkey.
/// This will only be called if [`Screenshots::hook_screenshots`] has been enabled, in which case Steam will not take the screenshot itself.
#[derive(Clone, Debug)]
pub struct ScreenshotRequested;

unsafe impl Callback for ScreenshotRequested {
const ID: i32 = sys::ScreenshotRequested_t__bindgen_ty_1::k_iCallback as _;
const SIZE: i32 = std::mem::size_of::<sys::ScreenshotRequested_t>() as _;

unsafe fn from_raw(_: *mut c_void) -> Self {
Self
}
}

#[derive(Clone, Debug, Error)]
pub enum ScreenshotReadyError {
/// The screenshot could not be loaded or parsed.
#[error("The screenshot could not be loaded or parsed")]
Fail,
/// The screenshot could not be saved to the disk.
#[error("The screenshot could not be saved to the disk")]
IoFailure,
}

/// A screenshot successfully written or otherwise added to the library and can now be tagged.
#[derive(Clone, Debug)]
pub struct ScreenshotReady {
/// The screenshot handle that has been written.
pub local_handle: Result<ScreenshotHandle, ScreenshotReadyError>,
}

unsafe impl Callback for ScreenshotReady {
const ID: i32 = sys::ScreenshotReady_t__bindgen_ty_1::k_iCallback as _;
const SIZE: i32 = std::mem::size_of::<sys::ScreenshotReady_t>() as _;

unsafe fn from_raw(raw: *mut c_void) -> Self {
let status = *(raw as *mut sys::ScreenshotReady_t);
let local_handle = match status.m_eResult {
sys::EResult::k_EResultOK => Ok(status.m_hLocal),
sys::EResult::k_EResultIOFailure => Err(ScreenshotReadyError::Fail),
_ => Err(ScreenshotReadyError::Fail),
};

Self { local_handle }
}
}

fn path_to_absolute_cstring(filename: &Path) -> Option<CString> {
let filename = filename.canonicalize().ok()?;
Some(CString::new(filename.to_str()?).unwrap())
}

0 comments on commit 07cf098

Please sign in to comment.