mirror of
https://github.com/Stremio/stremio-shell-ng.git
synced 2026-01-11 22:40:32 +00:00
Progress on the IPC
This commit is contained in:
parent
56d255f44a
commit
d3d05a43fb
4 changed files with 211 additions and 110 deletions
|
|
@ -5,7 +5,7 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
once_cell = "1.3.1"
|
||||
native-windows-gui = { version = "1.0.4", features = ["high-dpi"] }
|
||||
native-windows-gui = { version = "1.0.4", features = ["high-dpi", "notice"] }
|
||||
native-windows-derive = "1.0.3"
|
||||
winapi = { version = "0.3.9", features = [
|
||||
"libloaderapi", "handleapi",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,43 @@
|
|||
use native_windows_derive::NwgUi;
|
||||
use native_windows_gui as nwg;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crate::stremio_app::stremio_player::Player;
|
||||
use crate::stremio_app::stremio_wevbiew::WebView;
|
||||
|
||||
//////////////////////////////////////////
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCRequest {
|
||||
id: u64,
|
||||
args: Option<Vec<serde_json::Value>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponseDataTransport {
|
||||
properties: Vec<Vec<String>>,
|
||||
signals: Vec<String>,
|
||||
methods: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponseData {
|
||||
transport: RPCResponseDataTransport,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponse {
|
||||
id: u64,
|
||||
object: String,
|
||||
#[serde(rename = "type")]
|
||||
response_type: u32,
|
||||
data: RPCResponseData,
|
||||
}
|
||||
//////////////////////////////////////////
|
||||
|
||||
#[derive(Default, NwgUi)]
|
||||
pub struct MainWindow {
|
||||
#[nwg_control(title: "Stremio", flags: "MAIN_WINDOW|VISIBLE")]
|
||||
|
|
@ -26,13 +59,74 @@ impl MainWindow {
|
|||
self.window
|
||||
.set_size(dimensions.0 as u32, dimensions.1 as u32);
|
||||
self.window.set_position(x, y);
|
||||
// let video_path = "/home/ivo/storage/bbb_sunflower_1080p_30fps_normal.mp4";
|
||||
let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4";
|
||||
self.player.command(&["loadfile", video_path]);
|
||||
// self.player.set_prop("time-pos", 120.0);
|
||||
self.player.set_prop("speed", 2.0);
|
||||
// self.player.set_prop("pause", true);
|
||||
self.player.command(&["stop"]);
|
||||
|
||||
let player_channel = self.player.channel.borrow();
|
||||
let (player_tx, player_rx) = player_channel.as_ref().unwrap();
|
||||
let player_tx = player_tx.clone();
|
||||
let player_rx = Arc::clone(player_rx);
|
||||
|
||||
let web_channel = self.webview.channel.borrow();
|
||||
let (web_tx, web_rx) = web_channel.as_ref().unwrap();
|
||||
let web_tx = web_tx.clone();
|
||||
let web_rx = Arc::clone(web_rx);
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
// Read message from player
|
||||
{
|
||||
let rx = player_rx.lock().unwrap();
|
||||
if let Ok(msg) = rx.try_recv() {
|
||||
println!("APP GOT FROM PLAYER {}", msg);
|
||||
// web_tx.send(msg).ok();
|
||||
}
|
||||
};
|
||||
|
||||
// read message from WebView
|
||||
{
|
||||
let rx = web_rx.lock().unwrap();
|
||||
if let Ok(msg) = rx.try_recv() {
|
||||
let msg: RPCRequest = serde_json::from_str(&msg).unwrap();
|
||||
if msg.id == 0 {
|
||||
let resp: RPCResponse = RPCResponse {
|
||||
id: 0,
|
||||
object: "transport".to_string(),
|
||||
response_type: 3,
|
||||
data: RPCResponseData {
|
||||
transport: RPCResponseDataTransport {
|
||||
properties: vec![
|
||||
vec![],
|
||||
vec![
|
||||
"".to_string(),
|
||||
"shellVersion".to_string(),
|
||||
"".to_string(),
|
||||
"5.0.0".to_string(),
|
||||
],
|
||||
],
|
||||
signals: vec![],
|
||||
methods: vec![vec!["onEvent".to_string(), "".to_string()]],
|
||||
},
|
||||
},
|
||||
};
|
||||
let resp_json = serde_json::to_string(&resp).unwrap();
|
||||
web_tx.send(resp_json).ok();
|
||||
} else if let Some(args) = msg.args {
|
||||
// TODO: this can panic
|
||||
let method = serde_json::from_value::<String>(args[0].clone()).unwrap();
|
||||
if method.starts_with("mpv-") {
|
||||
let resp_json = serde_json::to_string(&args).unwrap();
|
||||
player_tx.send(resp_json).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
// // let video_path = "/home/ivo/storage/bbb_sunflower_1080p_30fps_normal.mp4";
|
||||
// let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4";
|
||||
// self.player.command(&["loadfile", video_path]);
|
||||
// // self.player.set_prop("time-pos", 120.0);
|
||||
// self.player.set_prop("speed", 2.0);
|
||||
// // self.player.set_prop("pause", true);
|
||||
// self.player.command(&["stop"]);
|
||||
}
|
||||
fn on_quit(&self) {
|
||||
nwg::stop_thread_dispatch();
|
||||
|
|
|
|||
|
|
@ -1,26 +1,12 @@
|
|||
use native_windows_gui::{self as nwg, PartialUi};
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Player {
|
||||
mpv: RefCell<Option<mpv::MpvHandler>>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn command(&self, args: &[&str]) {
|
||||
let mut mpv = self.mpv.borrow_mut();
|
||||
let mpv = mpv.as_mut().expect("Failed to create MPV");
|
||||
if let Err(e) = mpv.command(args) {
|
||||
eprintln!("Failed to execute command {:?} - {:?}", args, e);
|
||||
}
|
||||
}
|
||||
pub fn set_prop<T: mpv::MpvFormat>(&self, prop: &str, val: T) {
|
||||
let mut mpv = self.mpv.borrow_mut();
|
||||
let mpv = mpv.as_mut().expect("Failed to create MPV");
|
||||
if let Err(e) = mpv.set_property(prop, val) {
|
||||
eprintln!("Failed to set property {} - {:?}", prop, e);
|
||||
}
|
||||
}
|
||||
pub channel: RefCell<Option<(mpsc::Sender<String>, Arc<Mutex<mpsc::Receiver<String>>>)>>,
|
||||
}
|
||||
|
||||
impl PartialUi for Player {
|
||||
|
|
@ -28,39 +14,66 @@ impl PartialUi for Player {
|
|||
data: &mut Self,
|
||||
parent: Option<W>,
|
||||
) -> Result<(), nwg::NwgError> {
|
||||
let mut mpv_builder =
|
||||
mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder");
|
||||
mpv_builder
|
||||
.set_option(
|
||||
"wid",
|
||||
parent
|
||||
.expect("No parent window")
|
||||
.into()
|
||||
.hwnd()
|
||||
.expect("Cannot obtain window handle") as i64,
|
||||
)
|
||||
.expect("failed setting wid");
|
||||
// mpv_builder.set_option("vo", "gpu").expect("unable to set vo");
|
||||
// win, opengl: works but least performancy, 10-15% CPU
|
||||
// winvk, vulkan: works as good as d3d11
|
||||
// d3d11, d1d11: works great
|
||||
// dxinterop, auto: works, slightly more cpu use than d3d11
|
||||
// default (auto) seems to be d3d11 (vo/gpu/d3d11)
|
||||
mpv_builder
|
||||
.set_option("gpu-context", "angle")
|
||||
.and_then(|_| mpv_builder.set_option("gpu-api", "auto"))
|
||||
.expect("setting gpu options failed");
|
||||
mpv_builder
|
||||
.try_hardware_decoding()
|
||||
.expect("failed setting hwdec");
|
||||
mpv_builder
|
||||
.set_option("terminal", "yes")
|
||||
.expect("failed setting terminal");
|
||||
mpv_builder
|
||||
.set_option("msg-level", "all=v")
|
||||
.expect("failed setting msg-level");
|
||||
//mpv_builder.set_option("quiet", "yes").expect("failed setting msg-level");
|
||||
data.mpv = RefCell::new(mpv_builder.build().ok());
|
||||
let (tx, rx) = mpsc::channel::<String>();
|
||||
let (tx1, rx1) = mpsc::channel::<String>();
|
||||
data.channel = RefCell::new(Some((tx, Arc::new(Mutex::new(rx1)))));
|
||||
let hwnd = parent
|
||||
.expect("No parent window")
|
||||
.into()
|
||||
.hwnd()
|
||||
.expect("Cannot obtain window handle") as i64;
|
||||
thread::spawn(move || {
|
||||
let mut mpv_builder =
|
||||
mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder");
|
||||
mpv_builder
|
||||
.set_option("wid", hwnd)
|
||||
.expect("failed setting wid");
|
||||
// mpv_builder.set_option("vo", "gpu").expect("unable to set vo");
|
||||
// win, opengl: works but least performancy, 10-15% CPU
|
||||
// winvk, vulkan: works as good as d3d11
|
||||
// d3d11, d1d11: works great
|
||||
// dxinterop, auto: works, slightly more cpu use than d3d11
|
||||
// default (auto) seems to be d3d11 (vo/gpu/d3d11)
|
||||
mpv_builder
|
||||
.set_option("gpu-context", "angle")
|
||||
.and_then(|_| mpv_builder.set_option("gpu-api", "auto"))
|
||||
.expect("setting gpu options failed");
|
||||
mpv_builder
|
||||
.try_hardware_decoding()
|
||||
.expect("failed setting hwdec");
|
||||
mpv_builder
|
||||
.set_option("terminal", "yes")
|
||||
.expect("failed setting terminal");
|
||||
mpv_builder
|
||||
.set_option("msg-level", "all=v")
|
||||
.expect("failed setting msg-level");
|
||||
//mpv_builder.set_option("quiet", "yes").expect("failed setting msg-level");
|
||||
let mut mpv = mpv_builder.build().unwrap();
|
||||
'main: loop {
|
||||
// wait up to 0.0 seconds for an event.
|
||||
while let Some(event) = mpv.wait_event(0.0) {
|
||||
// even if you don't do anything with the events, it is still necessary to empty
|
||||
// the event loop
|
||||
// TODO: Parse and format the Event in proper JSON format
|
||||
tx1.send(format!("{:?}", event)).ok();
|
||||
println!("RECEIVED EVENT : {:?}", event);
|
||||
match event {
|
||||
mpv::Event::Shutdown | mpv::Event::EndFile(_) => {
|
||||
break 'main;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
if let Ok(msg) = rx.try_recv() {
|
||||
println!("PLAYER RECEIVED MESSAGE: {}", msg);
|
||||
// let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4";
|
||||
// mpv.command(&["loadfile", video_path]).ok();
|
||||
// mpv.command(&["stop"]).ok();
|
||||
// mpv.set_property("paused", true).ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,22 @@
|
|||
use native_windows_gui::{self as nwg, PartialUi};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use webview2::Controller;
|
||||
use winapi::shared::windef::HWND__;
|
||||
use winapi::um::winuser::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCRequest {
|
||||
id: u64,
|
||||
args: Option<Vec<serde_json::Value>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponseDataTransport {
|
||||
properties: Vec<Vec<String>>,
|
||||
signals: Vec<String>,
|
||||
methods: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponseData {
|
||||
transport: RPCResponseDataTransport,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct RPCResponse {
|
||||
id: u64,
|
||||
object: String,
|
||||
#[serde(rename = "type")]
|
||||
response_type: u32,
|
||||
data: RPCResponseData,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WebView {
|
||||
controller: Rc<OnceCell<Controller>>,
|
||||
pub channel: RefCell<Option<(mpsc::Sender<String>, Arc<Mutex<mpsc::Receiver<String>>>)>>,
|
||||
notice: nwg::Notice,
|
||||
compute: RefCell<Option<thread::JoinHandle<()>>>,
|
||||
message: Arc<Mutex<Option<String>>>,
|
||||
}
|
||||
|
||||
impl WebView {
|
||||
|
|
@ -61,9 +40,19 @@ impl PartialUi for WebView {
|
|||
data: &mut Self,
|
||||
parent: Option<W>,
|
||||
) -> Result<(), nwg::NwgError> {
|
||||
let (tx, rx) = mpsc::channel::<String>();
|
||||
let (tx1, rx1) = mpsc::channel::<String>();
|
||||
data.channel = RefCell::new(Some((tx, Arc::new(Mutex::new(rx1)))));
|
||||
|
||||
let parent = parent.expect("No parent window").into();
|
||||
let hwnd = parent.hwnd().expect("Cannot obtain window handle");
|
||||
|
||||
let hwnd = parent.hwnd().expect("Cannot obtain window handle") as i64;
|
||||
nwg::Notice::builder()
|
||||
.parent(parent)
|
||||
.build(&mut data.notice)
|
||||
.ok();
|
||||
let controller_clone = data.controller.clone();
|
||||
let hwnd = hwnd as *mut HWND__;
|
||||
let result = webview2::EnvironmentBuilder::new()
|
||||
.with_additional_browser_arguments("--disable-gpu")
|
||||
.build(move |env| {
|
||||
|
|
@ -98,30 +87,12 @@ impl PartialUi for WebView {
|
|||
|_| Ok(()),
|
||||
)
|
||||
.ok();
|
||||
webview.add_web_message_received(|w, msg| {
|
||||
webview.add_web_message_received(move |_w, msg| {
|
||||
let msg = msg.try_get_web_message_as_string()?;
|
||||
let msg: RPCRequest = serde_json::from_str(&msg).unwrap();
|
||||
dbg!(msg.clone());
|
||||
if msg.id == 0 {
|
||||
let resp: RPCResponse = RPCResponse {
|
||||
id: 0,
|
||||
object: "transport".to_string(),
|
||||
response_type: 3,
|
||||
data: RPCResponseData {
|
||||
transport: RPCResponseDataTransport {
|
||||
properties: vec![vec![], vec!["".to_string(), "shellVersion".to_string(), "".to_string(), "5.0.0".to_string()]],
|
||||
signals: vec![],
|
||||
methods: vec![vec!["onEvent".to_string(), "".to_string()]]
|
||||
}
|
||||
}
|
||||
};
|
||||
let resp_json = serde_json::to_string(&resp).unwrap();
|
||||
dbg!(resp_json.clone());
|
||||
w.post_web_message_as_string(&resp_json).ok();
|
||||
}
|
||||
tx1.send(msg).ok();
|
||||
Ok(())
|
||||
}).ok();
|
||||
WebView::resize_to_window_bounds_and_show(Some(&controller), parent.hwnd());
|
||||
WebView::resize_to_window_bounds_and_show(Some(&controller), Some(hwnd));
|
||||
controller_clone
|
||||
.set(controller)
|
||||
.expect("Cannot update the controller");
|
||||
|
|
@ -135,6 +106,17 @@ impl PartialUi for WebView {
|
|||
&format!("{}", e),
|
||||
);
|
||||
}
|
||||
|
||||
let sender = data.notice.sender();
|
||||
let message = data.message.clone();
|
||||
*data.compute.borrow_mut() = Some(thread::spawn(move || loop {
|
||||
if let Ok(msg) = rx.try_recv() {
|
||||
let mut message = message.lock().unwrap();
|
||||
*message = Some(msg);
|
||||
sender.notice();
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn process_event<'a>(
|
||||
|
|
@ -145,6 +127,7 @@ impl PartialUi for WebView {
|
|||
) {
|
||||
use nwg::Event as E;
|
||||
match evt {
|
||||
E::OnInit => {}
|
||||
E::OnResize | E::OnWindowMaximize => {
|
||||
WebView::resize_to_window_bounds_and_show(self.controller.get(), handle.hwnd());
|
||||
}
|
||||
|
|
@ -153,6 +136,17 @@ impl PartialUi for WebView {
|
|||
controller.put_is_visible(false).ok();
|
||||
}
|
||||
}
|
||||
E::OnNotice => {
|
||||
let msg = self.message.clone();
|
||||
let mut msg = msg.lock().unwrap();
|
||||
if let Some(msg) = &*msg {
|
||||
if let Some(controller) = self.controller.get() {
|
||||
let webview = controller.get_webview().expect("Cannot get vebview");
|
||||
webview.post_web_message_as_string(msg).ok();
|
||||
}
|
||||
}
|
||||
*msg = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue