From 3c63cca37774651b168b629981a229db2b6fb0bc Mon Sep 17 00:00:00 2001 From: Patrick Reisert Date: Sat, 18 May 2019 02:10:14 +0200 Subject: [PATCH 1/5] Working prototype --- Cargo.toml | 1 + examples/async.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/rt/async_util.rs | 51 +++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 examples/async.rs diff --git a/Cargo.toml b/Cargo.toml index 108e497..6bf33b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" [dependencies] winapi = { version = "0.3", features = ["winnt", "combaseapi", "oleauto", "roapi", "roerrorapi", "hstring", "winstring", "winerror", "restrictederrorinfo"] } +futures-preview = "0.3.0-alpha.16" [features] nightly = [] diff --git a/examples/async.rs b/examples/async.rs new file mode 100644 index 0000000..766d2e7 --- /dev/null +++ b/examples/async.rs @@ -0,0 +1,66 @@ +#![feature(async_await)] + +extern crate winapi; +extern crate winrt; +extern crate futures; + +use futures::executor::block_on; + +use winrt::*; +use winrt::windows::foundation::*; +use winrt::windows::devices::enumeration::*; +use winrt::windows::devices::midi::*; + +fn main() { + let rt = RuntimeContext::init(); + block_on(run()); + rt.uninit(); +} + +async fn run() { + let device_selector = MidiOutPort::get_device_selector().unwrap(); + println!("{}", device_selector); + + let async_op = DeviceInformation::find_all_async().unwrap(); + + println!("CLS: {}", async_op.get_runtime_class_name()); + + let asi = async_op.query_interface::().unwrap(); + println!("IAsyncInfo: {:p}, Iasync_operation: {:p}", asi, async_op); + + let unknown = async_op.query_interface::().unwrap(); + println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); + + let unknown = asi.query_interface::().unwrap(); + println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); + + let id = asi.get_id().unwrap(); + println!("id: {:?}", id); + let status = asi.get_status().unwrap(); + println!("status: {:?}", status); + + let device_information_collection = (&*async_op).await.unwrap().unwrap(); + println!("CLS: {}", device_information_collection.get_runtime_class_name()); + let count = device_information_collection.get_size().unwrap(); + println!("Device Count: {}", count); + + let mut remember = None; + let mut i = 0; + for current in device_information_collection.into_iter() { + let current = current.expect("current was null"); + let device_name = current.get_name().unwrap(); + println!("Device Name ({}): {}", i, device_name); + if i == 100 { + // remember the 100th value and try to find it later using IndexOf + remember = Some(current); + } + i += 1; + } + assert_eq!(i, count); + + if let Some(mut r) = remember { + let (index, found) = device_information_collection.index_of(&mut r).unwrap(); + println!("Found remembered value: {} (index: {})", found, index); + assert_eq!(index, 100); + } +} diff --git a/src/lib.rs b/src/lib.rs index 0d5438c..9319826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ #![allow(dead_code,non_upper_case_globals,non_snake_case)] extern crate winapi as w; +extern crate futures; mod guid; pub use guid::Guid; diff --git a/src/rt/async_util.rs b/src/rt/async_util.rs index 9e0bca7..145475d 100644 --- a/src/rt/async_util.rs +++ b/src/rt/async_util.rs @@ -1,3 +1,6 @@ +use futures::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::sync::{Arc, Mutex, Condvar}; use crate::{ @@ -92,6 +95,34 @@ impl RtAsyncAction for IAsyncOperation impl_blocking_wait!{ AsyncOperationCompletedHandler } } +impl<'a, T: RtType + 'static> Future for &'a IAsyncOperation + where AsyncOperationCompletedHandler: ComIid +{ + type Output = Result<::Out>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let info = crate::comptr::query_interface::<_, IAsyncInfo>(*self).expect("query_interface failed"); + let status = info.get_status().expect("get_status failed"); + match status { + crate::langcompat::ASYNC_STATUS_COMPLETED => { + Poll::Ready(self.get_results()) + }, + crate::langcompat::ASYNC_STATUS_STARTED => { + let waker = cx.waker().clone(); + + let handler = AsyncOperationCompletedHandler::new(move |_op, _status| { + waker.wake_by_ref(); + Ok(()) + }); + + self.set_completed(&handler).expect("set_completed failed"); + Poll::Pending + } + _ => unimplemented!() + } + } +} + impl RtAsyncOperation for IAsyncOperation where AsyncOperationCompletedHandler: ComIid { @@ -118,4 +149,22 @@ impl RtAsyncOperation for IAsyncOperat fn get_results(&self) -> Result { self.get_results() } -} \ No newline at end of file +} + +// Make await syntax work with `ComPtr` directly +/*impl<'a, T: RtType + 'static> Future for &'a crate::ComPtr + where &'a T: Future, + //&'a crate::rt::gen::windows::foundation::IAsyncOperation : futures::Future, + //&'a crate::ComPtr: Unpin +{ + type Output = <&'a T as Future>::Output; + #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + use std::ops::DerefMut; + //unsafe { self.map_unchecked(|ptr| ptr.deref().deref()) }.poll(self, cx) + let s: Pin<&mut &_>/*: Pin<&mut &IAsyncOperation<_>>*/ = unsafe { self.map_unchecked_mut(|mut ptr| &mut&**ptr) }; + //let s: Pin<&mut &'a IAsyncOperation> = Pin::new(&mut **self.get_mut().deref_mut().deref_mut()); + //let p: crate::ComPtr + s.poll(cx) + //unimplemented!() + } +}*/ From ec5e634036a9da36ff8331cca5313c635ecf9192 Mon Sep 17 00:00:00 2001 From: Patrick Reisert Date: Sat, 18 May 2019 03:59:20 +0200 Subject: [PATCH 2/5] Add interval timer --- Cargo.toml | 3 +- examples/async.rs | 73 ++++++++++++++++++++++++++------------------ src/rt/async_util.rs | 9 ++++-- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bf33b1..5d6e563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ edition = "2018" [dependencies] winapi = { version = "0.3", features = ["winnt", "combaseapi", "oleauto", "roapi", "roerrorapi", "hstring", "winstring", "winerror", "restrictederrorinfo"] } -futures-preview = "0.3.0-alpha.16" +futures-preview = { version = "0.3.0-alpha.16", features = ["async-await", "nightly"] } +futures-native-timers = { path = "../futures-native-timers" } [features] nightly = [] diff --git a/examples/async.rs b/examples/async.rs index 766d2e7..8af6020 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -4,7 +4,13 @@ extern crate winapi; extern crate winrt; extern crate futures; +use std::time::Duration; + use futures::executor::block_on; +use futures::prelude::*; +use futures::select; + +use futures_native_timers::Interval; use winrt::*; use winrt::windows::foundation::*; @@ -18,49 +24,58 @@ fn main() { } async fn run() { + let device_selector = MidiOutPort::get_device_selector().unwrap(); println!("{}", device_selector); - let async_op = DeviceInformation::find_all_async().unwrap(); + // let async_op = DeviceInformation::find_all_async().unwrap(); - println!("CLS: {}", async_op.get_runtime_class_name()); + // println!("CLS: {}", async_op.get_runtime_class_name()); - let asi = async_op.query_interface::().unwrap(); - println!("IAsyncInfo: {:p}, Iasync_operation: {:p}", asi, async_op); + // let asi = async_op.query_interface::().unwrap(); + // println!("IAsyncInfo: {:p}, Iasync_operation: {:p}", asi, async_op); - let unknown = async_op.query_interface::().unwrap(); - println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); + // let unknown = async_op.query_interface::().unwrap(); + // println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); - let unknown = asi.query_interface::().unwrap(); - println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); + // let unknown = asi.query_interface::().unwrap(); + // println!("IAsyncInfo: {:p}, Iasync_operation: {:p}, IUnknown: {:p}", asi, async_op, unknown); - let id = asi.get_id().unwrap(); - println!("id: {:?}", id); - let status = asi.get_status().unwrap(); - println!("status: {:?}", status); + // let id = asi.get_id().unwrap(); + // println!("id: {:?}", id); + // let status = asi.get_status().unwrap(); + // println!("status: {:?}", status); + + let mut interval = Interval::new(Duration::from_millis(100)); + let mut async_op = DeviceInformation::find_all_async().unwrap().fuse(); // TODO: get rid of fuse() + + let work = async { + let mut result = None; + loop { + select! { + _ = interval.next() => { + use std::io::Write; + print!("."); + std::io::stdout().flush().unwrap(); + }, + res = async_op => { + result = Some(res); + println!(""); + break; + } + }; + } + result.unwrap() + }; - let device_information_collection = (&*async_op).await.unwrap().unwrap(); + let device_information_collection = work.await.unwrap().unwrap(); println!("CLS: {}", device_information_collection.get_runtime_class_name()); let count = device_information_collection.get_size().unwrap(); println!("Device Count: {}", count); - - let mut remember = None; - let mut i = 0; - for current in device_information_collection.into_iter() { + + for (i, current) in device_information_collection.into_iter().enumerate().take(10) { let current = current.expect("current was null"); let device_name = current.get_name().unwrap(); println!("Device Name ({}): {}", i, device_name); - if i == 100 { - // remember the 100th value and try to find it later using IndexOf - remember = Some(current); - } - i += 1; - } - assert_eq!(i, count); - - if let Some(mut r) = remember { - let (index, found) = device_information_collection.index_of(&mut r).unwrap(); - println!("Found remembered value: {} (index: {})", found, index); - assert_eq!(index, 100); } } diff --git a/src/rt/async_util.rs b/src/rt/async_util.rs index 145475d..a4f25e0 100644 --- a/src/rt/async_util.rs +++ b/src/rt/async_util.rs @@ -95,19 +95,24 @@ impl RtAsyncAction for IAsyncOperation impl_blocking_wait!{ AsyncOperationCompletedHandler } } -impl<'a, T: RtType + 'static> Future for &'a IAsyncOperation +impl<'a, T: RtType + 'static> Future for crate::ComPtr> where AsyncOperationCompletedHandler: ComIid { type Output = Result<::Out>; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let info = crate::comptr::query_interface::<_, IAsyncInfo>(*self).expect("query_interface failed"); + use std::ops::Deref; + + let info = crate::comptr::query_interface::<_, IAsyncInfo>(self.deref().deref()).expect("query_interface failed"); let status = info.get_status().expect("get_status failed"); match status { crate::langcompat::ASYNC_STATUS_COMPLETED => { Poll::Ready(self.get_results()) }, crate::langcompat::ASYNC_STATUS_STARTED => { + if self.get_completed().expect("get_completed failed").is_some() { + return Poll::Pending; + } let waker = cx.waker().clone(); let handler = AsyncOperationCompletedHandler::new(move |_op, _status| { From 0f72497a19fb158e5fe61541d0376e6eea0e8c3a Mon Sep 17 00:00:00 2001 From: Patrick Reisert Date: Sat, 18 May 2019 14:58:26 +0200 Subject: [PATCH 3/5] Cleanup --- examples/async.rs | 2 +- src/rt/async_util.rs | 116 ++++++++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/examples/async.rs b/examples/async.rs index 8af6020..5896ae1 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -47,7 +47,7 @@ async fn run() { // println!("status: {:?}", status); let mut interval = Interval::new(Duration::from_millis(100)); - let mut async_op = DeviceInformation::find_all_async().unwrap().fuse(); // TODO: get rid of fuse() + let mut async_op = DeviceInformation::find_all_async().unwrap().fuse(); let work = async { let mut result = None; diff --git a/src/rt/async_util.rs b/src/rt/async_util.rs index a4f25e0..ecd8dd5 100644 --- a/src/rt/async_util.rs +++ b/src/rt/async_util.rs @@ -1,5 +1,6 @@ use futures::future::Future; use std::pin::Pin; +use std::ops::Deref; use std::task::{Context, Poll}; use std::sync::{Arc, Mutex, Condvar}; @@ -78,8 +79,7 @@ macro_rules! impl_blocking_wait { } } -impl RtAsyncAction for IAsyncAction -{ +impl RtAsyncAction for IAsyncAction { impl_blocking_wait!{ AsyncActionCompletedHandler } } @@ -95,39 +95,6 @@ impl RtAsyncAction for IAsyncOperation impl_blocking_wait!{ AsyncOperationCompletedHandler } } -impl<'a, T: RtType + 'static> Future for crate::ComPtr> - where AsyncOperationCompletedHandler: ComIid -{ - type Output = Result<::Out>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - use std::ops::Deref; - - let info = crate::comptr::query_interface::<_, IAsyncInfo>(self.deref().deref()).expect("query_interface failed"); - let status = info.get_status().expect("get_status failed"); - match status { - crate::langcompat::ASYNC_STATUS_COMPLETED => { - Poll::Ready(self.get_results()) - }, - crate::langcompat::ASYNC_STATUS_STARTED => { - if self.get_completed().expect("get_completed failed").is_some() { - return Poll::Pending; - } - let waker = cx.waker().clone(); - - let handler = AsyncOperationCompletedHandler::new(move |_op, _status| { - waker.wake_by_ref(); - Ok(()) - }); - - self.set_completed(&handler).expect("set_completed failed"); - Poll::Pending - } - _ => unimplemented!() - } - } -} - impl RtAsyncOperation for IAsyncOperation where AsyncOperationCompletedHandler: ComIid { @@ -156,20 +123,67 @@ impl RtAsyncOperation for IAsyncOperat } } -// Make await syntax work with `ComPtr` directly -/*impl<'a, T: RtType + 'static> Future for &'a crate::ComPtr - where &'a T: Future, - //&'a crate::rt::gen::windows::foundation::IAsyncOperation : futures::Future, - //&'a crate::ComPtr: Unpin -{ - type Output = <&'a T as Future>::Output; - #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - use std::ops::DerefMut; - //unsafe { self.map_unchecked(|ptr| ptr.deref().deref()) }.poll(self, cx) - let s: Pin<&mut &_>/*: Pin<&mut &IAsyncOperation<_>>*/ = unsafe { self.map_unchecked_mut(|mut ptr| &mut&**ptr) }; - //let s: Pin<&mut &'a IAsyncOperation> = Pin::new(&mut **self.get_mut().deref_mut().deref_mut()); - //let p: crate::ComPtr - s.poll(cx) - //unimplemented!() + +macro_rules! impl_poll { + ($handler:ident => $retexpr:tt) => { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let info = crate::comptr::query_interface::<_, IAsyncInfo>(self.deref().deref()).expect("query_interface failed"); + let status = info.get_status().expect("get_status failed"); + match status { + crate::windows::foundation::AsyncStatus::Completed => { + Poll::Ready($retexpr(self)) + }, + crate::windows::foundation::AsyncStatus::Started => { + // Calling poll multiple times must work correctly, so we have to check that we didn't already install the Completed handler + if self.get_completed().expect("get_completed failed").is_some() { + // TODO: We might have to check that the installed handler is actually + // the one with the waker (because the user could have installed one independently). + // Or we document that the user is not allowed to do that. + return Poll::Pending; + } + let waker = cx.waker().clone(); + + let handler = $handler::new(move |_op, _status| { + waker.wake_by_ref(); + Ok(()) + }); + + self.set_completed(&handler).expect("set_completed failed"); + Poll::Pending + } + _ => unimplemented!() // FIXME + } + } } -}*/ +} + + +impl Future for crate::ComPtr { + type Output = (); + + impl_poll!{ AsyncActionCompletedHandler => { |_: Pin<&mut Self>| () } } +} + +impl Future for crate::ComPtr> + where AsyncActionWithProgressCompletedHandler

: ComIid +{ + type Output = (); + + impl_poll!{ AsyncActionWithProgressCompletedHandler => { |_: Pin<&mut Self>| () } } +} + +impl Future for crate::ComPtr> + where AsyncOperationCompletedHandler: ComIid +{ + type Output = Result<::Out>; + + impl_poll!{ AsyncOperationCompletedHandler => { |s: Pin<&mut Self>| s.get_results() } } +} + +impl Future for crate::ComPtr> + where AsyncOperationWithProgressCompletedHandler: ComIid +{ + type Output = Result<::Out>; + + impl_poll!{ AsyncOperationWithProgressCompletedHandler => { |s: Pin<&mut Self>| s.get_results() } } +} From ec54dbafee9fadef19f99963161d7ba7e9b15759 Mon Sep 17 00:00:00 2001 From: Patrick Reisert Date: Sat, 18 May 2019 15:30:33 +0200 Subject: [PATCH 4/5] Expand example (still contains hardcoded paths!) --- examples/async.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/examples/async.rs b/examples/async.rs index 5896ae1..88e7557 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -16,6 +16,8 @@ use winrt::*; use winrt::windows::foundation::*; use winrt::windows::devices::enumeration::*; use winrt::windows::devices::midi::*; +use winrt::windows::media::*; +use winrt::windows::storage::StorageFile; fn main() { let rt = RuntimeContext::init(); @@ -78,4 +80,48 @@ async fn run() { let device_name = current.get_name().unwrap(); println!("Device Name ({}): {}", i, device_name); } + + print!("Transcoding media: "); + let source = StorageFile::get_file_from_path_async(&*FastHString::new("D:\\Desktop\\test-transcode\\test.mp3")).unwrap().await.expect("get_file_from_path_async failed").unwrap(); + let dest = StorageFile::get_file_from_path_async(&*FastHString::new("D:\\Desktop\\test-transcode\\test.flac")).unwrap().await.expect("get_file_from_path_async failed").unwrap(); + let profile = mediaproperties::MediaEncodingProfile::create_flac(mediaproperties::AudioEncodingQuality::Medium).unwrap().unwrap(); + let transcoder = transcoding::MediaTranscoder::new(); + let prepared = transcoder.prepare_file_transcode_async(&source, &dest, &profile).unwrap().await.unwrap().unwrap(); + assert!(prepared.get_can_transcode().unwrap()); + + let mut interval = Interval::new(Duration::from_millis(100)); + let async_op = prepared.transcode_async().unwrap(); + // TODO: there should be a way to await the created AsyncActionProgressHandler (and any other delegate) as an async stream + async_op.set_progress(&AsyncActionProgressHandler::new(|_info, progress| { + print_progress(progress); + Ok(()) + })).unwrap(); + let mut async_op = async_op.fuse(); + + let work = async { + let mut result = None; + loop { + select! { + _ = interval.next() => { + use std::io::Write; + print!("."); + std::io::stdout().flush().unwrap(); + }, + res = async_op => { + result = Some(res); + println!(""); + break; + } + }; + } + result.unwrap() + }; + + work.await +} + +fn print_progress(progress: f64) { + use std::io::Write; + print!("{:.0}%", progress); + std::io::stdout().flush().unwrap(); } From 8edfe32f7b181ebfaf3c6b474e575f677579243a Mon Sep 17 00:00:00 2001 From: Patrick Reisert Date: Mon, 10 Jun 2019 13:38:31 +0200 Subject: [PATCH 5/5] Updates --- examples/async.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/async.rs b/examples/async.rs index 88e7557..6a674f4 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -20,9 +20,7 @@ use winrt::windows::media::*; use winrt::windows::storage::StorageFile; fn main() { - let rt = RuntimeContext::init(); block_on(run()); - rt.uninit(); } async fn run() { @@ -52,7 +50,7 @@ async fn run() { let mut async_op = DeviceInformation::find_all_async().unwrap().fuse(); let work = async { - let mut result = None; + let result; loop { select! { _ = interval.next() => { @@ -61,13 +59,13 @@ async fn run() { std::io::stdout().flush().unwrap(); }, res = async_op => { - result = Some(res); + result = res; println!(""); break; } }; } - result.unwrap() + result }; let device_information_collection = work.await.unwrap().unwrap(); @@ -99,7 +97,7 @@ async fn run() { let mut async_op = async_op.fuse(); let work = async { - let mut result = None; + let result; loop { select! { _ = interval.next() => { @@ -108,13 +106,13 @@ async fn run() { std::io::stdout().flush().unwrap(); }, res = async_op => { - result = Some(res); + result = res; println!(""); break; } }; } - result.unwrap() + result }; work.await