playdate_graphics/
video.rs1use core::ffi::{c_char, c_int, c_float};
4
5use sys::ffi::LCDVideoPlayer;
6use sys::ffi::{CString, CStr};
7use fs::Path;
8
9use crate::Graphics;
10use crate::bitmap::{AnyBitmap, BitmapRef};
11use crate::error::ApiError;
12use crate::error::Error;
13
14
15#[derive(Debug, Clone, Copy)]
16pub struct Video<Api: api::Api = api::Default>(Api);
17
18impl Video<api::Default> {
19 #[allow(non_snake_case)]
23 pub fn Default() -> Self { Self(Default::default()) }
24}
25
26impl Video<api::Cache> {
27 #[allow(non_snake_case)]
31 pub fn Cached() -> Self { Self(Default::default()) }
32}
33
34impl<Api: Default + api::Api> Default for Video<Api> {
35 fn default() -> Self { Self(Default::default()) }
36}
37
38impl<Api: Default + api::Api> Video<Api> {
39 pub fn new() -> Self { Self(Default::default()) }
40}
41
42impl<Api: api::Api> Video<Api> {
43 pub fn new_with(api: Api) -> Self { Self(api) }
44}
45
46impl<Api: api::Api + Copy> Video<Api> {
47 #[doc(alias = "sys::ffi::playdate_video::loadVideo")]
51 pub fn load<P: AsRef<Path>>(&self, path: P) -> Result<VideoPlayer<Api>, ApiError> {
52 VideoPlayer::load_with(self.0, path)
53 }
54}
55
56
57impl<Api: crate::api::Api> Graphics<Api> {
58 #[doc(alias = "sys::ffi::playdate_graphics::video")]
62 pub fn video(&self) -> Video<api::Cache> { Video::new_with(self.0.video::<api::Cache>()) }
63
64 #[doc(alias = "sys::ffi::playdate_graphics::video")]
68 pub fn video_with<VideoApi: api::Api>(&self, api: VideoApi) -> Video<VideoApi> { Video::new_with(api) }
69}
70
71
72#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
73pub struct VideoPlayer<Api: api::Api = api::Default, const FREE_ON_DROP: bool = true>(*mut LCDVideoPlayer, Api);
74
75impl<Api: api::Api, const FOD: bool> Drop for VideoPlayer<Api, FOD> {
76 fn drop(&mut self) {
77 if FOD && !self.0.is_null() {
78 let f = self.1.free_player();
79 unsafe { f(self.0) };
80 self.0 = core::ptr::null_mut();
81 }
82 }
83}
84
85
86impl<Api: api::Api + Copy> VideoPlayer<Api, true> {
87 pub fn into_shared(mut self) -> VideoPlayer<Api, false> {
92 let res = VideoPlayer(self.0, self.1);
93 self.0 = core::ptr::null_mut();
94 res
95 }
96}
97
98
99impl<Api: api::Api> VideoPlayer<Api, true> {
100 #[doc(alias = "sys::ffi::playdate_video::loadVideo")]
104 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, ApiError>
105 where Api: Default {
106 let api = Api::default();
107 Self::load_with(api, path)
108 }
109
110 #[doc(alias = "sys::ffi::playdate_video::loadVideo")]
114 pub fn load_with<P: AsRef<Path>>(api: Api, path: P) -> Result<Self, ApiError> {
115 let path = CString::new(path.as_ref())?;
116
117 let f = api.load_video();
118 let ptr = unsafe { f(path.as_ptr() as *mut c_char) };
119 if ptr.is_null() {
120 Err(Error::Alloc.into())
122 } else {
123 Ok(Self(ptr, api))
124 }
125 }
126}
127
128
129impl<Api: api::Api, const FOD: bool> VideoPlayer<Api, FOD> {
130 #[doc(alias = "sys::ffi::playdate_video::setContext")]
137 pub fn set_context<'a, 'b: 'a>(&'a self, bitmap: &'b impl AnyBitmap) -> Result<(), Error> {
138 let f = self.1.set_context();
139 if unsafe { f(self.0, bitmap.as_raw()) } != 0 {
140 Ok(())
141 } else {
142 Err(self.get_error().unwrap_or(Error::Unknown))
143 }
144 }
145
146 #[doc(alias = "sys::ffi::playdate_video::getContext")]
152 pub fn get_context(&self) -> Result<BitmapRef<'_>, Error> {
153 let f = self.1.get_context();
154 let ptr = unsafe { f(self.0) };
155 if ptr.is_null() {
156 Err(Error::Alloc)
157 } else {
158 Ok(BitmapRef::from(ptr))
159 }
160 }
161
162
163 #[doc(alias = "sys::ffi::playdate_video::useScreenContext")]
165 pub fn use_screen_context(&self) {
166 let f = self.1.use_screen_context();
167 unsafe { f(self.0) }
168 }
169
170 #[doc(alias = "sys::ffi::playdate_video::renderFrame")]
177 pub fn render_frame(&self, n: c_int) -> Result<(), Error> {
178 let f = self.1.render_frame();
179 if unsafe { f(self.0, n) } != 0 {
180 Ok(())
181 } else {
182 Err(self.get_error().unwrap_or(Error::Unknown))
183 }
184 }
185
186 #[doc(alias = "sys::ffi::playdate_video::renderFrame")]
190 pub fn info(&self) -> VideoPlayerOutInfo {
191 let mut info = VideoPlayerOutInfo::default();
192 self.info_to(&mut info);
193 info
194 }
195
196 #[doc(alias = "sys::ffi::playdate_video::renderFrame")]
200 pub fn info_to(&self, info: &mut VideoPlayerOutInfo) {
201 let f = self.1.get_info();
202 unsafe {
203 f(
204 self.0,
205 &mut info.width,
206 &mut info.height,
207 &mut info.frame_rate,
208 &mut info.frame_count,
209 &mut info.current_frame,
210 )
211 };
212 }
213
214 #[doc(alias = "sys::ffi::playdate_video::renderFrame")]
228 pub fn info_raw(&self,
229 width: Option<&mut c_int>,
230 height: Option<&mut c_int>,
231 frame_rate: Option<&mut c_float>,
232 frame_count: Option<&mut c_int>,
233 current_frame: Option<&mut c_int>) {
234 let f = self.1.get_info();
235 unsafe {
236 use core::ptr::null_mut;
237 f(
238 self.0,
239 width.map_or(null_mut() as _, |v| v as *mut _),
240 height.map_or(null_mut() as _, |v| v as *mut _),
241 frame_rate.map_or(null_mut() as _, |v| v as *mut _),
242 frame_count.map_or(null_mut() as _, |v| v as *mut _),
243 current_frame.map_or(null_mut() as _, |v| v as *mut _),
244 )
245 };
246 }
247
248
249 #[must_use = "Error message is borrowed from C part, must be used immediately or converted to owned string."]
257 #[inline(always)]
258 pub fn get_error(&self) -> Option<Error> { self.get_error_cstr().map(Error::video_from) }
259
260 #[doc(alias = "sys::ffi::playdate_video::getError")]
266 #[must_use = "Error message is borrowed from C part, must be used immediately or converted to owned string."]
267 pub fn get_error_cstr(&self) -> Option<&CStr> {
268 let f = self.1.get_error();
269 let ptr = unsafe { f(self.0) };
270 if ptr.is_null() {
271 None
272 } else {
273 unsafe { CStr::from_ptr(ptr as _) }.into()
274 }
275 }
276}
277
278
279#[derive(Debug, Clone, Default)]
280pub struct VideoPlayerOutInfo {
281 pub width: c_int,
282 pub height: c_int,
283 pub frame_rate: c_float,
284 pub frame_count: c_int,
285 pub current_frame: c_int,
286}
287
288
289pub mod api {
290 use core::ffi::c_char;
291 use core::ffi::c_float;
292 use core::ffi::c_int;
293 use core::ptr::NonNull;
294
295 use sys::ffi::LCDBitmap;
296 use sys::ffi::LCDVideoPlayer;
297 use sys::ffi::playdate_video;
298
299
300 #[derive(Debug, Clone, Copy, core::default::Default)]
304 pub struct Default;
305 impl Api for Default {}
306
307
308 #[derive(Clone, Copy)]
314 #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
315 pub struct Cache(&'static playdate_video);
316
317 impl core::default::Default for Cache {
318 fn default() -> Self { Self(sys::api!(graphics.video)) }
319 }
320
321 impl From<*const playdate_video> for Cache {
322 #[inline(always)]
323 fn from(ptr: *const playdate_video) -> Self { Self(unsafe { ptr.as_ref() }.expect("video")) }
324 }
325
326 impl From<&'static playdate_video> for Cache {
327 #[inline(always)]
328 fn from(r: &'static playdate_video) -> Self { Self(r) }
329 }
330
331 impl From<NonNull<playdate_video>> for Cache {
332 #[inline(always)]
333 fn from(ptr: NonNull<playdate_video>) -> Self { Self(unsafe { ptr.as_ref() }) }
334 }
335
336 impl From<&'_ NonNull<playdate_video>> for Cache {
337 #[inline(always)]
338 fn from(ptr: &NonNull<playdate_video>) -> Self { Self(unsafe { ptr.as_ref() }) }
339 }
340
341
342 impl Api for Cache {
343 #[inline(always)]
344 fn load_video(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut LCDVideoPlayer {
345 self.0.loadVideo.expect("loadVideo")
346 }
347
348 #[inline(always)]
349 fn free_player(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) {
350 self.0.freePlayer.expect("freePlayer")
351 }
352
353 #[inline(always)]
354 fn set_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, context: *mut LCDBitmap) -> c_int {
355 self.0.setContext.expect("setContext")
356 }
357
358 #[inline(always)]
359 fn use_screen_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) {
360 self.0.useScreenContext.expect("useScreenContext")
361 }
362
363 #[inline(always)]
364 fn render_frame(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, n: c_int) -> c_int {
365 self.0.renderFrame.expect("renderFrame")
366 }
367
368 #[inline(always)]
369 fn get_error(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *const c_char {
370 self.0.getError.expect("getError")
371 }
372
373 #[inline(always)]
374 fn get_info(
375 &self)
376 -> unsafe extern "C" fn(p: *mut LCDVideoPlayer,
377 outWidth: *mut c_int,
378 outHeight: *mut c_int,
379 outFrameRate: *mut c_float,
380 outFrameCount: *mut c_int,
381 outCurrentFrame: *mut c_int) {
382 *sys::api!(graphics.video.getInfo)
383 }
384
385 #[inline(always)]
386 fn get_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *mut LCDBitmap {
387 self.0.getContext.expect("getContext")
388 }
389 }
390
391
392 pub trait Api {
393 #[doc(alias = "sys::ffi::playdate_video::loadVideo")]
395 #[inline(always)]
396 fn load_video(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut LCDVideoPlayer {
397 *sys::api!(graphics.video.loadVideo)
398 }
399
400 #[doc(alias = "sys::ffi::playdate_video::freePlayer")]
402 #[inline(always)]
403 fn free_player(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) {
404 *sys::api!(graphics.video.freePlayer)
405 }
406
407 #[doc(alias = "sys::ffi::playdate_video::setContext")]
409 #[inline(always)]
410 fn set_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, context: *mut LCDBitmap) -> c_int {
411 *sys::api!(graphics.video.setContext)
412 }
413
414 #[doc(alias = "sys::ffi::playdate_video::useScreenContext")]
416 #[inline(always)]
417 fn use_screen_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) {
418 *sys::api!(graphics.video.useScreenContext)
419 }
420
421 #[doc(alias = "sys::ffi::playdate_video::renderFrame")]
423 #[inline(always)]
424 fn render_frame(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, n: c_int) -> c_int {
425 *sys::api!(graphics.video.renderFrame)
426 }
427
428 #[doc(alias = "sys::ffi::playdate_video::getError")]
430 #[inline(always)]
431 fn get_error(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *const c_char {
432 *sys::api!(graphics.video.getError)
433 }
434
435 #[doc(alias = "sys::ffi::playdate_video::getInfo")]
437 #[inline(always)]
438 fn get_info(
439 &self)
440 -> unsafe extern "C" fn(p: *mut LCDVideoPlayer,
441 outWidth: *mut c_int,
442 outHeight: *mut c_int,
443 outFrameRate: *mut c_float,
444 outFrameCount: *mut c_int,
445 outCurrentFrame: *mut c_int) {
446 *sys::api!(graphics.video.getInfo)
447 }
448
449 #[doc(alias = "sys::ffi::playdate_video::getContext")]
451 #[inline(always)]
452 fn get_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *mut LCDBitmap {
453 *sys::api!(graphics.video.getContext)
454 }
455 }
456}