1use crate::core_wrapper::Interfaces;
3use once_cell::unsync::Lazy;
4use std::collections::HashMap;
5
6use super::*;
7
8static 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
53pub 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 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 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, ) -> 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
893pub type ResetContext<'a> = GenericContext<'a>;
895
896pub type DeinitContext<'a> = GenericContext<'a>;
898
899pub type GetSerializeSizeContext<'a> = GenericContext<'a>;
901
902pub type SerializeContext<'a> = GenericContext<'a>;
904
905pub type UnserializeContext<'a> = GenericContext<'a>;
907
908pub type UnloadGameContext<'a> = GenericContext<'a>;
910
911pub type CheatResetContext<'a> = GenericContext<'a>;
913
914pub type CheatSetContext<'a> = GenericContext<'a>;
916
917pub type GetRegionContext<'a> = GenericContext<'a>;
919
920pub type GetMemoryDataContext<'a> = GenericContext<'a>;
922
923pub 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
977pub 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 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, stencil: false, context_reset: Some(retro_hw_context_reset_callback),
1148 context_destroy: Some(retro_hw_context_destroyed_callback),
1149
1150 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
1225pub 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 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 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
1269pub 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 pub fn poll_input(&self) {
1313 if let Some(callback) = self.input_poll_callback {
1314 unsafe {
1315 (callback)();
1316 }
1317 }
1318 }
1319
1320 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 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 #[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 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 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 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 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}