libretro_core/
callbacks.rs1use crate::raw;
8
9#[derive(Clone, Copy, Debug)]
10pub struct CoreProcAddress {
11 raw: raw::retro_proc_address_t,
12}
13
14impl CoreProcAddress {
15 pub const fn from_fn(function: unsafe extern "C" fn()) -> Self {
16 Self {
17 raw: Some(function),
18 }
19 }
20
21 pub(crate) const fn as_raw(self) -> raw::retro_proc_address_t {
22 self.raw
23 }
24}
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
27pub enum AudioCallbackState {
28 #[default]
29 Inactive,
30 Active,
31}
32
33impl AudioCallbackState {
34 pub const fn from_active(active: bool) -> Self {
35 if active { Self::Active } else { Self::Inactive }
36 }
37
38 pub const fn is_active(self) -> bool {
39 matches!(self, Self::Active)
40 }
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
44pub struct AudioBufferOccupancy(u32);
45
46impl AudioBufferOccupancy {
47 pub const fn from_percent(percent: u8) -> Option<Self> {
48 if percent <= 100 {
49 Some(Self(percent as u32))
50 } else {
51 None
52 }
53 }
54
55 pub const fn from_raw_percent(percent: u32) -> Self {
56 Self(percent)
57 }
58
59 pub const fn raw_percent(self) -> u32 {
60 self.0
61 }
62
63 pub const fn percent(self) -> Option<u8> {
64 if self.0 <= 100 {
65 Some(self.0 as u8)
66 } else {
67 None
68 }
69 }
70}
71
72#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
73pub struct AudioBufferStatus {
74 pub active: bool,
75 pub occupancy: AudioBufferOccupancy,
76 pub underrun_likely: bool,
77}
78
79impl AudioBufferStatus {
80 pub const fn new(active: bool, occupancy: AudioBufferOccupancy, underrun_likely: bool) -> Self {
81 Self {
82 active,
83 occupancy,
84 underrun_likely,
85 }
86 }
87
88 pub const fn from_raw(active: bool, occupancy: u32, underrun_likely: bool) -> Self {
89 Self::new(
90 active,
91 AudioBufferOccupancy::from_raw_percent(occupancy),
92 underrun_likely,
93 )
94 }
95}
96
97#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
98pub struct FrameTime {
99 micros: i64,
100}
101
102impl FrameTime {
103 pub const fn from_micros(micros: i64) -> Self {
104 Self { micros }
105 }
106
107 pub const fn as_micros(self) -> i64 {
108 self.micros
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::{
115 AudioBufferOccupancy, AudioBufferStatus, AudioCallbackState, CoreProcAddress, FrameTime,
116 };
117
118 unsafe extern "C" fn test_proc_address() {}
119
120 #[test]
121 fn core_proc_address_wraps_type_erased_function_pointer() {
122 let address = CoreProcAddress::from_fn(test_proc_address)
123 .as_raw()
124 .expect("function pointer should be present");
125 let expected: unsafe extern "C" fn() = test_proc_address;
126
127 assert!(std::ptr::fn_addr_eq(address, expected));
128 }
129
130 #[test]
131 fn audio_callback_state_encodes_frontend_active_flag() {
132 assert_eq!(
133 AudioCallbackState::from_active(false),
134 AudioCallbackState::Inactive
135 );
136 assert_eq!(
137 AudioCallbackState::from_active(true),
138 AudioCallbackState::Active
139 );
140 assert!(!AudioCallbackState::Inactive.is_active());
141 assert!(AudioCallbackState::Active.is_active());
142 }
143
144 #[test]
145 fn audio_buffer_occupancy_accepts_libretro_percent_range() {
146 assert_eq!(
147 AudioBufferOccupancy::from_percent(100)
148 .expect("100 percent is valid")
149 .percent(),
150 Some(100)
151 );
152 assert_eq!(AudioBufferOccupancy::from_percent(101), None);
153 }
154
155 #[test]
156 fn audio_buffer_status_preserves_out_of_range_frontend_values() {
157 let status = AudioBufferStatus::from_raw(true, 150, true);
158
159 assert_eq!(status.occupancy.raw_percent(), 150);
160 assert_eq!(status.occupancy.percent(), None);
161 }
162
163 #[test]
164 fn frame_time_preserves_signed_libretro_values() {
165 assert_eq!(FrameTime::from_micros(16_667).as_micros(), 16_667);
166 assert_eq!(FrameTime::from_micros(-1).as_micros(), -1);
167 }
168}