rust_libretro/
contexts.rs

1//! This module contains abstractions of the libretro environment callbacks.
2use crate::core_wrapper::Interfaces;
3use once_cell::unsync::Lazy;
4use std::collections::HashMap;
5
6use super::*;
7
8/// This would only be used in [`Core::on_run`] from a single thread.
9static mut FALLBACK_FRAMEBUFFER: Lazy<Vec<u8>> = Lazy::new(Vec::new);
10
11#[doc(hidden)]
12macro_rules! into_generic {
13    ($type:ty, $lifetime:tt) => {
14        into_generic!($type, GenericContext, $lifetime);
15    };
16    ($type:ty, $other:ident, $lifetime:tt) => {
17        impl<$lifetime> From<&$type> for $other<$lifetime> {
18            fn from(other: &$type) -> $other<$lifetime> {
19                $other::new(other.environment_callback, Arc::clone(&other.interfaces))
20            }
21        }
22
23        impl<$lifetime> From<&mut $type> for $other<$lifetime> {
24            fn from(other: &mut $type) -> $other<$lifetime> {
25                $other::new(other.environment_callback, Arc::clone(&other.interfaces))
26            }
27        }
28    };
29}
30
31#[doc(hidden)]
32macro_rules! make_context {
33    ($name:ident $(, #[doc = $doc:tt ])?) => {
34        $(#[doc = $doc])?
35        pub struct $name<'a> {
36            pub(crate) environment_callback: &'a retro_environment_t,
37            pub(crate) interfaces: Interfaces,
38        }
39
40        impl<'a> $name<'a> {
41            pub(crate) fn new(environment_callback: &'a retro_environment_t, interfaces: Interfaces) -> Self {
42                Self {
43                    environment_callback,
44                    interfaces
45                }
46            }
47        }
48
49        into_generic!($name<'a>, 'a);
50    };
51}
52
53/// Exposes environment callbacks that are safe to call in every context.
54pub struct GenericContext<'a> {
55    pub(crate) environment_callback: &'a retro_environment_t,
56    pub(crate) interfaces: Interfaces,
57}
58
59impl<'a> GenericContext<'a> {
60    pub(crate) fn new(
61        environment_callback: &'a retro_environment_t,
62        interfaces: Interfaces,
63    ) -> Self {
64        Self {
65            environment_callback,
66            interfaces,
67        }
68    }
69
70    pub unsafe fn environment_callback(&self) -> &'a retro_environment_t {
71        self.environment_callback
72    }
73
74    pub unsafe fn interfaces(&self) -> Interfaces {
75        Arc::clone(&self.interfaces)
76    }
77
78    /// Enables the [`Core::on_keyboard_event`] callback.
79    pub fn enable_keyboard_callback(&self) -> bool {
80        self.set_keyboard_callback(retro_keyboard_callback {
81            callback: Some(retro_keyboard_callback_fn),
82        })
83    }
84
85    /// Enables the [`Core::on_write_audio`] and [`Core::on_audio_set_state`] callbacks.
86    pub fn enable_audio_callback(&self) -> bool {
87        self.set_audio_callback(retro_audio_callback {
88            callback: Some(retro_audio_callback_fn),
89            set_state: Some(retro_audio_set_state_callback_fn),
90        })
91    }
92
93    pub fn enable_disk_control_interface(&self) -> bool {
94        self.set_disk_control_interface(retro_disk_control_callback {
95            set_eject_state: Some(retro_set_eject_state_callback),
96            get_eject_state: Some(retro_get_eject_state_callback),
97            get_image_index: Some(retro_get_image_index_callback),
98            set_image_index: Some(retro_set_image_index_callback),
99            get_num_images: Some(retro_get_num_images_callback),
100            replace_image_index: Some(retro_replace_image_index_callback),
101            add_image_index: Some(retro_add_image_index_callback),
102        })
103    }
104
105    pub fn enable_extended_disk_control_interface(&self) -> Result<(), Box<dyn std::error::Error>> {
106        if self.get_disk_control_interface_version() >= 1 {
107            let success = self.set_disk_control_ext_interface(retro_disk_control_ext_callback {
108                set_eject_state: Some(retro_set_eject_state_callback),
109                get_eject_state: Some(retro_get_eject_state_callback),
110                get_image_index: Some(retro_get_image_index_callback),
111                set_image_index: Some(retro_set_image_index_callback),
112                get_num_images: Some(retro_get_num_images_callback),
113                replace_image_index: Some(retro_replace_image_index_callback),
114                add_image_index: Some(retro_add_image_index_callback),
115
116                set_initial_image: Some(retro_set_initial_image_callback),
117                get_image_path: Some(retro_get_image_path_callback),
118                get_image_label: Some(retro_get_image_label_callback),
119            });
120
121            if !success {
122                return Err("Failed to enable the extended disk control interface.".into());
123            }
124        } else {
125            return Err("The extended disk control interface is unsupported.".into());
126        }
127
128        Ok(())
129    }
130
131    pub fn enable_audio_buffer_status_callback(&self) -> bool {
132        let data = retro_audio_buffer_status_callback {
133            callback: Some(retro_audio_buffer_status_callback_fn),
134        };
135
136        self.set_audio_buffer_status_callback(data)
137    }
138
139    #[proc::unstable(feature = "env-commands")]
140    pub fn set_led_state(&self, led: i32, state: i32) {
141        let interfaces = self.interfaces.read().unwrap();
142
143        if let Some(interface) = interfaces.led_interface {
144            if let Some(set_led_state) = interface.set_led_state {
145                unsafe { set_led_state(led, state) };
146            }
147        }
148    }
149
150    pub fn set_rumble_state(&self, port: u32, effect: retro_rumble_effect, strength: u16) -> bool {
151        let interfaces = self.interfaces.read().unwrap();
152
153        if let Some(interface) = interfaces.rumble_interface {
154            if let Some(set_rumble_state) = interface.set_rumble_state {
155                return unsafe { set_rumble_state(port, effect, strength) };
156            }
157        }
158
159        false
160    }
161
162    pub fn start_perf_counter(
163        &mut self,
164        name: &'static str,
165    ) -> Result<(), Box<dyn std::error::Error>> {
166        use std::collections::hash_map::Entry;
167
168        let mut interfaces = self.interfaces.write().unwrap();
169
170        let interface = interfaces
171            .perf_interface
172            .interface
173            .ok_or("Performance interface not found, did you call `enable_perf_interface()`?")?;
174
175        let counter = match interfaces.perf_interface.counters.entry(name) {
176            Entry::Occupied(counter) => counter.into_mut(),
177            Entry::Vacant(entry) => {
178                let ident = CString::new(name)?;
179                let ptr = ident.as_ptr();
180
181                entry.insert(PerfCounter {
182                    ident,
183                    counter: retro_perf_counter {
184                        ident: ptr,
185                        start: 0,
186                        total: 0,
187                        call_cnt: 0,
188                        registered: false,
189                    },
190                })
191            }
192        };
193
194        if !counter.counter.registered {
195            let register = interface
196                .perf_register
197                .ok_or("`perf_register()` is missing on the performance interface")?;
198
199            unsafe {
200                register(&mut counter.counter as *mut _);
201            }
202        }
203
204        let start = interface
205            .perf_start
206            .ok_or("`perf_start()` is missing on the performance interface")?;
207
208        unsafe {
209            start(&mut counter.counter as *mut _);
210        }
211
212        Ok(())
213    }
214
215    pub fn stop_perf_counter(
216        &mut self,
217        name: &'static str,
218    ) -> Result<(), Box<dyn std::error::Error>> {
219        use std::collections::hash_map::Entry;
220
221        let mut interfaces = self.interfaces.write().unwrap();
222
223        let interface = interfaces
224            .perf_interface
225            .interface
226            .ok_or("Performance interface not found, did you call `enable_perf_interface()`?")?;
227
228        match interfaces.perf_interface.counters.entry(name) {
229            Entry::Occupied(counter) => {
230                let counter = counter.into_mut();
231
232                if counter.counter.registered {
233                    let stop = interface
234                        .perf_stop
235                        .ok_or("`perf_stop()` is missing on the performance interface")?;
236
237                    unsafe {
238                        stop(&mut counter.counter as *mut _);
239                    }
240
241                    return Ok(());
242                }
243
244                return Err(format!("Performance counter “{name}” has not been registered").into());
245            }
246            _ => Err(format!("Unknown performance counter “{name}”").into()),
247        }
248    }
249
250    pub fn perf_log(&self) -> Result<(), Box<dyn std::error::Error>> {
251        let interfaces = self.interfaces.read().unwrap();
252
253        let interface = interfaces
254            .perf_interface
255            .interface
256            .ok_or("Performance interface not found, did you call `enable_perf_interface()`?")?;
257
258        let log = interface
259            .perf_log
260            .ok_or("`perf_log()` is missing on the performance interface")?;
261
262        unsafe {
263            log();
264        }
265
266        Ok(())
267    }
268
269    pub fn perf_get_time_usec(&self) -> i64 {
270        let interfaces = self.interfaces.read().unwrap();
271
272        if let Some(interface) = interfaces.perf_interface.interface {
273            if let Some(get_time_usec) = interface.get_time_usec {
274                return unsafe { get_time_usec() };
275            }
276
277            #[cfg(feature = "log")]
278            log::error!("`get_time_usec()` is missing on the performance interface");
279        }
280
281        #[cfg(feature = "log")]
282        log::error!("Performance interface not found, did you call `enable_perf_interface()`?");
283
284        0
285    }
286
287    pub fn perf_get_counter(&self) -> u64 {
288        let interfaces = self.interfaces.read().unwrap();
289
290        if let Some(interface) = interfaces.perf_interface.interface {
291            if let Some(get_perf_counter) = interface.get_perf_counter {
292                return unsafe { get_perf_counter() };
293            }
294
295            #[cfg(feature = "log")]
296            log::error!("`get_perf_counter()` is missing on the performance interface");
297        }
298
299        #[cfg(feature = "log")]
300        log::error!("Performance interface not found, did you call `enable_perf_interface()`?");
301
302        0
303    }
304
305    pub fn get_cpu_features(&self) -> CpuFeatures {
306        let interfaces = self.interfaces.read().unwrap();
307
308        if let Some(interface) = interfaces.perf_interface.interface {
309            if let Some(get_cpu_features) = interface.get_cpu_features {
310                return unsafe { CpuFeatures::from_bits_unchecked(get_cpu_features()) };
311            }
312
313            #[cfg(feature = "log")]
314            log::error!("`get_cpu_features()` is missing on the performance interface");
315        }
316
317        #[cfg(feature = "log")]
318        log::error!("Performance interface not found, did you call `enable_perf_interface()`?");
319
320        CpuFeatures::empty()
321    }
322
323    pub fn location_service_start(&self) {
324        let interfaces = self.interfaces.read().unwrap();
325
326        if let Some(interface) = interfaces.location_interface {
327            if let Some(start) = interface.start {
328                unsafe { start() };
329            }
330        }
331    }
332
333    pub fn location_service_stop(&self) {
334        let interfaces = self.interfaces.read().unwrap();
335
336        if let Some(interface) = interfaces.location_interface {
337            if let Some(stop) = interface.stop {
338                unsafe { stop() };
339            }
340        }
341    }
342
343    pub fn location_service_get_position(&self) -> Option<Position> {
344        let interfaces = self.interfaces.read().unwrap();
345
346        if let Some(interface) = interfaces.location_interface {
347            if let Some(get_position) = interface.get_position {
348                let mut lat = 0f64;
349                let mut lon = 0f64;
350                let mut horiz_accuracy = 0f64;
351                let mut vert_accuracy = 0f64;
352
353                unsafe {
354                    if !get_position(
355                        &mut lat as *mut f64,
356                        &mut lon as *mut f64,
357                        &mut horiz_accuracy as *mut f64,
358                        &mut vert_accuracy as *mut f64,
359                    ) {
360                        return None;
361                    }
362                };
363
364                return Some(Position {
365                    lat,
366                    lon,
367                    horiz_accuracy,
368                    vert_accuracy,
369                });
370            }
371        }
372
373        None
374    }
375
376    pub fn location_service_set_interval(&self, interval_ms: u32, interval_distance: u32) {
377        let interfaces = self.interfaces.read().unwrap();
378
379        if let Some(interface) = interfaces.location_interface {
380            if let Some(set_interval) = interface.set_interval {
381                unsafe { set_interval(interval_ms, interval_distance) };
382            }
383        }
384    }
385
386    pub fn midi_input_enabled(&self) -> bool {
387        let interfaces = self.interfaces.read().unwrap();
388
389        if let Some(interface) = interfaces.midi_interface {
390            if let Some(input_enabled) = interface.input_enabled {
391                return unsafe { input_enabled() };
392            }
393        }
394
395        false
396    }
397
398    pub fn midi_output_enabled(&self) -> bool {
399        let interfaces = self.interfaces.read().unwrap();
400
401        if let Some(interface) = interfaces.midi_interface {
402            if let Some(output_enabled) = interface.output_enabled {
403                return unsafe { output_enabled() };
404            }
405        }
406
407        false
408    }
409
410    pub fn midi_read_next(&self) -> Option<u8> {
411        let interfaces = self.interfaces.read().unwrap();
412
413        if let Some(interface) = interfaces.midi_interface {
414            if let Some(read) = interface.read {
415                let mut value = 0;
416                unsafe {
417                    if read(&mut value as *mut u8) {
418                        return Some(value);
419                    }
420                }
421            }
422        }
423
424        None
425    }
426
427    pub fn midi_write_byte(&self, value: u8, delta_time: u32) -> bool {
428        let interfaces = self.interfaces.read().unwrap();
429
430        if let Some(interface) = interfaces.midi_interface {
431            if let Some(write) = interface.write {
432                return unsafe { write(value, delta_time) };
433            }
434        }
435
436        false
437    }
438
439    pub fn midi_flush(&self) -> bool {
440        let interfaces = self.interfaces.read().unwrap();
441
442        if let Some(interface) = interfaces.midi_interface {
443            if let Some(flush) = interface.flush {
444                return unsafe { flush() };
445            }
446        }
447
448        false
449    }
450
451    #[proc::unstable(feature = "env-commands")]
452    pub fn vfs_get_path(&self, handle: &mut retro_vfs_file_handle) -> Option<CString> {
453        let interfaces = self.interfaces.read().unwrap();
454
455        if let Some(interface) = interfaces.vfs_interface_info.interface {
456            if let Some(get_path) = interface.get_path {
457                let ptr = unsafe { get_path(handle) };
458                if !ptr.is_null() {
459                    let path = CStr::from_ptr(ptr).to_owned();
460                    return Some(path);
461                }
462            }
463        }
464
465        None
466    }
467
468    #[proc::unstable(feature = "env-commands")]
469    pub fn vfs_open(
470        &self,
471        path: &str,
472        mode: VfsFileOpenFlags,
473        hints: VfsFileOpenHints,
474    ) -> Result<retro_vfs_file_handle, Box<dyn std::error::Error>> {
475        let interfaces = self.interfaces.read().unwrap();
476
477        if let Some(interface) = interfaces.vfs_interface_info.interface {
478            if let Some(open) = interface.open {
479                let path = CString::new(path)?;
480
481                let handle = unsafe { open(path.as_ptr(), mode.bits(), hints.bits()) };
482                if !handle.is_null() {
483                    return Ok(*handle);
484                }
485            }
486        }
487
488        Err("Failed to open file".into())
489    }
490
491    #[proc::unstable(feature = "env-commands")]
492    pub fn vfs_close(
493        &self,
494        mut handle: retro_vfs_file_handle,
495    ) -> Result<(), Box<dyn std::error::Error>> {
496        let interfaces = self.interfaces.read().unwrap();
497
498        if let Some(interface) = interfaces.vfs_interface_info.interface {
499            if let Some(close) = interface.close {
500                if unsafe { close(&mut handle) } == 0 {
501                    return Ok(());
502                }
503            }
504        }
505
506        Err("Failed to close file".into())
507    }
508
509    #[proc::unstable(feature = "env-commands")]
510    pub fn vfs_size(
511        &self,
512        handle: &mut retro_vfs_file_handle,
513    ) -> Result<u64, Box<dyn std::error::Error>> {
514        let interfaces = self.interfaces.read().unwrap();
515
516        if let Some(interface) = interfaces.vfs_interface_info.interface {
517            if let Some(size) = interface.size {
518                let size = unsafe { size(handle) };
519                if size >= 0 {
520                    return Ok(size as u64);
521                }
522            }
523        }
524
525        Err("Failed to get file size".into())
526    }
527
528    #[proc::unstable(feature = "env-commands")]
529    pub fn vfs_truncate(
530        &self,
531        handle: &mut retro_vfs_file_handle,
532        length: i64, // no idea why the API wants signed values
533    ) -> Result<(), Box<dyn std::error::Error>> {
534        let interfaces = self.interfaces.read().unwrap();
535
536        if interfaces.vfs_interface_info.supported_version < 2 {
537            return Err(format!(
538                "VFS interface version 2 required, but the frontend only supports version {}",
539                interfaces.vfs_interface_info.supported_version
540            )
541            .into());
542        }
543
544        if let Some(interface) = interfaces.vfs_interface_info.interface {
545            if let Some(truncate) = interface.truncate {
546                if unsafe { truncate(handle, length) } == 0 {
547                    return Ok(());
548                }
549            }
550        }
551
552        Err("Failed to truncate file".into())
553    }
554
555    #[proc::unstable(feature = "env-commands")]
556    pub fn vfs_tell(
557        &self,
558        handle: &mut retro_vfs_file_handle,
559    ) -> Result<u64, Box<dyn std::error::Error>> {
560        let interfaces = self.interfaces.read().unwrap();
561
562        if let Some(interface) = interfaces.vfs_interface_info.interface {
563            if let Some(tell) = interface.tell {
564                let position = unsafe { tell(handle) };
565                if position >= 0 {
566                    return Ok(position as u64);
567                }
568            }
569        }
570
571        Err("Failed to get cursor position".into())
572    }
573
574    #[proc::unstable(feature = "env-commands")]
575    pub fn vfs_seek(
576        &self,
577        handle: &mut retro_vfs_file_handle,
578        offset: i64,
579        seek_position: VfsSeekPosition,
580    ) -> Result<u64, Box<dyn std::error::Error>> {
581        let interfaces = self.interfaces.read().unwrap();
582
583        if let Some(interface) = interfaces.vfs_interface_info.interface {
584            if let Some(seek) = interface.seek {
585                let position = unsafe { seek(handle, offset, seek_position as i32) };
586                if position >= 0 {
587                    return Ok(position as u64);
588                }
589            }
590        }
591
592        Err("Failed to seek into file".into())
593    }
594
595    #[proc::unstable(feature = "env-commands")]
596    pub fn vfs_read(
597        &self,
598        handle: &mut retro_vfs_file_handle,
599        length: usize,
600    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
601        let interfaces = self.interfaces.read().unwrap();
602
603        if let Some(interface) = interfaces.vfs_interface_info.interface {
604            if let Some(read) = interface.read {
605                let mut buffer = Vec::with_capacity(length);
606
607                let read_length =
608                    unsafe { read(handle, buffer.as_mut_ptr() as *mut _, length as u64) };
609                if read_length >= 0 {
610                    return Ok(buffer);
611                }
612            }
613        }
614
615        Err("Failed to read from file".into())
616    }
617
618    #[proc::unstable(feature = "env-commands")]
619    pub fn vfs_write(
620        &self,
621        handle: &mut retro_vfs_file_handle,
622        buffer: &mut [u8],
623    ) -> Result<u64, Box<dyn std::error::Error>> {
624        let interfaces = self.interfaces.read().unwrap();
625
626        if let Some(interface) = interfaces.vfs_interface_info.interface {
627            if let Some(write) = interface.write {
628                let bytes_written =
629                    unsafe { write(handle, buffer.as_mut_ptr() as *mut _, buffer.len() as u64) };
630                if bytes_written >= 0 {
631                    return Ok(bytes_written as u64);
632                }
633            }
634        }
635
636        Err("Failed to write to file".into())
637    }
638
639    #[proc::unstable(feature = "env-commands")]
640    pub fn vfs_flush(
641        &self,
642        handle: &mut retro_vfs_file_handle,
643    ) -> Result<(), Box<dyn std::error::Error>> {
644        let interfaces = self.interfaces.read().unwrap();
645
646        if let Some(interface) = interfaces.vfs_interface_info.interface {
647            if let Some(flush) = interface.flush {
648                if unsafe { flush(handle) } == 0 {
649                    return Ok(());
650                }
651            }
652        }
653
654        Err("Failed to flush file".into())
655    }
656
657    #[proc::unstable(feature = "env-commands")]
658    pub fn vfs_remove(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
659        let interfaces = self.interfaces.read().unwrap();
660
661        if let Some(interface) = interfaces.vfs_interface_info.interface {
662            if let Some(remove) = interface.remove {
663                let path = CString::new(path)?;
664
665                if unsafe { remove(path.as_ptr()) } == 0 {
666                    return Ok(());
667                }
668            }
669        }
670
671        Err("Failed to remove file".into())
672    }
673
674    #[proc::unstable(feature = "env-commands")]
675    pub fn vfs_rename(
676        &self,
677        old_path: &str,
678        new_path: &str,
679    ) -> Result<(), Box<dyn std::error::Error>> {
680        let interfaces = self.interfaces.read().unwrap();
681
682        if let Some(interface) = interfaces.vfs_interface_info.interface {
683            if let Some(rename) = interface.rename {
684                let old_path = CString::new(old_path)?;
685                let new_path = CString::new(new_path)?;
686
687                if unsafe { rename(old_path.as_ptr(), new_path.as_ptr()) } == 0 {
688                    return Ok(());
689                }
690            }
691        }
692
693        Err("Failed to rename file".into())
694    }
695
696    #[proc::unstable(feature = "env-commands")]
697    pub fn vfs_stat(&self, path: &str) -> Result<(VfsStat, u64), Box<dyn std::error::Error>> {
698        let interfaces = self.interfaces.read().unwrap();
699
700        if interfaces.vfs_interface_info.supported_version < 3 {
701            return Err(format!(
702                "VFS interface version 3 required, but the frontend only supports version {}",
703                interfaces.vfs_interface_info.supported_version
704            )
705            .into());
706        }
707
708        if let Some(interface) = interfaces.vfs_interface_info.interface {
709            if let Some(stat) = interface.stat {
710                let path = CString::new(path)?;
711                let (stat, size) = unsafe {
712                    let mut size = 0i32;
713                    let value = stat(path.as_ptr(), &mut size);
714
715                    (VfsStat::from_bits_unchecked(value), size)
716                };
717
718                if stat.is_empty() {
719                    return Err(format!("Invalid stat bitmask: {stat:#?}").into());
720                } else if size < 0 {
721                    return Err(format!("Invalid file size: {size}").into());
722                }
723
724                return Ok((stat, size as u64));
725            }
726        }
727
728        Err("Failed to stat file".into())
729    }
730
731    #[proc::unstable(feature = "env-commands")]
732    pub fn vfs_mkdir(&self, dir: &str) -> Result<(), Box<dyn std::error::Error>> {
733        let interfaces = self.interfaces.read().unwrap();
734
735        if interfaces.vfs_interface_info.supported_version < 3 {
736            return Err(format!(
737                "VFS interface version 3 required, but the frontend only supports version {}",
738                interfaces.vfs_interface_info.supported_version
739            )
740            .into());
741        }
742
743        if let Some(interface) = interfaces.vfs_interface_info.interface {
744            if let Some(mkdir) = interface.mkdir {
745                let dir = CString::new(dir)?;
746
747                match unsafe { mkdir(dir.as_ptr()) } {
748                    0 => return Ok(()),
749                    -2 => return Err("Failed to create directory: Exists already".into()),
750                    _ => (),
751                }
752            }
753        }
754
755        Err("Failed to create directory".into())
756    }
757
758    #[proc::unstable(feature = "env-commands")]
759    pub fn vfs_opendir(
760        &self,
761        dir: &str,
762        include_hidden: bool,
763    ) -> Result<retro_vfs_dir_handle, Box<dyn std::error::Error>> {
764        let interfaces = self.interfaces.read().unwrap();
765
766        if interfaces.vfs_interface_info.supported_version < 3 {
767            return Err(format!(
768                "VFS interface version 3 required, but the frontend only supports version {}",
769                interfaces.vfs_interface_info.supported_version
770            )
771            .into());
772        }
773
774        if let Some(interface) = interfaces.vfs_interface_info.interface {
775            if let Some(opendir) = interface.opendir {
776                let dir = CString::new(dir)?;
777
778                let handle = unsafe { opendir(dir.as_ptr(), include_hidden) };
779                if !handle.is_null() {
780                    return Ok(*handle);
781                }
782            }
783        }
784
785        Err("Failed to open directory".into())
786    }
787
788    #[proc::unstable(feature = "env-commands")]
789    pub fn vfs_readdir(
790        &self,
791        handle: &mut retro_vfs_dir_handle,
792    ) -> Result<(), Box<dyn std::error::Error>> {
793        let interfaces = self.interfaces.read().unwrap();
794
795        if interfaces.vfs_interface_info.supported_version < 3 {
796            return Err(format!(
797                "VFS interface version 3 required, but the frontend only supports version {}",
798                interfaces.vfs_interface_info.supported_version
799            )
800            .into());
801        }
802
803        if let Some(interface) = interfaces.vfs_interface_info.interface {
804            if let Some(readdir) = interface.readdir {
805                if unsafe { readdir(handle) } {
806                    return Ok(());
807                }
808            }
809        }
810
811        Err("Failed to read directory".into())
812    }
813
814    #[proc::unstable(feature = "env-commands")]
815    pub fn vfs_dirent_get_name(
816        &self,
817        handle: &mut retro_vfs_dir_handle,
818    ) -> Result<CString, Box<dyn std::error::Error>> {
819        let interfaces = self.interfaces.read().unwrap();
820
821        if interfaces.vfs_interface_info.supported_version < 3 {
822            return Err(format!(
823                "VFS interface version 3 required, but the frontend only supports version {}",
824                interfaces.vfs_interface_info.supported_version
825            )
826            .into());
827        }
828
829        if let Some(interface) = interfaces.vfs_interface_info.interface {
830            if let Some(dirent_get_name) = interface.dirent_get_name {
831                let ptr = unsafe { dirent_get_name(handle) };
832                if !ptr.is_null() {
833                    let name = CStr::from_ptr(ptr).to_owned();
834                    return Ok(name);
835                }
836            }
837        }
838
839        Err("Failed to get entry name".into())
840    }
841
842    #[proc::unstable(feature = "env-commands")]
843    pub fn vfs_dirent_is_dir(
844        &self,
845        handle: &mut retro_vfs_dir_handle,
846    ) -> Result<bool, Box<dyn std::error::Error>> {
847        let interfaces = self.interfaces.read().unwrap();
848
849        if interfaces.vfs_interface_info.supported_version < 3 {
850            return Err(format!(
851                "VFS interface version 3 required, but the frontend only supports version {}",
852                interfaces.vfs_interface_info.supported_version
853            )
854            .into());
855        }
856
857        if let Some(interface) = interfaces.vfs_interface_info.interface {
858            if let Some(dirent_is_dir) = interface.dirent_is_dir {
859                return Ok(unsafe { dirent_is_dir(handle) });
860            }
861        }
862
863        Err("Failed to check if the entry is a directory".into())
864    }
865
866    #[proc::unstable(feature = "env-commands")]
867    pub fn vfs_closedir(
868        &self,
869        mut handle: retro_vfs_dir_handle,
870    ) -> Result<(), Box<dyn std::error::Error>> {
871        let interfaces = self.interfaces.read().unwrap();
872
873        if interfaces.vfs_interface_info.supported_version < 3 {
874            return Err(format!(
875                "VFS interface version 3 required, but the frontend only supports version {}",
876                interfaces.vfs_interface_info.supported_version
877            )
878            .into());
879        }
880
881        if let Some(interface) = interfaces.vfs_interface_info.interface {
882            if let Some(closedir) = interface.closedir {
883                if unsafe { closedir(&mut handle) } == 0 {
884                    return Ok(());
885                }
886            }
887        }
888
889        Err("Failed to close directory".into())
890    }
891}
892
893/// Functions that are safe to be called in [`Core::on_reset`].
894pub type ResetContext<'a> = GenericContext<'a>;
895
896/// Functions that are safe to be called in [`Core::on_deinit`].
897pub type DeinitContext<'a> = GenericContext<'a>;
898
899/// Functions that are safe to be called in [`Core::get_serialize_size`].
900pub type GetSerializeSizeContext<'a> = GenericContext<'a>;
901
902/// Functions that are safe to be called in [`Core::on_serialize`].
903pub type SerializeContext<'a> = GenericContext<'a>;
904
905/// Functions that are safe to be called in [`Core::on_unserialize`].
906pub type UnserializeContext<'a> = GenericContext<'a>;
907
908/// Functions that are safe to be called in [`Core::on_unload_game`].
909pub type UnloadGameContext<'a> = GenericContext<'a>;
910
911/// Functions that are safe to be called in [`Core::on_cheat_reset`].
912pub type CheatResetContext<'a> = GenericContext<'a>;
913
914/// Functions that are safe to be called in [`Core::on_cheat_set`].
915pub type CheatSetContext<'a> = GenericContext<'a>;
916
917/// Functions that are safe to be called in [`Core::on_get_region`].
918pub type GetRegionContext<'a> = GenericContext<'a>;
919
920/// Functions that are safe to be called in [`Core::get_memory_data`].
921pub type GetMemoryDataContext<'a> = GenericContext<'a>;
922
923/// Functions that are safe to be called in [`Core::get_memory_size`].
924pub type GetMemorySizeContext<'a> = GenericContext<'a>;
925
926make_context!(GetAvInfoContext, #[doc = "Functions that are safe to be called in [`Core::on_get_av_info`]"]);
927make_context!(InitContext, #[doc = "Functions that are safe to be called in [`Core::on_init`]"]);
928make_context!(OptionsChangedContext, #[doc = "Functions that are safe to be called in [`Core::on_options_changed`]"]);
929
930make_context!(LoadGameSpecialContext, #[doc = "Functions that are safe to be called in [`Core::on_load_game_special`]"]);
931into_generic!(LoadGameSpecialContext<'a>, LoadGameContext, 'a);
932
933make_context!(SetEnvironmentContext, #[doc = "Functions that are safe to be called in [`Core::on_set_environment`]"]);
934
935impl<'a> SetEnvironmentContext<'a> {
936    pub fn enable_proc_address_interface(&mut self) -> bool {
937        self.set_proc_address_callback(retro_get_proc_address_interface {
938            get_proc_address: Some(retro_get_proc_address_callback),
939        })
940    }
941
942    pub fn enable_options_update_display_callback(&mut self) -> bool {
943        self.set_core_options_update_display_callback(retro_core_options_update_display_callback {
944            callback: Some(retro_core_options_update_display_callback_fn),
945        })
946    }
947
948    #[proc::unstable(feature = "env-commands")]
949    pub fn enable_vfs_interface(
950        &mut self,
951        min_version: u32,
952    ) -> Result<u32, Box<dyn std::error::Error>> {
953        let mut interfaces = self.interfaces.write().unwrap();
954
955        let info = self.get_vfs_interface(retro_vfs_interface_info {
956            required_interface_version: min_version,
957            iface: std::ptr::null_mut(),
958        });
959
960        if let Some(info) = info {
961            if !info.iface.is_null() && info.required_interface_version >= min_version {
962                interfaces.vfs_interface_info = VfsInterfaceInfo {
963                    supported_version: info.required_interface_version,
964                    interface: Some(*info.iface),
965                }
966            }
967        }
968
969        if interfaces.vfs_interface_info.interface.is_some() {
970            Ok(interfaces.vfs_interface_info.supported_version)
971        } else {
972            Err("Failed to enable VFS interface".into())
973        }
974    }
975}
976
977/// Functions that are safe to be called in [`Core::on_load_game`].
978///
979/// For a description of the callbacks see [`CoreWrapper`].
980pub struct LoadGameContext<'a> {
981    pub(crate) environment_callback: &'a retro_environment_t,
982    pub(crate) interfaces: Interfaces,
983}
984
985impl<'a> LoadGameContext<'a> {
986    pub(crate) fn new(
987        environment_callback: &'a retro_environment_t,
988        interfaces: Interfaces,
989    ) -> Self {
990        Self {
991            environment_callback,
992            interfaces,
993        }
994    }
995
996    /// The reference represents the time of one frame.
997    /// It is computed as `1000000 / fps`, but the implementation will resolve the
998    /// rounding to ensure that framestepping, etc is exact.
999    pub fn enable_frame_time_callback(&self, reference: i64) {
1000        self.set_frame_time_callback(retro_frame_time_callback {
1001            callback: Some(retro_frame_time_callback_fn),
1002            reference,
1003        });
1004    }
1005
1006    #[proc::unstable(feature = "env-commands")]
1007    pub fn enable_camera_interface(
1008        &mut self,
1009        caps: u64,
1010        width: u32,
1011        height: u32,
1012    ) -> Result<(), Box<dyn std::error::Error>> {
1013        use retro_camera_buffer::*;
1014
1015        let enable_raw = caps & (1 << RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER as u64) > 0;
1016        let enable_opengl = caps & (1 << RETRO_CAMERA_BUFFER_OPENGL_TEXTURE as u64) > 0;
1017
1018        let mut interfaces = self.interfaces.write().unwrap();
1019
1020        interfaces.camera_interface = self.get_camera_interface(retro_camera_callback {
1021            caps,
1022            width,
1023            height,
1024
1025            start: None,
1026            stop: None,
1027
1028            frame_raw_framebuffer: if enable_raw {
1029                Some(retro_camera_frame_raw_framebuffer_callback)
1030            } else {
1031                None
1032            },
1033            frame_opengl_texture: if enable_opengl {
1034                Some(retro_camera_frame_opengl_texture_callback)
1035            } else {
1036                None
1037            },
1038            initialized: Some(retro_camera_initialized_callback),
1039            deinitialized: Some(retro_camera_deinitialized_callback),
1040        });
1041
1042        if interfaces.camera_interface.is_some() {
1043            Ok(())
1044        } else {
1045            Err("Failed to enable camera interface".into())
1046        }
1047    }
1048
1049    #[proc::unstable(feature = "env-commands")]
1050    pub fn enable_sensor_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1051        let ctx: GenericContext = self.into();
1052        let mut interfaces = self.interfaces.write().unwrap();
1053        interfaces.sensor_interface = ctx.get_sensor_interface();
1054
1055        if interfaces.sensor_interface.is_some() {
1056            Ok(())
1057        } else {
1058            Err("Failed to enable sensor interface".into())
1059        }
1060    }
1061
1062    #[proc::unstable(feature = "env-commands")]
1063    pub fn enable_led_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1064        let ctx: GenericContext = self.into();
1065        let mut interfaces = self.interfaces.write().unwrap();
1066        interfaces.led_interface = ctx.get_led_interface();
1067
1068        if interfaces.led_interface.is_some() {
1069            Ok(())
1070        } else {
1071            Err("Failed to enable led interface".into())
1072        }
1073    }
1074
1075    #[proc::unstable(feature = "env-commands")]
1076    pub fn enable_midi_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1077        let ctx: GenericContext = self.into();
1078        let mut interfaces = self.interfaces.write().unwrap();
1079        interfaces.midi_interface = ctx.get_midi_interface();
1080
1081        if interfaces.midi_interface.is_some() {
1082            Ok(())
1083        } else {
1084            Err("Failed to enable MIDI interface".into())
1085        }
1086    }
1087
1088    pub fn enable_location_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1089        let ctx: GenericContext = self.into();
1090        let mut interfaces = self.interfaces.write().unwrap();
1091        interfaces.location_interface = ctx.get_location_callback();
1092
1093        if let Some(mut interface) = interfaces.location_interface {
1094            interface.initialized = Some(retro_location_lifetime_status_initialized_callback);
1095            interface.deinitialized = Some(retro_location_lifetime_status_deinitialized_callback);
1096            Ok(())
1097        } else {
1098            Err("Failed to enable location interface".into())
1099        }
1100    }
1101
1102    pub fn enable_rumble_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1103        let mut interfaces = self.interfaces.write().unwrap();
1104        interfaces.rumble_interface = self.get_rumble_interface();
1105
1106        if interfaces.rumble_interface.is_some() {
1107            Ok(())
1108        } else {
1109            Err("Failed to enable rumble interface".into())
1110        }
1111    }
1112
1113    pub fn enable_perf_interface(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1114        let ctx: GenericContext = self.into();
1115        let mut interfaces = self.interfaces.write().unwrap();
1116        interfaces.perf_interface = PerfCounters {
1117            interface: ctx.get_perf_interface(),
1118            counters: HashMap::new(),
1119        };
1120
1121        if interfaces.perf_interface.interface.is_some() {
1122            Ok(())
1123        } else {
1124            Err("Failed to enable perf interface".into())
1125        }
1126    }
1127
1128    pub unsafe fn enable_hw_render(
1129        &mut self,
1130        context_type: retro_hw_context_type,
1131        bottom_left_origin: bool,
1132        version_major: u32,
1133        version_minor: u32,
1134        debug_context: bool,
1135    ) -> bool {
1136        let data = retro_hw_render_callback {
1137            context_type,
1138            bottom_left_origin,
1139            version_major,
1140            version_minor,
1141            cache_context: true,
1142            debug_context,
1143
1144            depth: false,   // obsolete
1145            stencil: false, // obsolete
1146
1147            context_reset: Some(retro_hw_context_reset_callback),
1148            context_destroy: Some(retro_hw_context_destroyed_callback),
1149
1150            // Set by the frontend
1151            get_current_framebuffer: None,
1152            get_proc_address: None,
1153        };
1154
1155        self.set_hw_render(data)
1156    }
1157
1158    #[proc::unstable(feature = "env-commands")]
1159    pub unsafe fn set_hw_render_context_negotiation_interface_data<
1160        T: HwRenderContextNegotiationInterface + 'static,
1161    >(
1162        &mut self,
1163        interface: T,
1164    ) -> bool {
1165        assert!(
1166            std::mem::size_of::<T>()
1167                >= std::mem::size_of::<retro_hw_render_context_negotiation_interface>()
1168        );
1169
1170        let mut interfaces = self.interfaces.write().unwrap();
1171
1172        let data = Box::new(interface);
1173
1174        interfaces
1175            .hw_render_context_negotiation_interface
1176            .replace(data);
1177
1178        let interface = interfaces
1179            .hw_render_context_negotiation_interface
1180            .as_ref()
1181            .unwrap()
1182            .as_any()
1183            .downcast_ref::<T>()
1184            .unwrap();
1185
1186        let interface =
1187            interface as *const _ as *const retro_hw_render_context_negotiation_interface;
1188
1189        self.set_hw_render_context_negotiation_interface(&*interface)
1190    }
1191
1192    #[proc::unstable(feature = "env-commands")]
1193    pub unsafe fn enable_hw_render_negotiation_interface(
1194        &mut self,
1195        interface_type: retro_hw_render_context_negotiation_interface_type,
1196        interface_version: u32,
1197    ) -> bool {
1198        self.set_hw_render_context_negotiation_interface_data(
1199            retro_hw_render_context_negotiation_interface {
1200                interface_type,
1201                interface_version,
1202            },
1203        )
1204    }
1205
1206    #[cfg(feature = "vulkan")]
1207    #[proc::unstable(feature = "env-commands")]
1208    pub unsafe fn enable_hw_render_negotiation_interface_vulkan(
1209        &mut self,
1210        get_application_info: retro_vulkan_get_application_info_t,
1211        create_device: retro_vulkan_create_device_t,
1212        destroy_device: retro_vulkan_destroy_device_t,
1213    ) -> bool {
1214        self.set_hw_render_context_negotiation_interface_data(retro_hw_render_context_negotiation_interface_vulkan {
1215            interface_type: retro_hw_render_context_negotiation_interface_type::RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN,
1216            interface_version: RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
1217            get_application_info,
1218            create_device,
1219            destroy_device,
1220        })
1221    }
1222}
1223into_generic!(LoadGameContext<'a>, 'a);
1224
1225/// Functions that are safe to be called in [`Core::on_write_audio`].
1226///
1227/// For a description of the callbacks see [`CoreWrapper`].
1228pub struct AudioContext<'a> {
1229    pub(crate) environment_callback: &'a retro_environment_t,
1230    pub(crate) interfaces: Interfaces,
1231
1232    pub(crate) audio_sample_batch_callback: &'a retro_audio_sample_batch_t,
1233    pub(crate) audio_sample_callback: &'a retro_audio_sample_t,
1234}
1235
1236impl AudioContext<'_> {
1237    /// Renders multiple audio frames in one go if [`AudioContext::audio_sample_batch_callback`] has been set.
1238    ///
1239    /// One frame is defined as a sample of left and right channels, interleaved.
1240    /// I.e. `let buf: [u16; 4] = [ l, r, l, r ];` would be 2 frames.
1241    ///
1242    /// Only one of the audio callbacks must ever be used.
1243    pub fn batch_audio_samples(&self, samples: &[i16]) {
1244        if let Some(callback) = self.audio_sample_batch_callback {
1245            let len = samples.len();
1246
1247            unsafe {
1248                (callback)(samples.as_ptr(), len / 2);
1249            }
1250        }
1251    }
1252
1253    /// Renders a single audio frame if [`AudioContext::audio_sample_callback`] has been set.
1254    /// Should only be used if implementation generates a single sample at a time.
1255    /// Format is signed 16-bit native endian.
1256    ///
1257    /// Only one of the audio callbacks must ever be used.
1258    pub fn queue_audio_sample(&self, left: i16, right: i16) {
1259        if let Some(callback) = self.audio_sample_callback {
1260            unsafe {
1261                (callback)(left, right);
1262            }
1263        }
1264    }
1265}
1266
1267into_generic!(AudioContext<'a>, 'a);
1268
1269/// Functions that are safe to be called in [`Core::on_run`].
1270///
1271/// For a description of the callbacks see [`CoreWrapper`].
1272pub struct RunContext<'a> {
1273    pub(crate) environment_callback: &'a retro_environment_t,
1274    pub(crate) interfaces: Interfaces,
1275
1276    pub(crate) audio_sample_batch_callback: &'a retro_audio_sample_batch_t,
1277    pub(crate) audio_sample_callback: &'a retro_audio_sample_t,
1278    pub(crate) input_poll_callback: &'a retro_input_poll_t,
1279    pub(crate) input_state_callback: &'a retro_input_state_t,
1280    pub(crate) video_refresh_callback: &'a retro_video_refresh_t,
1281
1282    pub(crate) can_dupe: bool,
1283    pub(crate) had_frame: &'a mut bool,
1284    pub(crate) last_width: &'a mut u32,
1285    pub(crate) last_height: &'a mut u32,
1286    pub(crate) last_pitch: &'a mut usize,
1287
1288    pub(crate) supports_bitmasks: bool,
1289}
1290
1291into_generic!(RunContext<'a>, 'a);
1292
1293impl<'a> From<&mut RunContext<'a>> for AudioContext<'a> {
1294    fn from(other: &mut RunContext<'a>) -> AudioContext<'a> {
1295        AudioContext {
1296            environment_callback: other.environment_callback,
1297            interfaces: Arc::clone(&other.interfaces),
1298
1299            audio_sample_batch_callback: other.audio_sample_batch_callback,
1300            audio_sample_callback: other.audio_sample_callback,
1301        }
1302    }
1303}
1304
1305impl RunContext<'_> {
1306    #[inline(always)]
1307    pub fn can_dupe(&self) -> bool {
1308        self.can_dupe
1309    }
1310
1311    /// Polls for input if [`RunContext::input_poll_callback`] has been set
1312    pub fn poll_input(&self) {
1313        if let Some(callback) = self.input_poll_callback {
1314            unsafe {
1315                (callback)();
1316            }
1317        }
1318    }
1319
1320    /// Gets the input state for the given player and device if [`RunContext::input_state_callback`] has been set
1321    pub fn get_input_state(&self, port: u32, device: u32, index: u32, id: u32) -> i16 {
1322        if let Some(callback) = self.input_state_callback {
1323            unsafe { (callback)(port, device, index, id) }
1324        } else {
1325            0
1326        }
1327    }
1328
1329    /// Queries the libretro frontend for the state of each joypad button
1330    /// by making an environment call for every button separately.
1331    ///
1332    /// See also [`Self::get_joypad_bitmask`].
1333    pub fn get_joypad_state(&self, port: u32, index: u32) -> JoypadState {
1334        if let Some(callback) = self.input_state_callback {
1335            let mut mask = JoypadState::empty();
1336
1337            unsafe {
1338                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_B) != 0 {
1339                    mask |= JoypadState::B
1340                }
1341                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_Y) != 0 {
1342                    mask |= JoypadState::Y
1343                }
1344                if (callback)(
1345                    port,
1346                    RETRO_DEVICE_JOYPAD,
1347                    index,
1348                    RETRO_DEVICE_ID_JOYPAD_SELECT,
1349                ) != 0
1350                {
1351                    mask |= JoypadState::SELECT
1352                }
1353                if (callback)(
1354                    port,
1355                    RETRO_DEVICE_JOYPAD,
1356                    index,
1357                    RETRO_DEVICE_ID_JOYPAD_START,
1358                ) != 0
1359                {
1360                    mask |= JoypadState::START
1361                }
1362                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_UP) != 0 {
1363                    mask |= JoypadState::UP
1364                }
1365                if (callback)(
1366                    port,
1367                    RETRO_DEVICE_JOYPAD,
1368                    index,
1369                    RETRO_DEVICE_ID_JOYPAD_DOWN,
1370                ) != 0
1371                {
1372                    mask |= JoypadState::DOWN
1373                }
1374                if (callback)(
1375                    port,
1376                    RETRO_DEVICE_JOYPAD,
1377                    index,
1378                    RETRO_DEVICE_ID_JOYPAD_LEFT,
1379                ) != 0
1380                {
1381                    mask |= JoypadState::LEFT
1382                }
1383                if (callback)(
1384                    port,
1385                    RETRO_DEVICE_JOYPAD,
1386                    index,
1387                    RETRO_DEVICE_ID_JOYPAD_RIGHT,
1388                ) != 0
1389                {
1390                    mask |= JoypadState::RIGHT
1391                }
1392                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_A) != 0 {
1393                    mask |= JoypadState::A
1394                }
1395                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_X) != 0 {
1396                    mask |= JoypadState::X
1397                }
1398                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_L) != 0 {
1399                    mask |= JoypadState::L
1400                }
1401                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_R) != 0 {
1402                    mask |= JoypadState::R
1403                }
1404                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_L2) != 0 {
1405                    mask |= JoypadState::L2
1406                }
1407                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_R2) != 0 {
1408                    mask |= JoypadState::R2
1409                }
1410                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_L3) != 0 {
1411                    mask |= JoypadState::L3
1412                }
1413                if (callback)(port, RETRO_DEVICE_JOYPAD, index, RETRO_DEVICE_ID_JOYPAD_R3) != 0 {
1414                    mask |= JoypadState::R3
1415                }
1416            }
1417
1418            return mask;
1419        }
1420
1421        JoypadState::empty()
1422    }
1423
1424    /// Queries the frontend for the joypad state with the more efficient, but currently experimental,
1425    /// joypad bitmask feature. Only a single call into the frontend gets made.
1426    #[proc::unstable(feature = "env-commands")]
1427    pub fn get_joypad_bitmask(&self, port: u32, index: u32) -> JoypadState {
1428        if let Some(callback) = self.input_state_callback {
1429            if self.supports_bitmasks {
1430                return JoypadState::from_bits_truncate((callback)(
1431                    port,
1432                    RETRO_DEVICE_JOYPAD,
1433                    index,
1434                    RETRO_DEVICE_ID_JOYPAD_MASK,
1435                ) as u16);
1436            }
1437
1438            // Fallback
1439            return self.get_joypad_state(port, index);
1440        }
1441
1442        JoypadState::empty()
1443    }
1444
1445    #[proc::unstable(feature = "env-commands")]
1446    pub fn get_current_framebuffer(
1447        &self,
1448        width: u32,
1449        height: u32,
1450        access_flags: MemoryAccess,
1451        format: PixelFormat,
1452    ) -> Result<Framebuffer, Box<dyn std::error::Error>> {
1453        let ctx: GenericContext = self.into();
1454
1455        let fb = ctx.get_current_software_framebuffer(retro_framebuffer {
1456            data: std::ptr::null_mut(),
1457            width,
1458            height,
1459            pitch: 0,
1460            format: format.into(),
1461            access_flags: access_flags.bits(),
1462            memory_flags: 0,
1463        });
1464
1465        if let Some(fb) = fb {
1466            if !fb.data.is_null() {
1467                // TODO: Can we get rid of the raw pointer and PhantomData in an ergonomic way?
1468                // When defining `data` as `&'a mut [u8]` it has the same lifetime as `self`,
1469                // which means we borrow `self` for as long as this `FrameBuffer` exists.
1470                // Thus we cannot pass the `FrameBuffer` to `Self::draw_frame` for example.
1471                return Ok(Framebuffer {
1472                    data: fb.data as *mut u8,
1473                    data_len: fb.height as usize * fb.pitch,
1474                    phantom: ::core::marker::PhantomData,
1475
1476                    width: fb.width,
1477                    height: fb.height,
1478                    pitch: fb.pitch,
1479                    format: fb.format.into(),
1480                    access_flags: MemoryAccess::from_bits_unchecked(fb.access_flags),
1481                    memory_flags: MemoryType::from_bits_unchecked(fb.memory_flags),
1482                });
1483            }
1484        }
1485
1486        Err("Failed to get current software framebuffer".into())
1487    }
1488
1489    #[proc::unstable(feature = "env-commands")]
1490    pub fn get_current_framebuffer_or_fallback(
1491        &self,
1492        width: u32,
1493        height: u32,
1494        access_flags: MemoryAccess,
1495        format: PixelFormat,
1496    ) -> Framebuffer {
1497        match self.get_current_framebuffer(width, height, access_flags, format) {
1498            Ok(fb) if fb.access_flags.intersects(access_flags) => fb,
1499            _ => {
1500                let data = unsafe { &mut FALLBACK_FRAMEBUFFER };
1501
1502                let pitch = width as usize * format.bit_per_pixel();
1503                let data_len = width as usize * height as usize * pitch;
1504
1505                if data.len() < data_len {
1506                    data.resize(data_len, 0);
1507                }
1508
1509                Framebuffer {
1510                    data: data.as_mut_ptr(),
1511                    data_len,
1512                    phantom: ::core::marker::PhantomData,
1513
1514                    width,
1515                    height,
1516                    pitch,
1517                    format,
1518                    access_flags: MemoryAccess::READ | MemoryAccess::WRITE,
1519                    memory_flags: MemoryType::UNCACHED,
1520                }
1521            }
1522        }
1523    }
1524
1525    /// Draws a new frame if [`RunContext::video_refresh_callback`] has been set
1526    pub fn draw_frame(&mut self, data: &[u8], width: u32, height: u32, pitch: usize) {
1527        if let Some(callback) = self.video_refresh_callback {
1528            *self.had_frame = true;
1529            *self.last_width = width;
1530            *self.last_height = height;
1531            *self.last_pitch = pitch;
1532
1533            unsafe { (callback)(data.as_ptr() as *const c_void, width, height, pitch) }
1534        }
1535    }
1536
1537    /// Duplicates the previous frame
1538    pub fn dupe_frame(&self) {
1539        if !self.can_dupe {
1540            eprintln!("[ERROR] This frontend does not support frame duping!");
1541            return;
1542        } else if !*self.had_frame {
1543            eprintln!("[ERROR] Cannot dupe frame, no previous frame has been drawn!");
1544            return;
1545        }
1546
1547        if let Some(callback) = self.video_refresh_callback {
1548            unsafe {
1549                (callback)(
1550                    std::ptr::null() as *const c_void,
1551                    *self.last_width,
1552                    *self.last_height,
1553                    *self.last_pitch,
1554                )
1555            }
1556        }
1557    }
1558
1559    pub fn draw_framebuffer(&mut self, framebuffer: retro_framebuffer) {
1560        if let Some(callback) = self.video_refresh_callback {
1561            *self.had_frame = true;
1562            *self.last_width = framebuffer.width;
1563            *self.last_height = framebuffer.height;
1564            *self.last_pitch = framebuffer.pitch;
1565
1566            unsafe {
1567                (callback)(
1568                    framebuffer.data,
1569                    framebuffer.width,
1570                    framebuffer.height,
1571                    framebuffer.pitch,
1572                )
1573            }
1574        }
1575    }
1576
1577    pub fn draw_hardware_frame(&mut self, width: u32, height: u32, pitch: usize) {
1578        if let Some(callback) = self.video_refresh_callback {
1579            *self.had_frame = true;
1580            *self.last_width = width;
1581            *self.last_height = height;
1582            *self.last_pitch = pitch;
1583
1584            unsafe {
1585                (callback)(
1586                    RETRO_HW_FRAME_BUFFER_VALID as *const c_void,
1587                    width,
1588                    height,
1589                    pitch,
1590                )
1591            }
1592        }
1593    }
1594
1595    #[proc::unstable(feature = "env-commands")]
1596    pub fn camera_start(&self) {
1597        let interfaces = self.interfaces.read().unwrap();
1598
1599        if let Some(interface) = interfaces.camera_interface {
1600            if let Some(start) = interface.start {
1601                unsafe { start() };
1602            }
1603        }
1604    }
1605
1606    #[proc::unstable(feature = "env-commands")]
1607    pub fn camera_stop(&self) {
1608        let interfaces = self.interfaces.read().unwrap();
1609
1610        if let Some(interface) = interfaces.camera_interface {
1611            if let Some(stop) = interface.stop {
1612                unsafe { stop() };
1613            }
1614        }
1615    }
1616}