1use crate::client::*;
2use crate::gfx;
3use crate::os;
4use crate::reloader;
5
6use std::process::ExitStatus;
7use std::process::Command;
8use std::io::{self, Write};
9
10pub struct PluginReloadResponder {
12 pub name: String,
14 pub path: String,
16 pub output_filepath: String,
18 pub files: Vec<String>
20}
21
22pub trait Plugin<D: gfx::Device, A: os::App> {
24 fn create() -> Self where Self: Sized;
26 fn setup(&mut self, client: Client<D, A>) -> Client<D, A>;
28 fn update(&mut self, client: Client<D, A>) -> Client<D, A>;
30 fn ui(&mut self, client: Client<D, A>) -> Client<D, A>;
32 fn unload(&mut self, client: Client<D, A>) -> Client<D, A>;
34}
35
36pub fn build_all() {
38 let path = super::get_data_path("..");
39 let output = if super::get_config_name() == "release" {
40 Command::new("cargo")
41 .current_dir(path)
42 .arg("build")
43 .arg("--release")
44 .output()
45 .expect("hotline::hot_lib:: hot lib failed to build!")
46 }
47 else {
48 Command::new("cargo")
49 .current_dir(path)
50 .arg("build")
51 .output()
52 .expect("hotline::hot_lib:: hot lib failed to build!")
53 };
54
55 if !output.stdout.is_empty() {
56 println!("{}", String::from_utf8(output.stdout).unwrap());
57 }
58
59 if !output.stderr.is_empty() {
60 println!("{}", String::from_utf8(output.stderr).unwrap());
61 }
62}
63
64impl reloader::ReloadResponder for PluginReloadResponder {
66 fn add_file(&mut self, path: &str) {
67 self.files.push(path.to_string());
68 }
69
70 fn get_files(&self) -> Vec<String> {
71 let src_path = self.path.to_string() + "/" + &self.name.to_string() + "/src";
74 let src_files = super::get_files_recursive(&src_path, Vec::new());
75 let mut result = self.files.to_vec();
76 result.extend(src_files);
77 result
78 }
79
80 fn get_last_mtime(&self) -> std::time::SystemTime {
81 let meta = std::fs::metadata(&self.output_filepath);
82 if meta.is_ok() {
83 std::fs::metadata(&self.output_filepath).unwrap().modified().unwrap()
84 }
85 else {
86 std::time::SystemTime::now()
87 }
88 }
89
90 fn build(&mut self) -> ExitStatus {
91 let output = if super::get_config_name() == "release" {
92 Command::new("cargo")
93 .current_dir(&self.path)
94 .arg("build")
95 .arg("--release")
96 .arg("-p")
97 .arg(&self.name)
98 .output()
99 .expect("hotline::hot_lib:: hot lib failed to build!")
100 }
101 else {
102 Command::new("cargo")
103 .current_dir(&self.path)
104 .arg("build")
105 .arg("-p")
106 .arg(&self.name)
107 .output()
108 .expect("hotline::hot_lib:: hot lib failed to build!")
109 };
110
111 let mut stdout = io::stdout().lock();
112
113 if !output.stdout.is_empty() {
114 stdout.write_all(&output.stdout).unwrap();
115 }
116
117 if !output.stderr.is_empty() {
118 stdout.write_all(&output.stderr).unwrap();
119 }
120
121 output.status
122 }
123}
124
125#[macro_export]
136macro_rules! hotline_plugin {
137 ($input:ident) => {
138 #[no_mangle]
141 pub fn create() -> *mut core::ffi::c_void {
142 let plugin = $input::create();
143 let ptr = Box::into_raw(Box::new(plugin));
144 ptr.cast()
145 }
146
147 #[no_mangle]
149 pub fn update(mut client: client::Client<gfx_platform::Device, os_platform::App>, ptr: *mut core::ffi::c_void) -> client::Client<gfx_platform::Device, os_platform::App> {
150 unsafe {
151 let plugin = ptr.cast::<$input>();
152 let plugin = plugin.as_mut().unwrap();
153 plugin.update(client)
154 }
155 }
156
157 #[no_mangle]
159 pub fn setup(mut client: client::Client<gfx_platform::Device, os_platform::App>, ptr: *mut core::ffi::c_void) -> client::Client<gfx_platform::Device, os_platform::App> {
160 unsafe {
161 let plugin = ptr.cast::<$input>();
162 let plugin = plugin.as_mut().unwrap();
163 plugin.setup(client)
164 }
165 }
166
167 #[no_mangle]
169 pub fn unload(mut client: client::Client<gfx_platform::Device, os_platform::App>, ptr: *mut core::ffi::c_void) -> client::Client<gfx_platform::Device, os_platform::App> {
170 unsafe {
171 let plugin = ptr.cast::<$input>();
172 let plugin = plugin.as_mut().unwrap();
173 plugin.unload(client)
174 }
175 }
176
177 #[no_mangle]
179 pub fn ui(mut client: client::Client<gfx_platform::Device, os_platform::App>, ptr: *mut core::ffi::c_void, imgui_ctx: *mut core::ffi::c_void) -> client::Client<gfx_platform::Device, os_platform::App> {
180 unsafe {
181 let plugin = ptr.cast::<$input>();
182 let plugin = plugin.as_mut().unwrap();
183 client.imgui.set_current_context(imgui_ctx);
184 plugin.ui(client)
185 }
186 }
187 }
188}
189
190pub type PluginInstance = *mut core::ffi::c_void;